ai-memory-layer 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -12
- package/README.md +435 -320
- package/bin/memory-server.mjs +0 -0
- package/dist/adapters/memory/embeddings.d.ts.map +1 -1
- package/dist/adapters/memory/embeddings.js +12 -1
- package/dist/adapters/memory/embeddings.js.map +1 -1
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +1281 -48
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/postgres/index.d.ts +1 -0
- package/dist/adapters/postgres/index.d.ts.map +1 -1
- package/dist/adapters/postgres/index.js +1770 -42
- package/dist/adapters/postgres/index.js.map +1 -1
- package/dist/adapters/sqlite/embeddings.d.ts.map +1 -1
- package/dist/adapters/sqlite/embeddings.js +49 -12
- package/dist/adapters/sqlite/embeddings.js.map +1 -1
- package/dist/adapters/sqlite/index.d.ts.map +1 -1
- package/dist/adapters/sqlite/index.js +1720 -38
- package/dist/adapters/sqlite/index.js.map +1 -1
- package/dist/adapters/sqlite/mappers.d.ts +39 -4
- package/dist/adapters/sqlite/mappers.d.ts.map +1 -1
- package/dist/adapters/sqlite/mappers.js +87 -0
- package/dist/adapters/sqlite/mappers.js.map +1 -1
- package/dist/adapters/sqlite/schema.d.ts +1 -1
- package/dist/adapters/sqlite/schema.d.ts.map +1 -1
- package/dist/adapters/sqlite/schema.js +297 -1
- package/dist/adapters/sqlite/schema.js.map +1 -1
- package/dist/adapters/sync-to-async.d.ts.map +1 -1
- package/dist/adapters/sync-to-async.js +54 -0
- package/dist/adapters/sync-to-async.js.map +1 -1
- package/dist/contracts/async-storage.d.ts +61 -1
- package/dist/contracts/async-storage.d.ts.map +1 -1
- package/dist/contracts/cognitive.d.ts +37 -0
- package/dist/contracts/cognitive.d.ts.map +1 -0
- package/dist/contracts/cognitive.js +24 -0
- package/dist/contracts/cognitive.js.map +1 -0
- package/dist/contracts/coordination.d.ts +101 -0
- package/dist/contracts/coordination.d.ts.map +1 -0
- package/dist/contracts/coordination.js +26 -0
- package/dist/contracts/coordination.js.map +1 -0
- package/dist/contracts/embedding.d.ts +1 -1
- package/dist/contracts/embedding.d.ts.map +1 -1
- package/dist/contracts/errors.d.ts +28 -0
- package/dist/contracts/errors.d.ts.map +1 -0
- package/dist/contracts/errors.js +41 -0
- package/dist/contracts/errors.js.map +1 -0
- package/dist/contracts/identity.d.ts +2 -0
- package/dist/contracts/identity.d.ts.map +1 -1
- package/dist/contracts/identity.js +26 -1
- package/dist/contracts/identity.js.map +1 -1
- package/dist/contracts/observability.d.ts +2 -1
- package/dist/contracts/observability.d.ts.map +1 -1
- package/dist/contracts/observability.js +11 -0
- package/dist/contracts/observability.js.map +1 -1
- package/dist/contracts/profile.d.ts +29 -0
- package/dist/contracts/profile.d.ts.map +1 -0
- package/dist/contracts/profile.js +2 -0
- package/dist/contracts/profile.js.map +1 -0
- package/dist/contracts/session-state.d.ts +10 -0
- package/dist/contracts/session-state.d.ts.map +1 -0
- package/dist/contracts/session-state.js +2 -0
- package/dist/contracts/session-state.js.map +1 -0
- package/dist/contracts/storage.d.ts +73 -1
- package/dist/contracts/storage.d.ts.map +1 -1
- package/dist/contracts/storage.js +16 -1
- package/dist/contracts/storage.js.map +1 -1
- package/dist/contracts/temporal.d.ts +112 -0
- package/dist/contracts/temporal.d.ts.map +1 -0
- package/dist/contracts/temporal.js +31 -0
- package/dist/contracts/temporal.js.map +1 -0
- package/dist/contracts/types.d.ts +135 -0
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js +27 -0
- package/dist/contracts/types.js.map +1 -1
- package/dist/core/associations.d.ts +18 -0
- package/dist/core/associations.d.ts.map +1 -0
- package/dist/core/associations.js +185 -0
- package/dist/core/associations.js.map +1 -0
- package/dist/core/circuit-breaker.d.ts +9 -0
- package/dist/core/circuit-breaker.d.ts.map +1 -1
- package/dist/core/circuit-breaker.js +13 -1
- package/dist/core/circuit-breaker.js.map +1 -1
- package/dist/core/cognitive.d.ts +5 -0
- package/dist/core/cognitive.d.ts.map +1 -0
- package/dist/core/cognitive.js +120 -0
- package/dist/core/cognitive.js.map +1 -0
- package/dist/core/context.d.ts +72 -1
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +471 -45
- package/dist/core/context.js.map +1 -1
- package/dist/core/episodic.d.ts +28 -0
- package/dist/core/episodic.d.ts.map +1 -0
- package/dist/core/episodic.js +371 -0
- package/dist/core/episodic.js.map +1 -0
- package/dist/core/formatter.d.ts +4 -0
- package/dist/core/formatter.d.ts.map +1 -1
- package/dist/core/formatter.js +103 -0
- package/dist/core/formatter.js.map +1 -1
- package/dist/core/maintenance.d.ts +1 -0
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +75 -0
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/manager.d.ts +159 -7
- package/dist/core/manager.d.ts.map +1 -1
- package/dist/core/manager.js +740 -31
- package/dist/core/manager.js.map +1 -1
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +210 -178
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/playbook.d.ts +35 -0
- package/dist/core/playbook.d.ts.map +1 -0
- package/dist/core/playbook.js +184 -0
- package/dist/core/playbook.js.map +1 -0
- package/dist/core/profile.d.ts +8 -0
- package/dist/core/profile.d.ts.map +1 -0
- package/dist/core/profile.js +103 -0
- package/dist/core/profile.js.map +1 -0
- package/dist/core/quick.d.ts +5 -0
- package/dist/core/quick.d.ts.map +1 -1
- package/dist/core/quick.js +10 -1
- package/dist/core/quick.js.map +1 -1
- package/dist/core/runtime.d.ts +17 -1
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +88 -5
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/streaming.d.ts +1 -1
- package/dist/core/streaming.d.ts.map +1 -1
- package/dist/core/temporal.d.ts +29 -0
- package/dist/core/temporal.d.ts.map +1 -0
- package/dist/core/temporal.js +447 -0
- package/dist/core/temporal.js.map +1 -0
- package/dist/core/validation.d.ts +3 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +25 -10
- package/dist/core/validation.js.map +1 -1
- package/dist/core/workspace-detect.d.ts +17 -0
- package/dist/core/workspace-detect.d.ts.map +1 -0
- package/dist/core/workspace-detect.js +55 -0
- package/dist/core/workspace-detect.js.map +1 -0
- package/dist/embeddings/resilience.d.ts.map +1 -1
- package/dist/embeddings/resilience.js +19 -8
- package/dist/embeddings/resilience.js.map +1 -1
- package/dist/index.d.ts +21 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/integrations/claude-agent.d.ts +6 -0
- package/dist/integrations/claude-agent.d.ts.map +1 -1
- package/dist/integrations/claude-agent.js +5 -1
- package/dist/integrations/claude-agent.js.map +1 -1
- package/dist/integrations/claude-tools.d.ts +5 -4
- package/dist/integrations/claude-tools.d.ts.map +1 -1
- package/dist/integrations/claude-tools.js +155 -2
- package/dist/integrations/claude-tools.js.map +1 -1
- package/dist/integrations/middleware.d.ts +6 -0
- package/dist/integrations/middleware.d.ts.map +1 -1
- package/dist/integrations/middleware.js +11 -1
- package/dist/integrations/middleware.js.map +1 -1
- package/dist/integrations/openai-tools.d.ts +5 -4
- package/dist/integrations/openai-tools.d.ts.map +1 -1
- package/dist/integrations/openai-tools.js +170 -2
- package/dist/integrations/openai-tools.js.map +1 -1
- package/dist/integrations/vercel-ai.d.ts +6 -0
- package/dist/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/integrations/vercel-ai.js +4 -0
- package/dist/integrations/vercel-ai.js.map +1 -1
- package/dist/server/http-server.d.ts +8 -0
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/http-server.js +976 -58
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/mcp-server.d.ts +8 -0
- package/dist/server/mcp-server.d.ts.map +1 -1
- package/dist/server/mcp-server.js +1157 -37
- package/dist/server/mcp-server.js.map +1 -1
- package/dist/server/parsing.d.ts +12 -0
- package/dist/server/parsing.d.ts.map +1 -0
- package/dist/server/parsing.js +42 -0
- package/dist/server/parsing.js.map +1 -0
- package/dist/summarizers/prompts.d.ts +4 -0
- package/dist/summarizers/prompts.d.ts.map +1 -1
- package/dist/summarizers/prompts.js +42 -0
- package/dist/summarizers/prompts.js.map +1 -1
- package/docs/ULTIMATE_MEMORY_LAYER_ROADMAP.md +291 -0
- package/docs/prd.json +1498 -0
- package/openapi.yaml +1945 -112
- package/package.json +7 -5
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { normalizeScope } from '../contracts/identity.js';
|
|
2
|
+
import { ResourceNotFoundError, ScopeMismatchError, ValidationError, } from '../contracts/errors.js';
|
|
2
3
|
import { DEFAULT_EXTRACTION_POLICY } from '../contracts/policy.js';
|
|
4
|
+
import { UniqueConstraintError } from '../contracts/storage.js';
|
|
3
5
|
import { assertCompactionTrigger, assertFactConfidence, assertFactType, assertFactType as assertExtractedFactType, assertNonEmpty, assertStringArray, assertMaxEntries, nowSeconds, } from './validation.js';
|
|
4
6
|
import { classifyFactRelation, createRegexExtractor, normalizeFactText, normalizeExtractedFact, normalizeKnowledgeMemory, } from './extractor.js';
|
|
5
7
|
import { emitMemoryEvent } from './telemetry.js';
|
|
6
8
|
import { assessCandidateTrust, buildKnowledgeConflict } from './trust.js';
|
|
7
9
|
import { getNativeSyncAdapter } from '../adapters/sync-to-async.js';
|
|
10
|
+
import { autoDetectAssociations } from './associations.js';
|
|
8
11
|
function resolveExtractionPolicy(policy) {
|
|
9
12
|
return {
|
|
10
13
|
...DEFAULT_EXTRACTION_POLICY,
|
|
@@ -89,6 +92,185 @@ function mergeRecoveredFacts(extracted, recovered, maxFactsPerExtraction) {
|
|
|
89
92
|
}
|
|
90
93
|
return merged;
|
|
91
94
|
}
|
|
95
|
+
function resolveCreatedKnowledgeClass(knowledgeClass, knowledgeState, evidence) {
|
|
96
|
+
const supportEvidence = evidence.filter((item) => item.support_polarity === 'supports');
|
|
97
|
+
const outcomeFailures = supportEvidence.filter((item) => item.outcome === 'failure').length;
|
|
98
|
+
const outcomeSuccesses = supportEvidence.filter((item) => item.outcome === 'success').length;
|
|
99
|
+
if (outcomeFailures > outcomeSuccesses && ['strategy', 'procedure'].includes(knowledgeClass)) {
|
|
100
|
+
return 'anti_pattern';
|
|
101
|
+
}
|
|
102
|
+
if (knowledgeState === 'trusted' && knowledgeClass === 'procedure' && outcomeSuccesses > 0) {
|
|
103
|
+
return 'strategy';
|
|
104
|
+
}
|
|
105
|
+
return knowledgeClass;
|
|
106
|
+
}
|
|
107
|
+
function shouldSupersedeExistingKnowledge(relation, relatedKnowledge, decision, conflictStrategy) {
|
|
108
|
+
return Boolean(relatedKnowledge &&
|
|
109
|
+
(decision === 'supersede_existing' ||
|
|
110
|
+
relation === 'update' ||
|
|
111
|
+
(relation === 'conflict' && conflictStrategy === 'supersede')));
|
|
112
|
+
}
|
|
113
|
+
function buildPromotionAuditPayload(input) {
|
|
114
|
+
return {
|
|
115
|
+
...input.normalizedScope,
|
|
116
|
+
working_memory_id: input.workingMemoryId,
|
|
117
|
+
fact: input.fact.fact,
|
|
118
|
+
fact_type: input.fact.factType,
|
|
119
|
+
fact_subject: input.fact.subject,
|
|
120
|
+
fact_attribute: input.fact.attribute,
|
|
121
|
+
fact_value: input.fact.value,
|
|
122
|
+
normalized_fact: input.fact.normalizedFact,
|
|
123
|
+
slot_key: input.fact.slotKey,
|
|
124
|
+
is_negated: input.fact.isNegated,
|
|
125
|
+
confidence: input.confidence,
|
|
126
|
+
confidence_score: input.confidenceScore,
|
|
127
|
+
verification_status: input.verificationStatus,
|
|
128
|
+
source_text: input.sourceText,
|
|
129
|
+
decision: input.relation === 'update'
|
|
130
|
+
? 'updated'
|
|
131
|
+
: input.relation === 'conflict'
|
|
132
|
+
? 'conflict'
|
|
133
|
+
: input.relation === 'compatible'
|
|
134
|
+
? 'compatible'
|
|
135
|
+
: 'created',
|
|
136
|
+
created_knowledge_id: input.createdKnowledgeId,
|
|
137
|
+
related_knowledge_id: input.relatedKnowledgeId,
|
|
138
|
+
detail: input.relation === 'conflict'
|
|
139
|
+
? input.verificationNotes ?? `Conflict strategy '${input.conflictStrategy}'`
|
|
140
|
+
: input.relation === 'update'
|
|
141
|
+
? input.verificationNotes ?? 'Superseded prior related knowledge'
|
|
142
|
+
: input.relation === 'compatible'
|
|
143
|
+
? input.verificationNotes ?? 'Created alongside compatible related knowledge'
|
|
144
|
+
: input.verificationNotes ?? 'Created new knowledge memory',
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function bindCandidateEvidenceToCandidate(candidateEvidenceInputs, candidateId) {
|
|
148
|
+
return candidateEvidenceInputs.map((item) => ({
|
|
149
|
+
...item,
|
|
150
|
+
knowledge_candidate_id: candidateId,
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
function bindCandidateEvidenceToKnowledge(candidateEvidence, knowledgeId) {
|
|
154
|
+
return candidateEvidence.map(({ id: _id, ...item }) => ({
|
|
155
|
+
...item,
|
|
156
|
+
knowledge_candidate_id: null,
|
|
157
|
+
knowledge_memory_id: knowledgeId,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
function isPromiseLike(value) {
|
|
161
|
+
return typeof value === 'object' && value !== null && 'then' in value;
|
|
162
|
+
}
|
|
163
|
+
function chainMaybe(value, next) {
|
|
164
|
+
return isPromiseLike(value) ? value.then(next) : next(value);
|
|
165
|
+
}
|
|
166
|
+
function attemptMaybe(work, onError) {
|
|
167
|
+
try {
|
|
168
|
+
const result = work();
|
|
169
|
+
return isPromiseLike(result) ? result.catch(onError) : result;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return onError(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function assertSyncResult(value) {
|
|
176
|
+
if (isPromiseLike(value)) {
|
|
177
|
+
throw new Error('Expected synchronous persistence result');
|
|
178
|
+
}
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
function ignoreUniqueConstraint(error) {
|
|
182
|
+
if (!(error instanceof UniqueConstraintError)) {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function buildPromotionPersistencePlan(input) {
|
|
187
|
+
const promotedState = input.trustAssessment.state === 'trusted' ? 'trusted' : 'provisional';
|
|
188
|
+
const createdKnowledgeClass = resolveCreatedKnowledgeClass(input.grounded.candidate.knowledge_class, promotedState, input.candidateEvidenceInputs);
|
|
189
|
+
const promotedKnowledgeInput = buildKnowledgeInput(input.normalizedScope, input.workingMemoryId, input.fact, {
|
|
190
|
+
confidence: input.resolvedConfidence,
|
|
191
|
+
confidenceScore: input.resolvedConfidenceScore,
|
|
192
|
+
trustScore: input.trustAssessment.trust_score,
|
|
193
|
+
knowledgeState: promotedState,
|
|
194
|
+
knowledgeClass: createdKnowledgeClass,
|
|
195
|
+
verificationStatus: input.resolvedVerificationStatus,
|
|
196
|
+
verificationNotes: input.verificationNotes ??
|
|
197
|
+
(input.trustAssessment.reasons.length > 0
|
|
198
|
+
? input.trustAssessment.reasons.join(', ')
|
|
199
|
+
: null),
|
|
200
|
+
contradictionScore: input.conflict?.severity === 'high' ? 1 : 0,
|
|
201
|
+
sourceTurnIds: input.grounded.supportedTurnIds,
|
|
202
|
+
});
|
|
203
|
+
const auditInput = buildPromotionAuditPayload({
|
|
204
|
+
normalizedScope: input.normalizedScope,
|
|
205
|
+
workingMemoryId: input.workingMemoryId,
|
|
206
|
+
fact: input.fact,
|
|
207
|
+
confidence: input.resolvedConfidence,
|
|
208
|
+
confidenceScore: input.trustAssessment.trust_score,
|
|
209
|
+
verificationStatus: input.resolvedVerificationStatus,
|
|
210
|
+
sourceText: input.fact.sourceText ?? input.fact.fact,
|
|
211
|
+
relation: input.strongestRelation.relation,
|
|
212
|
+
createdKnowledgeId: 0,
|
|
213
|
+
relatedKnowledgeId: input.strongestRelation.related?.id ?? null,
|
|
214
|
+
verificationNotes: input.verificationNotes,
|
|
215
|
+
conflictStrategy: input.policy.conflictStrategy,
|
|
216
|
+
});
|
|
217
|
+
return {
|
|
218
|
+
candidateInput: input.candidateInput,
|
|
219
|
+
candidateEvidenceInputs: input.candidateEvidenceInputs,
|
|
220
|
+
promotedKnowledgeInput,
|
|
221
|
+
auditInput,
|
|
222
|
+
supersededKnowledgeId: shouldSupersedeExistingKnowledge(input.strongestRelation.relation, input.strongestRelation.related, input.trustAssessment.decision, input.policy.conflictStrategy)
|
|
223
|
+
? input.strongestRelation.related.id
|
|
224
|
+
: null,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function persistRejectedKnowledgeCandidateCore(adapter, candidateInput, candidateEvidenceInputs) {
|
|
228
|
+
return chainMaybe(adapter.insertKnowledgeCandidate(candidateInput), (rejectedCandidate) => {
|
|
229
|
+
if (candidateEvidenceInputs.length === 0) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
return chainMaybe(adapter.insertKnowledgeEvidenceBatch(bindCandidateEvidenceToCandidate(candidateEvidenceInputs, rejectedCandidate.id)), () => undefined);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function persistRejectedKnowledgeCandidateAsync(adapter, candidateInput, candidateEvidenceInputs) {
|
|
236
|
+
await Promise.resolve(persistRejectedKnowledgeCandidateCore(adapter, candidateInput, candidateEvidenceInputs));
|
|
237
|
+
}
|
|
238
|
+
function persistRejectedKnowledgeCandidateSync(adapter, candidateInput, candidateEvidenceInputs) {
|
|
239
|
+
assertSyncResult(persistRejectedKnowledgeCandidateCore(adapter, candidateInput, candidateEvidenceInputs));
|
|
240
|
+
}
|
|
241
|
+
function persistPromotedKnowledgeCore(adapter, normalizedScope, plan) {
|
|
242
|
+
return chainMaybe(adapter.insertKnowledgeCandidate(plan.candidateInput), (candidate) => {
|
|
243
|
+
const candidateEvidenceResult = plan.candidateEvidenceInputs.length > 0
|
|
244
|
+
? adapter.insertKnowledgeEvidenceBatch(bindCandidateEvidenceToCandidate(plan.candidateEvidenceInputs, candidate.id))
|
|
245
|
+
: [];
|
|
246
|
+
return chainMaybe(candidateEvidenceResult, (candidateEvidence) => chainMaybe(adapter.promoteKnowledgeCandidate(candidate.id, plan.promotedKnowledgeInput), (knowledge) => chainMaybe(candidateEvidence.length > 0
|
|
247
|
+
? adapter.insertKnowledgeEvidenceBatch(bindCandidateEvidenceToKnowledge(candidateEvidence, knowledge.id))
|
|
248
|
+
: undefined, () => chainMaybe(plan.supersededKnowledgeId != null
|
|
249
|
+
? chainMaybe(adapter.supersedeKnowledgeMemory(plan.supersededKnowledgeId, knowledge.id), () => attemptMaybe(() => adapter.insertAssociation({
|
|
250
|
+
...normalizedScope,
|
|
251
|
+
source_kind: 'knowledge',
|
|
252
|
+
source_id: knowledge.id,
|
|
253
|
+
target_kind: 'knowledge',
|
|
254
|
+
target_id: plan.supersededKnowledgeId,
|
|
255
|
+
association_type: 'supersedes',
|
|
256
|
+
confidence: 1,
|
|
257
|
+
auto_generated: true,
|
|
258
|
+
}), (error) => {
|
|
259
|
+
ignoreUniqueConstraint(error);
|
|
260
|
+
return undefined;
|
|
261
|
+
}))
|
|
262
|
+
: undefined, () => chainMaybe(adapter.insertKnowledgeMemoryAudit({
|
|
263
|
+
...plan.auditInput,
|
|
264
|
+
created_knowledge_id: knowledge.id,
|
|
265
|
+
}), () => knowledge)))));
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
async function persistPromotedKnowledgeAsync(adapter, normalizedScope, plan) {
|
|
269
|
+
return Promise.resolve(persistPromotedKnowledgeCore(adapter, normalizedScope, plan));
|
|
270
|
+
}
|
|
271
|
+
function persistPromotedKnowledgeSync(adapter, normalizedScope, plan) {
|
|
272
|
+
return assertSyncResult(persistPromotedKnowledgeCore(adapter, normalizedScope, plan));
|
|
273
|
+
}
|
|
92
274
|
function evidenceSourceTypeForTurn(turn) {
|
|
93
275
|
if (turn.role === 'assistant')
|
|
94
276
|
return 'assistant_turn';
|
|
@@ -195,14 +377,14 @@ function assertTurnsMatchScope(turns, scope, sessionId) {
|
|
|
195
377
|
const normalized = normalizeScope(scope);
|
|
196
378
|
for (const turn of turns) {
|
|
197
379
|
if (turn.session_id !== sessionId) {
|
|
198
|
-
throw new
|
|
380
|
+
throw new ValidationError(`Memory validation: turn ${turn.id} session_id '${turn.session_id}' does not match '${sessionId}'`);
|
|
199
381
|
}
|
|
200
382
|
if (turn.tenant_id !== normalized.tenant_id ||
|
|
201
383
|
turn.system_id !== normalized.system_id ||
|
|
202
384
|
turn.workspace_id !== normalized.workspace_id ||
|
|
203
385
|
turn.collaboration_id !== normalized.collaboration_id ||
|
|
204
386
|
turn.scope_id !== normalized.scope_id) {
|
|
205
|
-
throw new
|
|
387
|
+
throw new ScopeMismatchError(`Memory validation: turn ${turn.id} does not belong to the requested scope`);
|
|
206
388
|
}
|
|
207
389
|
}
|
|
208
390
|
}
|
|
@@ -216,7 +398,7 @@ export async function commitCompaction(adapter, input) {
|
|
|
216
398
|
assertMaxEntries(input.topicTags, 'topicTags', 5);
|
|
217
399
|
const turnsToArchive = sortTurnsAscending(input.turnsToArchive);
|
|
218
400
|
if (turnsToArchive.length === 0) {
|
|
219
|
-
throw new
|
|
401
|
+
throw new ValidationError("Memory validation: 'turnsToArchive' must not be empty");
|
|
220
402
|
}
|
|
221
403
|
assertTurnsMatchScope(turnsToArchive, normalizedScope, input.sessionId);
|
|
222
404
|
const turnIdStart = turnsToArchive[0].id;
|
|
@@ -306,13 +488,13 @@ export async function commitCompaction(adapter, input) {
|
|
|
306
488
|
export async function compactTurns(adapter, scope, sessionId, turnsToCompact, summarize, trigger, retainedTurnCount, telemetry) {
|
|
307
489
|
assertCompactionTrigger(trigger, 'trigger');
|
|
308
490
|
if (!Number.isInteger(retainedTurnCount) || retainedTurnCount < 0) {
|
|
309
|
-
throw new
|
|
491
|
+
throw new ValidationError(`Memory validation: 'retainedTurnCount' must be a non-negative integer, got '${retainedTurnCount}'`);
|
|
310
492
|
}
|
|
311
493
|
const orderedTurns = sortTurnsAscending(turnsToCompact);
|
|
312
494
|
assertTurnsMatchScope(orderedTurns, scope, sessionId);
|
|
313
495
|
const turnsToArchive = orderedTurns.slice(0, Math.max(0, orderedTurns.length - retainedTurnCount));
|
|
314
496
|
if (turnsToArchive.length === 0) {
|
|
315
|
-
throw new
|
|
497
|
+
throw new ValidationError('Memory validation: no turns are eligible for compaction');
|
|
316
498
|
}
|
|
317
499
|
const startedAtMs = Date.now();
|
|
318
500
|
const summary = await summarize(turnsToArchive);
|
|
@@ -339,14 +521,14 @@ export async function promoteToKnowledge(adapter, workingMemoryId, input) {
|
|
|
339
521
|
const normalizedScope = normalizeScope(input.scope);
|
|
340
522
|
const workingMemory = await adapter.getWorkingMemoryById(workingMemoryId);
|
|
341
523
|
if (!workingMemory) {
|
|
342
|
-
throw new
|
|
524
|
+
throw new ResourceNotFoundError(`Memory validation: working memory ${workingMemoryId} was not found`);
|
|
343
525
|
}
|
|
344
526
|
if (workingMemory.tenant_id !== normalizedScope.tenant_id ||
|
|
345
527
|
workingMemory.system_id !== normalizedScope.system_id ||
|
|
346
528
|
workingMemory.workspace_id !== normalizedScope.workspace_id ||
|
|
347
529
|
workingMemory.collaboration_id !== normalizedScope.collaboration_id ||
|
|
348
530
|
workingMemory.scope_id !== normalizedScope.scope_id) {
|
|
349
|
-
throw new
|
|
531
|
+
throw new ScopeMismatchError(`Memory validation: working memory ${workingMemoryId} does not belong to the requested scope`);
|
|
350
532
|
}
|
|
351
533
|
return runAtomicStorage(adapter, async () => {
|
|
352
534
|
const knowledgeMemory = await adapter.insertKnowledgeMemory({
|
|
@@ -391,14 +573,14 @@ export async function extractKnowledge(adapter, workingMemoryId, scope, extracto
|
|
|
391
573
|
const policy = resolveExtractionPolicy(options?.policy);
|
|
392
574
|
const workingMemory = await adapter.getWorkingMemoryById(workingMemoryId);
|
|
393
575
|
if (!workingMemory) {
|
|
394
|
-
throw new
|
|
576
|
+
throw new ResourceNotFoundError(`Memory validation: working memory ${workingMemoryId} was not found`);
|
|
395
577
|
}
|
|
396
578
|
if (workingMemory.tenant_id !== normalizedScope.tenant_id ||
|
|
397
579
|
workingMemory.system_id !== normalizedScope.system_id ||
|
|
398
580
|
workingMemory.workspace_id !== normalizedScope.workspace_id ||
|
|
399
581
|
workingMemory.collaboration_id !== normalizedScope.collaboration_id ||
|
|
400
582
|
workingMemory.scope_id !== normalizedScope.scope_id) {
|
|
401
|
-
throw new
|
|
583
|
+
throw new ScopeMismatchError(`Memory validation: working memory ${workingMemoryId} does not belong to the requested scope`);
|
|
402
584
|
}
|
|
403
585
|
const extracted = (await extractor(workingMemory.summary, workingMemory.key_entities, workingMemory.topic_tags))
|
|
404
586
|
.slice(0, policy.maxFactsPerExtraction)
|
|
@@ -652,23 +834,7 @@ export async function extractKnowledge(adapter, workingMemoryId, scope, extracto
|
|
|
652
834
|
continue;
|
|
653
835
|
}
|
|
654
836
|
if (trustAssessment.decision === 'reject_candidate') {
|
|
655
|
-
await runAtomicStorage(adapter,
|
|
656
|
-
const rejectedCandidate = await adapter.insertKnowledgeCandidate(candidateInput);
|
|
657
|
-
if (candidateEvidenceInputs.length > 0) {
|
|
658
|
-
await adapter.insertKnowledgeEvidenceBatch(candidateEvidenceInputs.map((item) => ({
|
|
659
|
-
...item,
|
|
660
|
-
knowledge_candidate_id: rejectedCandidate.id,
|
|
661
|
-
})));
|
|
662
|
-
}
|
|
663
|
-
}, (syncAdapter) => {
|
|
664
|
-
const rejectedCandidate = syncAdapter.insertKnowledgeCandidate(candidateInput);
|
|
665
|
-
if (candidateEvidenceInputs.length > 0) {
|
|
666
|
-
syncAdapter.insertKnowledgeEvidenceBatch(candidateEvidenceInputs.map((item) => ({
|
|
667
|
-
...item,
|
|
668
|
-
knowledge_candidate_id: rejectedCandidate.id,
|
|
669
|
-
})));
|
|
670
|
-
}
|
|
671
|
-
});
|
|
837
|
+
await runAtomicStorage(adapter, () => persistRejectedKnowledgeCandidateAsync(adapter, candidateInput, candidateEvidenceInputs), (syncAdapter) => persistRejectedKnowledgeCandidateSync(syncAdapter, candidateInput, candidateEvidenceInputs));
|
|
672
838
|
await adapter.insertKnowledgeMemoryAudit({
|
|
673
839
|
...normalizedScope,
|
|
674
840
|
working_memory_id: workingMemoryId,
|
|
@@ -746,165 +912,31 @@ export async function extractKnowledge(adapter, workingMemoryId, scope, extracto
|
|
|
746
912
|
});
|
|
747
913
|
continue;
|
|
748
914
|
}
|
|
749
|
-
const
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
grounded.candidate.knowledge_class === 'procedure' &&
|
|
765
|
-
outcomeSuccesses > 0
|
|
766
|
-
? 'strategy'
|
|
767
|
-
: grounded.candidate.knowledge_class;
|
|
768
|
-
const knowledge = await adapter.promoteKnowledgeCandidate(candidate.id, buildKnowledgeInput(normalizedScope, workingMemoryId, fact, {
|
|
769
|
-
confidence: resolvedConfidence,
|
|
770
|
-
confidenceScore: resolvedConfidenceScore,
|
|
771
|
-
trustScore: trustAssessment.trust_score,
|
|
772
|
-
knowledgeState: trustAssessment.state === 'trusted' ? 'trusted' : 'provisional',
|
|
773
|
-
knowledgeClass: createdKnowledgeClass,
|
|
774
|
-
verificationStatus: resolvedVerificationStatus,
|
|
775
|
-
verificationNotes: verificationNotes ?? (trustAssessment.reasons.length > 0 ? trustAssessment.reasons.join(', ') : null),
|
|
776
|
-
contradictionScore: conflict?.severity === 'high' ? 1 : 0,
|
|
777
|
-
sourceTurnIds: grounded.supportedTurnIds,
|
|
778
|
-
}));
|
|
779
|
-
if (candidateEvidence.length > 0) {
|
|
780
|
-
await adapter.insertKnowledgeEvidenceBatch(candidateEvidence.map(({ id: _id, ...item }) => ({
|
|
781
|
-
...item,
|
|
782
|
-
knowledge_candidate_id: null,
|
|
783
|
-
knowledge_memory_id: knowledge.id,
|
|
784
|
-
})));
|
|
785
|
-
}
|
|
786
|
-
if (strongestRelation.related &&
|
|
787
|
-
(trustAssessment.decision === 'supersede_existing' ||
|
|
788
|
-
strongestRelation.relation === 'update' ||
|
|
789
|
-
(strongestRelation.relation === 'conflict' && policy.conflictStrategy === 'supersede'))) {
|
|
790
|
-
await adapter.supersedeKnowledgeMemory(strongestRelation.related.id, knowledge.id);
|
|
791
|
-
}
|
|
792
|
-
await adapter.insertKnowledgeMemoryAudit({
|
|
793
|
-
...normalizedScope,
|
|
794
|
-
working_memory_id: workingMemoryId,
|
|
795
|
-
fact: fact.fact,
|
|
796
|
-
fact_type: fact.factType,
|
|
797
|
-
fact_subject: fact.subject,
|
|
798
|
-
fact_attribute: fact.attribute,
|
|
799
|
-
fact_value: fact.value,
|
|
800
|
-
normalized_fact: fact.normalizedFact,
|
|
801
|
-
slot_key: fact.slotKey,
|
|
802
|
-
is_negated: fact.isNegated,
|
|
803
|
-
confidence: resolvedConfidence,
|
|
804
|
-
confidence_score: trustAssessment.trust_score,
|
|
805
|
-
verification_status: resolvedVerificationStatus,
|
|
806
|
-
source_text: fact.sourceText ?? fact.fact,
|
|
807
|
-
decision: strongestRelation.relation === 'update'
|
|
808
|
-
? 'updated'
|
|
809
|
-
: strongestRelation.relation === 'conflict'
|
|
810
|
-
? 'conflict'
|
|
811
|
-
: strongestRelation.relation === 'compatible'
|
|
812
|
-
? 'compatible'
|
|
813
|
-
: 'created',
|
|
814
|
-
created_knowledge_id: knowledge.id,
|
|
815
|
-
related_knowledge_id: strongestRelation.related?.id ?? null,
|
|
816
|
-
detail: strongestRelation.relation === 'conflict'
|
|
817
|
-
? verificationNotes ?? `Conflict strategy '${policy.conflictStrategy}'`
|
|
818
|
-
: strongestRelation.relation === 'update'
|
|
819
|
-
? verificationNotes ?? 'Superseded prior related knowledge'
|
|
820
|
-
: strongestRelation.relation === 'compatible'
|
|
821
|
-
? verificationNotes ?? 'Created alongside compatible related knowledge'
|
|
822
|
-
: verificationNotes ?? 'Created new knowledge memory',
|
|
823
|
-
});
|
|
824
|
-
return knowledge;
|
|
825
|
-
}, (syncAdapter) => {
|
|
826
|
-
const candidate = syncAdapter.insertKnowledgeCandidate(candidateInput);
|
|
827
|
-
const candidateEvidence = candidateEvidenceInputs.length > 0
|
|
828
|
-
? syncAdapter.insertKnowledgeEvidenceBatch(candidateEvidenceInputs.map((item) => ({
|
|
829
|
-
...item,
|
|
830
|
-
knowledge_candidate_id: candidate.id,
|
|
831
|
-
})))
|
|
832
|
-
: [];
|
|
833
|
-
const supportEvidence = candidateEvidence.filter((item) => item.support_polarity === 'supports');
|
|
834
|
-
const outcomeFailures = supportEvidence.filter((item) => item.outcome === 'failure').length;
|
|
835
|
-
const outcomeSuccesses = supportEvidence.filter((item) => item.outcome === 'success').length;
|
|
836
|
-
const createdKnowledgeClass = outcomeFailures > outcomeSuccesses &&
|
|
837
|
-
['strategy', 'procedure'].includes(grounded.candidate.knowledge_class)
|
|
838
|
-
? 'anti_pattern'
|
|
839
|
-
: trustAssessment.state === 'trusted' &&
|
|
840
|
-
grounded.candidate.knowledge_class === 'procedure' &&
|
|
841
|
-
outcomeSuccesses > 0
|
|
842
|
-
? 'strategy'
|
|
843
|
-
: grounded.candidate.knowledge_class;
|
|
844
|
-
const knowledge = syncAdapter.promoteKnowledgeCandidate(candidate.id, buildKnowledgeInput(normalizedScope, workingMemoryId, fact, {
|
|
845
|
-
confidence: resolvedConfidence,
|
|
846
|
-
confidenceScore: resolvedConfidenceScore,
|
|
847
|
-
trustScore: trustAssessment.trust_score,
|
|
848
|
-
knowledgeState: trustAssessment.state === 'trusted' ? 'trusted' : 'provisional',
|
|
849
|
-
knowledgeClass: createdKnowledgeClass,
|
|
850
|
-
verificationStatus: resolvedVerificationStatus,
|
|
851
|
-
verificationNotes: verificationNotes ?? (trustAssessment.reasons.length > 0 ? trustAssessment.reasons.join(', ') : null),
|
|
852
|
-
contradictionScore: conflict?.severity === 'high' ? 1 : 0,
|
|
853
|
-
sourceTurnIds: grounded.supportedTurnIds,
|
|
854
|
-
}));
|
|
855
|
-
if (candidateEvidence.length > 0) {
|
|
856
|
-
syncAdapter.insertKnowledgeEvidenceBatch(candidateEvidence.map(({ id: _id, ...item }) => ({
|
|
857
|
-
...item,
|
|
858
|
-
knowledge_candidate_id: null,
|
|
859
|
-
knowledge_memory_id: knowledge.id,
|
|
860
|
-
})));
|
|
861
|
-
}
|
|
862
|
-
if (strongestRelation.related &&
|
|
863
|
-
(trustAssessment.decision === 'supersede_existing' ||
|
|
864
|
-
strongestRelation.relation === 'update' ||
|
|
865
|
-
(strongestRelation.relation === 'conflict' && policy.conflictStrategy === 'supersede'))) {
|
|
866
|
-
syncAdapter.supersedeKnowledgeMemory(strongestRelation.related.id, knowledge.id);
|
|
867
|
-
}
|
|
868
|
-
syncAdapter.insertKnowledgeMemoryAudit({
|
|
869
|
-
...normalizedScope,
|
|
870
|
-
working_memory_id: workingMemoryId,
|
|
871
|
-
fact: fact.fact,
|
|
872
|
-
fact_type: fact.factType,
|
|
873
|
-
fact_subject: fact.subject,
|
|
874
|
-
fact_attribute: fact.attribute,
|
|
875
|
-
fact_value: fact.value,
|
|
876
|
-
normalized_fact: fact.normalizedFact,
|
|
877
|
-
slot_key: fact.slotKey,
|
|
878
|
-
is_negated: fact.isNegated,
|
|
879
|
-
confidence: resolvedConfidence,
|
|
880
|
-
confidence_score: trustAssessment.trust_score,
|
|
881
|
-
verification_status: resolvedVerificationStatus,
|
|
882
|
-
source_text: fact.sourceText ?? fact.fact,
|
|
883
|
-
decision: strongestRelation.relation === 'update'
|
|
884
|
-
? 'updated'
|
|
885
|
-
: strongestRelation.relation === 'conflict'
|
|
886
|
-
? 'conflict'
|
|
887
|
-
: strongestRelation.relation === 'compatible'
|
|
888
|
-
? 'compatible'
|
|
889
|
-
: 'created',
|
|
890
|
-
created_knowledge_id: knowledge.id,
|
|
891
|
-
related_knowledge_id: strongestRelation.related?.id ?? null,
|
|
892
|
-
detail: strongestRelation.relation === 'conflict'
|
|
893
|
-
? verificationNotes ?? `Conflict strategy '${policy.conflictStrategy}'`
|
|
894
|
-
: strongestRelation.relation === 'update'
|
|
895
|
-
? verificationNotes ?? 'Superseded prior related knowledge'
|
|
896
|
-
: strongestRelation.relation === 'compatible'
|
|
897
|
-
? verificationNotes ?? 'Created alongside compatible related knowledge'
|
|
898
|
-
: verificationNotes ?? 'Created new knowledge memory',
|
|
899
|
-
});
|
|
900
|
-
return knowledge;
|
|
915
|
+
const promotionPlan = buildPromotionPersistencePlan({
|
|
916
|
+
normalizedScope,
|
|
917
|
+
workingMemoryId,
|
|
918
|
+
fact,
|
|
919
|
+
candidateInput,
|
|
920
|
+
candidateEvidenceInputs,
|
|
921
|
+
grounded,
|
|
922
|
+
trustAssessment,
|
|
923
|
+
strongestRelation,
|
|
924
|
+
resolvedConfidence,
|
|
925
|
+
resolvedConfidenceScore,
|
|
926
|
+
resolvedVerificationStatus,
|
|
927
|
+
verificationNotes,
|
|
928
|
+
conflict,
|
|
929
|
+
policy,
|
|
901
930
|
});
|
|
931
|
+
const createdFact = await runAtomicStorage(adapter, () => persistPromotedKnowledgeAsync(adapter, normalizedScope, promotionPlan), (syncAdapter) => persistPromotedKnowledgeSync(syncAdapter, normalizedScope, promotionPlan));
|
|
902
932
|
duplicateLookup.set(normalizedFact, createdFact);
|
|
903
933
|
normalizedKnowledge.push({
|
|
904
934
|
memory: createdFact,
|
|
905
935
|
normalized: normalizeKnowledgeMemory(createdFact),
|
|
906
936
|
});
|
|
907
937
|
created.push(createdFact);
|
|
938
|
+
// Auto-detect associations between the new fact and all known knowledge (including batch-created)
|
|
939
|
+
await autoDetectAssociations(adapter, normalizedScope, createdFact, [...activeKnowledge, ...created.slice(0, -1)]);
|
|
908
940
|
}
|
|
909
941
|
emitMemoryEvent('extraction', normalizedScope, options, Date.now() - startedAt, {
|
|
910
942
|
workingMemoryId,
|