@jungjaehoon/mama-core 5.0.0 → 5.2.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/README.md +1 -1
- package/dist/api/catalog.js +1 -1
- package/dist/knowledge/access.js +1 -1
- package/dist/knowledge/index.d.ts +1 -1
- package/dist/knowledge/index.js +2 -1
- package/dist/knowledge/search.d.ts +6 -1
- package/dist/knowledge/search.js +17 -11
- package/dist/memory/api.d.ts +3 -1
- package/dist/memory/api.js +64 -15
- package/dist/memory/provenance-live.js +1 -1
- package/dist/memory/types.d.ts +1 -1
- package/dist/memory/types.js +1 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The shared engine for storage, records and revisions, evidence links, memory, se
|
|
|
4
4
|
runtime drivers. Consumers supply their own database, principals, source access and product
|
|
5
5
|
vocabulary through public exports. Core does not require the MAMA OS daemon.
|
|
6
6
|
|
|
7
|
-
Version **5.
|
|
7
|
+
Version **5.2.0**; Node.js 22.13+. This README describes the current rebuild checkout.
|
|
8
8
|
See [architecture](../../docs/explanation/architecture.md) and
|
|
9
9
|
[the shared-engine goal](../../INTENT.md).
|
|
10
10
|
|
package/dist/api/catalog.js
CHANGED
|
@@ -1367,7 +1367,7 @@ function coreActionRegistrations(knowledge, adapter, deps) {
|
|
|
1367
1367
|
memoryId: typeof body.memory_id === 'string' ? body.memory_id : '',
|
|
1368
1368
|
status,
|
|
1369
1369
|
reason: typeof body.reason === 'string' ? body.reason : '',
|
|
1370
|
-
}, { ...writeAccess, scopes }, requiredOperationId(context, 'memory.retire'));
|
|
1370
|
+
}, { ...writeAccess, scopes }, requiredOperationId(context, 'memory.retire'), context.session);
|
|
1371
1371
|
recordWriteReceipt(context, 'memory.retire', retired.id);
|
|
1372
1372
|
return retired;
|
|
1373
1373
|
},
|
package/dist/knowledge/access.js
CHANGED
|
@@ -17,7 +17,7 @@ const tableColumnCache = new WeakMap();
|
|
|
17
17
|
// Keep memory-truth quarantine semantics excluded for legacy/backcompat rows even though the
|
|
18
18
|
// decisions.status terminal states: 'superseded' is history a caller holding
|
|
19
19
|
// scope may read under includeReplaced; the rest are holds excluded always.
|
|
20
|
-
const RETIRED_MEMORY_STATUSES = new Set(['
|
|
20
|
+
const RETIRED_MEMORY_STATUSES = new Set(['contradicted', 'stale']);
|
|
21
21
|
class TwinRefNotVisibleError extends Error {
|
|
22
22
|
constructor(ref) {
|
|
23
23
|
super(`Twin ref is not visible to requested visibility: ${ref.kind}:${ref.id}`);
|
|
@@ -19,7 +19,7 @@ export { observationVersionId, appendObservationVersion, getObservationVersion,
|
|
|
19
19
|
export { createWork, readWork, reviseWork, withdrawWork } from './commitments.js';
|
|
20
20
|
export { parseExactDueAt, assertIsoDate, assertWorkPatchValues, WORK_PATCH_DEADLINE_PATTERN, WORK_PATCH_DUE_AT_PATTERN, type ParsedExactDueAt, } from './work-dates.js';
|
|
21
21
|
export type { CommitmentPage, CommitmentChainEntry, CommitmentRevision, CommitmentView, CreateWorkCommand, ReviseWorkCommand, WithdrawWorkCommand, WorkRead, WorkWriteResult, } from './commitments.js';
|
|
22
|
-
export { vectorSearch, fts5Search } from './search.js';
|
|
22
|
+
export { vectorSearch, fts5Search, RECALL_EXCLUDED_STATUSES } from './search.js';
|
|
23
23
|
export { queryDecisionGraph, querySemanticEdges, STATED_DECISION_EDGES, getGraphNeighborhood, getGraphPaths, getGraphTimeline, queryGraph, AgentGraphValidationError, type AgentGraphAdapter, type AgentGraphEdgeFilters, type AgentGraphResult, type AgentGraphCurrentProjection, type AgentGraphPath, type AgentGraphTimelineEvent, type AgentGraphTimelineMemoryEvent, type AgentGraphTimelineCaseEvent, type AgentGraphTimelineRawEvent, type AgentGraphTimelineEdgeEvent, type GraphNeighborhoodInput, type GraphPathsInput, type GraphPathsResult, type GraphTimelineInput, type GraphTimelineResult, } from './graph-query.js';
|
|
24
24
|
export type { SourceIngestCommand, SourceIngestReceipt } from './source-ingest.js';
|
|
25
25
|
export interface KnowledgeOptions extends JudgmentKnowledgeOptions {
|
package/dist/knowledge/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AgentGraphValidationError = exports.queryGraph = exports.getGraphTimeline = exports.getGraphPaths = exports.getGraphNeighborhood = exports.STATED_DECISION_EDGES = exports.querySemanticEdges = exports.queryDecisionGraph = exports.fts5Search = exports.vectorSearch = exports.WORK_PATCH_DUE_AT_PATTERN = exports.WORK_PATCH_DEADLINE_PATTERN = exports.assertWorkPatchValues = exports.assertIsoDate = exports.parseExactDueAt = exports.withdrawWork = exports.reviseWork = exports.readWork = exports.createWork = exports.isObservationVisibilityRowVisible = exports.isObservationVersionVisible = exports.readObservationVersion = exports.searchOwnerObservationVersions = exports.getObservationVersion = exports.appendObservationVersion = exports.observationVersionId = exports.correctIdentity = exports.ingestSource = exports.TWIN_REF_KINDS = exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.TwinRefNotVisibleError = exports.visibleTwinRefKeysRecursive = exports.visibleTwinRefKeys = exports.listVisibleTwinEdgesForRefs = exports.isChannelGranted = exports.channelGrantClause = exports.assertTwinRefsVisible = exports.linkEdgeId = exports.findLink = exports.appendLink = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.getTwinEdge = exports.appendJudgment = exports.JudgmentError = exports.WORK_RECLASSIFY_DISPOSITIONS = exports.judgmentRecordId = void 0;
|
|
3
|
+
exports.AgentGraphValidationError = exports.queryGraph = exports.getGraphTimeline = exports.getGraphPaths = exports.getGraphNeighborhood = exports.STATED_DECISION_EDGES = exports.querySemanticEdges = exports.queryDecisionGraph = exports.RECALL_EXCLUDED_STATUSES = exports.fts5Search = exports.vectorSearch = exports.WORK_PATCH_DUE_AT_PATTERN = exports.WORK_PATCH_DEADLINE_PATTERN = exports.assertWorkPatchValues = exports.assertIsoDate = exports.parseExactDueAt = exports.withdrawWork = exports.reviseWork = exports.readWork = exports.createWork = exports.isObservationVisibilityRowVisible = exports.isObservationVersionVisible = exports.readObservationVersion = exports.searchOwnerObservationVersions = exports.getObservationVersion = exports.appendObservationVersion = exports.observationVersionId = exports.correctIdentity = exports.ingestSource = exports.TWIN_REF_KINDS = exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.TwinRefNotVisibleError = exports.visibleTwinRefKeysRecursive = exports.visibleTwinRefKeys = exports.listVisibleTwinEdgesForRefs = exports.isChannelGranted = exports.channelGrantClause = exports.assertTwinRefsVisible = exports.linkEdgeId = exports.findLink = exports.appendLink = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.getTwinEdge = exports.appendJudgment = exports.JudgmentError = exports.WORK_RECLASSIFY_DISPOSITIONS = exports.judgmentRecordId = void 0;
|
|
4
4
|
exports.createKnowledge = createKnowledge;
|
|
5
5
|
const links_js_1 = require("./links.js");
|
|
6
6
|
const judgments_js_1 = require("./judgments.js");
|
|
@@ -62,6 +62,7 @@ Object.defineProperty(exports, "WORK_PATCH_DUE_AT_PATTERN", { enumerable: true,
|
|
|
62
62
|
var search_js_1 = require("./search.js");
|
|
63
63
|
Object.defineProperty(exports, "vectorSearch", { enumerable: true, get: function () { return search_js_1.vectorSearch; } });
|
|
64
64
|
Object.defineProperty(exports, "fts5Search", { enumerable: true, get: function () { return search_js_1.fts5Search; } });
|
|
65
|
+
Object.defineProperty(exports, "RECALL_EXCLUDED_STATUSES", { enumerable: true, get: function () { return search_js_1.RECALL_EXCLUDED_STATUSES; } });
|
|
65
66
|
var graph_query_js_2 = require("./graph-query.js");
|
|
66
67
|
Object.defineProperty(exports, "queryDecisionGraph", { enumerable: true, get: function () { return graph_query_js_2.queryDecisionGraph; } });
|
|
67
68
|
Object.defineProperty(exports, "querySemanticEdges", { enumerable: true, get: function () { return graph_query_js_2.querySemanticEdges; } });
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { DatabaseInstance, DecisionRecord } from '../db-manager.js';
|
|
10
10
|
import type { MemoryKindFilter } from '../memory/types.js';
|
|
11
|
+
/** Statuses default recall leaves out; history (`includeHistory`) shows them. */
|
|
12
|
+
export declare const RECALL_EXCLUDED_STATUSES: readonly ["superseded", "contradicted", "stale"];
|
|
11
13
|
/**
|
|
12
14
|
* Brute-force cosine similarity search over stored embeddings.
|
|
13
15
|
*
|
|
@@ -31,7 +33,10 @@ export declare function vectorSearch(adapter: DatabaseInstance, queryEmbedding:
|
|
|
31
33
|
* @param limit - Max rows to return
|
|
32
34
|
* @returns Matching decision IDs with BM25 rank scores
|
|
33
35
|
*/
|
|
34
|
-
export declare function fts5Search(adapter: DatabaseInstance, query: string, limit?: number, kind?: MemoryKindFilter
|
|
36
|
+
export declare function fts5Search(adapter: DatabaseInstance, query: string, limit?: number, kind?: MemoryKindFilter, exclude?: {
|
|
37
|
+
statuses?: readonly string[];
|
|
38
|
+
amendments?: boolean;
|
|
39
|
+
}): Promise<{
|
|
35
40
|
id: string;
|
|
36
41
|
rank: number;
|
|
37
42
|
}[]>;
|
package/dist/knowledge/search.js
CHANGED
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* @module knowledge/search
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.RECALL_EXCLUDED_STATUSES = void 0;
|
|
11
12
|
exports.vectorSearch = vectorSearch;
|
|
12
13
|
exports.fts5Search = fts5Search;
|
|
14
|
+
/** Statuses default recall leaves out; history (`includeHistory`) shows them. */
|
|
15
|
+
exports.RECALL_EXCLUDED_STATUSES = ['superseded', 'contradicted', 'stale'];
|
|
13
16
|
/**
|
|
14
17
|
* Brute-force cosine similarity search over stored embeddings.
|
|
15
18
|
*
|
|
@@ -59,7 +62,7 @@ async function vectorSearch(adapter, queryEmbedding, limit = 5, threshold = 0.7,
|
|
|
59
62
|
* @param limit - Max rows to return
|
|
60
63
|
* @returns Matching decision IDs with BM25 rank scores
|
|
61
64
|
*/
|
|
62
|
-
async function fts5Search(adapter, query, limit = 10, kind) {
|
|
65
|
+
async function fts5Search(adapter, query, limit = 10, kind, exclude) {
|
|
63
66
|
// An absent FTS table is a real answer - no rows. A failing adapter is not,
|
|
64
67
|
// so this lookup is left unguarded and its errors reach the caller.
|
|
65
68
|
const tableCheck = adapter
|
|
@@ -68,24 +71,27 @@ async function fts5Search(adapter, query, limit = 10, kind) {
|
|
|
68
71
|
if (!tableCheck)
|
|
69
72
|
return [];
|
|
70
73
|
// Query execution - let errors propagate to the caller
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const kinds = Array.isArray(kind) ? kind : kind === undefined ? [] : [kind];
|
|
75
|
+
const kindClause = kinds.length === 0 ? '' : `AND d.kind IN (${kinds.map(() => '?').join(', ')})`;
|
|
76
|
+
// Excluded rows are left out before LIMIT, so they cannot fill the pool ahead of rows that stay.
|
|
77
|
+
const statuses = exclude?.statuses ?? [];
|
|
78
|
+
const statusClause = statuses.length === 0
|
|
79
|
+
? ''
|
|
80
|
+
: `AND (d.status IS NULL OR d.status NOT IN (${statuses.map(() => '?').join(', ')}))`;
|
|
81
|
+
const amendmentClause = exclude?.amendments
|
|
82
|
+
? "AND json_extract(d.payload_json, '$.amended') IS NULL"
|
|
83
|
+
: '';
|
|
76
84
|
const stmt = adapter.prepare(`
|
|
77
85
|
SELECT d.id, rank
|
|
78
86
|
FROM decisions_fts
|
|
79
87
|
JOIN decisions d ON decisions_fts.rowid = d.rowid
|
|
80
88
|
WHERE decisions_fts MATCH ?
|
|
81
89
|
${kindClause}
|
|
90
|
+
${statusClause}
|
|
91
|
+
${amendmentClause}
|
|
82
92
|
ORDER BY rank
|
|
83
93
|
LIMIT ?
|
|
84
94
|
`);
|
|
85
|
-
return
|
|
86
|
-
? stmt.all(query, ...kind, limit)
|
|
87
|
-
: kind === undefined
|
|
88
|
-
? stmt.all(query, limit)
|
|
89
|
-
: stmt.all(query, kind, limit));
|
|
95
|
+
return stmt.all(query, ...kinds, ...statuses, limit);
|
|
90
96
|
}
|
|
91
97
|
//# sourceMappingURL=search.js.map
|
package/dist/memory/api.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export declare function buildDecisionId(topic: string): string;
|
|
|
30
30
|
export interface ReadMemoryRecordsOptions {
|
|
31
31
|
kind?: MemoryKindFilter;
|
|
32
32
|
status?: MemoryStatus | readonly MemoryStatus[];
|
|
33
|
+
/** Leave out records that only amend another (a retirement or an outcome change), as recall does. */
|
|
34
|
+
excludeAmendments?: boolean;
|
|
33
35
|
}
|
|
34
36
|
/** Read complete memory records whose stored scopes intersect the admitted scopes. */
|
|
35
37
|
export declare function readMemoryRecordsInScopes(adapter: DatabaseInstance, scopes: readonly MemoryScopeRef[], options?: ReadMemoryRecordsOptions): Promise<MemoryRecord[]>;
|
|
@@ -76,7 +78,7 @@ export declare function retireMemoryRecord(adapter: DatabaseInstance, input: {
|
|
|
76
78
|
memoryId: string;
|
|
77
79
|
status: MemoryRetirementStatus;
|
|
78
80
|
reason: string;
|
|
79
|
-
}, access: JudgmentAccess, commandId: string): Promise<{
|
|
81
|
+
}, access: JudgmentAccess, commandId: string, session?: ActionSessionFacts): Promise<{
|
|
80
82
|
success: true;
|
|
81
83
|
id: string;
|
|
82
84
|
status: MemoryRetirementStatus;
|
package/dist/memory/api.js
CHANGED
|
@@ -167,6 +167,9 @@ async function readMemoryRecordsInScopes(adapter, scopes, options = {}) {
|
|
|
167
167
|
conditions.push(`COALESCE(d.status, 'active') IN (${statuses.map(() => '?').join(', ')})`);
|
|
168
168
|
params.push(...statuses);
|
|
169
169
|
}
|
|
170
|
+
if (options.excludeAmendments === true) {
|
|
171
|
+
conditions.push(`json_extract(d.payload_json, '$.amended') IS NULL`);
|
|
172
|
+
}
|
|
170
173
|
const rows = adapter
|
|
171
174
|
.prepare(`SELECT d.id, d.topic, d.decision, d.reasoning, d.confidence, d.created_at, d.updated_at,
|
|
172
175
|
d.trust_context, d.kind, d.status, d.summary, d.event_date, d.event_datetime,
|
|
@@ -713,7 +716,7 @@ async function saveLegacyMemory(adapter, input, legacy, access) {
|
|
|
713
716
|
return saveMemoryInternal(adapter, clean, provenance, effectiveAccess, legacy);
|
|
714
717
|
}
|
|
715
718
|
/** Append an access-checked status amendment for one stored memory record. */
|
|
716
|
-
async function retireMemoryRecord(adapter, input, access, commandId) {
|
|
719
|
+
async function retireMemoryRecord(adapter, input, access, commandId, session) {
|
|
717
720
|
const id = input.memoryId.trim();
|
|
718
721
|
const reason = input.reason.trim();
|
|
719
722
|
if (!id)
|
|
@@ -729,11 +732,30 @@ async function retireMemoryRecord(adapter, input, access, commandId) {
|
|
|
729
732
|
}
|
|
730
733
|
const admittedScopes = record.scopes.filter((recordScope) => access.scopes.some((scope) => scope.kind === recordScope.kind && scope.id === recordScope.id));
|
|
731
734
|
const summary = `Status '${input.status}' applied to ${id}: ${reason}`;
|
|
735
|
+
// Who retired it, stated by the host as for a save, so a retirement can be traced to its turn.
|
|
736
|
+
const provenance = (0, provenance_js_1.normalizeMemoryWriteProvenance)({
|
|
737
|
+
actor: session?.actor ?? 'main_agent',
|
|
738
|
+
agent_id: access.agentId,
|
|
739
|
+
model_run_id: session?.modelRunId,
|
|
740
|
+
envelope_hash: session?.envelopeHash,
|
|
741
|
+
tool_name: session?.toolName,
|
|
742
|
+
gateway_call_id: session?.gatewayCallId,
|
|
743
|
+
context_packet_id: session?.contextPacketId,
|
|
744
|
+
source_turn_id: session?.sourceTurnId,
|
|
745
|
+
source_message_ref: session?.sourceMessageRef,
|
|
746
|
+
source_refs: session?.sourceRefs ? [...session.sourceRefs] : undefined,
|
|
747
|
+
});
|
|
732
748
|
const receipt = await (0, index_js_1.appendJudgment)({
|
|
733
749
|
commandId,
|
|
734
750
|
topic: `judgment/${id}`,
|
|
735
751
|
summary,
|
|
736
752
|
recordKind: 'judgment',
|
|
753
|
+
sourceRefs: provenance.source_refs,
|
|
754
|
+
provenance: provenance.provenance,
|
|
755
|
+
agentId: provenance.agent_id,
|
|
756
|
+
modelRunId: provenance.model_run_id,
|
|
757
|
+
envelopeHash: provenance.envelope_hash,
|
|
758
|
+
gatewayCallId: provenance.gateway_call_id,
|
|
737
759
|
payload: { amended: id, status: input.status, reason },
|
|
738
760
|
scopes: admittedScopes,
|
|
739
761
|
links: [{ relation: 'amends', target: { kind: 'memory', id } }],
|
|
@@ -747,12 +769,15 @@ async function buildProfile(adapter, scopes) {
|
|
|
747
769
|
const records = await loadScopedMemories(adapter, scopes);
|
|
748
770
|
return (0, profile_builder_js_1.classifyProfileEntries)(records);
|
|
749
771
|
}
|
|
750
|
-
const EXCLUDED_STATUSES = new Set(
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
772
|
+
const EXCLUDED_STATUSES = new Set(search_js_1.RECALL_EXCLUDED_STATUSES);
|
|
773
|
+
/** The ids among these that only amend another record (a retirement or an outcome change). */
|
|
774
|
+
function amendmentIds(adapter, ids) {
|
|
775
|
+
const rows = adapter
|
|
776
|
+
.prepare(`SELECT id FROM decisions WHERE id IN (${ids.map(() => '?').join(',')})
|
|
777
|
+
AND json_extract(payload_json, '$.amended') IS NOT NULL`)
|
|
778
|
+
.all(...ids);
|
|
779
|
+
return new Set(rows.map((row) => row.id));
|
|
780
|
+
}
|
|
756
781
|
async function recallMemory(adapter, query, options = {}) {
|
|
757
782
|
const bundle = (0, types_js_1.createEmptyRecallBundle)(query);
|
|
758
783
|
const searchOptions = (0, search_quality_js_1.normalizeSearchQualityOptions)(options);
|
|
@@ -930,7 +955,7 @@ async function recallMemory(adapter, query, options = {}) {
|
|
|
930
955
|
.map((t) => stemToken(t))
|
|
931
956
|
.filter((t) => !FTS5_NOISE_WORDS.has(t));
|
|
932
957
|
const ftsQuery = ftsTokens.length > 0 ? ftsTokens.join(' OR ') : query;
|
|
933
|
-
const ftsResults = await (0, search_js_1.fts5Search)(searchAdapter, ftsQuery, lexicalLimit, options.kind);
|
|
958
|
+
const ftsResults = await (0, search_js_1.fts5Search)(searchAdapter, ftsQuery, lexicalLimit, options.kind, options.includeHistory ? undefined : { statuses: [...EXCLUDED_STATUSES], amendments: true });
|
|
934
959
|
if (ftsResults.length > 0) {
|
|
935
960
|
const adapter = searchAdapter;
|
|
936
961
|
const fallbackSource = {
|
|
@@ -1012,6 +1037,10 @@ async function recallMemory(adapter, query, options = {}) {
|
|
|
1012
1037
|
if (options.kind !== undefined) {
|
|
1013
1038
|
lexicalRecords = lexicalRecords.filter((r) => matchesKind(r.kind));
|
|
1014
1039
|
}
|
|
1040
|
+
if (!options.includeHistory && lexicalRecords.length > 0) {
|
|
1041
|
+
const amendments = amendmentIds(adapter, lexicalRecords.map((r) => r.id));
|
|
1042
|
+
lexicalRecords = lexicalRecords.filter((r) => !amendments.has(r.id));
|
|
1043
|
+
}
|
|
1015
1044
|
lexicalCandidates = buildLexicalCandidates(lexicalRecords, query);
|
|
1016
1045
|
if (subQueries.length > 1) {
|
|
1017
1046
|
for (const sq of subQueries.slice(1)) {
|
|
@@ -1187,6 +1216,15 @@ async function recallMemory(adapter, query, options = {}) {
|
|
|
1187
1216
|
];
|
|
1188
1217
|
});
|
|
1189
1218
|
fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || acceptedPrimaryIds.has(hit.source_id));
|
|
1219
|
+
// A retirement or an outcome change audits another record; it is not a belief. Default recall
|
|
1220
|
+
// leaves it out and history shows it. Only the host writers put `amended` in a payload.
|
|
1221
|
+
if (!options.includeHistory && matched.length > 0) {
|
|
1222
|
+
const amendments = amendmentIds(adapter, matched.map((record) => record.id));
|
|
1223
|
+
if (amendments.size > 0) {
|
|
1224
|
+
matched = matched.filter((record) => !amendments.has(record.id));
|
|
1225
|
+
fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || !amendments.has(hit.source_id));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1190
1228
|
// Honor options.limit on the final memories (matched is RRF-rank-sorted, canonical
|
|
1191
1229
|
// dual-write appends last). Without this cap the full fusion set (hundreds of records)
|
|
1192
1230
|
// flowed into bundle.memories AND the per-record enrichment SQL loops below.
|
|
@@ -1198,14 +1236,20 @@ async function recallMemory(adapter, query, options = {}) {
|
|
|
1198
1236
|
const cappedIds = new Set(matched.map((record) => record.id));
|
|
1199
1237
|
fusedHits = fusedHits.filter((hit) => hit.source_type !== 'decision' || cappedIds.has(hit.source_id));
|
|
1200
1238
|
}
|
|
1201
|
-
//
|
|
1202
|
-
//
|
|
1203
|
-
//
|
|
1204
|
-
// excluded from search. This recovers their key information so it is not lost.
|
|
1239
|
+
// Superseded records are excluded from search, so an active record carries what it replaced:
|
|
1240
|
+
// the reader sees the correction and what it corrected. A predecessor is shown only inside the
|
|
1241
|
+
// reader's scopes, and marked as replaced.
|
|
1205
1242
|
if (matched.length > 0) {
|
|
1206
1243
|
const stmtChain = adapter.prepare(`SELECT id, summary, decision FROM decisions WHERE superseded_by = ?`);
|
|
1244
|
+
const readerScopes = options.scopes && options.scopes.length > 0
|
|
1245
|
+
? new Set(options.scopes.map((scope) => `${scope.kind}:${scope.id}`))
|
|
1246
|
+
: null;
|
|
1207
1247
|
for (const record of matched) {
|
|
1208
|
-
|
|
1248
|
+
let predecessors = stmtChain.all(record.id);
|
|
1249
|
+
if (readerScopes && predecessors.length > 0) {
|
|
1250
|
+
const scopeMap = batchLoadScopes(adapter, predecessors.map((predecessor) => predecessor.id));
|
|
1251
|
+
predecessors = predecessors.filter((predecessor) => (scopeMap.get(predecessor.id) ?? []).some((scope) => readerScopes.has(`${scope.kind}:${scope.id}`)));
|
|
1252
|
+
}
|
|
1209
1253
|
if (predecessors.length > 0) {
|
|
1210
1254
|
const extra = predecessors
|
|
1211
1255
|
.map((p) => String(p.summary ?? p.decision ?? ''))
|
|
@@ -1213,8 +1257,8 @@ async function recallMemory(adapter, query, options = {}) {
|
|
|
1213
1257
|
.join(' | ');
|
|
1214
1258
|
if (extra) {
|
|
1215
1259
|
record.details = record.details
|
|
1216
|
-
? `${record.details}\n[
|
|
1217
|
-
: `[
|
|
1260
|
+
? `${record.details}\n[Replaced by this record] ${extra}`
|
|
1261
|
+
: `[Replaced by this record] ${extra}`;
|
|
1218
1262
|
}
|
|
1219
1263
|
}
|
|
1220
1264
|
}
|
|
@@ -1243,6 +1287,11 @@ async function recallMemory(adapter, query, options = {}) {
|
|
|
1243
1287
|
const status = row?.status || '';
|
|
1244
1288
|
return matchesKind(row?.kind) && (!status || !EXCLUDED_STATUSES.has(status));
|
|
1245
1289
|
});
|
|
1290
|
+
// A link an agent stated can point at a retirement or an outcome change; it stays out too.
|
|
1291
|
+
if (expandedOnly.length > 0) {
|
|
1292
|
+
const amendments = amendmentIds(adapter, expandedOnly.map((e) => e.id));
|
|
1293
|
+
expandedOnly = expandedOnly.filter((e) => !amendments.has(e.id));
|
|
1294
|
+
}
|
|
1246
1295
|
}
|
|
1247
1296
|
else if (options.kind !== undefined) {
|
|
1248
1297
|
expandedOnly = expandedOnly.filter((e) => {
|
|
@@ -24,7 +24,7 @@ exports.OBSERVATION_COLUMNS = `observation_id AS event_index_id, source AS sourc
|
|
|
24
24
|
* Statuses every memory read path excludes (source-readers.ts:293, and recall's own
|
|
25
25
|
* history exclusion). Mirrored so a citation cannot present a retired record as current.
|
|
26
26
|
*/
|
|
27
|
-
const RETIRED_MEMORY_STATUSES = new Set(['superseded', '
|
|
27
|
+
const RETIRED_MEMORY_STATUSES = new Set(['superseded', 'contradicted', 'stale']);
|
|
28
28
|
/**
|
|
29
29
|
* Parse a stored source ref into what it actually is.
|
|
30
30
|
*
|
package/dist/memory/types.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type MemoryKind = (typeof MEMORY_KINDS)[number];
|
|
|
29
29
|
export type MemoryKindFilter = MemoryKind | [MemoryKind, ...MemoryKind[]];
|
|
30
30
|
export declare const MEMORY_STATUSES: readonly ["active", "superseded", "contradicted", "stale"];
|
|
31
31
|
export type MemoryStatus = (typeof MEMORY_STATUSES)[number];
|
|
32
|
-
export declare const MEMORY_TRUTH_STATUSES: readonly ["active", "superseded", "contradicted", "stale"
|
|
32
|
+
export declare const MEMORY_TRUTH_STATUSES: readonly ["active", "superseded", "contradicted", "stale"];
|
|
33
33
|
export type MemoryTruthStatus = (typeof MEMORY_TRUTH_STATUSES)[number];
|
|
34
34
|
export declare const MEMORY_EDGE_TYPES: readonly ["supersedes", "builds_on", "synthesizes", "contradicts"];
|
|
35
35
|
export type MemoryEdgeType = (typeof MEMORY_EDGE_TYPES)[number];
|
package/dist/memory/types.js
CHANGED
|
@@ -35,13 +35,7 @@ exports.MEMORY_KINDS = [
|
|
|
35
35
|
'workflow',
|
|
36
36
|
];
|
|
37
37
|
exports.MEMORY_STATUSES = ['active', 'superseded', 'contradicted', 'stale'];
|
|
38
|
-
exports.MEMORY_TRUTH_STATUSES = [
|
|
39
|
-
'active',
|
|
40
|
-
'superseded',
|
|
41
|
-
'contradicted',
|
|
42
|
-
'stale',
|
|
43
|
-
'quarantined',
|
|
44
|
-
];
|
|
38
|
+
exports.MEMORY_TRUTH_STATUSES = ['active', 'superseded', 'contradicted', 'stale'];
|
|
45
39
|
exports.MEMORY_EDGE_TYPES = ['supersedes', 'builds_on', 'synthesizes', 'contradicts'];
|
|
46
40
|
exports.MEMORY_AGENT_ACTIONS = [
|
|
47
41
|
'save',
|