@jungjaehoon/mama-core 4.1.0 → 5.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/README.md +10 -10
- package/db/migrations/099-commitment-revision-graph.sql +3 -0
- package/dist/api/catalog.js +3 -6
- package/dist/api/dispatch.js +12 -1
- package/dist/db-adapter/node-sqlite-adapter.d.ts +5 -1
- package/dist/db-adapter/node-sqlite-adapter.js +27 -24
- package/dist/db-manager.d.ts +2 -13
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -6
- package/dist/knowledge/commitments.d.ts +2 -0
- package/dist/knowledge/commitments.js +1 -0
- package/dist/knowledge/graph-query.d.ts +10 -1
- package/dist/knowledge/graph-query.js +118 -25
- package/dist/knowledge/index.d.ts +11 -3
- package/dist/knowledge/index.js +9 -10
- package/dist/knowledge/judgments.d.ts +3 -1
- package/dist/knowledge/judgments.js +46 -26
- package/dist/knowledge/links.d.ts +50 -0
- package/dist/knowledge/links.js +152 -0
- package/dist/mama-api.d.ts +22 -7
- package/dist/mama-api.js +64 -15
- package/dist/memory/api.d.ts +16 -11
- package/dist/memory/api.js +376 -400
- package/dist/memory/decision-links.d.ts +54 -0
- package/dist/memory/decision-links.js +195 -0
- package/dist/memory/graph-read.d.ts +0 -16
- package/dist/memory/graph-read.js +10 -51
- package/dist/memory/judgment-types.d.ts +1 -20
- package/dist/memory/types.d.ts +13 -0
- package/dist/memory/write-adapters.d.ts +1 -39
- package/dist/memory/write-adapters.js +0 -98
- package/package.json +1 -1
- package/dist/knowledge/commitment-revision-migration.d.ts +0 -4
- package/dist/knowledge/commitment-revision-migration.js +0 -50
- package/dist/knowledge/decision-edges.d.ts +0 -70
- package/dist/knowledge/decision-edges.js +0 -123
- package/dist/memory/evolution-engine.d.ts +0 -22
- package/dist/memory/evolution-engine.js +0 -133
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 **
|
|
7
|
+
Version **5.0.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
|
|
|
@@ -13,15 +13,15 @@ See [architecture](../../docs/explanation/architecture.md) and
|
|
|
13
13
|
[package.json](package.json) defines supported import paths; [src/index.ts](src/index.ts)
|
|
14
14
|
defines root exports. Representative root exports:
|
|
15
15
|
|
|
16
|
-
| Area | Root exports
|
|
17
|
-
| --------- |
|
|
18
|
-
| Storage | `createAdapter`, `SQLiteAdapter`, `initDB`, `getDB`, `getAdapter`, `closeDB`
|
|
19
|
-
| Records | `createKnowledge`, `appendJudgment`, `ingestSource`
|
|
20
|
-
| Memory | `mama`, `createMamaApi`, `saveMemory`, `recallMemory`, `ingestConversation`, `MEMORY_KINDS`
|
|
21
|
-
| Search | `generateEmbedding`, `generateEnhancedEmbedding`, `cosineSimilarity`, `EmbeddingCache`
|
|
22
|
-
| Evidence | `getMemoryProvenance`, `resolveMemoryProvenance`, `
|
|
23
|
-
| Execution | `createCatalog`, `createDispatcher`, `createClient`, `startRuntime`, `createActionIpcServer`
|
|
24
|
-
| Traces | `beginModelRun`, `commitModelRun`, `appendToolTrace`, `listToolTracesForRun`
|
|
16
|
+
| Area | Root exports |
|
|
17
|
+
| --------- | -------------------------------------------------------------------------------------------- |
|
|
18
|
+
| Storage | `createAdapter`, `SQLiteAdapter`, `initDB`, `getDB`, `getAdapter`, `closeDB` |
|
|
19
|
+
| Records | `createKnowledge`, `appendJudgment`, `ingestSource` |
|
|
20
|
+
| Memory | `mama`, `createMamaApi`, `saveMemory`, `recallMemory`, `ingestConversation`, `MEMORY_KINDS` |
|
|
21
|
+
| Search | `generateEmbedding`, `generateEnhancedEmbedding`, `cosineSimilarity`, `EmbeddingCache` |
|
|
22
|
+
| Evidence | `getMemoryProvenance`, `resolveMemoryProvenance`, `listVisibleTwinEdgesForRefs` |
|
|
23
|
+
| Execution | `createCatalog`, `createDispatcher`, `createClient`, `startRuntime`, `createActionIpcServer` |
|
|
24
|
+
| Traces | `beginModelRun`, `commitModelRun`, `appendToolTrace`, `listToolTracesForRun` |
|
|
25
25
|
|
|
26
26
|
Every public subpath below is relative to `@jungjaehoon/mama-core`:
|
|
27
27
|
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
-- 099 once backfilled a host builds_on from each commitment revision to the one before it. The
|
|
2
|
+
-- host no longer writes edges the agent did not state (owner, 2026-09-30); the order of revisions
|
|
3
|
+
-- lives in commitment_assignments. The version is kept so databases that crossed it stay aligned.
|
|
1
4
|
INSERT OR IGNORE INTO schema_version (version, description)
|
|
2
5
|
VALUES (99, 'Commitment revision graph links');
|
package/dist/api/catalog.js
CHANGED
|
@@ -899,7 +899,7 @@ function coreActionRegistrations(knowledge, adapter, deps) {
|
|
|
899
899
|
{
|
|
900
900
|
contract: {
|
|
901
901
|
name: 'memory.search',
|
|
902
|
-
summary: 'Search or list memory records under the caller authority. Omitted scopes read the admitted corpus; explicit scopes must be a subset of it. With a query this is the semantic recall path (vector + lexical fusion, learned ranker when enabled); without one it is the exact topic-prefix ledger read.',
|
|
902
|
+
summary: 'Search or list memory records under the caller authority. Omitted scopes read the admitted corpus; explicit scopes must be a subset of it. With a query this is the semantic recall path (vector + lexical fusion, learned ranker when enabled), each hit lists the records its stated links reach (links: id, topic, relation, reason, corrected_by), and a result reached through a link names the hit it came from (related_to), the relation (graph_source), the reason (edge_reason) and any correction (edge_corrected_by); a hit that is one revision of a work item names it (work_item: commitment_id, revision, head_revision), and an earlier revision can rank above the head that corrected it, so open the head before answering from it; without one it is the exact topic-prefix ledger read.',
|
|
903
903
|
inputSchema: memorySearchSchema,
|
|
904
904
|
examples: [
|
|
905
905
|
{
|
|
@@ -1078,14 +1078,14 @@ function coreActionRegistrations(knowledge, adapter, deps) {
|
|
|
1078
1078
|
{
|
|
1079
1079
|
contract: {
|
|
1080
1080
|
name: 'memory.read:graph',
|
|
1081
|
-
summary: 'The decision graph this caller may see: `view: graph` returns a bounded page of nodes with the edges joining admitted nodes and the admitted total; `nodes` returns the named ids; `detail` returns one record in full
|
|
1081
|
+
summary: 'The decision graph this caller may see: `view: graph` returns a bounded page of nodes with the edges joining admitted nodes and the admitted total; `nodes` returns the named ids; `detail` returns one record in full. Every view is bounded by the admitted scopes.',
|
|
1082
1082
|
inputSchema: {
|
|
1083
1083
|
type: 'object',
|
|
1084
1084
|
additionalProperties: false,
|
|
1085
1085
|
properties: {
|
|
1086
1086
|
view: {
|
|
1087
1087
|
type: 'string',
|
|
1088
|
-
enum: ['graph', 'nodes', 'detail'
|
|
1088
|
+
enum: ['graph', 'nodes', 'detail'],
|
|
1089
1089
|
description: 'Memory graph view, e.g. "detail".',
|
|
1090
1090
|
},
|
|
1091
1091
|
id: { type: 'string', description: 'One memory id for detail, e.g. "mem_123".' },
|
|
@@ -1132,9 +1132,6 @@ function coreActionRegistrations(knowledge, adapter, deps) {
|
|
|
1132
1132
|
}
|
|
1133
1133
|
return { nodes: await (0, graph_read_js_1.readGraphNodes)(adapter, scopes, { ids, limit: null }) };
|
|
1134
1134
|
}
|
|
1135
|
-
if (view === 'similarity') {
|
|
1136
|
-
return { edges: await (0, graph_read_js_1.readGraphSimilarityEdges)(adapter, scopes) };
|
|
1137
|
-
}
|
|
1138
1135
|
const [nodes, edges, total] = await Promise.all([
|
|
1139
1136
|
(0, graph_read_js_1.readGraphNodes)(adapter, scopes, { limit }),
|
|
1140
1137
|
(0, graph_read_js_1.readGraphEdges)(adapter, scopes),
|
package/dist/api/dispatch.js
CHANGED
|
@@ -272,7 +272,11 @@ function validateInput(schema, value, path) {
|
|
|
272
272
|
if (schema.oneOf !== undefined) {
|
|
273
273
|
const matches = schema.oneOf.filter((sub) => validateInput(sub, value, path) === null).length;
|
|
274
274
|
if (matches !== 1) {
|
|
275
|
-
|
|
275
|
+
// A refusal names what is allowed: "(0 matched)" alone once left a caller dropping the
|
|
276
|
+
// field and the write with it.
|
|
277
|
+
const shapes = schema.oneOf.map(shapeName).join(', ');
|
|
278
|
+
const described = schema.description === undefined ? '' : ` ${schema.description}`;
|
|
279
|
+
return `${path} must match exactly one of: ${shapes} (${matches} matched).${described}`;
|
|
276
280
|
}
|
|
277
281
|
}
|
|
278
282
|
if (typeof value === 'number') {
|
|
@@ -349,6 +353,13 @@ function validateInput(schema, value, path) {
|
|
|
349
353
|
}
|
|
350
354
|
return null;
|
|
351
355
|
}
|
|
356
|
+
function shapeName(schema) {
|
|
357
|
+
if (schema.const !== undefined)
|
|
358
|
+
return JSON.stringify(schema.const);
|
|
359
|
+
if (schema.enum !== undefined)
|
|
360
|
+
return schema.enum.map((value) => JSON.stringify(value)).join(' | ');
|
|
361
|
+
return schema.type ?? 'a described shape';
|
|
362
|
+
}
|
|
352
363
|
function typeMismatch(type, value) {
|
|
353
364
|
switch (type) {
|
|
354
365
|
case 'object':
|
|
@@ -94,7 +94,11 @@ export declare class NodeSQLiteAdapter implements DatabaseInstance {
|
|
|
94
94
|
private rebuildRegistry069;
|
|
95
95
|
private rebuildRecordActors070;
|
|
96
96
|
private applyRepairMigration;
|
|
97
|
-
|
|
97
|
+
/**
|
|
98
|
+
* A migration that drops and recreates a referenced table: foreign keys go off outside the
|
|
99
|
+
* transaction (a PRAGMA inside it does nothing), so the drop cascades to no row.
|
|
100
|
+
*/
|
|
101
|
+
private applyWithForeignKeysOff;
|
|
98
102
|
private operatorMemoryCommitIntentTableSql;
|
|
99
103
|
private hasOperatorMemoryCommitIntentClaimInvariant;
|
|
100
104
|
private assertMigration040BaseComplete;
|
|
@@ -14,7 +14,6 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
14
14
|
const node_sqlite_statement_js_1 = require("./node-sqlite-statement.js");
|
|
15
15
|
const debug_logger_js_1 = require("../debug-logger.js");
|
|
16
16
|
const embedder_js_1 = require("../embedding/embedder.js");
|
|
17
|
-
const commitment_revision_migration_js_1 = require("../knowledge/commitment-revision-migration.js");
|
|
18
17
|
const SQLITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
19
18
|
// Migration 071 origin invariant, kept in ONE place so the DDL the rebuild
|
|
20
19
|
// writes and the shape verification that admits a table cannot drift. A row is
|
|
@@ -322,7 +321,7 @@ class NodeSQLiteAdapter {
|
|
|
322
321
|
}
|
|
323
322
|
// Re-read one decision's effective status into the cache. MUST be called after
|
|
324
323
|
// any status transition that can move a row OUT of an excluded state (e.g.
|
|
325
|
-
//
|
|
324
|
+
// a status amendment back to active): the vectorSearch pre-filter drops
|
|
326
325
|
// excluded rowids before the api post-filter ever sees them, so a stale
|
|
327
326
|
// excluded entry would make an active row unrecallable until restart.
|
|
328
327
|
refreshDecisionStatusCache(rowid) {
|
|
@@ -675,22 +674,6 @@ class NodeSQLiteAdapter {
|
|
|
675
674
|
if (version <= currentVersion) {
|
|
676
675
|
continue;
|
|
677
676
|
}
|
|
678
|
-
if (isCore && version === 99) {
|
|
679
|
-
try {
|
|
680
|
-
this.exec('BEGIN TRANSACTION');
|
|
681
|
-
(0, commitment_revision_migration_js_1.backfillCommitmentRevisionGraph)(this);
|
|
682
|
-
this.exec(fs_1.default.readFileSync(path_1.default.join(migrationsDir, file), 'utf8'));
|
|
683
|
-
this.stampMigration(sourceName, version);
|
|
684
|
-
this.exec('COMMIT');
|
|
685
|
-
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} applied successfully`);
|
|
686
|
-
}
|
|
687
|
-
catch (err) {
|
|
688
|
-
this.exec('ROLLBACK');
|
|
689
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
690
|
-
throw new Error(`Migration ${file} failed: ${message}`);
|
|
691
|
-
}
|
|
692
|
-
continue;
|
|
693
|
-
}
|
|
694
677
|
if (isCore && version === 98) {
|
|
695
678
|
const decisionColumns = this.tableColumns('decisions');
|
|
696
679
|
if (!this.tableExists('decisions_fts') ||
|
|
@@ -698,11 +681,27 @@ class NodeSQLiteAdapter {
|
|
|
698
681
|
(0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} deferred: the full decisions projection is not present`);
|
|
699
682
|
continue;
|
|
700
683
|
}
|
|
701
|
-
this.
|
|
684
|
+
this.applyWithForeignKeysOff(migrationsDir, file);
|
|
702
685
|
this.stampMigration(sourceName, version);
|
|
703
686
|
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} reconciled successfully`);
|
|
704
687
|
continue;
|
|
705
688
|
}
|
|
689
|
+
// 084 rebuilds memory_scopes. Its own PRAGMA foreign_keys = OFF is a no-op inside the
|
|
690
|
+
// migration transaction, so dropping the table cascaded to every memory scope binding
|
|
691
|
+
// (a development memory at schema 80 lost all 414 on the way to 099).
|
|
692
|
+
if (isCore && version === 84) {
|
|
693
|
+
if (!this.tableExists('memory_scopes')) {
|
|
694
|
+
// As the plain path does for an ALTER TABLE on a missing table: a legacy database whose
|
|
695
|
+
// scope tables come later from the structural repair.
|
|
696
|
+
(0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} skipped: memory_scopes does not exist`);
|
|
697
|
+
this.stampMigration(sourceName, version);
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
this.applyWithForeignKeysOff(migrationsDir, file);
|
|
701
|
+
this.stampMigration(sourceName, version);
|
|
702
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} applied successfully`);
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
706
705
|
// Runtime MetricsStore may already own project_id/channel_id. Reconcile
|
|
707
706
|
// this additive migration atomically instead of accepting a duplicate skip.
|
|
708
707
|
if (isCore && version === 68) {
|
|
@@ -1347,15 +1346,19 @@ class NodeSQLiteAdapter {
|
|
|
1347
1346
|
throw new Error(`Repair migration ${fileName} failed: ${message}`);
|
|
1348
1347
|
}
|
|
1349
1348
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1349
|
+
/**
|
|
1350
|
+
* A migration that drops and recreates a referenced table: foreign keys go off outside the
|
|
1351
|
+
* transaction (a PRAGMA inside it does nothing), so the drop cascades to no row.
|
|
1352
|
+
*/
|
|
1353
|
+
applyWithForeignKeysOff(migrationsDir, file) {
|
|
1354
|
+
const migrationPath = path_1.default.join(migrationsDir, file);
|
|
1352
1355
|
if (!fs_1.default.existsSync(migrationPath)) {
|
|
1353
|
-
throw new Error(
|
|
1356
|
+
throw new Error(`Migration ${file} SQL is missing`);
|
|
1354
1357
|
}
|
|
1355
1358
|
const previousForeignKeys = this.readForeignKeysEnabled();
|
|
1356
1359
|
this.exec('PRAGMA foreign_keys = OFF');
|
|
1357
1360
|
if (this.readForeignKeysEnabled()) {
|
|
1358
|
-
throw new Error(
|
|
1361
|
+
throw new Error(`Migration ${file} could not disable foreign_keys before its rebuild`);
|
|
1359
1362
|
}
|
|
1360
1363
|
try {
|
|
1361
1364
|
this.transaction(() => {
|
|
@@ -1363,7 +1366,7 @@ class NodeSQLiteAdapter {
|
|
|
1363
1366
|
});
|
|
1364
1367
|
const violations = this.prepare('PRAGMA foreign_key_check').all();
|
|
1365
1368
|
if (violations.length > 0) {
|
|
1366
|
-
throw new Error(
|
|
1369
|
+
throw new Error(`Migration ${file} left foreign key violations`);
|
|
1367
1370
|
}
|
|
1368
1371
|
}
|
|
1369
1372
|
finally {
|
package/dist/db-manager.d.ts
CHANGED
|
@@ -81,7 +81,6 @@ export interface DecisionRecord {
|
|
|
81
81
|
refined_from?: string | string[] | null;
|
|
82
82
|
created_at: number;
|
|
83
83
|
updated_at?: number;
|
|
84
|
-
edges?: DecisionEdgeRow[];
|
|
85
84
|
/** ISO 8601 date when the event actually occurred. Null if not set. */
|
|
86
85
|
event_date?: string | null;
|
|
87
86
|
/** Source event timestamp in milliseconds when known. Null if not set. */
|
|
@@ -94,19 +93,9 @@ export interface OutcomeData {
|
|
|
94
93
|
duration_days?: number | null;
|
|
95
94
|
confidence?: number | null;
|
|
96
95
|
}
|
|
97
|
-
export interface DecisionEdgeRow {
|
|
98
|
-
from_id: string;
|
|
99
|
-
to_id: string;
|
|
100
|
-
relationship: string;
|
|
101
|
-
reason?: string | null;
|
|
102
|
-
weight?: number;
|
|
103
|
-
created_at?: number;
|
|
104
|
-
created_by?: string;
|
|
105
|
-
approved_by_user?: number | null;
|
|
106
|
-
decision_id?: string | null;
|
|
107
|
-
evidence?: string | null;
|
|
108
|
-
}
|
|
109
96
|
export interface SemanticEdgeItem {
|
|
97
|
+
/** The link's id when it is a `twin_edges` row; null for a legacy `decision_edges` row. */
|
|
98
|
+
edge_id?: string | null;
|
|
110
99
|
from_id: string;
|
|
111
100
|
to_id: string;
|
|
112
101
|
relationship: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import mama from './mama-api.js';
|
|
|
14
14
|
export { mama };
|
|
15
15
|
export { createMamaApi, type MamaApi } from './mama-api.js';
|
|
16
16
|
export { MEMORY_SCOPE_KINDS, MEMORY_KINDS, MEMORY_STATUSES, MEMORY_EDGE_TYPES, createEmptyRecallBundle, type MemoryScopeKind, type MemoryKind, type MemoryKindFilter, type MemoryStatus, type MemoryEdgeType, type MemoryScopeRef, type MemorySourceRef, type MemoryRecord, type MemoryWriteProvenance, type MemoryProvenanceRecord, type PublicSaveMemoryInput, type PublicIngestMemoryInput, type PublicIngestConversationInput, type MemorySearchResultHit, type MemoryEdge, type ProfileSnapshot, type RecallBundle, type ConversationMessage, type IngestConversationInput, type ExtractedMemoryUnit, type IngestConversationResult, type AuditFindingRecord, canonicalizeContextScopes, } from './memory/types.js';
|
|
17
|
-
export { saveMemory, saveJudgmentRecord, saveLegacyMemory,
|
|
17
|
+
export { saveMemory, saveJudgmentRecord, saveLegacyMemory, readMemoryRecordById, readMemoryRecordsInScopes, retireMemoryRecord, recallMemory, buildProfile, ingestMemory, buildMemoryBootstrap, createAuditAck, recordMemoryAudit, ingestConversation, upsertChannelSummary, getChannelSummary, type MemoryRetirementStatus, type ReadMemoryRecordsOptions, } from './memory/api.js';
|
|
18
18
|
export { createKnowledge, appendJudgment, ingestSource, JudgmentError, type Knowledge, type KnowledgeOptions, type JudgmentAccess, type SourceIngestCommand, type SourceIngestReceipt, type WorkGraphPage, type WorkGraphQuery, type WorkReference, } from './knowledge/index.js';
|
|
19
19
|
export { queryRelevantTruth } from './memory/truth-store.js';
|
|
20
20
|
export { createAuditFinding, listOpenAuditFindings } from './memory/finding-store.js';
|
|
@@ -30,7 +30,7 @@ export { MODEL_RUN_STATUSES, type ModelRunStatus, type BeginModelRunInput, type
|
|
|
30
30
|
export { beginModelRun, commitModelRun, failModelRun, getModelRun, } from './runtime/model-run-store.js';
|
|
31
31
|
export { appendToolTrace, appendOperationToolTrace, listToolTracesForRun, listToolTraces, readToolTrace, } from './runtime/tool-trace-store.js';
|
|
32
32
|
export { TWIN_EDGE_SOURCES, TWIN_EDGE_TYPES, TWIN_REF_KINDS, type InsertTwinEdgeInput, type ListVisibleTwinEdgesOptions, type TwinEdgeInsert, type TwinEdgeRecord, type TwinEdgeSource, type TwinEdgeType, type TwinRef, type TwinRefKind, type TwinScopeRef, } from './knowledge/twin-edge-types.js';
|
|
33
|
-
export { getTwinEdge,
|
|
33
|
+
export { getTwinEdge, listTwinEdgesForRefs, mapTwinEdgeRow } from './knowledge/judgments.js';
|
|
34
34
|
export { listVisibleTwinEdgesForRefs } from './knowledge/access.js';
|
|
35
35
|
export type { ActionCall, ActionContract, ActionExample, ActionFailure, ActionFailureKind, ActionResult, ActionSchemaObject, ActionSessionFacts, } from './action-contracts.js';
|
|
36
36
|
export { createCatalog, coreActionRegistrations, UnknownActionError, type ActionCatalog, type ActionContext, type ActionExec, type ActionRegistration, type MemoryReadAllowance, } from './api/catalog.js';
|
package/dist/index.js
CHANGED
|
@@ -25,9 +25,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
25
25
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
26
|
};
|
|
27
27
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.insertMemoryEventInTransaction = exports.appendMemoryEvent = exports.listOpenAuditFindings = exports.createAuditFinding = exports.queryRelevantTruth = exports.JudgmentError = exports.ingestSource = exports.appendJudgment = exports.createKnowledge = exports.getChannelSummary = exports.upsertChannelSummary = exports.ingestConversation = exports.recordMemoryAudit = exports.createAuditAck = exports.buildMemoryBootstrap = exports.
|
|
29
|
-
exports.newRequestId = exports.IpcTransportError = exports.encodeFrame = exports.createActionIpcServer = exports.startRuntime = exports.createClient = exports.withCallReceipt = exports.validateInput = exports.createDispatcher = exports.UnknownActionError = exports.coreActionRegistrations = exports.createCatalog = exports.listVisibleTwinEdgesForRefs = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.
|
|
30
|
-
exports.FALLBACK_MODEL = exports.DEFAULT_MODEL = exports.listModels = exports.isAvailable = exports.generate = exports.extractCodeExample = exports.extractQuickAnswer = exports.estimateTokens = exports.ensureTokenBudget = exports.formatTrustContext = exports.formatList = exports.formatRecall = exports.formatLegacyContext = exports.formatContext = exports.isMAMAError = exports.wrapError = exports.ErrorCodes = exports.TimeoutError = exports.RateLimitError = exports.LinkError = exports.ConfigurationError = exports.EmbeddingError = exports.DatabaseError = exports.ValidationError = exports.NotFoundError = exports.MAMAError = exports.DebugLogger = exports.error = exports.warn = exports.info = exports.debug = exports.logSearching = exports.logLoading = exports.logError = exports.logFailed = exports.logComplete = exports.logProgress = exports.formatTopNContext = exports.selectTopDecisions = exports.calculateRelevance = exports.CanonicalizeError = exports.targetRefHash =
|
|
28
|
+
exports.listRecentMemoryEvents = exports.listMemoryEventsForMemory = exports.insertMemoryEventInTransaction = exports.appendMemoryEvent = exports.listOpenAuditFindings = exports.createAuditFinding = exports.queryRelevantTruth = exports.JudgmentError = exports.ingestSource = exports.appendJudgment = exports.createKnowledge = exports.getChannelSummary = exports.upsertChannelSummary = exports.ingestConversation = exports.recordMemoryAudit = exports.createAuditAck = exports.buildMemoryBootstrap = exports.ingestMemory = exports.buildProfile = exports.recallMemory = exports.retireMemoryRecord = exports.readMemoryRecordsInScopes = exports.readMemoryRecordById = exports.saveLegacyMemory = exports.saveJudgmentRecord = exports.saveMemory = exports.canonicalizeContextScopes = exports.createEmptyRecallBundle = exports.MEMORY_EDGE_TYPES = exports.MEMORY_STATUSES = exports.MEMORY_KINDS = exports.MEMORY_SCOPE_KINDS = exports.createMamaApi = exports.mama = exports.SQLiteAdapter = exports.createAdapter = exports.getDbPath = exports.updateDecisionOutcome = exports.closeDB = exports.getAdapter = exports.getDB = exports.initDB = exports.EmbeddingCache = exports.EMBEDDING_PREFIX_SCHEME = exports.MODEL_NAME = exports.EMBEDDING_DIM = exports.embeddingCache = exports.cosineSimilarity = exports.generateEnhancedEmbedding = exports.generateEmbedding = void 0;
|
|
29
|
+
exports.canonicalizeJSON = exports.IPC_MAX_FRAME_BYTES = exports.sendIpcRequest = exports.newRequestId = exports.IpcTransportError = exports.encodeFrame = exports.createActionIpcServer = exports.startRuntime = exports.createClient = exports.withCallReceipt = exports.validateInput = exports.createDispatcher = exports.UnknownActionError = exports.coreActionRegistrations = exports.createCatalog = exports.listVisibleTwinEdgesForRefs = exports.mapTwinEdgeRow = exports.listTwinEdgesForRefs = exports.getTwinEdge = exports.TWIN_REF_KINDS = exports.TWIN_EDGE_TYPES = exports.TWIN_EDGE_SOURCES = exports.readToolTrace = exports.listToolTraces = exports.listToolTracesForRun = exports.appendOperationToolTrace = exports.appendToolTrace = exports.getModelRun = exports.failModelRun = exports.commitModelRun = exports.beginModelRun = exports.MODEL_RUN_STATUSES = exports.scanMemoryWriteInput = exports.scanForSecrets = exports.sanitizeRecallText = exports.sanitizeRecallBundle = exports.resolveMemoryProvenance = exports.toIndexedEvent = exports.resolveMemoryProvenanceLive = exports.parseSourceRef = exports.isMessageRefVisible = exports.isEventVisibleNow = exports.OBSERVATION_COLUMNS = exports.listMemoryProvenanceAudit = exports.getMemoryProvenanceAudit = exports.listMemoriesByModelRunId = exports.listMemoriesByGatewayCallId = exports.listMemoriesByEnvelopeHash = exports.getMemoryProvenance = exports.normalizeMemoryWriteProvenance = void 0;
|
|
30
|
+
exports.FALLBACK_MODEL = exports.DEFAULT_MODEL = exports.listModels = exports.isAvailable = exports.generate = exports.extractCodeExample = exports.extractQuickAnswer = exports.estimateTokens = exports.ensureTokenBudget = exports.formatTrustContext = exports.formatList = exports.formatRecall = exports.formatLegacyContext = exports.formatContext = exports.isMAMAError = exports.wrapError = exports.ErrorCodes = exports.TimeoutError = exports.RateLimitError = exports.LinkError = exports.ConfigurationError = exports.EmbeddingError = exports.DatabaseError = exports.ValidationError = exports.NotFoundError = exports.MAMAError = exports.DebugLogger = exports.error = exports.warn = exports.info = exports.debug = exports.logSearching = exports.logLoading = exports.logError = exports.logFailed = exports.logComplete = exports.logProgress = exports.formatTopNContext = exports.selectTopDecisions = exports.calculateRelevance = exports.CanonicalizeError = exports.targetRefHash = void 0;
|
|
31
31
|
var embedder_js_1 = require("./embedding/embedder.js");
|
|
32
32
|
Object.defineProperty(exports, "generateEmbedding", { enumerable: true, get: function () { return embedder_js_1.generateEmbedding; } });
|
|
33
33
|
Object.defineProperty(exports, "generateEnhancedEmbedding", { enumerable: true, get: function () { return embedder_js_1.generateEnhancedEmbedding; } });
|
|
@@ -63,14 +63,12 @@ var api_js_1 = require("./memory/api.js");
|
|
|
63
63
|
Object.defineProperty(exports, "saveMemory", { enumerable: true, get: function () { return api_js_1.saveMemory; } });
|
|
64
64
|
Object.defineProperty(exports, "saveJudgmentRecord", { enumerable: true, get: function () { return api_js_1.saveJudgmentRecord; } });
|
|
65
65
|
Object.defineProperty(exports, "saveLegacyMemory", { enumerable: true, get: function () { return api_js_1.saveLegacyMemory; } });
|
|
66
|
-
Object.defineProperty(exports, "promoteMemoryStatus", { enumerable: true, get: function () { return api_js_1.promoteMemoryStatus; } });
|
|
67
66
|
Object.defineProperty(exports, "readMemoryRecordById", { enumerable: true, get: function () { return api_js_1.readMemoryRecordById; } });
|
|
68
67
|
Object.defineProperty(exports, "readMemoryRecordsInScopes", { enumerable: true, get: function () { return api_js_1.readMemoryRecordsInScopes; } });
|
|
69
68
|
Object.defineProperty(exports, "retireMemoryRecord", { enumerable: true, get: function () { return api_js_1.retireMemoryRecord; } });
|
|
70
69
|
Object.defineProperty(exports, "recallMemory", { enumerable: true, get: function () { return api_js_1.recallMemory; } });
|
|
71
70
|
Object.defineProperty(exports, "buildProfile", { enumerable: true, get: function () { return api_js_1.buildProfile; } });
|
|
72
71
|
Object.defineProperty(exports, "ingestMemory", { enumerable: true, get: function () { return api_js_1.ingestMemory; } });
|
|
73
|
-
Object.defineProperty(exports, "evolveMemory", { enumerable: true, get: function () { return api_js_1.evolveMemory; } });
|
|
74
72
|
Object.defineProperty(exports, "buildMemoryBootstrap", { enumerable: true, get: function () { return api_js_1.buildMemoryBootstrap; } });
|
|
75
73
|
Object.defineProperty(exports, "createAuditAck", { enumerable: true, get: function () { return api_js_1.createAuditAck; } });
|
|
76
74
|
Object.defineProperty(exports, "recordMemoryAudit", { enumerable: true, get: function () { return api_js_1.recordMemoryAudit; } });
|
|
@@ -136,7 +134,6 @@ Object.defineProperty(exports, "TWIN_EDGE_TYPES", { enumerable: true, get: funct
|
|
|
136
134
|
Object.defineProperty(exports, "TWIN_REF_KINDS", { enumerable: true, get: function () { return twin_edge_types_js_1.TWIN_REF_KINDS; } });
|
|
137
135
|
var judgments_js_1 = require("./knowledge/judgments.js");
|
|
138
136
|
Object.defineProperty(exports, "getTwinEdge", { enumerable: true, get: function () { return judgments_js_1.getTwinEdge; } });
|
|
139
|
-
Object.defineProperty(exports, "insertTwinEdge", { enumerable: true, get: function () { return judgments_js_1.insertTwinEdge; } });
|
|
140
137
|
Object.defineProperty(exports, "listTwinEdgesForRefs", { enumerable: true, get: function () { return judgments_js_1.listTwinEdgesForRefs; } });
|
|
141
138
|
Object.defineProperty(exports, "mapTwinEdgeRow", { enumerable: true, get: function () { return judgments_js_1.mapTwinEdgeRow; } });
|
|
142
139
|
var access_js_1 = require("./knowledge/access.js");
|
|
@@ -36,6 +36,8 @@ export interface CommitmentChainEntry {
|
|
|
36
36
|
revision: number;
|
|
37
37
|
operation: CommitmentRevision['operation'];
|
|
38
38
|
eventDatetime: number | null;
|
|
39
|
+
/** When the revision was written; the day of a revision with no event time. */
|
|
40
|
+
createdAt: number;
|
|
39
41
|
status: string | null;
|
|
40
42
|
stage: string | null;
|
|
41
43
|
summary: string | null;
|
|
@@ -106,6 +106,7 @@ function buildChain(adapter, revisions, admitted) {
|
|
|
106
106
|
revision: revision.revision,
|
|
107
107
|
operation: revision.operation,
|
|
108
108
|
eventDatetime: revision.eventDatetime,
|
|
109
|
+
createdAt: revision.createdAt,
|
|
109
110
|
status: revision.operation === 'withdraw' ? 'cancelled' : stringField(values, 'status'),
|
|
110
111
|
stage: stringField(values, 'stage'),
|
|
111
112
|
summary: readJudgmentSummary(adapter, revision.recordRef.id, admitted),
|
|
@@ -13,13 +13,22 @@ import type { DatabaseAdapter, DecisionRecord, SemanticEdges } from '../db-manag
|
|
|
13
13
|
import { type JudgmentAccess } from './judgments.js';
|
|
14
14
|
import type { WorkGraphPage, WorkGraphQuery } from '../memory/judgment-types.js';
|
|
15
15
|
import type { TwinEdgeRecord, TwinEdgeType, TwinProjectRef, TwinRef, TwinScopeRef } from '../knowledge/twin-edge-types.js';
|
|
16
|
+
/** The reasons the host's evolution rules gave the legacy `decision_edges` rows they wrote. */
|
|
17
|
+
export declare const HOST_EDGE_REASON_PREFIXES: readonly ["Semantically similar", "Updated fact", "Related but distinct"];
|
|
18
|
+
/**
|
|
19
|
+
* The edges an agent stated between decisions, in the `decision_edges` columns plus `edge_id`:
|
|
20
|
+
* links in `twin_edges` between two memories that the host did not write, and the legacy
|
|
21
|
+
* `decision_edges` rows whose reason is not one the host gave. Search expansion follows these and
|
|
22
|
+
* no host edge (owner, 2026-09-30): a similarity is no ground that two records are the same case.
|
|
23
|
+
*/
|
|
24
|
+
export declare const STATED_DECISION_EDGES: string;
|
|
16
25
|
/**
|
|
17
26
|
* Walk a topic's supersedes chain with a recursive CTE.
|
|
18
27
|
*
|
|
19
28
|
* @param adapter - Database to read through
|
|
20
29
|
* @param topic - Decision topic to query
|
|
21
30
|
* @param anchorId - Start from this decision instead of the topic's current head
|
|
22
|
-
* @returns Decisions ordered by recency
|
|
31
|
+
* @returns Decisions ordered by recency
|
|
23
32
|
*/
|
|
24
33
|
export declare function queryDecisionGraph(adapter: DatabaseAdapter, topic: string, anchorId?: string): Promise<DecisionRecord[]>;
|
|
25
34
|
/**
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @module knowledge/graph-query
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.AgentGraphValidationError = void 0;
|
|
14
|
+
exports.AgentGraphValidationError = exports.STATED_DECISION_EDGES = exports.HOST_EDGE_REASON_PREFIXES = void 0;
|
|
15
15
|
exports.queryDecisionGraph = queryDecisionGraph;
|
|
16
16
|
exports.querySemanticEdges = querySemanticEdges;
|
|
17
17
|
exports.getGraphNeighborhood = getGraphNeighborhood;
|
|
@@ -44,13 +44,38 @@ function parseRefinedFrom(decision) {
|
|
|
44
44
|
throw new Error(`decision ${decision.id} has unreadable refined_from: ${message}`);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
/** The reasons the host's evolution rules gave the legacy `decision_edges` rows they wrote. */
|
|
48
|
+
exports.HOST_EDGE_REASON_PREFIXES = [
|
|
49
|
+
'Semantically similar',
|
|
50
|
+
'Updated fact',
|
|
51
|
+
'Related but distinct',
|
|
52
|
+
];
|
|
53
|
+
/**
|
|
54
|
+
* The edges an agent stated between decisions, in the `decision_edges` columns plus `edge_id`:
|
|
55
|
+
* links in `twin_edges` between two memories that the host did not write, and the legacy
|
|
56
|
+
* `decision_edges` rows whose reason is not one the host gave. Search expansion follows these and
|
|
57
|
+
* no host edge (owner, 2026-09-30): a similarity is no ground that two records are the same case.
|
|
58
|
+
*/
|
|
59
|
+
exports.STATED_DECISION_EDGES = `(
|
|
60
|
+
SELECT NULL AS edge_id, from_id, to_id, relationship, reason, weight, created_at, created_by,
|
|
61
|
+
approved_by_user, decision_id, evidence
|
|
62
|
+
FROM decision_edges
|
|
63
|
+
WHERE ${exports.HOST_EDGE_REASON_PREFIXES.map((prefix) => `COALESCE(reason, '') NOT LIKE '${prefix}%'`).join(' AND ')}
|
|
64
|
+
UNION ALL
|
|
65
|
+
SELECT edge_id, subject_id, object_id, edge_type,
|
|
66
|
+
COALESCE(json_extract(relation_attrs_json, '$.reason'), reason_text), confidence,
|
|
67
|
+
created_at, source, NULL, NULL, NULL
|
|
68
|
+
FROM twin_edges
|
|
69
|
+
WHERE subject_kind = 'memory' AND object_kind = 'memory' AND source <> 'code'
|
|
70
|
+
AND edge_type <> 'derived_from'
|
|
71
|
+
)`;
|
|
47
72
|
/**
|
|
48
73
|
* Walk a topic's supersedes chain with a recursive CTE.
|
|
49
74
|
*
|
|
50
75
|
* @param adapter - Database to read through
|
|
51
76
|
* @param topic - Decision topic to query
|
|
52
77
|
* @param anchorId - Start from this decision instead of the topic's current head
|
|
53
|
-
* @returns Decisions ordered by recency
|
|
78
|
+
* @returns Decisions ordered by recency
|
|
54
79
|
*/
|
|
55
80
|
async function queryDecisionGraph(adapter, topic, anchorId) {
|
|
56
81
|
try {
|
|
@@ -66,13 +91,7 @@ async function queryDecisionGraph(adapter, topic, anchorId) {
|
|
|
66
91
|
SELECT * FROM decision_chain ORDER BY created_at DESC, id DESC
|
|
67
92
|
`)
|
|
68
93
|
.all(topic);
|
|
69
|
-
const edgesStmt = adapter.prepare(`
|
|
70
|
-
SELECT * FROM decision_edges
|
|
71
|
-
WHERE from_id = ?
|
|
72
|
-
AND (approved_by_user = 1 OR approved_by_user IS NULL)
|
|
73
|
-
`);
|
|
74
94
|
for (const decision of decisions) {
|
|
75
|
-
decision.edges = edgesStmt.all(decision.id);
|
|
76
95
|
decision.refined_from = parseRefinedFrom(decision);
|
|
77
96
|
}
|
|
78
97
|
return decisions;
|
|
@@ -96,15 +115,7 @@ async function queryDecisionGraph(adapter, topic, anchorId) {
|
|
|
96
115
|
ORDER BY created_at DESC
|
|
97
116
|
`);
|
|
98
117
|
const decisions = stmt.all(anchorId);
|
|
99
|
-
// Join with decision_edges to include relationships
|
|
100
|
-
// Prepare statement once outside loop for performance
|
|
101
|
-
const edgesStmt = adapter.prepare(`
|
|
102
|
-
SELECT * FROM decision_edges
|
|
103
|
-
WHERE from_id = ?
|
|
104
|
-
AND (approved_by_user = 1 OR approved_by_user IS NULL)
|
|
105
|
-
`);
|
|
106
118
|
for (const decision of decisions) {
|
|
107
|
-
decision.edges = edgesStmt.all(decision.id);
|
|
108
119
|
decision.refined_from = parseRefinedFrom(decision);
|
|
109
120
|
}
|
|
110
121
|
return decisions;
|
|
@@ -146,7 +157,7 @@ async function querySemanticEdges(adapter, decisionIds) {
|
|
|
146
157
|
// Query outgoing edges (from_id = decision)
|
|
147
158
|
const outgoingStmt = adapter.prepare(`
|
|
148
159
|
SELECT e.*, d.topic, d.decision, d.confidence, d.created_at
|
|
149
|
-
FROM
|
|
160
|
+
FROM ${exports.STATED_DECISION_EDGES} e
|
|
150
161
|
JOIN decisions d ON e.to_id = d.id
|
|
151
162
|
WHERE e.from_id IN (${placeholders})
|
|
152
163
|
AND e.relationship IN (${edgeTypePlaceholders})
|
|
@@ -157,7 +168,7 @@ async function querySemanticEdges(adapter, decisionIds) {
|
|
|
157
168
|
// Query incoming edges (to_id = decision)
|
|
158
169
|
const incomingStmt = adapter.prepare(`
|
|
159
170
|
SELECT e.*, d.topic, d.decision, d.confidence, d.created_at
|
|
160
|
-
FROM
|
|
171
|
+
FROM ${exports.STATED_DECISION_EDGES} e
|
|
161
172
|
JOIN decisions d ON e.from_id = d.id
|
|
162
173
|
WHERE e.to_id IN (${placeholders})
|
|
163
174
|
AND e.relationship IN (${edgeTypePlaceholders})
|
|
@@ -547,6 +558,29 @@ function loadTimelineRecordEvent(adapter, ref) {
|
|
|
547
558
|
}
|
|
548
559
|
return null;
|
|
549
560
|
}
|
|
561
|
+
/**
|
|
562
|
+
* Edges as the reader may see them. Both ends being visible does not make a link's evidence
|
|
563
|
+
* visible, so evidence refs the reader cannot see are left out. Entries without a kind are the
|
|
564
|
+
* link entries ({id, reason}) an earlier version copied into a record's own edges; they repeat the
|
|
565
|
+
* edge's target and reason.
|
|
566
|
+
*/
|
|
567
|
+
function withVisibleEvidence(adapter, edges, visibility) {
|
|
568
|
+
const refsOf = (edge) => Array.isArray(edge.evidence_refs)
|
|
569
|
+
? edge.evidence_refs.filter((ref) => typeof ref?.id === 'string' &&
|
|
570
|
+
twin_edge_types_js_1.TWIN_REF_KINDS.includes(ref.kind))
|
|
571
|
+
: [];
|
|
572
|
+
const visible = (0, access_js_1.visibleTwinRefKeysRecursive)(adapter, edges.flatMap(refsOf), visibility);
|
|
573
|
+
return edges.map((edge) => {
|
|
574
|
+
if (!Array.isArray(edge.evidence_refs))
|
|
575
|
+
return edge;
|
|
576
|
+
const evidence = refsOf(edge).filter((ref) => visible.has(`${ref.kind}\0${ref.id}`));
|
|
577
|
+
return {
|
|
578
|
+
...edge,
|
|
579
|
+
evidence_refs: evidence.length > 0 ? evidence : null,
|
|
580
|
+
evidence_refs_json: evidence.length > 0 ? JSON.stringify(evidence) : null,
|
|
581
|
+
};
|
|
582
|
+
});
|
|
583
|
+
}
|
|
550
584
|
function getGraphNeighborhood(adapter, input) {
|
|
551
585
|
const depth = normalizeDepth(input.depth, 1);
|
|
552
586
|
const limit = normalizeLimit(input.limit, DEFAULT_GRAPH_LIMIT);
|
|
@@ -598,7 +632,11 @@ function getGraphNeighborhood(adapter, input) {
|
|
|
598
632
|
}
|
|
599
633
|
frontier = nextFrontier;
|
|
600
634
|
}
|
|
601
|
-
return {
|
|
635
|
+
return {
|
|
636
|
+
nodes,
|
|
637
|
+
edges: withVisibleEvidence(adapter, edges, { ...visibility, asOfMs: input.as_of_ms }),
|
|
638
|
+
current_projection: projectCurrentEdges(adapter, edges, visibility),
|
|
639
|
+
};
|
|
602
640
|
}
|
|
603
641
|
function getGraphPaths(adapter, input) {
|
|
604
642
|
const maxDepth = normalizeDepth(input.max_depth, 3);
|
|
@@ -665,8 +703,12 @@ function getGraphPaths(adapter, input) {
|
|
|
665
703
|
const pathEdges = [
|
|
666
704
|
...new Map(paths.flatMap((path) => path.edges).map((edge) => [edge.edge_id, edge])).values(),
|
|
667
705
|
];
|
|
706
|
+
const shown = new Map(withVisibleEvidence(adapter, pathEdges, visibility).map((edge) => [edge.edge_id, edge]));
|
|
668
707
|
return {
|
|
669
|
-
paths
|
|
708
|
+
paths: paths.map((path) => ({
|
|
709
|
+
...path,
|
|
710
|
+
edges: path.edges.map((edge) => shown.get(edge.edge_id)),
|
|
711
|
+
})),
|
|
670
712
|
limit_reached: limitReached || paths.length >= limit,
|
|
671
713
|
current_projection: projectCurrentEdges(adapter, pathEdges, visibility),
|
|
672
714
|
};
|
|
@@ -698,11 +740,11 @@ function getGraphTimeline(adapter, input) {
|
|
|
698
740
|
const limit = normalizeLimit(input.limit, DEFAULT_GRAPH_LIMIT);
|
|
699
741
|
const window = timelineWindow(input);
|
|
700
742
|
const seeds = timelineSeeds(adapter, input.ref, visibility);
|
|
701
|
-
const edges = listFilteredEdges(adapter, seeds, {
|
|
743
|
+
const edges = withVisibleEvidence(adapter, listFilteredEdges(adapter, seeds, {
|
|
702
744
|
visibility,
|
|
703
745
|
edge_filters: input.edge_filters,
|
|
704
746
|
as_of_ms: input.as_of_ms,
|
|
705
|
-
}).filter((edge) => isInTimelineWindow(edge.created_at, window));
|
|
747
|
+
}).filter((edge) => isInTimelineWindow(edge.created_at, window)), { ...visibility, asOfMs: input.as_of_ms });
|
|
706
748
|
const refs = [];
|
|
707
749
|
const seenRefs = new Set();
|
|
708
750
|
for (const seed of seeds) {
|
|
@@ -765,6 +807,50 @@ function graphAccessInput(access) {
|
|
|
765
807
|
max_source_ms: access.maxSourceMs,
|
|
766
808
|
};
|
|
767
809
|
}
|
|
810
|
+
/**
|
|
811
|
+
* Later links that contradict an edge, each with its reason, and the links that contradict those:
|
|
812
|
+
* the edge row is never edited, so a correction is read from the edges that point at it.
|
|
813
|
+
*/
|
|
814
|
+
function edgeCorrections(adapter, edgeIds, visibility, asOfMs) {
|
|
815
|
+
const byTarget = new Map();
|
|
816
|
+
const seen = new Set(edgeIds);
|
|
817
|
+
let frontier = [...edgeIds];
|
|
818
|
+
while (frontier.length > 0) {
|
|
819
|
+
const placeholders = frontier.map(() => '?').join(', ');
|
|
820
|
+
const rows = adapter
|
|
821
|
+
.prepare(`SELECT edge_id, subject_kind, subject_id, object_id, reason_text, created_at
|
|
822
|
+
FROM twin_edges
|
|
823
|
+
WHERE object_kind = 'edge' AND edge_type = 'contradicts'
|
|
824
|
+
AND object_id IN (${placeholders}) AND created_at <= ?
|
|
825
|
+
ORDER BY created_at, edge_id`)
|
|
826
|
+
.all(...frontier, asOfMs);
|
|
827
|
+
const subjects = rows.map((row) => ({ kind: row.subject_kind, id: row.subject_id }));
|
|
828
|
+
const visible = (0, access_js_1.visibleTwinRefKeysRecursive)(adapter, subjects, visibility);
|
|
829
|
+
frontier = [];
|
|
830
|
+
for (const row of rows) {
|
|
831
|
+
if (!visible.has(`${row.subject_kind}\0${row.subject_id}`))
|
|
832
|
+
continue;
|
|
833
|
+
const list = byTarget.get(row.object_id) ?? [];
|
|
834
|
+
list.push({ edgeId: row.edge_id, reason: row.reason_text, at: row.created_at });
|
|
835
|
+
byTarget.set(row.object_id, list);
|
|
836
|
+
if (!seen.has(row.edge_id)) {
|
|
837
|
+
seen.add(row.edge_id);
|
|
838
|
+
frontier.push(row.edge_id);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
const nest = (edgeId) => byTarget.get(edgeId)?.map((correction) => {
|
|
843
|
+
const further = nest(correction.edgeId);
|
|
844
|
+
return further ? { ...correction, correctedBy: further } : correction;
|
|
845
|
+
});
|
|
846
|
+
const corrections = new Map();
|
|
847
|
+
for (const edgeId of edgeIds) {
|
|
848
|
+
const list = nest(edgeId);
|
|
849
|
+
if (list)
|
|
850
|
+
corrections.set(edgeId, list);
|
|
851
|
+
}
|
|
852
|
+
return corrections;
|
|
853
|
+
}
|
|
768
854
|
/**
|
|
769
855
|
* Name-free entry: registry alias, decisions FTS, observation metadata. Search
|
|
770
856
|
* seeds are candidates, not citations — refs the caller cannot see are dropped
|
|
@@ -1127,8 +1213,9 @@ function hydrateNode(adapter, ref, ctx, access) {
|
|
|
1127
1213
|
}
|
|
1128
1214
|
return hydrateRecordNode(adapter, ref);
|
|
1129
1215
|
}
|
|
1130
|
-
function pageEdgeAttrs(edge) {
|
|
1216
|
+
function pageEdgeAttrs(edge, correctedBy) {
|
|
1131
1217
|
return {
|
|
1218
|
+
...(correctedBy?.length ? { corrected_by: correctedBy } : {}),
|
|
1132
1219
|
edge_type: edge.edge_type,
|
|
1133
1220
|
relation_attrs: (edge.relation_attrs ?? null),
|
|
1134
1221
|
confidence: edge.confidence,
|
|
@@ -1137,6 +1224,7 @@ function pageEdgeAttrs(edge) {
|
|
|
1137
1224
|
model_run_id: edge.model_run_id,
|
|
1138
1225
|
reason_classification: edge.reason_classification,
|
|
1139
1226
|
reason_text: edge.reason_text,
|
|
1227
|
+
evidence_refs: (edge.evidence_refs ?? null),
|
|
1140
1228
|
created_at: edge.created_at,
|
|
1141
1229
|
content_hash: edge.content_hash.toString('hex'),
|
|
1142
1230
|
};
|
|
@@ -1556,7 +1644,12 @@ function queryGraph(adapter, query, access) {
|
|
|
1556
1644
|
nodes.sort((left, right) => (nodeOrder.get(`${left.ref.kind}:${left.ref.id}`) ?? Number.MAX_SAFE_INTEGER) -
|
|
1557
1645
|
(nodeOrder.get(`${right.ref.kind}:${right.ref.id}`) ?? Number.MAX_SAFE_INTEGER));
|
|
1558
1646
|
}
|
|
1559
|
-
const
|
|
1647
|
+
const corrections = edgeCorrections(adapter, collectedEdges.map((edge) => edge.edge_id), ctx.visibility, asOf);
|
|
1648
|
+
const shownEdges = withVisibleEvidence(adapter, collectedEdges, {
|
|
1649
|
+
...ctx.visibility,
|
|
1650
|
+
asOfMs: asOf,
|
|
1651
|
+
});
|
|
1652
|
+
const pageEdges = shownEdges.map((edge) => {
|
|
1560
1653
|
const fromProjection = projections.get(projectionKey(edge.edge_id, 'from'));
|
|
1561
1654
|
const toProjection = projections.get(projectionKey(edge.edge_id, 'to'));
|
|
1562
1655
|
return {
|
|
@@ -1566,7 +1659,7 @@ function queryGraph(adapter, query, access) {
|
|
|
1566
1659
|
to: edge.object_ref,
|
|
1567
1660
|
resolvedFrom: fromProjection?.current_ref ?? edge.subject_ref,
|
|
1568
1661
|
resolvedTo: toProjection?.current_ref ?? edge.object_ref,
|
|
1569
|
-
attrs: pageEdgeAttrs(edge),
|
|
1662
|
+
attrs: pageEdgeAttrs(edge, corrections.get(edge.edge_id)),
|
|
1570
1663
|
};
|
|
1571
1664
|
});
|
|
1572
1665
|
return {
|