@dsh-enhanced/personal-memory 0.1.48 → 0.1.53
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 -4
- package/lib/evidence-runtime.js +1 -1
- package/lib/evidence-runtime.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/learning-adoptions.d.ts +100 -0
- package/lib/learning-adoptions.d.ts.map +1 -0
- package/lib/learning-adoptions.js +85 -0
- package/lib/learning-adoptions.js.map +1 -0
- package/lib/learning-service.d.ts +38 -0
- package/lib/learning-service.d.ts.map +1 -0
- package/lib/learning-service.js +236 -0
- package/lib/learning-service.js.map +1 -0
- package/lib/service.d.ts +58 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +72 -11
- package/lib/service.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +45 -2
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +21 -0
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +263 -3
- package/lib/store.js.map +1 -1
- package/lib/types.d.ts +1 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +31 -20
package/lib/store.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MemoryEvidenceLedger } from './evidence-ledger.js';
|
|
2
|
+
import { type MemoryLearningAdoptionCommit, type MemoryLearningAdoptionGrant, type MemoryLearningAdoptionAvailability, type MemoryLearningAdoptionResult, type MemoryLearningManagedSource, type MemoryLearningSourceInvalidation, type MemoryLearningTarget, type MemoryLearningTargetInput, type MemoryLearningValidatedRef } from './learning-adoptions.js';
|
|
2
3
|
import type { ApprovedMemoryMutation, MemoryAgentContext, MemoryExportDocument, MemoryIdentity, MemoryMutation, MemoryOwnerNamespace, MemoryPromotionCancellationInput, MemoryPromotionCancellationResult, MemoryPromotionReference, MemoryPromotionSettlement, MemoryProposalInput, MemoryRecord, MemorySearchHit, MemorySearchRequest, MemorySnapshot, MemorySnapshotRequest, StoredMemoryProposal, StoredMemoryProposalIntent, StoredMemoryPromotionResult } from './types.js';
|
|
3
4
|
export type MemoryStoreErrorCode = 'content-too-large' | 'duplicate-content' | 'idempotency-conflict' | 'invalid-entry' | 'invalid-identity' | 'invalid-path' | 'not-found' | 'record-limit' | 'schema-too-new' | 'unsafe-file' | 'version-conflict';
|
|
4
5
|
export declare class MemoryStoreError extends Error {
|
|
@@ -72,6 +73,26 @@ export declare class MemoryStore {
|
|
|
72
73
|
readonly evidence: MemoryEvidenceLedger;
|
|
73
74
|
constructor(options: MemoryStoreOptions);
|
|
74
75
|
close(): void;
|
|
76
|
+
/** Register a frozen Host grant; reusing its id with different authority never resets quota. */
|
|
77
|
+
registerLearningGrant(input: MemoryLearningAdoptionGrant): Readonly<{
|
|
78
|
+
authorityId: string;
|
|
79
|
+
grantDigest: string;
|
|
80
|
+
}>;
|
|
81
|
+
/** Read the already registered grant without creating it or reserving budget. */
|
|
82
|
+
inspectLearningAdoptionAvailability(input: MemoryLearningAdoptionGrant): MemoryLearningAdoptionAvailability;
|
|
83
|
+
lookupLearningAdoption(input: Readonly<{
|
|
84
|
+
authorityId: string;
|
|
85
|
+
operationId: string;
|
|
86
|
+
requestDigest: string;
|
|
87
|
+
}>): Readonly<MemoryLearningAdoptionResult> | undefined;
|
|
88
|
+
applyLearningAdoption(input: MemoryLearningAdoptionCommit): Readonly<MemoryLearningAdoptionResult>;
|
|
89
|
+
/** This synchronous Host view admits only source-validated exact versions. */
|
|
90
|
+
withLearningVisibility<T>(validatedExactRefs: readonly MemoryLearningValidatedRef[], callback: () => T): T;
|
|
91
|
+
inspectLearningTarget(input: MemoryLearningTargetInput): MemoryLearningTarget | undefined;
|
|
92
|
+
listManagedLearningSources(contextInput: MemoryAgentContext): readonly Readonly<MemoryLearningManagedSource>[];
|
|
93
|
+
invalidateManagedLearningSource(input: MemoryLearningSourceInvalidation): Readonly<{
|
|
94
|
+
invalidated: boolean;
|
|
95
|
+
}>;
|
|
75
96
|
resolveProposalNotAfter(namespace: MemoryOwnerNamespace, idempotencyKey: string, ttlMs: number, requested?: number): number;
|
|
76
97
|
get(namespace: MemoryOwnerNamespace, identity: MemoryIdentity, id: string): MemoryRecord | undefined;
|
|
77
98
|
list(namespace: MemoryOwnerNamespace, identity: MemoryIdentity, options?: {
|
package/lib/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/lib/store.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { isAbsolute } from 'node:path';
|
|
3
|
-
import { validatePreferenceMemoryPromotionCancellationRequest, withPreferenceMemoryPromotionCancellationReceiptDigest, withPreferenceMemoryPromotionResultDigest, } from '@dsh-enhanced/assistant-growth-contract';
|
|
3
|
+
import { growthObjectDigest, memoryLearningRequestDigest, validateMemoryLearningOwner, validatePreferenceMemoryPromotionCancellationRequest, withPreferenceMemoryPromotionCancellationReceiptDigest, withPreferenceMemoryPromotionResultDigest, } from '@dsh-enhanced/assistant-growth-contract';
|
|
4
4
|
import { APPROVAL_DISPLAY_BUDGET } from '@dsh-enhanced/assistant-policy';
|
|
5
5
|
import { MemoryDatabaseError, openMemoryDatabase } from './sqlite.js';
|
|
6
6
|
import { MemoryEvidenceLedger } from './evidence-ledger.js';
|
|
7
7
|
import { tokenizeMemory } from './tokenize.js';
|
|
8
8
|
import { memoryKnowledgeText, normalizeMemoryKnowledge } from './knowledge.js';
|
|
9
|
+
import { learningGrantDigest, learningRecordDigest, learningResultDigest, validateLearningGrant, validateLearningRequest, validateLearningReviewReceipt, } from './learning-adoptions.js';
|
|
9
10
|
export class MemoryStoreError extends Error {
|
|
10
11
|
code;
|
|
11
12
|
constructor(code, message) {
|
|
@@ -289,6 +290,7 @@ export class MemoryStore {
|
|
|
289
290
|
#now;
|
|
290
291
|
#closed = false;
|
|
291
292
|
#reading = false;
|
|
293
|
+
#learningVisibility;
|
|
292
294
|
evidence;
|
|
293
295
|
constructor(options) {
|
|
294
296
|
this.#maxContentBytes = options.maxContentBytes ?? 4_096;
|
|
@@ -316,6 +318,262 @@ export class MemoryStore {
|
|
|
316
318
|
this.#closed = true;
|
|
317
319
|
this.#database.close();
|
|
318
320
|
}
|
|
321
|
+
#learningNamespace(owner) {
|
|
322
|
+
return Object.freeze({ mode: 'delivery', principalDigest: memoryPrincipalDigest(owner.principalId),
|
|
323
|
+
principalRecordId: owner.principalRecordId, principalVersion: owner.principalVersion });
|
|
324
|
+
}
|
|
325
|
+
#learningIdentity(owner) {
|
|
326
|
+
return Object.freeze({ owner: 'agent', scope: 'workspace', workspace: owner.workspace, agentPreset: owner.agentPreset });
|
|
327
|
+
}
|
|
328
|
+
#learningGrantRow(authorityId) {
|
|
329
|
+
return this.#database.prepare(`SELECT grant_json, grant_digest, used_mutations, used_content_bytes
|
|
330
|
+
FROM memory_learning_adoptions WHERE authority_id = ? AND operation_id = '' AND row_kind = 'grant'`)
|
|
331
|
+
.get(authorityId);
|
|
332
|
+
}
|
|
333
|
+
/** Register a frozen Host grant; reusing its id with different authority never resets quota. */
|
|
334
|
+
registerLearningGrant(input) {
|
|
335
|
+
const grant = validateLearningGrant(input);
|
|
336
|
+
const digest = learningGrantDigest(grant);
|
|
337
|
+
return this.#transaction(() => {
|
|
338
|
+
const existing = this.#learningGrantRow(grant.authorityId);
|
|
339
|
+
if (existing === undefined)
|
|
340
|
+
this.#database.prepare(`INSERT INTO memory_learning_adoptions(
|
|
341
|
+
authority_id, operation_id, row_kind, grant_json, grant_digest, used_mutations, used_content_bytes)
|
|
342
|
+
VALUES (?, '', 'grant', ?, ?, 0, 0)`).run(grant.authorityId, JSON.stringify(grant), digest);
|
|
343
|
+
else if (existing.grant_digest !== digest || growthObjectDigest(JSON.parse(existing.grant_json)) !== digest) {
|
|
344
|
+
throw new MemoryStoreError('idempotency-conflict', 'learning grant authority changed');
|
|
345
|
+
}
|
|
346
|
+
return Object.freeze({ authorityId: grant.authorityId, grantDigest: digest });
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
/** Read the already registered grant without creating it or reserving budget. */
|
|
350
|
+
inspectLearningAdoptionAvailability(input) {
|
|
351
|
+
const grant = validateLearningGrant(input);
|
|
352
|
+
const grantDigest = learningGrantDigest(grant);
|
|
353
|
+
const row = this.#learningGrantRow(grant.authorityId);
|
|
354
|
+
if (row === undefined || row.grant_digest !== grantDigest
|
|
355
|
+
|| growthObjectDigest(JSON.parse(row.grant_json)) !== grantDigest) {
|
|
356
|
+
throw new MemoryStoreError('idempotency-conflict', 'learning grant is absent or changed');
|
|
357
|
+
}
|
|
358
|
+
const remainingMutations = Math.max(0, grant.maxMutations - row.used_mutations);
|
|
359
|
+
const remainingContentBytes = Math.max(0, grant.maxTotalContentBytes - row.used_content_bytes);
|
|
360
|
+
return Object.freeze({ authorityId: grant.authorityId, grantDigest, expiresAt: grant.expiresAt,
|
|
361
|
+
remainingMutations, remainingContentBytes,
|
|
362
|
+
available: this.#now() < grant.expiresAt && remainingMutations > 0
|
|
363
|
+
&& (remainingContentBytes > 0 || grant.operations.includes('remove')) });
|
|
364
|
+
}
|
|
365
|
+
lookupLearningAdoption(input) {
|
|
366
|
+
if (typeof input.authorityId !== 'string' || typeof input.operationId !== 'string'
|
|
367
|
+
|| !SHA_256.test(input.requestDigest))
|
|
368
|
+
throw new MemoryStoreError('invalid-entry', 'learning adoption lookup is invalid');
|
|
369
|
+
const row = this.#database.prepare(`SELECT request_digest, result_json FROM memory_learning_adoptions
|
|
370
|
+
WHERE authority_id = ? AND operation_id = ? AND row_kind = 'operation'`).get(input.authorityId, input.operationId);
|
|
371
|
+
if (row === undefined)
|
|
372
|
+
return undefined;
|
|
373
|
+
if (row.request_digest !== input.requestDigest)
|
|
374
|
+
throw new MemoryStoreError('idempotency-conflict', 'learning operation was reused');
|
|
375
|
+
return Object.freeze(JSON.parse(row.result_json));
|
|
376
|
+
}
|
|
377
|
+
applyLearningAdoption(input) {
|
|
378
|
+
const grant = validateLearningGrant(input.grant);
|
|
379
|
+
const request = validateLearningRequest(input.request);
|
|
380
|
+
const requestDigest = memoryLearningRequestDigest(request);
|
|
381
|
+
const review = validateLearningReviewReceipt(input.reviewReceipt, request, grant);
|
|
382
|
+
if (growthObjectDigest(request.owner) !== growthObjectDigest(grant.owner)
|
|
383
|
+
|| !grant.operations.includes(request.mutation.op)
|
|
384
|
+
|| (request.mutation.op !== 'remove' && !grant.kinds.includes(request.mutation.entry.kind))
|
|
385
|
+
|| !Number.isSafeInteger(input.sourceObservedAt) || input.sourceObservedAt < 0) {
|
|
386
|
+
throw new MemoryStoreError('invalid-entry', 'learning operation exceeds grant scope');
|
|
387
|
+
}
|
|
388
|
+
const contentBytes = request.mutation.op === 'remove' ? 0
|
|
389
|
+
: Buffer.byteLength(request.mutation.entry.content, 'utf8')
|
|
390
|
+
+ (request.mutation.entry.knowledge === undefined ? 0 : Buffer.byteLength(JSON.stringify(request.mutation.entry.knowledge), 'utf8'));
|
|
391
|
+
const namespace = this.#learningNamespace(request.owner);
|
|
392
|
+
const identity = this.#learningIdentity(request.owner);
|
|
393
|
+
return this.#transaction(() => {
|
|
394
|
+
const prior = this.lookupLearningAdoption({ authorityId: grant.authorityId, operationId: request.operationId, requestDigest });
|
|
395
|
+
if (prior !== undefined)
|
|
396
|
+
return prior;
|
|
397
|
+
const current = this.#learningGrantRow(grant.authorityId);
|
|
398
|
+
if (current === undefined || current.grant_digest !== learningGrantDigest(grant)
|
|
399
|
+
|| growthObjectDigest(JSON.parse(current.grant_json)) !== current.grant_digest) {
|
|
400
|
+
throw new MemoryStoreError('idempotency-conflict', 'learning grant is absent or changed');
|
|
401
|
+
}
|
|
402
|
+
const now = this.#now();
|
|
403
|
+
if (now >= grant.expiresAt)
|
|
404
|
+
throw new MemoryStoreError('invalid-entry', 'learning grant expired');
|
|
405
|
+
if (current.used_mutations >= grant.maxMutations || current.used_content_bytes + contentBytes > grant.maxTotalContentBytes) {
|
|
406
|
+
throw new MemoryStoreError('record-limit', 'learning adoption quota exceeded');
|
|
407
|
+
}
|
|
408
|
+
if (request.mutation.op !== 'add') {
|
|
409
|
+
const target = this.inspectLearningTarget({ owner: request.owner, id: request.mutation.id,
|
|
410
|
+
expectedVersion: request.mutation.expectedVersion });
|
|
411
|
+
if (target === undefined)
|
|
412
|
+
throw new MemoryStoreError('version-conflict', 'learning target is no longer managed');
|
|
413
|
+
}
|
|
414
|
+
const operationKey = growthObjectDigest([grant.authorityId, request.operationId]);
|
|
415
|
+
const idempotencyKey = `memory-learning:${operationKey}`;
|
|
416
|
+
const reservedAudit = this.#database.prepare(`SELECT 1 AS used FROM memory_audit
|
|
417
|
+
WHERE namespace_key = ? AND idempotency_key = ?`).get(memoryOwnerNamespaceKey(namespace), idempotencyKey);
|
|
418
|
+
if (reservedAudit !== undefined)
|
|
419
|
+
throw new MemoryStoreError('idempotency-conflict', 'learning audit key was already used');
|
|
420
|
+
const entry = request.mutation.op === 'remove' ? undefined
|
|
421
|
+
: { kind: request.mutation.entry.kind, content: request.mutation.entry.content,
|
|
422
|
+
sensitivity: 'private', trust: 'agent-observed', confidence: 0.5,
|
|
423
|
+
provenance: { source: 'assistant-memory-learning', observedAt: input.sourceObservedAt,
|
|
424
|
+
uri: `delivery:foreground:${request.source.inboxId}` },
|
|
425
|
+
expiresAt: now + grant.maxRecordTtlMs,
|
|
426
|
+
...(request.mutation.entry.knowledge === undefined ? {} : { knowledge: request.mutation.entry.knowledge }) };
|
|
427
|
+
const mutation = request.mutation.op === 'remove'
|
|
428
|
+
? { op: 'remove', namespace, identity, id: request.mutation.id, expectedVersion: request.mutation.expectedVersion, idempotencyKey }
|
|
429
|
+
: request.mutation.op === 'replace'
|
|
430
|
+
? { op: 'replace', namespace, identity, id: request.mutation.id, expectedVersion: request.mutation.expectedVersion,
|
|
431
|
+
idempotencyKey, entry: entry }
|
|
432
|
+
: { op: 'add', namespace, identity, idempotencyKey, entry: entry };
|
|
433
|
+
const record = this.#applyMutationInCurrentTransaction(mutation);
|
|
434
|
+
const body = { protocol: 'memory-learning-adoption/v1', authorityId: grant.authorityId,
|
|
435
|
+
operationId: request.operationId, requestDigest, reviewReceiptDigest: review.receiptDigest,
|
|
436
|
+
record, recordDigest: learningRecordDigest(record), adoptedAt: now };
|
|
437
|
+
const result = Object.freeze({ ...body, receiptDigest: learningResultDigest(body) });
|
|
438
|
+
const updated = this.#database.prepare(`UPDATE memory_learning_adoptions
|
|
439
|
+
SET used_mutations = used_mutations + 1, used_content_bytes = used_content_bytes + ?
|
|
440
|
+
WHERE authority_id = ? AND operation_id = '' AND row_kind = 'grant'
|
|
441
|
+
AND grant_digest = ? AND used_mutations = ? AND used_content_bytes = ?`).run(contentBytes, grant.authorityId, current.grant_digest, current.used_mutations, current.used_content_bytes);
|
|
442
|
+
if (updated.changes !== 1)
|
|
443
|
+
throw new MemoryStoreError('version-conflict', 'learning quota changed');
|
|
444
|
+
this.#database.prepare(`INSERT INTO memory_learning_adoptions(
|
|
445
|
+
authority_id, operation_id, row_kind, request_json, request_digest, review_receipt_json,
|
|
446
|
+
source_digest, result_json, record_id, record_version, record_digest)
|
|
447
|
+
VALUES (?, ?, 'operation', ?, ?, ?, ?, ?, ?, ?, ?)`).run(grant.authorityId, request.operationId, JSON.stringify(request), requestDigest, JSON.stringify(review), request.source.sourceDigest, JSON.stringify(result), record.id, record.version, body.recordDigest);
|
|
448
|
+
return result;
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
#learningOperationForRecord(record) {
|
|
452
|
+
return this.#database.prepare(`SELECT authority_id, operation_id, request_json, source_digest,
|
|
453
|
+
record_digest, invalidated_at FROM memory_learning_adoptions
|
|
454
|
+
WHERE row_kind = 'operation' AND record_id = ? AND record_version = ? LIMIT 1`).get(record.id, record.version);
|
|
455
|
+
}
|
|
456
|
+
#learningVisible(record) {
|
|
457
|
+
const managed = this.#learningOperationForRecord(record);
|
|
458
|
+
if (managed === undefined)
|
|
459
|
+
return true;
|
|
460
|
+
return managed.invalidated_at === null && managed.record_digest === learningRecordDigest(record)
|
|
461
|
+
&& this.#learningVisibility?.has(`${record.id}:${record.version}:${managed.record_digest}`) === true;
|
|
462
|
+
}
|
|
463
|
+
/** This synchronous Host view admits only source-validated exact versions. */
|
|
464
|
+
withLearningVisibility(validatedExactRefs, callback) {
|
|
465
|
+
if (!Array.isArray(validatedExactRefs) || validatedExactRefs.length > 10_000
|
|
466
|
+
|| typeof callback !== 'function' || this.#learningVisibility !== undefined) {
|
|
467
|
+
throw new MemoryStoreError('invalid-entry', 'learning visibility requires a bounded synchronous view');
|
|
468
|
+
}
|
|
469
|
+
const refs = new Set();
|
|
470
|
+
for (const ref of validatedExactRefs) {
|
|
471
|
+
if (!ref || typeof ref.id !== 'string' || !Number.isSafeInteger(ref.version) || ref.version < 1
|
|
472
|
+
|| !SHA_256.test(ref.recordDigest))
|
|
473
|
+
throw new MemoryStoreError('invalid-entry', 'learning visibility ref is invalid');
|
|
474
|
+
const key = `${ref.id}:${ref.version}:${ref.recordDigest}`;
|
|
475
|
+
if (refs.has(key))
|
|
476
|
+
throw new MemoryStoreError('invalid-entry', 'duplicate learning visibility ref');
|
|
477
|
+
refs.add(key);
|
|
478
|
+
}
|
|
479
|
+
this.#learningVisibility = refs;
|
|
480
|
+
try {
|
|
481
|
+
const result = callback();
|
|
482
|
+
if ((typeof result === 'object' || typeof result === 'function') && result !== null && 'then' in result) {
|
|
483
|
+
try {
|
|
484
|
+
Promise.prototype.then.call(result, undefined, () => undefined);
|
|
485
|
+
}
|
|
486
|
+
catch { /* Not a native Promise. */ }
|
|
487
|
+
throw new MemoryStoreError('invalid-entry', 'learning visibility callback must be synchronous');
|
|
488
|
+
}
|
|
489
|
+
return result;
|
|
490
|
+
}
|
|
491
|
+
finally {
|
|
492
|
+
this.#learningVisibility = undefined;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
inspectLearningTarget(input) {
|
|
496
|
+
const owner = validateMemoryLearningOwner(input.owner);
|
|
497
|
+
if (typeof input.id !== 'string' || !Number.isSafeInteger(input.expectedVersion) || input.expectedVersion < 1) {
|
|
498
|
+
throw new MemoryStoreError('invalid-entry', 'learning target is invalid');
|
|
499
|
+
}
|
|
500
|
+
const record = this.#selectRecord(normalizeIdentity(this.#learningIdentity(owner), this.#learningNamespace(owner)), input.id);
|
|
501
|
+
if (record === undefined || record.status !== 'active' || this.#expired(record)
|
|
502
|
+
|| record.version !== input.expectedVersion)
|
|
503
|
+
return undefined;
|
|
504
|
+
const actual = this.#toRecord(record);
|
|
505
|
+
const managed = this.#learningOperationForRecord(actual);
|
|
506
|
+
if (managed === undefined || managed.invalidated_at !== null || managed.record_digest !== learningRecordDigest(actual))
|
|
507
|
+
return undefined;
|
|
508
|
+
const request = validateLearningRequest(JSON.parse(managed.request_json));
|
|
509
|
+
if (growthObjectDigest(request.owner) !== growthObjectDigest(owner) || request.mutation.op === 'remove'
|
|
510
|
+
|| (actual.kind !== 'fact' && actual.kind !== 'experience'))
|
|
511
|
+
return undefined;
|
|
512
|
+
return Object.freeze({ id: actual.id, version: actual.version, managed: true,
|
|
513
|
+
kind: actual.kind, content: actual.content,
|
|
514
|
+
...(actual.knowledge === undefined ? {} : { knowledge: actual.knowledge }) });
|
|
515
|
+
}
|
|
516
|
+
listManagedLearningSources(contextInput) {
|
|
517
|
+
const context = normalizeAgentContext(contextInput);
|
|
518
|
+
if (context.namespace.mode !== 'delivery')
|
|
519
|
+
return Object.freeze([]);
|
|
520
|
+
const identity = normalizeIdentity({ owner: 'agent', scope: 'workspace', workspace: context.workspace,
|
|
521
|
+
agentPreset: context.agentPreset }, context.namespace);
|
|
522
|
+
const rows = this.#database.prepare(`SELECT adoption.authority_id, adoption.request_json,
|
|
523
|
+
adoption.record_id, adoption.record_version, adoption.record_digest, adoption.source_digest
|
|
524
|
+
FROM memory_learning_adoptions adoption JOIN memory_records record ON record.id = adoption.record_id
|
|
525
|
+
WHERE adoption.row_kind = 'operation' AND adoption.invalidated_at IS NULL
|
|
526
|
+
AND record.version = adoption.record_version AND record.status = 'active'
|
|
527
|
+
AND record.namespace_key = ? AND record.owner = 'agent' AND record.scope = 'workspace'
|
|
528
|
+
AND record.workspace = ? AND record.agent_preset = ?
|
|
529
|
+
AND (record.expires_at IS NULL OR record.expires_at > ?)
|
|
530
|
+
ORDER BY adoption.authority_id, adoption.operation_id`).all(identity.namespaceKey, identity.workspace, identity.agentPreset, this.#now());
|
|
531
|
+
return Object.freeze(rows.flatMap(row => {
|
|
532
|
+
const record = this.#selectRecord(identity, row.record_id);
|
|
533
|
+
if (record === undefined || row.record_digest !== learningRecordDigest(this.#toRecord(record)))
|
|
534
|
+
return [];
|
|
535
|
+
const request = validateLearningRequest(JSON.parse(row.request_json));
|
|
536
|
+
return [Object.freeze({ authorityId: row.authority_id, request, id: row.record_id,
|
|
537
|
+
version: row.record_version, recordDigest: row.record_digest, sourceDigest: row.source_digest })];
|
|
538
|
+
}));
|
|
539
|
+
}
|
|
540
|
+
invalidateManagedLearningSource(input) {
|
|
541
|
+
const owner = validateMemoryLearningOwner(input.owner);
|
|
542
|
+
if (input.reason !== 'withdrawn' && input.reason !== 'source-changed')
|
|
543
|
+
throw new MemoryStoreError('invalid-entry', 'invalid source invalidation reason');
|
|
544
|
+
if (typeof input.id !== 'string' || !Number.isSafeInteger(input.version) || input.version < 1
|
|
545
|
+
|| !SHA_256.test(input.recordDigest) || !SHA_256.test(input.sourceDigest)) {
|
|
546
|
+
throw new MemoryStoreError('invalid-entry', 'invalid source invalidation ref');
|
|
547
|
+
}
|
|
548
|
+
const namespace = this.#learningNamespace(owner);
|
|
549
|
+
const identity = this.#learningIdentity(owner);
|
|
550
|
+
return this.#transaction(() => {
|
|
551
|
+
const row = this.#selectRecord(normalizeIdentity(identity, namespace), input.id);
|
|
552
|
+
if (row === undefined || row.status !== 'active' || row.version !== input.version)
|
|
553
|
+
return Object.freeze({ invalidated: false });
|
|
554
|
+
const actual = this.#toRecord(row);
|
|
555
|
+
const managed = this.#learningOperationForRecord(actual);
|
|
556
|
+
if (!managed || managed.invalidated_at !== null || managed.record_digest !== input.recordDigest
|
|
557
|
+
|| managed.source_digest !== input.sourceDigest || learningRecordDigest(actual) !== input.recordDigest) {
|
|
558
|
+
return Object.freeze({ invalidated: false });
|
|
559
|
+
}
|
|
560
|
+
const request = validateLearningRequest(JSON.parse(managed.request_json));
|
|
561
|
+
if (growthObjectDigest(request.owner) !== growthObjectDigest(owner))
|
|
562
|
+
return Object.freeze({ invalidated: false });
|
|
563
|
+
const invalidationKey = `memory-learning-invalidated:${growthObjectDigest([managed.authority_id, managed.operation_id])}`;
|
|
564
|
+
const reservedAudit = this.#database.prepare(`SELECT 1 AS used FROM memory_audit
|
|
565
|
+
WHERE namespace_key = ? AND idempotency_key = ?`).get(memoryOwnerNamespaceKey(namespace), invalidationKey);
|
|
566
|
+
if (reservedAudit !== undefined)
|
|
567
|
+
throw new MemoryStoreError('idempotency-conflict', 'learning invalidation audit key was already used');
|
|
568
|
+
this.#applyMutationInCurrentTransaction({ op: 'remove', namespace, identity, id: input.id,
|
|
569
|
+
expectedVersion: input.version, idempotencyKey: invalidationKey });
|
|
570
|
+
const changed = this.#database.prepare(`UPDATE memory_learning_adoptions SET invalidated_at = ?, invalidation_reason = ?
|
|
571
|
+
WHERE authority_id = ? AND operation_id = ? AND invalidated_at IS NULL`).run(this.#now(), input.reason, managed.authority_id, managed.operation_id);
|
|
572
|
+
if (changed.changes !== 1)
|
|
573
|
+
throw new MemoryStoreError('version-conflict', 'learning invalidation changed');
|
|
574
|
+
return Object.freeze({ invalidated: true });
|
|
575
|
+
});
|
|
576
|
+
}
|
|
319
577
|
resolveProposalNotAfter(namespace, idempotencyKey, ttlMs, requested) {
|
|
320
578
|
const key = memoryOwnerNamespaceKey(namespace);
|
|
321
579
|
const existing = this.#proposalByIdempotencyKey(key, idempotencyKey)
|
|
@@ -347,7 +605,8 @@ export class MemoryStore {
|
|
|
347
605
|
const row = this.#selectRecord(columns, id);
|
|
348
606
|
if (row === undefined || row.status !== 'active' || this.#expired(row))
|
|
349
607
|
return undefined;
|
|
350
|
-
|
|
608
|
+
const record = this.#toRecord(row);
|
|
609
|
+
return this.#learningVisible(record) ? record : undefined;
|
|
351
610
|
}
|
|
352
611
|
list(namespace, identity, options = {}) {
|
|
353
612
|
const columns = normalizeIdentity(identity, namespace);
|
|
@@ -358,7 +617,8 @@ export class MemoryStore {
|
|
|
358
617
|
`).all(columns.namespaceKey, columns.owner, columns.scope, columns.workspace, columns.agentPreset);
|
|
359
618
|
return rows
|
|
360
619
|
.filter(row => row.status === 'removed' ? options.includeRemoved === true : !this.#expired(row))
|
|
361
|
-
.map(row => this.#toRecord(row))
|
|
620
|
+
.map(row => this.#toRecord(row))
|
|
621
|
+
.filter(record => this.#learningVisible(record));
|
|
362
622
|
}
|
|
363
623
|
read(context, ids) {
|
|
364
624
|
const normalized = normalizeAgentContext(context);
|