@asm-agent/postgres 0.8.2
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/README.md +21 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +14 -0
- package/dist/config.js.map +1 -0
- package/dist/database.d.ts +58 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +373 -0
- package/dist/database.js.map +1 -0
- package/dist/grounded-memory.d.ts +148 -0
- package/dist/grounded-memory.d.ts.map +1 -0
- package/dist/grounded-memory.js +386 -0
- package/dist/grounded-memory.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/repositories.d.ts +53 -0
- package/dist/repositories.d.ts.map +1 -0
- package/dist/repositories.js +2 -0
- package/dist/repositories.js.map +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/migrations/0001_foundation.sql +143 -0
- package/migrations/0002_grounded_memory.sql +160 -0
- package/migrations/0003_evidence_cleanup.sql +6 -0
- package/migrations/0004_governance.sql +61 -0
- package/migrations/0005_asm_models.sql +64 -0
- package/migrations/0006_knowledge_workflows.sql +82 -0
- package/migrations/0007_asm_cm_runtime.sql +28 -0
- package/migrations/0008_promotion_security.sql +8 -0
- package/package.json +46 -0
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
export class GroundedMemoryStore {
|
|
3
|
+
database;
|
|
4
|
+
constructor(database) {
|
|
5
|
+
this.database = database;
|
|
6
|
+
}
|
|
7
|
+
async createEvidence(input) {
|
|
8
|
+
const canonical = typeof input.content === "string" ? input.content : stableJson(input.content);
|
|
9
|
+
const digest = createHash("sha256").update(canonical).digest("hex");
|
|
10
|
+
const result = await this.database.pool.query(`INSERT INTO asm_agent.evidence_snapshots
|
|
11
|
+
(id, owner_id, project_id, source_type, source_uri, content_type, content_text,
|
|
12
|
+
content_json, digest, captured_at, metadata)
|
|
13
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9,$10,$11::jsonb)
|
|
14
|
+
ON CONFLICT (owner_id, project_id, digest) DO NOTHING RETURNING id`, [
|
|
15
|
+
input.id,
|
|
16
|
+
input.ownerId,
|
|
17
|
+
input.projectId,
|
|
18
|
+
input.sourceType,
|
|
19
|
+
input.sourceUri ?? null,
|
|
20
|
+
input.contentType,
|
|
21
|
+
typeof input.content === "string" ? input.content : null,
|
|
22
|
+
typeof input.content === "string" ? null : stableJson(input.content),
|
|
23
|
+
digest,
|
|
24
|
+
input.capturedAt,
|
|
25
|
+
JSON.stringify(input.metadata ?? {}),
|
|
26
|
+
]);
|
|
27
|
+
if (result.rows[0])
|
|
28
|
+
return { id: result.rows[0].id, digest };
|
|
29
|
+
const existing = await this.database.pool.query("SELECT id FROM asm_agent.evidence_snapshots WHERE owner_id = $1 AND project_id = $2 AND digest = $3", [input.ownerId, input.projectId, digest]);
|
|
30
|
+
const row = existing.rows[0];
|
|
31
|
+
if (!row)
|
|
32
|
+
throw new Error("Evidence conflict could not be resolved");
|
|
33
|
+
return { id: row.id, digest };
|
|
34
|
+
}
|
|
35
|
+
async addEvidenceClaim(input) {
|
|
36
|
+
await this.database.pool.query(`INSERT INTO asm_agent.evidence_claims (id, evidence_id, claim, locator)
|
|
37
|
+
VALUES ($1,$2,$3,$4::jsonb)`, [input.id, input.evidenceId, requiredContent(input.claim), JSON.stringify(input.locator ?? {})]);
|
|
38
|
+
}
|
|
39
|
+
async createMemory(input) {
|
|
40
|
+
const content = requiredContent(input.content);
|
|
41
|
+
await this.database.transaction(async (client) => {
|
|
42
|
+
await assertEvidence(client, input.ownerId, input.projectId, input.evidenceIds);
|
|
43
|
+
await client.query(`INSERT INTO asm_agent.memory_records
|
|
44
|
+
(id, owner_id, project_id, session_id, kind, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`, [input.id, input.ownerId, input.projectId, input.sessionId ?? null, input.kind, input.expiresAt ?? null]);
|
|
45
|
+
await client.query("INSERT INTO asm_agent.memory_revisions (memory_id, revision, content, metadata) VALUES ($1,1,$2,$3::jsonb)", [input.id, content, JSON.stringify(input.metadata ?? {})]);
|
|
46
|
+
await insertEvidenceLinks(client, input.id, 1, input.evidenceIds);
|
|
47
|
+
await insertScopes(client, input.id, input.scopes ?? [{ type: "owner", id: input.ownerId }]);
|
|
48
|
+
await upsertProjections(client, input.id, 1, content, input.embedding);
|
|
49
|
+
});
|
|
50
|
+
return this.loadMemory(input.id, input.ownerId, input.projectId);
|
|
51
|
+
}
|
|
52
|
+
async correctMemory(input) {
|
|
53
|
+
const content = requiredContent(input.content);
|
|
54
|
+
await this.database.transaction(async (client) => {
|
|
55
|
+
await assertEvidence(client, input.ownerId, input.projectId, input.evidenceIds);
|
|
56
|
+
const locked = await client.query(`SELECT current_revision FROM asm_agent.memory_records
|
|
57
|
+
WHERE id = $1 AND owner_id = $2 AND project_id = $3 AND tombstoned_at IS NULL FOR UPDATE`, [input.memoryId, input.ownerId, input.projectId]);
|
|
58
|
+
const row = locked.rows[0];
|
|
59
|
+
if (!row)
|
|
60
|
+
throw new Error("Memory not found or revoked");
|
|
61
|
+
const revision = row.current_revision + 1;
|
|
62
|
+
await client.query(`INSERT INTO asm_agent.memory_revisions
|
|
63
|
+
(memory_id, revision, content, metadata, correction_reason) VALUES ($1,$2,$3,$4::jsonb,$5)`, [input.memoryId, revision, content, JSON.stringify(input.metadata ?? {}), input.reason]);
|
|
64
|
+
await insertEvidenceLinks(client, input.memoryId, revision, input.evidenceIds);
|
|
65
|
+
await client.query(`UPDATE asm_agent.memory_records SET current_revision = $2, updated_at = clock_timestamp()
|
|
66
|
+
WHERE id = $1`, [input.memoryId, revision]);
|
|
67
|
+
await upsertProjections(client, input.memoryId, revision, content, input.embedding);
|
|
68
|
+
});
|
|
69
|
+
return this.loadMemory(input.memoryId, input.ownerId, input.projectId);
|
|
70
|
+
}
|
|
71
|
+
async linkMemory(sourceMemoryId, targetMemoryId, relation) {
|
|
72
|
+
await this.database.pool.query(`INSERT INTO asm_agent.memory_links (source_memory_id, target_memory_id, relation)
|
|
73
|
+
VALUES ($1,$2,$3) ON CONFLICT DO NOTHING`, [sourceMemoryId, targetMemoryId, requiredContent(relation)]);
|
|
74
|
+
}
|
|
75
|
+
async inspectMemory(memoryId, reader, ownerId, projectId, includeRevoked = false) {
|
|
76
|
+
const result = await this.database.pool.query(`${memorySelect()} WHERE record.id = $1 AND record.owner_id = $2 AND record.project_id = $3
|
|
77
|
+
AND ($4::boolean OR (record.tombstoned_at IS NULL AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())))
|
|
78
|
+
AND ${scopePredicate(5, 6)}`, [memoryId, ownerId, projectId, includeRevoked, reader.type, reader.id]);
|
|
79
|
+
const row = result.rows[0];
|
|
80
|
+
if (!row)
|
|
81
|
+
throw new Error("Memory not found or reader is not authorized");
|
|
82
|
+
return this.inflateMemory(row);
|
|
83
|
+
}
|
|
84
|
+
async exportProject(ownerId, projectId, reader) {
|
|
85
|
+
const result = await this.database.pool.query(`${memorySelect()} WHERE record.owner_id = $1 AND record.project_id = $2
|
|
86
|
+
AND record.tombstoned_at IS NULL AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())
|
|
87
|
+
AND ${scopePredicate(3, 4)} ORDER BY record.created_at, record.id`, [ownerId, projectId, reader.type, reader.id]);
|
|
88
|
+
return Promise.all(result.rows.map((row) => this.inflateMemory(row)));
|
|
89
|
+
}
|
|
90
|
+
async revokeMemory(memoryId, ownerId, projectId, reason) {
|
|
91
|
+
return this.database.transaction(async (client) => revokeMemories(client, [memoryId], ownerId, projectId, reason));
|
|
92
|
+
}
|
|
93
|
+
async revokeEvidence(evidenceId, ownerId, projectId, reason) {
|
|
94
|
+
return this.database.transaction(async (client) => {
|
|
95
|
+
const evidence = await client.query(`UPDATE asm_agent.evidence_snapshots SET revoked_at = clock_timestamp()
|
|
96
|
+
WHERE id = $1 AND owner_id = $2 AND project_id = $3 AND revoked_at IS NULL RETURNING id`, [evidenceId, ownerId, projectId]);
|
|
97
|
+
if ((evidence.rowCount ?? 0) === 0)
|
|
98
|
+
return 0;
|
|
99
|
+
const memories = await client.query(`SELECT DISTINCT link.memory_id FROM asm_agent.memory_revision_evidence link
|
|
100
|
+
JOIN asm_agent.memory_records record ON record.id = link.memory_id
|
|
101
|
+
WHERE link.evidence_id = $1 AND record.tombstoned_at IS NULL`, [evidenceId]);
|
|
102
|
+
let revoked = 0;
|
|
103
|
+
for (const memory of memories.rows) {
|
|
104
|
+
if (await revokeMemories(client, [memory.memory_id], ownerId, projectId, reason))
|
|
105
|
+
revoked++;
|
|
106
|
+
}
|
|
107
|
+
return revoked;
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async requestErasure(memoryId, ownerId, projectId) {
|
|
111
|
+
const jobId = randomUUID();
|
|
112
|
+
return this.database.transaction(async (client) => {
|
|
113
|
+
await revokeMemories(client, [memoryId], ownerId, projectId, "erasure_requested");
|
|
114
|
+
const inserted = await client.query(`INSERT INTO asm_agent.memory_erasure_jobs (id, memory_id) VALUES ($1,$2)
|
|
115
|
+
ON CONFLICT (memory_id) WHERE status IN ('pending','running') DO NOTHING RETURNING id`, [jobId, memoryId]);
|
|
116
|
+
if (inserted.rows[0])
|
|
117
|
+
return inserted.rows[0].id;
|
|
118
|
+
const existing = await client.query(`SELECT id FROM asm_agent.memory_erasure_jobs WHERE memory_id = $1
|
|
119
|
+
AND status IN ('pending','running') ORDER BY requested_at DESC LIMIT 1`, [memoryId]);
|
|
120
|
+
const row = existing.rows[0];
|
|
121
|
+
if (!row)
|
|
122
|
+
throw new Error("Could not create or resolve memory erasure job");
|
|
123
|
+
return row.id;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async processErasure(memoryId) {
|
|
127
|
+
return this.database.transaction(async (client) => {
|
|
128
|
+
const job = await client.query(`UPDATE asm_agent.memory_erasure_jobs SET status = 'running', attempts = attempts + 1
|
|
129
|
+
WHERE memory_id = $1 AND status = 'pending' RETURNING id`, [memoryId]);
|
|
130
|
+
const row = job.rows[0];
|
|
131
|
+
if (!row)
|
|
132
|
+
return false;
|
|
133
|
+
await client.query("DELETE FROM asm_agent.memory_records WHERE id = $1 AND tombstoned_at IS NOT NULL", [
|
|
134
|
+
memoryId,
|
|
135
|
+
]);
|
|
136
|
+
await client.query(`UPDATE asm_agent.memory_erasure_jobs SET status = 'completed', completed_at = clock_timestamp()
|
|
137
|
+
WHERE id = $1`, [row.id]);
|
|
138
|
+
return true;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async listAuthorizedForLearnedRanking(input) {
|
|
142
|
+
if (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 128) {
|
|
143
|
+
throw new Error("Learned-ranking candidate limit must be between 1 and 128");
|
|
144
|
+
}
|
|
145
|
+
const rows = await this.database.pool.query(`SELECT record.id, record.current_revision AS revision, revision.content
|
|
146
|
+
FROM asm_agent.memory_records record
|
|
147
|
+
JOIN asm_agent.memory_revisions revision ON revision.memory_id = record.id
|
|
148
|
+
AND revision.revision = record.current_revision
|
|
149
|
+
WHERE record.owner_id = $1 AND record.project_id = $2 AND record.tombstoned_at IS NULL
|
|
150
|
+
AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())
|
|
151
|
+
AND ${scopePredicate(3, 4)}
|
|
152
|
+
ORDER BY record.updated_at DESC, record.id LIMIT $5`, [input.ownerId, input.projectId, input.reader.type, input.reader.id, input.limit]);
|
|
153
|
+
return rows.rows.map((row) => ({ memoryId: row.id, revision: row.revision, content: row.content }));
|
|
154
|
+
}
|
|
155
|
+
async recordLearnedRanking(input) {
|
|
156
|
+
if (input.queryEmbedding.length === 0 || input.queryEmbedding.some((value) => !Number.isFinite(value))) {
|
|
157
|
+
throw new Error("Learned-ranking query embedding must contain finite values");
|
|
158
|
+
}
|
|
159
|
+
const byId = new Map(input.candidates.map((candidate) => [candidate.memoryId, candidate]));
|
|
160
|
+
const seen = new Set();
|
|
161
|
+
const candidates = input.ranked.map((ranked) => {
|
|
162
|
+
const candidate = byId.get(ranked.memoryId);
|
|
163
|
+
if (!candidate || seen.has(ranked.memoryId)) {
|
|
164
|
+
throw new Error("Learned ranking contains an unknown or duplicate memory");
|
|
165
|
+
}
|
|
166
|
+
if (!Number.isFinite(ranked.score) || ranked.score < 0 || ranked.score > 1) {
|
|
167
|
+
throw new Error("Learned-ranking score must be between zero and one");
|
|
168
|
+
}
|
|
169
|
+
seen.add(ranked.memoryId);
|
|
170
|
+
return {
|
|
171
|
+
memoryId: candidate.memoryId,
|
|
172
|
+
revision: candidate.revision,
|
|
173
|
+
content: candidate.content,
|
|
174
|
+
score: ranked.score,
|
|
175
|
+
selected: true,
|
|
176
|
+
reasonCodes: ["phase76_late_interaction", "checkpoint_bound"],
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
return this.recordDecision({ ...input, limit: Math.max(1, input.ranked.length) }, "phase76-address-head-v1", candidates, input.latencyMs, input.queryEmbedding);
|
|
180
|
+
}
|
|
181
|
+
async retrieveLexical(input) {
|
|
182
|
+
const started = performance.now();
|
|
183
|
+
const rows = await this.database.pool.query(`SELECT record.id, record.current_revision AS revision, revision.content,
|
|
184
|
+
LEAST(1, ts_rank_cd(projection.document, websearch_to_tsquery('simple', $5)))::float8 AS score
|
|
185
|
+
FROM asm_agent.memory_records record
|
|
186
|
+
JOIN asm_agent.memory_revisions revision ON revision.memory_id = record.id
|
|
187
|
+
AND revision.revision = record.current_revision
|
|
188
|
+
JOIN asm_agent.memory_lexical_projections projection ON projection.memory_id = record.id
|
|
189
|
+
WHERE record.owner_id = $1 AND record.project_id = $2 AND record.tombstoned_at IS NULL
|
|
190
|
+
AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())
|
|
191
|
+
AND ${scopePredicate(3, 4)} AND projection.document @@ websearch_to_tsquery('simple', $5)
|
|
192
|
+
ORDER BY score DESC, record.id LIMIT $6`, [input.ownerId, input.projectId, input.reader.type, input.reader.id, input.query, input.limit]);
|
|
193
|
+
return this.recordDecision(input, "lexical-v1", rows.rows.map(toLexicalCandidate), performance.now() - started);
|
|
194
|
+
}
|
|
195
|
+
async retrieveVector(input) {
|
|
196
|
+
if (input.embedding.length === 0 || input.embedding.some((value) => !Number.isFinite(value))) {
|
|
197
|
+
throw new Error("Query embedding must contain finite values");
|
|
198
|
+
}
|
|
199
|
+
const started = performance.now();
|
|
200
|
+
const rows = await this.database.pool.query(`SELECT record.id, record.current_revision AS revision, revision.content, projection.embedding
|
|
201
|
+
FROM asm_agent.memory_records record
|
|
202
|
+
JOIN asm_agent.memory_revisions revision ON revision.memory_id = record.id
|
|
203
|
+
AND revision.revision = record.current_revision
|
|
204
|
+
JOIN asm_agent.memory_vector_projections projection ON projection.memory_id = record.id
|
|
205
|
+
WHERE record.owner_id = $1 AND record.project_id = $2 AND record.tombstoned_at IS NULL
|
|
206
|
+
AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())
|
|
207
|
+
AND projection.dimensions = $5 AND ${scopePredicate(3, 4)}`, [input.ownerId, input.projectId, input.reader.type, input.reader.id, input.embedding.length]);
|
|
208
|
+
const candidates = rows.rows
|
|
209
|
+
.map((row) => ({
|
|
210
|
+
memoryId: row.id,
|
|
211
|
+
revision: row.revision,
|
|
212
|
+
content: row.content,
|
|
213
|
+
score: cosineSimilarity(input.embedding, row.embedding),
|
|
214
|
+
selected: true,
|
|
215
|
+
reasonCodes: ["vector_similarity"],
|
|
216
|
+
}))
|
|
217
|
+
.sort((left, right) => right.score - left.score || left.memoryId.localeCompare(right.memoryId))
|
|
218
|
+
.slice(0, input.limit);
|
|
219
|
+
return this.recordDecision(input, "vector-l2-v1", candidates, performance.now() - started, input.embedding);
|
|
220
|
+
}
|
|
221
|
+
async addFeedback(input) {
|
|
222
|
+
await this.database.pool.query(`INSERT INTO asm_agent.retrieval_feedback
|
|
223
|
+
(id, decision_id, target_memory_id, rating, comment) VALUES ($1,$2,$3,$4,$5)`, [input.id, input.decisionId, input.targetMemoryId ?? null, input.rating, input.comment ?? null]);
|
|
224
|
+
}
|
|
225
|
+
async inflateMemory(row) {
|
|
226
|
+
const [evidence, scopes] = await Promise.all([
|
|
227
|
+
this.database.pool.query(`SELECT evidence_id FROM asm_agent.memory_revision_evidence
|
|
228
|
+
WHERE memory_id = $1 AND revision = $2 ORDER BY evidence_id`, [row.id, row.current_revision]),
|
|
229
|
+
this.database.pool.query("SELECT reader_type, reader_id FROM asm_agent.memory_reader_scopes WHERE memory_id = $1 ORDER BY reader_type, reader_id", [row.id]),
|
|
230
|
+
]);
|
|
231
|
+
return {
|
|
232
|
+
id: row.id,
|
|
233
|
+
ownerId: row.owner_id,
|
|
234
|
+
projectId: row.project_id,
|
|
235
|
+
sessionId: row.session_id ?? undefined,
|
|
236
|
+
kind: row.kind,
|
|
237
|
+
revision: row.current_revision,
|
|
238
|
+
content: row.content,
|
|
239
|
+
evidenceIds: evidence.rows.map((item) => item.evidence_id),
|
|
240
|
+
scopes: scopes.rows.map((scope) => ({ type: scope.reader_type, id: scope.reader_id })),
|
|
241
|
+
expiresAt: row.expires_at ?? undefined,
|
|
242
|
+
tombstonedAt: row.tombstoned_at ?? undefined,
|
|
243
|
+
metadata: row.metadata,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async loadMemory(memoryId, ownerId, projectId) {
|
|
247
|
+
const result = await this.database.pool.query(`${memorySelect()} WHERE record.id = $1 AND record.owner_id = $2 AND record.project_id = $3`, [memoryId, ownerId, projectId]);
|
|
248
|
+
const row = result.rows[0];
|
|
249
|
+
if (!row)
|
|
250
|
+
throw new Error("Memory not found");
|
|
251
|
+
return this.inflateMemory(row);
|
|
252
|
+
}
|
|
253
|
+
async recordDecision(input, algorithm, candidates, latencyMs, queryEmbedding) {
|
|
254
|
+
const decisionId = input.decisionId ?? randomUUID();
|
|
255
|
+
await this.database.transaction(async (client) => {
|
|
256
|
+
await client.query(`INSERT INTO asm_agent.retrieval_decisions
|
|
257
|
+
(id, owner_id, project_id, session_id, reader_type, reader_id, query, algorithm,
|
|
258
|
+
algorithm_version, query_embedding, latency_ms, reason_codes)
|
|
259
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'1',$9,$10,$11)`, [
|
|
260
|
+
decisionId,
|
|
261
|
+
input.ownerId,
|
|
262
|
+
input.projectId,
|
|
263
|
+
input.sessionId ?? null,
|
|
264
|
+
input.reader.type,
|
|
265
|
+
input.reader.id,
|
|
266
|
+
input.query,
|
|
267
|
+
algorithm,
|
|
268
|
+
queryEmbedding ?? null,
|
|
269
|
+
Math.max(0, Math.round(latencyMs)),
|
|
270
|
+
candidates.length > 0 ? ["candidates_found"] : ["no_candidates"],
|
|
271
|
+
]);
|
|
272
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
273
|
+
await client.query(`INSERT INTO asm_agent.retrieval_candidates
|
|
274
|
+
(decision_id, memory_id, revision, rank, score, selected, reason_codes)
|
|
275
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7)`, [
|
|
276
|
+
decisionId,
|
|
277
|
+
candidate.memoryId,
|
|
278
|
+
candidate.revision,
|
|
279
|
+
index + 1,
|
|
280
|
+
candidate.score,
|
|
281
|
+
candidate.selected,
|
|
282
|
+
candidate.reasonCodes,
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
return { decisionId, algorithm, candidates };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
function memorySelect() {
|
|
290
|
+
return `SELECT record.id, record.owner_id, record.project_id, record.session_id, record.kind,
|
|
291
|
+
record.current_revision, revision.content, revision.metadata, record.expires_at, record.tombstoned_at
|
|
292
|
+
FROM asm_agent.memory_records record JOIN asm_agent.memory_revisions revision
|
|
293
|
+
ON revision.memory_id = record.id AND revision.revision = record.current_revision`;
|
|
294
|
+
}
|
|
295
|
+
function scopePredicate(typeParameter, idParameter) {
|
|
296
|
+
return `EXISTS (SELECT 1 FROM asm_agent.memory_reader_scopes scope
|
|
297
|
+
WHERE scope.memory_id = record.id AND scope.reader_type = $${typeParameter} AND scope.reader_id = $${idParameter})`;
|
|
298
|
+
}
|
|
299
|
+
async function assertEvidence(client, ownerId, projectId, evidenceIds) {
|
|
300
|
+
if (evidenceIds.length === 0)
|
|
301
|
+
throw new Error("Grounded memory requires at least one evidence snapshot");
|
|
302
|
+
const result = await client.query(`SELECT id FROM asm_agent.evidence_snapshots WHERE id = ANY($1::uuid[])
|
|
303
|
+
AND owner_id = $2 AND project_id = $3 AND revoked_at IS NULL`, [evidenceIds, ownerId, projectId]);
|
|
304
|
+
if (result.rows.length !== new Set(evidenceIds).size)
|
|
305
|
+
throw new Error("Evidence missing, revoked, or outside project scope");
|
|
306
|
+
}
|
|
307
|
+
async function insertEvidenceLinks(client, memoryId, revision, evidenceIds) {
|
|
308
|
+
for (const evidenceId of new Set(evidenceIds)) {
|
|
309
|
+
await client.query(`INSERT INTO asm_agent.memory_revision_evidence (memory_id, revision, evidence_id) VALUES ($1,$2,$3)`, [memoryId, revision, evidenceId]);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async function insertScopes(client, memoryId, scopes) {
|
|
313
|
+
if (scopes.length === 0)
|
|
314
|
+
throw new Error("Memory requires at least one reader scope");
|
|
315
|
+
for (const scope of scopes) {
|
|
316
|
+
await client.query("INSERT INTO asm_agent.memory_reader_scopes (memory_id, reader_type, reader_id) VALUES ($1,$2,$3)", [memoryId, scope.type, scope.id]);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async function upsertProjections(client, memoryId, revision, content, embedding) {
|
|
320
|
+
await client.query(`INSERT INTO asm_agent.memory_lexical_projections (memory_id, revision, document)
|
|
321
|
+
VALUES ($1,$2,to_tsvector('simple',$3)) ON CONFLICT (memory_id) DO UPDATE
|
|
322
|
+
SET revision = EXCLUDED.revision, document = EXCLUDED.document, projected_at = clock_timestamp()`, [memoryId, revision, content]);
|
|
323
|
+
if (!embedding) {
|
|
324
|
+
await client.query("DELETE FROM asm_agent.memory_vector_projections WHERE memory_id = $1", [memoryId]);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (embedding.values.length === 0 || embedding.values.some((value) => !Number.isFinite(value))) {
|
|
328
|
+
throw new Error("Embedding must contain finite values");
|
|
329
|
+
}
|
|
330
|
+
await client.query(`INSERT INTO asm_agent.memory_vector_projections (memory_id, revision, model, dimensions, embedding)
|
|
331
|
+
VALUES ($1,$2,$3,$4,$5) ON CONFLICT (memory_id) DO UPDATE SET revision = EXCLUDED.revision,
|
|
332
|
+
model = EXCLUDED.model, dimensions = EXCLUDED.dimensions, embedding = EXCLUDED.embedding,
|
|
333
|
+
projected_at = clock_timestamp()`, [memoryId, revision, embedding.model, embedding.values.length, embedding.values]);
|
|
334
|
+
}
|
|
335
|
+
async function revokeMemories(client, memoryIds, ownerId, projectId, reason) {
|
|
336
|
+
const result = await client.query(`UPDATE asm_agent.memory_records SET tombstoned_at = clock_timestamp(), revocation_reason = $4,
|
|
337
|
+
updated_at = clock_timestamp() WHERE id = ANY($1::uuid[]) AND owner_id = $2 AND project_id = $3
|
|
338
|
+
AND tombstoned_at IS NULL RETURNING id`, [memoryIds, ownerId, projectId, reason]);
|
|
339
|
+
if (result.rows.length === 0)
|
|
340
|
+
return false;
|
|
341
|
+
const revokedIds = result.rows.map((row) => row.id);
|
|
342
|
+
await client.query("DELETE FROM asm_agent.memory_lexical_projections WHERE memory_id = ANY($1::uuid[])", [
|
|
343
|
+
revokedIds,
|
|
344
|
+
]);
|
|
345
|
+
await client.query("DELETE FROM asm_agent.memory_vector_projections WHERE memory_id = ANY($1::uuid[])", [
|
|
346
|
+
revokedIds,
|
|
347
|
+
]);
|
|
348
|
+
await client.query(`UPDATE asm_agent.retrieval_candidates SET eligible = false, selected = false,
|
|
349
|
+
reason_codes = array_append(reason_codes, 'revoked') WHERE memory_id = ANY($1::uuid[]) AND eligible = true`, [revokedIds]);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
function requiredContent(content) {
|
|
353
|
+
const normalized = content.trim();
|
|
354
|
+
if (!normalized)
|
|
355
|
+
throw new Error("Memory content must not be empty");
|
|
356
|
+
return normalized;
|
|
357
|
+
}
|
|
358
|
+
function stableJson(value) {
|
|
359
|
+
return JSON.stringify(value, Object.keys(value).sort());
|
|
360
|
+
}
|
|
361
|
+
function cosineSimilarity(left, right) {
|
|
362
|
+
let dot = 0;
|
|
363
|
+
let leftMagnitude = 0;
|
|
364
|
+
let rightMagnitude = 0;
|
|
365
|
+
for (let index = 0; index < left.length; index++) {
|
|
366
|
+
const leftValue = left[index] ?? 0;
|
|
367
|
+
const rightValue = right[index] ?? 0;
|
|
368
|
+
dot += leftValue * rightValue;
|
|
369
|
+
leftMagnitude += leftValue * leftValue;
|
|
370
|
+
rightMagnitude += rightValue * rightValue;
|
|
371
|
+
}
|
|
372
|
+
if (leftMagnitude === 0 || rightMagnitude === 0)
|
|
373
|
+
return 0;
|
|
374
|
+
return Math.max(0, Math.min(1, (dot / Math.sqrt(leftMagnitude * rightMagnitude) + 1) / 2));
|
|
375
|
+
}
|
|
376
|
+
function toLexicalCandidate(row) {
|
|
377
|
+
return {
|
|
378
|
+
memoryId: row.id,
|
|
379
|
+
revision: row.revision,
|
|
380
|
+
content: row.content,
|
|
381
|
+
score: row.score,
|
|
382
|
+
selected: true,
|
|
383
|
+
reasonCodes: ["lexical_match"],
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
//# sourceMappingURL=grounded-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounded-memory.js","sourceRoot":"","sources":["../src/grounded-memory.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomUUID } from \"node:crypto\";\nimport type { PoolClient, QueryResultRow } from \"pg\";\nimport type { PostgresStore } from \"./database.js\";\n\nexport type MemoryKind = \"observation\" | \"fact\" | \"instruction\";\nexport type ReaderType = \"owner\" | \"project\" | \"session\" | \"agent\";\nexport type FeedbackRating = \"positive\" | \"negative\" | \"correction\";\n\nexport interface ReaderScope {\n\ttype: ReaderType;\n\tid: string;\n}\n\nexport interface EvidenceSnapshotInput {\n\tid: string;\n\townerId: string;\n\tprojectId: string;\n\tsourceType: \"user\" | \"tool\" | \"document\" | \"system\";\n\tsourceUri?: string;\n\tcontentType: string;\n\tcontent: string | Record<string, unknown>;\n\tcapturedAt: Date;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface MemoryRecordView {\n\tid: string;\n\townerId: string;\n\tprojectId: string;\n\tsessionId?: string;\n\tkind: MemoryKind;\n\trevision: number;\n\tcontent: string;\n\tevidenceIds: string[];\n\tscopes: ReaderScope[];\n\texpiresAt?: Date;\n\ttombstonedAt?: Date;\n\tmetadata: Record<string, unknown>;\n}\n\nexport interface RetrievalCandidateView {\n\tmemoryId: string;\n\trevision: number;\n\tcontent: string;\n\tscore: number;\n\tselected: boolean;\n\treasonCodes: string[];\n}\n\nexport interface RetrievalResult {\n\tdecisionId: string;\n\talgorithm: \"lexical-v1\" | \"vector-l2-v1\" | \"phase76-address-head-v1\";\n\tcandidates: RetrievalCandidateView[];\n}\n\nexport interface AuthorizedMemoryCandidate {\n\tmemoryId: string;\n\trevision: number;\n\tcontent: string;\n}\n\nexport interface LearnedRankingInput {\n\tdecisionId?: string;\n\townerId: string;\n\tprojectId: string;\n\tsessionId?: string;\n\treader: ReaderScope;\n\tquery: string;\n\tlatencyMs: number;\n\tqueryEmbedding: number[];\n\tcandidates: AuthorizedMemoryCandidate[];\n\tranked: Array<{ memoryId: string; score: number }>;\n}\n\ninterface MemoryRow extends QueryResultRow {\n\tid: string;\n\towner_id: string;\n\tproject_id: string;\n\tsession_id: string | null;\n\tkind: MemoryKind;\n\tcurrent_revision: number;\n\tcontent: string;\n\tmetadata: Record<string, unknown>;\n\texpires_at: Date | null;\n\ttombstoned_at: Date | null;\n}\n\ninterface ScopeRow extends QueryResultRow {\n\treader_type: ReaderType;\n\treader_id: string;\n}\n\ninterface EvidenceIdRow extends QueryResultRow {\n\tevidence_id: string;\n}\n\ninterface LexicalRow extends QueryResultRow {\n\tid: string;\n\trevision: number;\n\tcontent: string;\n\tscore: number;\n}\n\ninterface VectorRow extends QueryResultRow {\n\tid: string;\n\trevision: number;\n\tcontent: string;\n\tembedding: number[];\n}\n\nexport class GroundedMemoryStore {\n\tconstructor(private readonly database: PostgresStore) {}\n\n\tasync createEvidence(input: EvidenceSnapshotInput): Promise<{ id: string; digest: string }> {\n\t\tconst canonical = typeof input.content === \"string\" ? input.content : stableJson(input.content);\n\t\tconst digest = createHash(\"sha256\").update(canonical).digest(\"hex\");\n\t\tconst result = await this.database.pool.query<{ id: string }>(\n\t\t\t`INSERT INTO asm_agent.evidence_snapshots\n\t\t\t (id, owner_id, project_id, source_type, source_uri, content_type, content_text,\n\t\t\t content_json, digest, captured_at, metadata)\n\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb,$9,$10,$11::jsonb)\n\t\t\t ON CONFLICT (owner_id, project_id, digest) DO NOTHING RETURNING id`,\n\t\t\t[\n\t\t\t\tinput.id,\n\t\t\t\tinput.ownerId,\n\t\t\t\tinput.projectId,\n\t\t\t\tinput.sourceType,\n\t\t\t\tinput.sourceUri ?? null,\n\t\t\t\tinput.contentType,\n\t\t\t\ttypeof input.content === \"string\" ? input.content : null,\n\t\t\t\ttypeof input.content === \"string\" ? null : stableJson(input.content),\n\t\t\t\tdigest,\n\t\t\t\tinput.capturedAt,\n\t\t\t\tJSON.stringify(input.metadata ?? {}),\n\t\t\t],\n\t\t);\n\t\tif (result.rows[0]) return { id: result.rows[0].id, digest };\n\t\tconst existing = await this.database.pool.query<{ id: string }>(\n\t\t\t\"SELECT id FROM asm_agent.evidence_snapshots WHERE owner_id = $1 AND project_id = $2 AND digest = $3\",\n\t\t\t[input.ownerId, input.projectId, digest],\n\t\t);\n\t\tconst row = existing.rows[0];\n\t\tif (!row) throw new Error(\"Evidence conflict could not be resolved\");\n\t\treturn { id: row.id, digest };\n\t}\n\n\tasync addEvidenceClaim(input: {\n\t\tid: string;\n\t\tevidenceId: string;\n\t\tclaim: string;\n\t\tlocator?: Record<string, unknown>;\n\t}): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.evidence_claims (id, evidence_id, claim, locator)\n\t\t\t VALUES ($1,$2,$3,$4::jsonb)`,\n\t\t\t[input.id, input.evidenceId, requiredContent(input.claim), JSON.stringify(input.locator ?? {})],\n\t\t);\n\t}\n\n\tasync createMemory(input: {\n\t\tid: string;\n\t\townerId: string;\n\t\tprojectId: string;\n\t\tsessionId?: string;\n\t\tkind: MemoryKind;\n\t\tcontent: string;\n\t\tevidenceIds: string[];\n\t\tscopes?: ReaderScope[];\n\t\texpiresAt?: Date;\n\t\tmetadata?: Record<string, unknown>;\n\t\tembedding?: { model: string; values: number[] };\n\t}): Promise<MemoryRecordView> {\n\t\tconst content = requiredContent(input.content);\n\t\tawait this.database.transaction(async (client) => {\n\t\t\tawait assertEvidence(client, input.ownerId, input.projectId, input.evidenceIds);\n\t\t\tawait client.query(\n\t\t\t\t`INSERT INTO asm_agent.memory_records\n\t\t\t\t (id, owner_id, project_id, session_id, kind, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`,\n\t\t\t\t[input.id, input.ownerId, input.projectId, input.sessionId ?? null, input.kind, input.expiresAt ?? null],\n\t\t\t);\n\t\t\tawait client.query(\n\t\t\t\t\"INSERT INTO asm_agent.memory_revisions (memory_id, revision, content, metadata) VALUES ($1,1,$2,$3::jsonb)\",\n\t\t\t\t[input.id, content, JSON.stringify(input.metadata ?? {})],\n\t\t\t);\n\t\t\tawait insertEvidenceLinks(client, input.id, 1, input.evidenceIds);\n\t\t\tawait insertScopes(client, input.id, input.scopes ?? [{ type: \"owner\", id: input.ownerId }]);\n\t\t\tawait upsertProjections(client, input.id, 1, content, input.embedding);\n\t\t});\n\t\treturn this.loadMemory(input.id, input.ownerId, input.projectId);\n\t}\n\n\tasync correctMemory(input: {\n\t\tmemoryId: string;\n\t\townerId: string;\n\t\tprojectId: string;\n\t\tcontent: string;\n\t\tevidenceIds: string[];\n\t\treason: string;\n\t\tmetadata?: Record<string, unknown>;\n\t\tembedding?: { model: string; values: number[] };\n\t}): Promise<MemoryRecordView> {\n\t\tconst content = requiredContent(input.content);\n\t\tawait this.database.transaction(async (client) => {\n\t\t\tawait assertEvidence(client, input.ownerId, input.projectId, input.evidenceIds);\n\t\t\tconst locked = await client.query<{ current_revision: number }>(\n\t\t\t\t`SELECT current_revision FROM asm_agent.memory_records\n\t\t\t\t WHERE id = $1 AND owner_id = $2 AND project_id = $3 AND tombstoned_at IS NULL FOR UPDATE`,\n\t\t\t\t[input.memoryId, input.ownerId, input.projectId],\n\t\t\t);\n\t\t\tconst row = locked.rows[0];\n\t\t\tif (!row) throw new Error(\"Memory not found or revoked\");\n\t\t\tconst revision = row.current_revision + 1;\n\t\t\tawait client.query(\n\t\t\t\t`INSERT INTO asm_agent.memory_revisions\n\t\t\t\t (memory_id, revision, content, metadata, correction_reason) VALUES ($1,$2,$3,$4::jsonb,$5)`,\n\t\t\t\t[input.memoryId, revision, content, JSON.stringify(input.metadata ?? {}), input.reason],\n\t\t\t);\n\t\t\tawait insertEvidenceLinks(client, input.memoryId, revision, input.evidenceIds);\n\t\t\tawait client.query(\n\t\t\t\t`UPDATE asm_agent.memory_records SET current_revision = $2, updated_at = clock_timestamp()\n\t\t\t\t WHERE id = $1`,\n\t\t\t\t[input.memoryId, revision],\n\t\t\t);\n\t\t\tawait upsertProjections(client, input.memoryId, revision, content, input.embedding);\n\t\t});\n\t\treturn this.loadMemory(input.memoryId, input.ownerId, input.projectId);\n\t}\n\n\tasync linkMemory(sourceMemoryId: string, targetMemoryId: string, relation: string): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.memory_links (source_memory_id, target_memory_id, relation)\n\t\t\t VALUES ($1,$2,$3) ON CONFLICT DO NOTHING`,\n\t\t\t[sourceMemoryId, targetMemoryId, requiredContent(relation)],\n\t\t);\n\t}\n\n\tasync inspectMemory(\n\t\tmemoryId: string,\n\t\treader: ReaderScope,\n\t\townerId: string,\n\t\tprojectId: string,\n\t\tincludeRevoked = false,\n\t): Promise<MemoryRecordView> {\n\t\tconst result = await this.database.pool.query<MemoryRow>(\n\t\t\t`${memorySelect()} WHERE record.id = $1 AND record.owner_id = $2 AND record.project_id = $3\n\t\t\t AND ($4::boolean OR (record.tombstoned_at IS NULL AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())))\n\t\t\t AND ${scopePredicate(5, 6)}`,\n\t\t\t[memoryId, ownerId, projectId, includeRevoked, reader.type, reader.id],\n\t\t);\n\t\tconst row = result.rows[0];\n\t\tif (!row) throw new Error(\"Memory not found or reader is not authorized\");\n\t\treturn this.inflateMemory(row);\n\t}\n\n\tasync exportProject(ownerId: string, projectId: string, reader: ReaderScope): Promise<MemoryRecordView[]> {\n\t\tconst result = await this.database.pool.query<MemoryRow>(\n\t\t\t`${memorySelect()} WHERE record.owner_id = $1 AND record.project_id = $2\n\t\t\t AND record.tombstoned_at IS NULL AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())\n\t\t\t AND ${scopePredicate(3, 4)} ORDER BY record.created_at, record.id`,\n\t\t\t[ownerId, projectId, reader.type, reader.id],\n\t\t);\n\t\treturn Promise.all(result.rows.map((row) => this.inflateMemory(row)));\n\t}\n\n\tasync revokeMemory(memoryId: string, ownerId: string, projectId: string, reason: string): Promise<boolean> {\n\t\treturn this.database.transaction(async (client) =>\n\t\t\trevokeMemories(client, [memoryId], ownerId, projectId, reason),\n\t\t);\n\t}\n\n\tasync revokeEvidence(evidenceId: string, ownerId: string, projectId: string, reason: string): Promise<number> {\n\t\treturn this.database.transaction(async (client) => {\n\t\t\tconst evidence = await client.query(\n\t\t\t\t`UPDATE asm_agent.evidence_snapshots SET revoked_at = clock_timestamp()\n\t\t\t\t WHERE id = $1 AND owner_id = $2 AND project_id = $3 AND revoked_at IS NULL RETURNING id`,\n\t\t\t\t[evidenceId, ownerId, projectId],\n\t\t\t);\n\t\t\tif ((evidence.rowCount ?? 0) === 0) return 0;\n\t\t\tconst memories = await client.query<{ memory_id: string }>(\n\t\t\t\t`SELECT DISTINCT link.memory_id FROM asm_agent.memory_revision_evidence link\n\t\t\t\t JOIN asm_agent.memory_records record ON record.id = link.memory_id\n\t\t\t\t WHERE link.evidence_id = $1 AND record.tombstoned_at IS NULL`,\n\t\t\t\t[evidenceId],\n\t\t\t);\n\t\t\tlet revoked = 0;\n\t\t\tfor (const memory of memories.rows) {\n\t\t\t\tif (await revokeMemories(client, [memory.memory_id], ownerId, projectId, reason)) revoked++;\n\t\t\t}\n\t\t\treturn revoked;\n\t\t});\n\t}\n\n\tasync requestErasure(memoryId: string, ownerId: string, projectId: string): Promise<string> {\n\t\tconst jobId = randomUUID();\n\t\treturn this.database.transaction(async (client) => {\n\t\t\tawait revokeMemories(client, [memoryId], ownerId, projectId, \"erasure_requested\");\n\t\t\tconst inserted = await client.query<{ id: string }>(\n\t\t\t\t`INSERT INTO asm_agent.memory_erasure_jobs (id, memory_id) VALUES ($1,$2)\n\t\t\t\t ON CONFLICT (memory_id) WHERE status IN ('pending','running') DO NOTHING RETURNING id`,\n\t\t\t\t[jobId, memoryId],\n\t\t\t);\n\t\t\tif (inserted.rows[0]) return inserted.rows[0].id;\n\t\t\tconst existing = await client.query<{ id: string }>(\n\t\t\t\t`SELECT id FROM asm_agent.memory_erasure_jobs WHERE memory_id = $1\n\t\t\t\t AND status IN ('pending','running') ORDER BY requested_at DESC LIMIT 1`,\n\t\t\t\t[memoryId],\n\t\t\t);\n\t\t\tconst row = existing.rows[0];\n\t\t\tif (!row) throw new Error(\"Could not create or resolve memory erasure job\");\n\t\t\treturn row.id;\n\t\t});\n\t}\n\n\tasync processErasure(memoryId: string): Promise<boolean> {\n\t\treturn this.database.transaction(async (client) => {\n\t\t\tconst job = await client.query<{ id: string }>(\n\t\t\t\t`UPDATE asm_agent.memory_erasure_jobs SET status = 'running', attempts = attempts + 1\n\t\t\t\t WHERE memory_id = $1 AND status = 'pending' RETURNING id`,\n\t\t\t\t[memoryId],\n\t\t\t);\n\t\t\tconst row = job.rows[0];\n\t\t\tif (!row) return false;\n\t\t\tawait client.query(\"DELETE FROM asm_agent.memory_records WHERE id = $1 AND tombstoned_at IS NOT NULL\", [\n\t\t\t\tmemoryId,\n\t\t\t]);\n\t\t\tawait client.query(\n\t\t\t\t`UPDATE asm_agent.memory_erasure_jobs SET status = 'completed', completed_at = clock_timestamp()\n\t\t\t\t WHERE id = $1`,\n\t\t\t\t[row.id],\n\t\t\t);\n\t\t\treturn true;\n\t\t});\n\t}\n\n\tasync listAuthorizedForLearnedRanking(input: {\n\t\townerId: string;\n\t\tprojectId: string;\n\t\treader: ReaderScope;\n\t\tlimit: number;\n\t}): Promise<AuthorizedMemoryCandidate[]> {\n\t\tif (!Number.isInteger(input.limit) || input.limit < 1 || input.limit > 128) {\n\t\t\tthrow new Error(\"Learned-ranking candidate limit must be between 1 and 128\");\n\t\t}\n\t\tconst rows = await this.database.pool.query<{\n\t\t\tid: string;\n\t\t\trevision: number;\n\t\t\tcontent: string;\n\t\t}>(\n\t\t\t`SELECT record.id, record.current_revision AS revision, revision.content\n\t\t\t FROM asm_agent.memory_records record\n\t\t\t JOIN asm_agent.memory_revisions revision ON revision.memory_id = record.id\n\t\t\t AND revision.revision = record.current_revision\n\t\t\t WHERE record.owner_id = $1 AND record.project_id = $2 AND record.tombstoned_at IS NULL\n\t\t\t AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())\n\t\t\t AND ${scopePredicate(3, 4)}\n\t\t\t ORDER BY record.updated_at DESC, record.id LIMIT $5`,\n\t\t\t[input.ownerId, input.projectId, input.reader.type, input.reader.id, input.limit],\n\t\t);\n\t\treturn rows.rows.map((row) => ({ memoryId: row.id, revision: row.revision, content: row.content }));\n\t}\n\n\tasync recordLearnedRanking(input: LearnedRankingInput): Promise<RetrievalResult> {\n\t\tif (input.queryEmbedding.length === 0 || input.queryEmbedding.some((value) => !Number.isFinite(value))) {\n\t\t\tthrow new Error(\"Learned-ranking query embedding must contain finite values\");\n\t\t}\n\t\tconst byId = new Map(input.candidates.map((candidate) => [candidate.memoryId, candidate]));\n\t\tconst seen = new Set<string>();\n\t\tconst candidates = input.ranked.map((ranked) => {\n\t\t\tconst candidate = byId.get(ranked.memoryId);\n\t\t\tif (!candidate || seen.has(ranked.memoryId)) {\n\t\t\t\tthrow new Error(\"Learned ranking contains an unknown or duplicate memory\");\n\t\t\t}\n\t\t\tif (!Number.isFinite(ranked.score) || ranked.score < 0 || ranked.score > 1) {\n\t\t\t\tthrow new Error(\"Learned-ranking score must be between zero and one\");\n\t\t\t}\n\t\t\tseen.add(ranked.memoryId);\n\t\t\treturn {\n\t\t\t\tmemoryId: candidate.memoryId,\n\t\t\t\trevision: candidate.revision,\n\t\t\t\tcontent: candidate.content,\n\t\t\t\tscore: ranked.score,\n\t\t\t\tselected: true,\n\t\t\t\treasonCodes: [\"phase76_late_interaction\", \"checkpoint_bound\"],\n\t\t\t};\n\t\t});\n\t\treturn this.recordDecision(\n\t\t\t{ ...input, limit: Math.max(1, input.ranked.length) },\n\t\t\t\"phase76-address-head-v1\",\n\t\t\tcandidates,\n\t\t\tinput.latencyMs,\n\t\t\tinput.queryEmbedding,\n\t\t);\n\t}\n\n\tasync retrieveLexical(input: RetrievalInput): Promise<RetrievalResult> {\n\t\tconst started = performance.now();\n\t\tconst rows = await this.database.pool.query<LexicalRow>(\n\t\t\t`SELECT record.id, record.current_revision AS revision, revision.content,\n\t\t\t LEAST(1, ts_rank_cd(projection.document, websearch_to_tsquery('simple', $5)))::float8 AS score\n\t\t\t FROM asm_agent.memory_records record\n\t\t\t JOIN asm_agent.memory_revisions revision ON revision.memory_id = record.id\n\t\t\t AND revision.revision = record.current_revision\n\t\t\t JOIN asm_agent.memory_lexical_projections projection ON projection.memory_id = record.id\n\t\t\t WHERE record.owner_id = $1 AND record.project_id = $2 AND record.tombstoned_at IS NULL\n\t\t\t AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())\n\t\t\t AND ${scopePredicate(3, 4)} AND projection.document @@ websearch_to_tsquery('simple', $5)\n\t\t\t ORDER BY score DESC, record.id LIMIT $6`,\n\t\t\t[input.ownerId, input.projectId, input.reader.type, input.reader.id, input.query, input.limit],\n\t\t);\n\t\treturn this.recordDecision(input, \"lexical-v1\", rows.rows.map(toLexicalCandidate), performance.now() - started);\n\t}\n\n\tasync retrieveVector(input: RetrievalInput & { embedding: number[] }): Promise<RetrievalResult> {\n\t\tif (input.embedding.length === 0 || input.embedding.some((value) => !Number.isFinite(value))) {\n\t\t\tthrow new Error(\"Query embedding must contain finite values\");\n\t\t}\n\t\tconst started = performance.now();\n\t\tconst rows = await this.database.pool.query<VectorRow>(\n\t\t\t`SELECT record.id, record.current_revision AS revision, revision.content, projection.embedding\n\t\t\t FROM asm_agent.memory_records record\n\t\t\t JOIN asm_agent.memory_revisions revision ON revision.memory_id = record.id\n\t\t\t AND revision.revision = record.current_revision\n\t\t\t JOIN asm_agent.memory_vector_projections projection ON projection.memory_id = record.id\n\t\t\t WHERE record.owner_id = $1 AND record.project_id = $2 AND record.tombstoned_at IS NULL\n\t\t\t AND (record.expires_at IS NULL OR record.expires_at > clock_timestamp())\n\t\t\t AND projection.dimensions = $5 AND ${scopePredicate(3, 4)}`,\n\t\t\t[input.ownerId, input.projectId, input.reader.type, input.reader.id, input.embedding.length],\n\t\t);\n\t\tconst candidates = rows.rows\n\t\t\t.map((row) => ({\n\t\t\t\tmemoryId: row.id,\n\t\t\t\trevision: row.revision,\n\t\t\t\tcontent: row.content,\n\t\t\t\tscore: cosineSimilarity(input.embedding, row.embedding),\n\t\t\t\tselected: true,\n\t\t\t\treasonCodes: [\"vector_similarity\"],\n\t\t\t}))\n\t\t\t.sort((left, right) => right.score - left.score || left.memoryId.localeCompare(right.memoryId))\n\t\t\t.slice(0, input.limit);\n\t\treturn this.recordDecision(input, \"vector-l2-v1\", candidates, performance.now() - started, input.embedding);\n\t}\n\n\tasync addFeedback(input: {\n\t\tid: string;\n\t\tdecisionId: string;\n\t\ttargetMemoryId?: string;\n\t\trating: FeedbackRating;\n\t\tcomment?: string;\n\t}): Promise<void> {\n\t\tawait this.database.pool.query(\n\t\t\t`INSERT INTO asm_agent.retrieval_feedback\n\t\t\t (id, decision_id, target_memory_id, rating, comment) VALUES ($1,$2,$3,$4,$5)`,\n\t\t\t[input.id, input.decisionId, input.targetMemoryId ?? null, input.rating, input.comment ?? null],\n\t\t);\n\t}\n\n\tprivate async inflateMemory(row: MemoryRow): Promise<MemoryRecordView> {\n\t\tconst [evidence, scopes] = await Promise.all([\n\t\t\tthis.database.pool.query<EvidenceIdRow>(\n\t\t\t\t`SELECT evidence_id FROM asm_agent.memory_revision_evidence\n\t\t\t\t WHERE memory_id = $1 AND revision = $2 ORDER BY evidence_id`,\n\t\t\t\t[row.id, row.current_revision],\n\t\t\t),\n\t\t\tthis.database.pool.query<ScopeRow>(\n\t\t\t\t\"SELECT reader_type, reader_id FROM asm_agent.memory_reader_scopes WHERE memory_id = $1 ORDER BY reader_type, reader_id\",\n\t\t\t\t[row.id],\n\t\t\t),\n\t\t]);\n\t\treturn {\n\t\t\tid: row.id,\n\t\t\townerId: row.owner_id,\n\t\t\tprojectId: row.project_id,\n\t\t\tsessionId: row.session_id ?? undefined,\n\t\t\tkind: row.kind,\n\t\t\trevision: row.current_revision,\n\t\t\tcontent: row.content,\n\t\t\tevidenceIds: evidence.rows.map((item) => item.evidence_id),\n\t\t\tscopes: scopes.rows.map((scope) => ({ type: scope.reader_type, id: scope.reader_id })),\n\t\t\texpiresAt: row.expires_at ?? undefined,\n\t\t\ttombstonedAt: row.tombstoned_at ?? undefined,\n\t\t\tmetadata: row.metadata,\n\t\t};\n\t}\n\n\tprivate async loadMemory(memoryId: string, ownerId: string, projectId: string): Promise<MemoryRecordView> {\n\t\tconst result = await this.database.pool.query<MemoryRow>(\n\t\t\t`${memorySelect()} WHERE record.id = $1 AND record.owner_id = $2 AND record.project_id = $3`,\n\t\t\t[memoryId, ownerId, projectId],\n\t\t);\n\t\tconst row = result.rows[0];\n\t\tif (!row) throw new Error(\"Memory not found\");\n\t\treturn this.inflateMemory(row);\n\t}\n\n\tprivate async recordDecision(\n\t\tinput: RetrievalInput,\n\t\talgorithm: RetrievalResult[\"algorithm\"],\n\t\tcandidates: RetrievalCandidateView[],\n\t\tlatencyMs: number,\n\t\tqueryEmbedding?: number[],\n\t): Promise<RetrievalResult> {\n\t\tconst decisionId = input.decisionId ?? randomUUID();\n\t\tawait this.database.transaction(async (client) => {\n\t\t\tawait client.query(\n\t\t\t\t`INSERT INTO asm_agent.retrieval_decisions\n\t\t\t\t (id, owner_id, project_id, session_id, reader_type, reader_id, query, algorithm,\n\t\t\t\t algorithm_version, query_embedding, latency_ms, reason_codes)\n\t\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'1',$9,$10,$11)`,\n\t\t\t\t[\n\t\t\t\t\tdecisionId,\n\t\t\t\t\tinput.ownerId,\n\t\t\t\t\tinput.projectId,\n\t\t\t\t\tinput.sessionId ?? null,\n\t\t\t\t\tinput.reader.type,\n\t\t\t\t\tinput.reader.id,\n\t\t\t\t\tinput.query,\n\t\t\t\t\talgorithm,\n\t\t\t\t\tqueryEmbedding ?? null,\n\t\t\t\t\tMath.max(0, Math.round(latencyMs)),\n\t\t\t\t\tcandidates.length > 0 ? [\"candidates_found\"] : [\"no_candidates\"],\n\t\t\t\t],\n\t\t\t);\n\t\t\tfor (const [index, candidate] of candidates.entries()) {\n\t\t\t\tawait client.query(\n\t\t\t\t\t`INSERT INTO asm_agent.retrieval_candidates\n\t\t\t\t\t (decision_id, memory_id, revision, rank, score, selected, reason_codes)\n\t\t\t\t\t VALUES ($1,$2,$3,$4,$5,$6,$7)`,\n\t\t\t\t\t[\n\t\t\t\t\t\tdecisionId,\n\t\t\t\t\t\tcandidate.memoryId,\n\t\t\t\t\t\tcandidate.revision,\n\t\t\t\t\t\tindex + 1,\n\t\t\t\t\t\tcandidate.score,\n\t\t\t\t\t\tcandidate.selected,\n\t\t\t\t\t\tcandidate.reasonCodes,\n\t\t\t\t\t],\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\treturn { decisionId, algorithm, candidates };\n\t}\n}\n\ninterface RetrievalInput {\n\tdecisionId?: string;\n\townerId: string;\n\tprojectId: string;\n\tsessionId?: string;\n\treader: ReaderScope;\n\tquery: string;\n\tlimit: number;\n}\n\nfunction memorySelect(): string {\n\treturn `SELECT record.id, record.owner_id, record.project_id, record.session_id, record.kind,\n\t record.current_revision, revision.content, revision.metadata, record.expires_at, record.tombstoned_at\n\t FROM asm_agent.memory_records record JOIN asm_agent.memory_revisions revision\n\t ON revision.memory_id = record.id AND revision.revision = record.current_revision`;\n}\n\nfunction scopePredicate(typeParameter: number, idParameter: number): string {\n\treturn `EXISTS (SELECT 1 FROM asm_agent.memory_reader_scopes scope\n\t WHERE scope.memory_id = record.id AND scope.reader_type = $${typeParameter} AND scope.reader_id = $${idParameter})`;\n}\n\nasync function assertEvidence(\n\tclient: PoolClient,\n\townerId: string,\n\tprojectId: string,\n\tevidenceIds: string[],\n): Promise<void> {\n\tif (evidenceIds.length === 0) throw new Error(\"Grounded memory requires at least one evidence snapshot\");\n\tconst result = await client.query<{ id: string }>(\n\t\t`SELECT id FROM asm_agent.evidence_snapshots WHERE id = ANY($1::uuid[])\n\t\t AND owner_id = $2 AND project_id = $3 AND revoked_at IS NULL`,\n\t\t[evidenceIds, ownerId, projectId],\n\t);\n\tif (result.rows.length !== new Set(evidenceIds).size)\n\t\tthrow new Error(\"Evidence missing, revoked, or outside project scope\");\n}\n\nasync function insertEvidenceLinks(\n\tclient: PoolClient,\n\tmemoryId: string,\n\trevision: number,\n\tevidenceIds: string[],\n): Promise<void> {\n\tfor (const evidenceId of new Set(evidenceIds)) {\n\t\tawait client.query(\n\t\t\t`INSERT INTO asm_agent.memory_revision_evidence (memory_id, revision, evidence_id) VALUES ($1,$2,$3)`,\n\t\t\t[memoryId, revision, evidenceId],\n\t\t);\n\t}\n}\n\nasync function insertScopes(client: PoolClient, memoryId: string, scopes: ReaderScope[]): Promise<void> {\n\tif (scopes.length === 0) throw new Error(\"Memory requires at least one reader scope\");\n\tfor (const scope of scopes) {\n\t\tawait client.query(\n\t\t\t\"INSERT INTO asm_agent.memory_reader_scopes (memory_id, reader_type, reader_id) VALUES ($1,$2,$3)\",\n\t\t\t[memoryId, scope.type, scope.id],\n\t\t);\n\t}\n}\n\nasync function upsertProjections(\n\tclient: PoolClient,\n\tmemoryId: string,\n\trevision: number,\n\tcontent: string,\n\tembedding?: { model: string; values: number[] },\n): Promise<void> {\n\tawait client.query(\n\t\t`INSERT INTO asm_agent.memory_lexical_projections (memory_id, revision, document)\n\t\t VALUES ($1,$2,to_tsvector('simple',$3)) ON CONFLICT (memory_id) DO UPDATE\n\t\t SET revision = EXCLUDED.revision, document = EXCLUDED.document, projected_at = clock_timestamp()`,\n\t\t[memoryId, revision, content],\n\t);\n\tif (!embedding) {\n\t\tawait client.query(\"DELETE FROM asm_agent.memory_vector_projections WHERE memory_id = $1\", [memoryId]);\n\t\treturn;\n\t}\n\tif (embedding.values.length === 0 || embedding.values.some((value) => !Number.isFinite(value))) {\n\t\tthrow new Error(\"Embedding must contain finite values\");\n\t}\n\tawait client.query(\n\t\t`INSERT INTO asm_agent.memory_vector_projections (memory_id, revision, model, dimensions, embedding)\n\t\t VALUES ($1,$2,$3,$4,$5) ON CONFLICT (memory_id) DO UPDATE SET revision = EXCLUDED.revision,\n\t\t model = EXCLUDED.model, dimensions = EXCLUDED.dimensions, embedding = EXCLUDED.embedding,\n\t\t projected_at = clock_timestamp()`,\n\t\t[memoryId, revision, embedding.model, embedding.values.length, embedding.values],\n\t);\n}\n\nasync function revokeMemories(\n\tclient: PoolClient,\n\tmemoryIds: string[],\n\townerId: string,\n\tprojectId: string,\n\treason: string,\n): Promise<boolean> {\n\tconst result = await client.query<{ id: string }>(\n\t\t`UPDATE asm_agent.memory_records SET tombstoned_at = clock_timestamp(), revocation_reason = $4,\n\t\t updated_at = clock_timestamp() WHERE id = ANY($1::uuid[]) AND owner_id = $2 AND project_id = $3\n\t\t AND tombstoned_at IS NULL RETURNING id`,\n\t\t[memoryIds, ownerId, projectId, reason],\n\t);\n\tif (result.rows.length === 0) return false;\n\tconst revokedIds = result.rows.map((row) => row.id);\n\tawait client.query(\"DELETE FROM asm_agent.memory_lexical_projections WHERE memory_id = ANY($1::uuid[])\", [\n\t\trevokedIds,\n\t]);\n\tawait client.query(\"DELETE FROM asm_agent.memory_vector_projections WHERE memory_id = ANY($1::uuid[])\", [\n\t\trevokedIds,\n\t]);\n\tawait client.query(\n\t\t`UPDATE asm_agent.retrieval_candidates SET eligible = false, selected = false,\n\t\t reason_codes = array_append(reason_codes, 'revoked') WHERE memory_id = ANY($1::uuid[]) AND eligible = true`,\n\t\t[revokedIds],\n\t);\n\treturn true;\n}\n\nfunction requiredContent(content: string): string {\n\tconst normalized = content.trim();\n\tif (!normalized) throw new Error(\"Memory content must not be empty\");\n\treturn normalized;\n}\n\nfunction stableJson(value: Record<string, unknown>): string {\n\treturn JSON.stringify(value, Object.keys(value).sort());\n}\n\nfunction cosineSimilarity(left: number[], right: number[]): number {\n\tlet dot = 0;\n\tlet leftMagnitude = 0;\n\tlet rightMagnitude = 0;\n\tfor (let index = 0; index < left.length; index++) {\n\t\tconst leftValue = left[index] ?? 0;\n\t\tconst rightValue = right[index] ?? 0;\n\t\tdot += leftValue * rightValue;\n\t\tleftMagnitude += leftValue * leftValue;\n\t\trightMagnitude += rightValue * rightValue;\n\t}\n\tif (leftMagnitude === 0 || rightMagnitude === 0) return 0;\n\treturn Math.max(0, Math.min(1, (dot / Math.sqrt(leftMagnitude * rightMagnitude) + 1) / 2));\n}\n\nfunction toLexicalCandidate(row: LexicalRow): RetrievalCandidateView {\n\treturn {\n\t\tmemoryId: row.id,\n\t\trevision: row.revision,\n\t\tcontent: row.content,\n\t\tscore: row.score,\n\t\tselected: true,\n\t\treasonCodes: [\"lexical_match\"],\n\t};\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ASM_AGENT_DATABASE_SCHEMA_ENV, ASM_AGENT_DATABASE_URL_ENV, loadPostgresConfig, type PostgresConfig, } from "./config.js";
|
|
2
|
+
export { PostgresStore, type ReadinessReport } from "./database.js";
|
|
3
|
+
export { type AuthorizedMemoryCandidate, type EvidenceSnapshotInput, type FeedbackRating, GroundedMemoryStore, type LearnedRankingInput, type MemoryKind, type MemoryRecordView, type ReaderScope, type ReaderType, type RetrievalCandidateView, type RetrievalResult, } from "./grounded-memory.js";
|
|
4
|
+
export type { DurableRepositories, GoalRepository, IdempotencyRepository, IdentityRepository, LeaseRepository, OutboxRepository, ScheduleRepository, SessionRepository, } from "./repositories.js";
|
|
5
|
+
export type { GoalRecord, GoalStatus, IdempotencyStartResult, LeaseRecord, OutboxEvent, OwnerRecord, ProjectRecord, ScheduleKind, ScheduleRecord, ScheduleStatus, SessionMessageInput, SessionMessageRecord, SessionRecord, SessionStatus, } from "./types.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,0BAA0B,EAC1B,kBAAkB,EAClB,KAAK,cAAc,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EACN,KAAK,yBAAyB,EAC9B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,sBAAsB,EAC3B,KAAK,eAAe,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EACX,mBAAmB,EACnB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACX,UAAU,EACV,UAAU,EACV,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,WAAW,EACX,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,aAAa,GACb,MAAM,YAAY,CAAC","sourcesContent":["export {\n\tASM_AGENT_DATABASE_SCHEMA_ENV,\n\tASM_AGENT_DATABASE_URL_ENV,\n\tloadPostgresConfig,\n\ttype PostgresConfig,\n} from \"./config.js\";\nexport { PostgresStore, type ReadinessReport } from \"./database.js\";\nexport {\n\ttype AuthorizedMemoryCandidate,\n\ttype EvidenceSnapshotInput,\n\ttype FeedbackRating,\n\tGroundedMemoryStore,\n\ttype LearnedRankingInput,\n\ttype MemoryKind,\n\ttype MemoryRecordView,\n\ttype ReaderScope,\n\ttype ReaderType,\n\ttype RetrievalCandidateView,\n\ttype RetrievalResult,\n} from \"./grounded-memory.js\";\nexport type {\n\tDurableRepositories,\n\tGoalRepository,\n\tIdempotencyRepository,\n\tIdentityRepository,\n\tLeaseRepository,\n\tOutboxRepository,\n\tScheduleRepository,\n\tSessionRepository,\n} from \"./repositories.js\";\nexport type {\n\tGoalRecord,\n\tGoalStatus,\n\tIdempotencyStartResult,\n\tLeaseRecord,\n\tOutboxEvent,\n\tOwnerRecord,\n\tProjectRecord,\n\tScheduleKind,\n\tScheduleRecord,\n\tScheduleStatus,\n\tSessionMessageInput,\n\tSessionMessageRecord,\n\tSessionRecord,\n\tSessionStatus,\n} from \"./types.js\";\n"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,0BAA0B,EAC1B,kBAAkB,GAElB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAwB,MAAM,eAAe,CAAC;AACpE,OAAO,EAIN,mBAAmB,GAQnB,MAAM,sBAAsB,CAAC","sourcesContent":["export {\n\tASM_AGENT_DATABASE_SCHEMA_ENV,\n\tASM_AGENT_DATABASE_URL_ENV,\n\tloadPostgresConfig,\n\ttype PostgresConfig,\n} from \"./config.js\";\nexport { PostgresStore, type ReadinessReport } from \"./database.js\";\nexport {\n\ttype AuthorizedMemoryCandidate,\n\ttype EvidenceSnapshotInput,\n\ttype FeedbackRating,\n\tGroundedMemoryStore,\n\ttype LearnedRankingInput,\n\ttype MemoryKind,\n\ttype MemoryRecordView,\n\ttype ReaderScope,\n\ttype ReaderType,\n\ttype RetrievalCandidateView,\n\ttype RetrievalResult,\n} from \"./grounded-memory.js\";\nexport type {\n\tDurableRepositories,\n\tGoalRepository,\n\tIdempotencyRepository,\n\tIdentityRepository,\n\tLeaseRepository,\n\tOutboxRepository,\n\tScheduleRepository,\n\tSessionRepository,\n} from \"./repositories.js\";\nexport type {\n\tGoalRecord,\n\tGoalStatus,\n\tIdempotencyStartResult,\n\tLeaseRecord,\n\tOutboxEvent,\n\tOwnerRecord,\n\tProjectRecord,\n\tScheduleKind,\n\tScheduleRecord,\n\tScheduleStatus,\n\tSessionMessageInput,\n\tSessionMessageRecord,\n\tSessionRecord,\n\tSessionStatus,\n} from \"./types.js\";\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { GoalRecord, IdempotencyStartResult, LeaseRecord, OutboxEvent, OwnerRecord, ProjectRecord, ScheduleRecord, SessionMessageInput, SessionMessageRecord, SessionRecord, SessionStatus } from "./types.js";
|
|
2
|
+
export interface IdentityRepository {
|
|
3
|
+
ensureOwner(record: OwnerRecord): Promise<void>;
|
|
4
|
+
ensureProject(record: ProjectRecord): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface SessionRepository {
|
|
7
|
+
createSession(input: {
|
|
8
|
+
id: string;
|
|
9
|
+
projectId: string;
|
|
10
|
+
parentSessionId?: string;
|
|
11
|
+
cwd: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
rlmDepth?: number;
|
|
14
|
+
}): Promise<SessionRecord>;
|
|
15
|
+
getSession(id: string): Promise<SessionRecord | undefined>;
|
|
16
|
+
updateSessionStatus(id: string, status: SessionStatus): Promise<boolean>;
|
|
17
|
+
appendMessage(sessionId: string, message: SessionMessageInput): Promise<number>;
|
|
18
|
+
listMessages(sessionId: string, afterSequence?: number): Promise<SessionMessageRecord[]>;
|
|
19
|
+
}
|
|
20
|
+
export interface GoalRepository {
|
|
21
|
+
upsertGoal(goal: GoalRecord): Promise<void>;
|
|
22
|
+
getCurrentGoal(sessionId: string): Promise<GoalRecord | undefined>;
|
|
23
|
+
}
|
|
24
|
+
export interface ScheduleRepository {
|
|
25
|
+
upsertSchedule(schedule: ScheduleRecord): Promise<void>;
|
|
26
|
+
listDueSchedules(now: Date, limit?: number): Promise<ScheduleRecord[]>;
|
|
27
|
+
}
|
|
28
|
+
export interface LeaseRepository {
|
|
29
|
+
acquireLease(input: {
|
|
30
|
+
resourceType: string;
|
|
31
|
+
resourceId: string;
|
|
32
|
+
ownerId: string;
|
|
33
|
+
ttlMs: number;
|
|
34
|
+
}): Promise<LeaseRecord | undefined>;
|
|
35
|
+
renewLease(lease: LeaseRecord, ttlMs: number): Promise<boolean>;
|
|
36
|
+
releaseLease(lease: LeaseRecord): Promise<boolean>;
|
|
37
|
+
}
|
|
38
|
+
export interface IdempotencyRepository {
|
|
39
|
+
startIdempotent(input: {
|
|
40
|
+
scope: string;
|
|
41
|
+
key: string;
|
|
42
|
+
requestHash: string;
|
|
43
|
+
expiresAt?: Date;
|
|
44
|
+
}): Promise<IdempotencyStartResult>;
|
|
45
|
+
completeIdempotent(scope: string, key: string, response: unknown): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export interface OutboxRepository {
|
|
48
|
+
enqueueOutbox(input: Omit<OutboxEvent, "id" | "attempts">): Promise<string>;
|
|
49
|
+
claimOutbox(workerId: string, limit?: number, lockMs?: number): Promise<OutboxEvent[]>;
|
|
50
|
+
markOutboxPublished(id: string, workerId: string): Promise<boolean>;
|
|
51
|
+
}
|
|
52
|
+
export type DurableRepositories = IdentityRepository & SessionRepository & GoalRepository & ScheduleRepository & LeaseRepository & IdempotencyRepository & OutboxRepository;
|
|
53
|
+
//# sourceMappingURL=repositories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../src/repositories.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tGoalRecord,\n\tIdempotencyStartResult,\n\tLeaseRecord,\n\tOutboxEvent,\n\tOwnerRecord,\n\tProjectRecord,\n\tScheduleRecord,\n\tSessionMessageInput,\n\tSessionMessageRecord,\n\tSessionRecord,\n\tSessionStatus,\n} from \"./types.js\";\n\nexport interface IdentityRepository {\n\tensureOwner(record: OwnerRecord): Promise<void>;\n\tensureProject(record: ProjectRecord): Promise<void>;\n}\n\nexport interface SessionRepository {\n\tcreateSession(input: {\n\t\tid: string;\n\t\tprojectId: string;\n\t\tparentSessionId?: string;\n\t\tcwd: string;\n\t\tname?: string;\n\t\trlmDepth?: number;\n\t}): Promise<SessionRecord>;\n\tgetSession(id: string): Promise<SessionRecord | undefined>;\n\tupdateSessionStatus(id: string, status: SessionStatus): Promise<boolean>;\n\tappendMessage(sessionId: string, message: SessionMessageInput): Promise<number>;\n\tlistMessages(sessionId: string, afterSequence?: number): Promise<SessionMessageRecord[]>;\n}\n\nexport interface GoalRepository {\n\tupsertGoal(goal: GoalRecord): Promise<void>;\n\tgetCurrentGoal(sessionId: string): Promise<GoalRecord | undefined>;\n}\n\nexport interface ScheduleRepository {\n\tupsertSchedule(schedule: ScheduleRecord): Promise<void>;\n\tlistDueSchedules(now: Date, limit?: number): Promise<ScheduleRecord[]>;\n}\n\nexport interface LeaseRepository {\n\tacquireLease(input: {\n\t\tresourceType: string;\n\t\tresourceId: string;\n\t\townerId: string;\n\t\tttlMs: number;\n\t}): Promise<LeaseRecord | undefined>;\n\trenewLease(lease: LeaseRecord, ttlMs: number): Promise<boolean>;\n\treleaseLease(lease: LeaseRecord): Promise<boolean>;\n}\n\nexport interface IdempotencyRepository {\n\tstartIdempotent(input: {\n\t\tscope: string;\n\t\tkey: string;\n\t\trequestHash: string;\n\t\texpiresAt?: Date;\n\t}): Promise<IdempotencyStartResult>;\n\tcompleteIdempotent(scope: string, key: string, response: unknown): Promise<void>;\n}\n\nexport interface OutboxRepository {\n\tenqueueOutbox(input: Omit<OutboxEvent, \"id\" | \"attempts\">): Promise<string>;\n\tclaimOutbox(workerId: string, limit?: number, lockMs?: number): Promise<OutboxEvent[]>;\n\tmarkOutboxPublished(id: string, workerId: string): Promise<boolean>;\n}\n\nexport type DurableRepositories = IdentityRepository &\n\tSessionRepository &\n\tGoalRepository &\n\tScheduleRepository &\n\tLeaseRepository &\n\tIdempotencyRepository &\n\tOutboxRepository;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.js","sourceRoot":"","sources":["../src/repositories.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tGoalRecord,\n\tIdempotencyStartResult,\n\tLeaseRecord,\n\tOutboxEvent,\n\tOwnerRecord,\n\tProjectRecord,\n\tScheduleRecord,\n\tSessionMessageInput,\n\tSessionMessageRecord,\n\tSessionRecord,\n\tSessionStatus,\n} from \"./types.js\";\n\nexport interface IdentityRepository {\n\tensureOwner(record: OwnerRecord): Promise<void>;\n\tensureProject(record: ProjectRecord): Promise<void>;\n}\n\nexport interface SessionRepository {\n\tcreateSession(input: {\n\t\tid: string;\n\t\tprojectId: string;\n\t\tparentSessionId?: string;\n\t\tcwd: string;\n\t\tname?: string;\n\t\trlmDepth?: number;\n\t}): Promise<SessionRecord>;\n\tgetSession(id: string): Promise<SessionRecord | undefined>;\n\tupdateSessionStatus(id: string, status: SessionStatus): Promise<boolean>;\n\tappendMessage(sessionId: string, message: SessionMessageInput): Promise<number>;\n\tlistMessages(sessionId: string, afterSequence?: number): Promise<SessionMessageRecord[]>;\n}\n\nexport interface GoalRepository {\n\tupsertGoal(goal: GoalRecord): Promise<void>;\n\tgetCurrentGoal(sessionId: string): Promise<GoalRecord | undefined>;\n}\n\nexport interface ScheduleRepository {\n\tupsertSchedule(schedule: ScheduleRecord): Promise<void>;\n\tlistDueSchedules(now: Date, limit?: number): Promise<ScheduleRecord[]>;\n}\n\nexport interface LeaseRepository {\n\tacquireLease(input: {\n\t\tresourceType: string;\n\t\tresourceId: string;\n\t\townerId: string;\n\t\tttlMs: number;\n\t}): Promise<LeaseRecord | undefined>;\n\trenewLease(lease: LeaseRecord, ttlMs: number): Promise<boolean>;\n\treleaseLease(lease: LeaseRecord): Promise<boolean>;\n}\n\nexport interface IdempotencyRepository {\n\tstartIdempotent(input: {\n\t\tscope: string;\n\t\tkey: string;\n\t\trequestHash: string;\n\t\texpiresAt?: Date;\n\t}): Promise<IdempotencyStartResult>;\n\tcompleteIdempotent(scope: string, key: string, response: unknown): Promise<void>;\n}\n\nexport interface OutboxRepository {\n\tenqueueOutbox(input: Omit<OutboxEvent, \"id\" | \"attempts\">): Promise<string>;\n\tclaimOutbox(workerId: string, limit?: number, lockMs?: number): Promise<OutboxEvent[]>;\n\tmarkOutboxPublished(id: string, workerId: string): Promise<boolean>;\n}\n\nexport type DurableRepositories = IdentityRepository &\n\tSessionRepository &\n\tGoalRepository &\n\tScheduleRepository &\n\tLeaseRepository &\n\tIdempotencyRepository &\n\tOutboxRepository;\n"]}
|