@jungjaehoon/mama-core 1.5.0 → 1.7.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 +48 -10
- package/db/migrations/032-add-memory-provenance-columns.sql +21 -0
- package/db/migrations/033-create-model-runs-and-tool-traces.sql +53 -0
- package/db/migrations/034-add-connector-event-scope-columns.sql +14 -0
- package/db/migrations/035-create-twin-edges.sql +63 -0
- package/db/migrations/036-create-agent-situation-packets.sql +61 -0
- package/db/migrations/037-create-context-packets.sql +32 -0
- package/dist/agent-graph/alias-write.d.ts +3 -0
- package/dist/agent-graph/alias-write.js +231 -0
- package/dist/agent-graph/entity-resolve.d.ts +3 -0
- package/dist/agent-graph/entity-resolve.js +275 -0
- package/dist/agent-graph/errors.d.ts +4 -0
- package/dist/agent-graph/errors.js +11 -0
- package/dist/agent-graph/graph-query.d.ts +5 -0
- package/dist/agent-graph/graph-query.js +359 -0
- package/dist/agent-graph/index.d.ts +6 -0
- package/dist/agent-graph/index.js +22 -0
- package/dist/agent-graph/types.d.ts +162 -0
- package/dist/agent-graph/types.js +3 -0
- package/dist/agent-situation/builder.d.ts +8 -0
- package/dist/agent-situation/builder.js +239 -0
- package/dist/agent-situation/cache-key.d.ts +4 -0
- package/dist/agent-situation/cache-key.js +64 -0
- package/dist/agent-situation/index.d.ts +7 -0
- package/dist/agent-situation/index.js +28 -0
- package/dist/agent-situation/packet-store.d.ts +27 -0
- package/dist/agent-situation/packet-store.js +228 -0
- package/dist/agent-situation/ranking-policy.d.ts +18 -0
- package/dist/agent-situation/ranking-policy.js +75 -0
- package/dist/agent-situation/source-readers.d.ts +52 -0
- package/dist/agent-situation/source-readers.js +300 -0
- package/dist/agent-situation/types.d.ts +195 -0
- package/dist/agent-situation/types.js +3 -0
- package/dist/cases/search-rollup.d.ts +4 -0
- package/dist/cases/search-rollup.js +84 -0
- package/dist/cases/wiki-page-index.js +6 -2
- package/dist/connectors/event-index.d.ts +1 -0
- package/dist/connectors/event-index.js +19 -4
- package/dist/connectors/raw-query.d.ts +18 -0
- package/dist/connectors/raw-query.js +305 -0
- package/dist/connectors/types.d.ts +40 -0
- package/dist/context-compile/boundary-defaults.d.ts +12 -0
- package/dist/context-compile/boundary-defaults.js +105 -0
- package/dist/context-compile/compiler-policy.d.ts +39 -0
- package/dist/context-compile/compiler-policy.js +147 -0
- package/dist/context-compile/compiler.d.ts +18 -0
- package/dist/context-compile/compiler.js +401 -0
- package/dist/context-compile/index.d.ts +9 -0
- package/dist/context-compile/index.js +25 -0
- package/dist/context-compile/packet-store.d.ts +9 -0
- package/dist/context-compile/packet-store.js +255 -0
- package/dist/context-compile/ref.d.ts +7 -0
- package/dist/context-compile/ref.js +102 -0
- package/dist/context-compile/source-readers.d.ts +69 -0
- package/dist/context-compile/source-readers.js +707 -0
- package/dist/context-compile/types.d.ts +138 -0
- package/dist/context-compile/types.js +11 -0
- package/dist/context-compile/visibility.d.ts +21 -0
- package/dist/context-compile/visibility.js +200 -0
- package/dist/db-adapter/node-sqlite-adapter.d.ts +9 -0
- package/dist/db-adapter/node-sqlite-adapter.js +207 -0
- package/dist/db-manager.d.ts +8 -1
- package/dist/db-manager.js +53 -5
- package/dist/edges/ref-validation.d.ts +8 -0
- package/dist/edges/ref-validation.js +301 -0
- package/dist/edges/store.d.ts +10 -0
- package/dist/edges/store.js +343 -0
- package/dist/edges/types.d.ts +88 -0
- package/dist/edges/types.js +18 -0
- package/dist/embeddings.d.ts +3 -0
- package/dist/embeddings.js +49 -9
- package/dist/entities/store.d.ts +4 -1
- package/dist/entities/store.js +206 -78
- package/dist/entities/types.d.ts +5 -1
- package/dist/entities/types.js +1 -1
- package/dist/index.d.ts +18 -3
- package/dist/index.js +54 -4
- package/dist/mama-api.d.ts +32 -35
- package/dist/mama-api.js +173 -22
- package/dist/memory/api.d.ts +17 -56
- package/dist/memory/api.js +494 -52
- package/dist/memory/event-store.d.ts +3 -0
- package/dist/memory/event-store.js +21 -2
- package/dist/memory/provenance-audit.d.ts +22 -0
- package/dist/memory/provenance-audit.js +112 -0
- package/dist/memory/provenance-query.d.ts +11 -0
- package/dist/memory/provenance-query.js +193 -0
- package/dist/memory/provenance.d.ts +29 -0
- package/dist/memory/provenance.js +137 -0
- package/dist/memory/scope-backfill.d.ts +17 -0
- package/dist/memory/scope-backfill.js +77 -0
- package/dist/memory/types.d.ts +87 -14
- package/dist/model-runs/store.d.ts +13 -0
- package/dist/model-runs/store.js +457 -0
- package/dist/model-runs/tool-trace-store.d.ts +4 -0
- package/dist/model-runs/tool-trace-store.js +114 -0
- package/dist/model-runs/types.d.ts +69 -0
- package/dist/model-runs/types.js +5 -0
- package/dist/runtime/trusted-provenance.d.ts +3 -0
- package/dist/runtime/trusted-provenance.js +6 -0
- package/dist/search/search-quality.d.ts +36 -0
- package/dist/search/search-quality.js +29 -0
- package/package.json +11 -3
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { MemoryScopeRef } from '../memory/types.js';
|
|
2
|
+
export declare const CONTEXT_REF_KINDS: readonly ["memory", "raw", "entity", "case"];
|
|
3
|
+
export type ContextRefKind = (typeof CONTEXT_REF_KINDS)[number];
|
|
4
|
+
export declare const UNSUPPORTED_CONTEXT_REF_KINDS: readonly ["decision", "wiki_page", "context_packet", "report"];
|
|
5
|
+
export type UnsupportedContextRefKind = (typeof UNSUPPORTED_CONTEXT_REF_KINDS)[number];
|
|
6
|
+
export type ContextRef = {
|
|
7
|
+
kind: 'memory';
|
|
8
|
+
id: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: 'raw';
|
|
11
|
+
raw_id: string;
|
|
12
|
+
connector: string;
|
|
13
|
+
source_id?: string;
|
|
14
|
+
channel_id?: string | null;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'entity';
|
|
17
|
+
id: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'case';
|
|
20
|
+
id: string;
|
|
21
|
+
};
|
|
22
|
+
export interface ContextRange {
|
|
23
|
+
start_ms?: number;
|
|
24
|
+
end_ms?: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ContextProjectRef {
|
|
27
|
+
kind: 'project';
|
|
28
|
+
id: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ContextCompileInput {
|
|
31
|
+
task: string;
|
|
32
|
+
scopes?: MemoryScopeRef[];
|
|
33
|
+
seed_refs?: ContextRef[];
|
|
34
|
+
connectors?: string[];
|
|
35
|
+
project_refs?: ContextProjectRef[];
|
|
36
|
+
tenant_id?: string | null;
|
|
37
|
+
range?: ContextRange;
|
|
38
|
+
as_of?: string | number | null;
|
|
39
|
+
limit?: number;
|
|
40
|
+
max_tool_calls?: number;
|
|
41
|
+
max_ms?: number;
|
|
42
|
+
max_tokens?: number;
|
|
43
|
+
strictness?: 'recall' | 'balanced' | 'strict' | 'low' | 'medium' | 'high';
|
|
44
|
+
}
|
|
45
|
+
export interface ContextBoundary {
|
|
46
|
+
scopes?: MemoryScopeRef[];
|
|
47
|
+
connectors?: string[];
|
|
48
|
+
project_refs?: ContextProjectRef[];
|
|
49
|
+
tenant_id?: string | null;
|
|
50
|
+
as_of?: string | number | null;
|
|
51
|
+
range?: ContextRange;
|
|
52
|
+
}
|
|
53
|
+
export interface CanonicalContextScopes {
|
|
54
|
+
scopes: MemoryScopeRef[];
|
|
55
|
+
scopeJson: string;
|
|
56
|
+
scopeHash: string;
|
|
57
|
+
}
|
|
58
|
+
export interface ContextVisibility {
|
|
59
|
+
scopes: MemoryScopeRef[];
|
|
60
|
+
scope_json: string;
|
|
61
|
+
scope_hash: string;
|
|
62
|
+
connectors: string[];
|
|
63
|
+
project_refs: ContextProjectRef[];
|
|
64
|
+
tenant_id: string | null;
|
|
65
|
+
as_of: string | number | null;
|
|
66
|
+
range: ContextRange | null;
|
|
67
|
+
}
|
|
68
|
+
export interface ContextEvidence {
|
|
69
|
+
ref: ContextRef;
|
|
70
|
+
title?: string;
|
|
71
|
+
excerpt?: string;
|
|
72
|
+
score?: number;
|
|
73
|
+
reasons?: string[];
|
|
74
|
+
retrieval_diagnostics?: unknown;
|
|
75
|
+
}
|
|
76
|
+
export interface ContextPacket {
|
|
77
|
+
packet_id: string;
|
|
78
|
+
mode: 'general';
|
|
79
|
+
task: string;
|
|
80
|
+
scopes: MemoryScopeRef[];
|
|
81
|
+
scope_hash: string;
|
|
82
|
+
generated_at: string;
|
|
83
|
+
range: ContextRange | null;
|
|
84
|
+
as_of: string | number | null;
|
|
85
|
+
compiler_version: string;
|
|
86
|
+
source_refs: ContextRef[];
|
|
87
|
+
selected_evidence: ContextEvidence[];
|
|
88
|
+
evidence_clusters: unknown[];
|
|
89
|
+
related_decisions: unknown[];
|
|
90
|
+
rejected_refs: ContextRef[];
|
|
91
|
+
rejected_refs_truncated: boolean;
|
|
92
|
+
rejected_summary: string[];
|
|
93
|
+
missing_context: string[];
|
|
94
|
+
caveats: string[];
|
|
95
|
+
expansion_trace: unknown[];
|
|
96
|
+
retrieval_diagnostics: unknown;
|
|
97
|
+
budget: {
|
|
98
|
+
max_tool_calls?: number;
|
|
99
|
+
used_tool_calls: number;
|
|
100
|
+
max_ms?: number;
|
|
101
|
+
elapsed_ms: number;
|
|
102
|
+
max_tokens?: number;
|
|
103
|
+
estimated_tokens: number;
|
|
104
|
+
budget_exhausted: boolean;
|
|
105
|
+
};
|
|
106
|
+
budget_manifest: {
|
|
107
|
+
budget_exhausted: boolean;
|
|
108
|
+
skipped_operators: string[];
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export interface ContextPacketRecord {
|
|
112
|
+
packet_id: string;
|
|
113
|
+
task: string;
|
|
114
|
+
packet_json: string;
|
|
115
|
+
packet: ContextPacket;
|
|
116
|
+
scope_json: string;
|
|
117
|
+
scopes: MemoryScopeRef[];
|
|
118
|
+
scope_hash: string;
|
|
119
|
+
envelope_hash: string;
|
|
120
|
+
model_run_id: string;
|
|
121
|
+
agent_id: string;
|
|
122
|
+
input_snapshot_ref: string;
|
|
123
|
+
source_refs_json: string;
|
|
124
|
+
source_refs: ContextRef[];
|
|
125
|
+
tenant_id: string;
|
|
126
|
+
project_id: string;
|
|
127
|
+
memory_scope_kind: MemoryScopeRef['kind'];
|
|
128
|
+
memory_scope_id: string;
|
|
129
|
+
created_at: number;
|
|
130
|
+
}
|
|
131
|
+
export interface TrustedContextPacketLookupInput {
|
|
132
|
+
packetId: string;
|
|
133
|
+
envelopeHash: string;
|
|
134
|
+
modelRunId?: string;
|
|
135
|
+
callerModelRunId?: string;
|
|
136
|
+
includeFailed?: boolean;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UNSUPPORTED_CONTEXT_REF_KINDS = exports.CONTEXT_REF_KINDS = void 0;
|
|
4
|
+
exports.CONTEXT_REF_KINDS = ['memory', 'raw', 'entity', 'case'];
|
|
5
|
+
exports.UNSUPPORTED_CONTEXT_REF_KINDS = [
|
|
6
|
+
'decision',
|
|
7
|
+
'wiki_page',
|
|
8
|
+
'context_packet',
|
|
9
|
+
'report',
|
|
10
|
+
];
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type MemoryScopeRef } from '../memory/types.js';
|
|
2
|
+
import type { ContextBoundary, ContextProjectRef, ContextRef } from './types.js';
|
|
3
|
+
type BoundaryCheckInput = {
|
|
4
|
+
boundary: ContextBoundary;
|
|
5
|
+
requestedScopes?: readonly MemoryScopeRef[];
|
|
6
|
+
requestedConnectors?: readonly string[];
|
|
7
|
+
requestedProjectRefs?: readonly ContextProjectRef[];
|
|
8
|
+
requestedTenantId?: string | null;
|
|
9
|
+
seedRefs?: readonly unknown[];
|
|
10
|
+
};
|
|
11
|
+
export declare function canonicalizeContextScopes(scopes: readonly MemoryScopeRef[] | undefined): {
|
|
12
|
+
scopes: MemoryScopeRef[];
|
|
13
|
+
scopeJson: string;
|
|
14
|
+
scopeHash: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function derivePrimaryContextScope(scopes: readonly MemoryScopeRef[]): MemoryScopeRef;
|
|
17
|
+
export declare function assertContextBoundaryAllowsInput(input: BoundaryCheckInput): void;
|
|
18
|
+
export declare function sanitizeContextPacketForVisibility<T>(packet: T): T;
|
|
19
|
+
export declare function normalizeSeedRefs(seedRefs: readonly unknown[] | undefined): ContextRef[];
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=visibility.d.ts.map
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canonicalizeContextScopes = canonicalizeContextScopes;
|
|
4
|
+
exports.derivePrimaryContextScope = derivePrimaryContextScope;
|
|
5
|
+
exports.assertContextBoundaryAllowsInput = assertContextBoundaryAllowsInput;
|
|
6
|
+
exports.sanitizeContextPacketForVisibility = sanitizeContextPacketForVisibility;
|
|
7
|
+
exports.normalizeSeedRefs = normalizeSeedRefs;
|
|
8
|
+
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const types_js_1 = require("../memory/types.js");
|
|
10
|
+
const ref_js_1 = require("./ref.js");
|
|
11
|
+
const SCOPE_ORDER = {
|
|
12
|
+
project: 0,
|
|
13
|
+
channel: 1,
|
|
14
|
+
user: 2,
|
|
15
|
+
global: 3,
|
|
16
|
+
};
|
|
17
|
+
function assertScope(scope) {
|
|
18
|
+
if (!types_js_1.MEMORY_SCOPE_KINDS.includes(scope.kind)) {
|
|
19
|
+
throw new Error(`Unsupported memory scope kind: ${scope.kind}`);
|
|
20
|
+
}
|
|
21
|
+
const id = scope.id.trim();
|
|
22
|
+
if (id.length === 0) {
|
|
23
|
+
throw new Error('Memory scope id must not be empty');
|
|
24
|
+
}
|
|
25
|
+
return { kind: scope.kind, id };
|
|
26
|
+
}
|
|
27
|
+
function scopeKey(scope) {
|
|
28
|
+
return `${scope.kind}\0${scope.id}`;
|
|
29
|
+
}
|
|
30
|
+
function connectorKey(connector) {
|
|
31
|
+
const trimmed = connector.trim();
|
|
32
|
+
if (trimmed.length === 0) {
|
|
33
|
+
throw new Error('Connector must not be empty');
|
|
34
|
+
}
|
|
35
|
+
return trimmed;
|
|
36
|
+
}
|
|
37
|
+
function projectKey(project) {
|
|
38
|
+
const kind = project.kind.trim();
|
|
39
|
+
const id = project.id.trim();
|
|
40
|
+
if (kind.length === 0 || id.length === 0) {
|
|
41
|
+
throw new Error('Project ref kind and id must not be empty');
|
|
42
|
+
}
|
|
43
|
+
return `${kind}\0${id}`;
|
|
44
|
+
}
|
|
45
|
+
function sortScopes(scopes) {
|
|
46
|
+
return [...scopes].sort((left, right) => {
|
|
47
|
+
const orderDiff = SCOPE_ORDER[left.kind] - SCOPE_ORDER[right.kind];
|
|
48
|
+
if (orderDiff !== 0) {
|
|
49
|
+
return orderDiff;
|
|
50
|
+
}
|
|
51
|
+
return left.id.localeCompare(right.id);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function canonicalizeContextScopes(scopes) {
|
|
55
|
+
const unique = new Map();
|
|
56
|
+
for (const scope of scopes ?? []) {
|
|
57
|
+
const normalized = assertScope(scope);
|
|
58
|
+
unique.set(scopeKey(normalized), normalized);
|
|
59
|
+
}
|
|
60
|
+
const canonicalScopes = sortScopes([...unique.values()]);
|
|
61
|
+
const scopeJson = JSON.stringify(canonicalScopes);
|
|
62
|
+
const scopeHash = (0, node_crypto_1.createHash)('sha256').update(scopeJson).digest('hex');
|
|
63
|
+
return {
|
|
64
|
+
scopes: canonicalScopes,
|
|
65
|
+
scopeJson,
|
|
66
|
+
scopeHash,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function derivePrimaryContextScope(scopes) {
|
|
70
|
+
const canonical = canonicalizeContextScopes(scopes).scopes;
|
|
71
|
+
const primary = canonical.find((scope) => scope.kind === 'project') ?? canonical[0];
|
|
72
|
+
return primary ?? { kind: 'global', id: 'default' };
|
|
73
|
+
}
|
|
74
|
+
function assertContextBoundaryAllowsInput(input) {
|
|
75
|
+
const hasScopeBoundary = Array.isArray(input.boundary.scopes);
|
|
76
|
+
const boundaryScopes = canonicalizeContextScopes(input.boundary.scopes).scopes;
|
|
77
|
+
const requestedScopes = canonicalizeContextScopes(input.requestedScopes).scopes;
|
|
78
|
+
if (hasScopeBoundary && requestedScopes.length > 0) {
|
|
79
|
+
const allowed = new Set(boundaryScopes.map(scopeKey));
|
|
80
|
+
const disallowed = requestedScopes.find((scope) => !allowed.has(scopeKey(scope)));
|
|
81
|
+
if (disallowed) {
|
|
82
|
+
throw new Error(`Requested scope is outside the context boundary: ${disallowed.kind}:${disallowed.id}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const hasConnectorBoundary = Array.isArray(input.boundary.connectors);
|
|
86
|
+
const boundaryConnectors = new Set((input.boundary.connectors ?? []).map(connectorKey));
|
|
87
|
+
const requestedConnectors = (input.requestedConnectors ?? []).map(connectorKey);
|
|
88
|
+
if (hasConnectorBoundary) {
|
|
89
|
+
const disallowedConnector = requestedConnectors.find((connector) => !boundaryConnectors.has(connector));
|
|
90
|
+
if (disallowedConnector) {
|
|
91
|
+
throw new Error(`Requested connector is outside the context boundary: ${disallowedConnector}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const hasProjectBoundary = Array.isArray(input.boundary.project_refs);
|
|
95
|
+
const boundaryProjects = new Set((input.boundary.project_refs ?? []).map(projectKey));
|
|
96
|
+
const requestedProjects = (input.requestedProjectRefs ?? []).map(projectKey);
|
|
97
|
+
if (hasProjectBoundary && requestedProjects.length > 0) {
|
|
98
|
+
const disallowedProject = requestedProjects.find((project) => !boundaryProjects.has(project));
|
|
99
|
+
if (disallowedProject) {
|
|
100
|
+
const [kind, id] = disallowedProject.split('\0');
|
|
101
|
+
throw new Error(`Requested project ref is outside the context boundary: ${kind}:${id}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const boundaryTenantId = input.boundary.tenant_id;
|
|
105
|
+
if (typeof boundaryTenantId === 'string' &&
|
|
106
|
+
boundaryTenantId.length > 0 &&
|
|
107
|
+
input.requestedTenantId !== undefined &&
|
|
108
|
+
input.requestedTenantId !== null &&
|
|
109
|
+
input.requestedTenantId !== boundaryTenantId) {
|
|
110
|
+
throw new Error('Requested tenant is outside the context boundary');
|
|
111
|
+
}
|
|
112
|
+
const seedRefs = (0, ref_js_1.normalizeContextRefs)(input.seedRefs);
|
|
113
|
+
assertSeedRefsAllowed(seedRefs, boundaryConnectors, hasConnectorBoundary);
|
|
114
|
+
}
|
|
115
|
+
function assertSeedRefsAllowed(seedRefs, boundaryConnectors, hasConnectorBoundary) {
|
|
116
|
+
for (const ref of seedRefs) {
|
|
117
|
+
if (ref.kind === 'raw' && hasConnectorBoundary && !boundaryConnectors.has(ref.connector)) {
|
|
118
|
+
throw new Error(`Seed raw ref connector is outside the context boundary: ${ref.connector}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function isRecord(value) {
|
|
123
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
124
|
+
}
|
|
125
|
+
function collectHiddenIdentifiers(value, hidden) {
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
for (const item of value) {
|
|
128
|
+
collectHiddenIdentifiers(item, hidden);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!isRecord(value)) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const maybeHidden = value;
|
|
136
|
+
if (maybeHidden.visible === false) {
|
|
137
|
+
for (const field of ['id', 'raw_id', 'source_id', 'channel_id']) {
|
|
138
|
+
const fieldValue = value[field];
|
|
139
|
+
if (typeof fieldValue === 'string' && fieldValue.length > 0) {
|
|
140
|
+
hidden.add(fieldValue);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
for (const child of Object.values(value)) {
|
|
145
|
+
collectHiddenIdentifiers(child, hidden);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
function isIdentifierBoundary(character) {
|
|
149
|
+
return character === undefined || !/[A-Za-z0-9_]/.test(character);
|
|
150
|
+
}
|
|
151
|
+
function containsHiddenIdentifier(value, identifier) {
|
|
152
|
+
if (identifier.length === 0) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
let index = value.indexOf(identifier);
|
|
156
|
+
while (index !== -1) {
|
|
157
|
+
const before = value[index - 1];
|
|
158
|
+
const after = value[index + identifier.length];
|
|
159
|
+
if (isIdentifierBoundary(before) && isIdentifierBoundary(after)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
index = value.indexOf(identifier, index + 1);
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
function sanitizeValue(value, hidden) {
|
|
167
|
+
if (typeof value === 'string') {
|
|
168
|
+
return [...hidden].some((identifier) => containsHiddenIdentifier(value, identifier))
|
|
169
|
+
? undefined
|
|
170
|
+
: value;
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(value)) {
|
|
173
|
+
return value
|
|
174
|
+
.map((item) => sanitizeValue(item, hidden))
|
|
175
|
+
.filter((item) => item !== undefined);
|
|
176
|
+
}
|
|
177
|
+
if (!isRecord(value)) {
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
if (value.visible === false) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
const sanitized = {};
|
|
184
|
+
for (const [key, child] of Object.entries(value)) {
|
|
185
|
+
const next = sanitizeValue(child, hidden);
|
|
186
|
+
if (next !== undefined) {
|
|
187
|
+
sanitized[key] = next;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return sanitized;
|
|
191
|
+
}
|
|
192
|
+
function sanitizeContextPacketForVisibility(packet) {
|
|
193
|
+
const hidden = new Set();
|
|
194
|
+
collectHiddenIdentifiers(packet, hidden);
|
|
195
|
+
return sanitizeValue(packet, hidden);
|
|
196
|
+
}
|
|
197
|
+
function normalizeSeedRefs(seedRefs) {
|
|
198
|
+
return (0, ref_js_1.normalizeContextRefs)(seedRefs).map((ref) => (0, ref_js_1.normalizeContextRef)(ref));
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=visibility.js.map
|
|
@@ -55,6 +55,15 @@ export declare class NodeSQLiteAdapter extends DatabaseAdapter {
|
|
|
55
55
|
insertEmbedding(rowid: number, embedding: Float32Array | number[]): RunResult | null;
|
|
56
56
|
getLastInsertRowid(): number;
|
|
57
57
|
runMigrations(migrationsDir: string): void;
|
|
58
|
+
private repairSkippedFeatureMigrations;
|
|
59
|
+
private applyRepairMigration;
|
|
60
|
+
private recoverMemoryProvenanceMigration032;
|
|
61
|
+
private assertMigration032Complete;
|
|
62
|
+
private recoverConnectorEventScopeMigration034;
|
|
63
|
+
private assertMigration034Complete;
|
|
64
|
+
private tableColumns;
|
|
65
|
+
private tableExists;
|
|
66
|
+
private indexExists;
|
|
58
67
|
private migrateFromVssMemories;
|
|
59
68
|
}
|
|
60
69
|
export default NodeSQLiteAdapter;
|
|
@@ -18,6 +18,7 @@ const debug_logger_js_1 = require("../debug-logger.js");
|
|
|
18
18
|
const embeddings_js_1 = require("../embeddings.js");
|
|
19
19
|
const LEGACY_DB_PATH = path_1.default.join(os_1.default.homedir(), '.spinelift', 'memories.db');
|
|
20
20
|
const DEFAULT_DB_PATH = path_1.default.join(os_1.default.homedir(), '.claude', 'mama-memory.db');
|
|
21
|
+
const SQLITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
21
22
|
// Prefer better-sqlite3 (includes FTS5) over node:sqlite (lacks FTS5)
|
|
22
23
|
let BetterSQLite3 = null;
|
|
23
24
|
let DatabaseSync = null;
|
|
@@ -320,6 +321,16 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
|
|
|
320
321
|
catch (err) {
|
|
321
322
|
this.exec('ROLLBACK');
|
|
322
323
|
const message = err instanceof Error ? err.message : String(err);
|
|
324
|
+
if (message.includes('duplicate column') && version === 32) {
|
|
325
|
+
this.recoverMemoryProvenanceMigration032();
|
|
326
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} recovered successfully`);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
if (message.includes('duplicate column') && version === 34) {
|
|
330
|
+
this.recoverConnectorEventScopeMigration034();
|
|
331
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Migration ${file} recovered successfully`);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
323
334
|
if (message.includes('duplicate column')) {
|
|
324
335
|
(0, debug_logger_js_1.warn)(`[node-sqlite-adapter] Migration ${file} skipped (duplicate column - already applied)`);
|
|
325
336
|
this.prepare('INSERT OR IGNORE INTO schema_version (version) VALUES (?)').run(version);
|
|
@@ -339,12 +350,208 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
|
|
|
339
350
|
throw new Error(`Migration ${file} failed: ${message}`);
|
|
340
351
|
}
|
|
341
352
|
}
|
|
353
|
+
this.repairSkippedFeatureMigrations(migrationsDir);
|
|
342
354
|
const embeddingsTables = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
|
|
343
355
|
if (embeddingsTables.length === 0) {
|
|
344
356
|
throw new Error('Embeddings table is missing after migrations. Add the required embeddings migration.');
|
|
345
357
|
}
|
|
346
358
|
this.migrateFromVssMemories();
|
|
347
359
|
}
|
|
360
|
+
repairSkippedFeatureMigrations(migrationsDir) {
|
|
361
|
+
if (this.tableExists('decisions')) {
|
|
362
|
+
const decisionColumns = this.tableColumns('decisions');
|
|
363
|
+
const hasMissingMemoryProvenanceColumn = [
|
|
364
|
+
'agent_id',
|
|
365
|
+
'model_run_id',
|
|
366
|
+
'envelope_hash',
|
|
367
|
+
'gateway_call_id',
|
|
368
|
+
'source_refs_json',
|
|
369
|
+
'provenance_json',
|
|
370
|
+
].some((column) => !decisionColumns.has(column));
|
|
371
|
+
const hasMissingMemoryProvenanceIndex = [
|
|
372
|
+
'idx_decisions_envelope_hash',
|
|
373
|
+
'idx_decisions_model_run_id',
|
|
374
|
+
'idx_decisions_gateway_call_id',
|
|
375
|
+
'idx_memory_events_memory_created',
|
|
376
|
+
].some((indexName) => !this.indexExists(indexName));
|
|
377
|
+
if (hasMissingMemoryProvenanceColumn || hasMissingMemoryProvenanceIndex) {
|
|
378
|
+
this.recoverMemoryProvenanceMigration032();
|
|
379
|
+
(0, debug_logger_js_1.info)('[node-sqlite-adapter] Repaired skipped memory provenance migration');
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (!this.tableExists('model_runs') || !this.tableExists('tool_traces')) {
|
|
383
|
+
this.applyRepairMigration(migrationsDir, '033-create-model-runs-and-tool-traces.sql', 'model run provenance');
|
|
384
|
+
}
|
|
385
|
+
if (this.tableExists('connector_event_index')) {
|
|
386
|
+
const connectorColumns = this.tableColumns('connector_event_index');
|
|
387
|
+
const hasMissingConnectorScopeColumn = [
|
|
388
|
+
'source_cursor',
|
|
389
|
+
'tenant_id',
|
|
390
|
+
'project_id',
|
|
391
|
+
'memory_scope_kind',
|
|
392
|
+
'memory_scope_id',
|
|
393
|
+
].some((column) => !connectorColumns.has(column));
|
|
394
|
+
if (hasMissingConnectorScopeColumn) {
|
|
395
|
+
this.recoverConnectorEventScopeMigration034();
|
|
396
|
+
(0, debug_logger_js_1.info)('[node-sqlite-adapter] Repaired skipped connector event scope migration');
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (!this.tableExists('twin_edges')) {
|
|
400
|
+
this.applyRepairMigration(migrationsDir, '035-create-twin-edges.sql', 'twin edge ledger');
|
|
401
|
+
}
|
|
402
|
+
if (!this.tableExists('agent_situation_packets') ||
|
|
403
|
+
!this.tableExists('agent_situation_refresh_leases')) {
|
|
404
|
+
this.applyRepairMigration(migrationsDir, '036-create-agent-situation-packets.sql', 'agent situation packet cache');
|
|
405
|
+
}
|
|
406
|
+
if (!this.tableExists('context_packets')) {
|
|
407
|
+
this.applyRepairMigration(migrationsDir, '037-create-context-packets.sql', 'context packet store');
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
applyRepairMigration(migrationsDir, fileName, label) {
|
|
411
|
+
const migrationPath = path_1.default.join(migrationsDir, fileName);
|
|
412
|
+
if (!fs_1.default.existsSync(migrationPath)) {
|
|
413
|
+
throw new Error(`Missing repair migration ${fileName} for ${label}`);
|
|
414
|
+
}
|
|
415
|
+
const migrationSQL = fs_1.default.readFileSync(migrationPath, 'utf8');
|
|
416
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Repairing skipped ${label} migration: ${fileName}`);
|
|
417
|
+
try {
|
|
418
|
+
this.transaction(() => {
|
|
419
|
+
this.exec(migrationSQL);
|
|
420
|
+
});
|
|
421
|
+
(0, debug_logger_js_1.info)(`[node-sqlite-adapter] Repair migration ${fileName} applied successfully`);
|
|
422
|
+
}
|
|
423
|
+
catch (err) {
|
|
424
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
425
|
+
throw new Error(`Repair migration ${fileName} failed: ${message}`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
recoverMemoryProvenanceMigration032() {
|
|
429
|
+
this.transaction(() => {
|
|
430
|
+
const expectedDecisionColumns = [
|
|
431
|
+
'agent_id',
|
|
432
|
+
'model_run_id',
|
|
433
|
+
'envelope_hash',
|
|
434
|
+
'gateway_call_id',
|
|
435
|
+
'source_refs_json',
|
|
436
|
+
'provenance_json',
|
|
437
|
+
];
|
|
438
|
+
const decisionColumns = this.tableColumns('decisions');
|
|
439
|
+
for (const column of expectedDecisionColumns) {
|
|
440
|
+
if (!decisionColumns.has(column)) {
|
|
441
|
+
this.exec(`ALTER TABLE decisions ADD COLUMN ${column} TEXT`);
|
|
442
|
+
decisionColumns.add(column);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
this.exec(`
|
|
446
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_envelope_hash
|
|
447
|
+
ON decisions(envelope_hash)
|
|
448
|
+
`);
|
|
449
|
+
this.exec(`
|
|
450
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_model_run_id
|
|
451
|
+
ON decisions(model_run_id)
|
|
452
|
+
`);
|
|
453
|
+
this.exec(`
|
|
454
|
+
CREATE INDEX IF NOT EXISTS idx_decisions_gateway_call_id
|
|
455
|
+
ON decisions(gateway_call_id)
|
|
456
|
+
`);
|
|
457
|
+
this.exec(`
|
|
458
|
+
CREATE INDEX IF NOT EXISTS idx_memory_events_memory_created
|
|
459
|
+
ON memory_events(memory_id, created_at DESC)
|
|
460
|
+
`);
|
|
461
|
+
this.assertMigration032Complete();
|
|
462
|
+
this.prepare('INSERT OR IGNORE INTO schema_version (version, description) VALUES (?, ?)').run(32, 'Add nullable memory provenance columns');
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
assertMigration032Complete() {
|
|
466
|
+
const decisionColumns = this.tableColumns('decisions');
|
|
467
|
+
for (const column of [
|
|
468
|
+
'agent_id',
|
|
469
|
+
'model_run_id',
|
|
470
|
+
'envelope_hash',
|
|
471
|
+
'gateway_call_id',
|
|
472
|
+
'source_refs_json',
|
|
473
|
+
'provenance_json',
|
|
474
|
+
]) {
|
|
475
|
+
if (!decisionColumns.has(column)) {
|
|
476
|
+
throw new Error(`Migration 032 recovery failed: missing decisions.${column}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
for (const indexName of [
|
|
480
|
+
'idx_decisions_envelope_hash',
|
|
481
|
+
'idx_decisions_model_run_id',
|
|
482
|
+
'idx_decisions_gateway_call_id',
|
|
483
|
+
'idx_memory_events_memory_created',
|
|
484
|
+
]) {
|
|
485
|
+
if (!this.indexExists(indexName)) {
|
|
486
|
+
throw new Error(`Migration 032 recovery failed: missing index ${indexName}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
recoverConnectorEventScopeMigration034() {
|
|
491
|
+
this.transaction(() => {
|
|
492
|
+
const expectedColumns = [
|
|
493
|
+
'source_cursor',
|
|
494
|
+
'tenant_id',
|
|
495
|
+
'project_id',
|
|
496
|
+
'memory_scope_kind',
|
|
497
|
+
'memory_scope_id',
|
|
498
|
+
];
|
|
499
|
+
const columns = this.tableColumns('connector_event_index');
|
|
500
|
+
for (const column of expectedColumns) {
|
|
501
|
+
if (!columns.has(column)) {
|
|
502
|
+
this.exec(`ALTER TABLE connector_event_index ADD COLUMN ${column} TEXT`);
|
|
503
|
+
columns.add(column);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
this.exec(`
|
|
507
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_scope
|
|
508
|
+
ON connector_event_index(tenant_id, project_id, memory_scope_kind, memory_scope_id)
|
|
509
|
+
`);
|
|
510
|
+
this.exec(`
|
|
511
|
+
CREATE INDEX IF NOT EXISTS idx_connector_event_source_cursor
|
|
512
|
+
ON connector_event_index(source_connector, source_cursor)
|
|
513
|
+
`);
|
|
514
|
+
this.assertMigration034Complete();
|
|
515
|
+
this.prepare('INSERT OR IGNORE INTO schema_version (version, description) VALUES (?, ?)').run(34, 'Add connector event scope columns');
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
assertMigration034Complete() {
|
|
519
|
+
const columns = this.tableColumns('connector_event_index');
|
|
520
|
+
for (const column of [
|
|
521
|
+
'source_cursor',
|
|
522
|
+
'tenant_id',
|
|
523
|
+
'project_id',
|
|
524
|
+
'memory_scope_kind',
|
|
525
|
+
'memory_scope_id',
|
|
526
|
+
]) {
|
|
527
|
+
if (!columns.has(column)) {
|
|
528
|
+
throw new Error(`Migration 034 recovery failed: missing connector_event_index.${column}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
for (const indexName of ['idx_connector_event_scope', 'idx_connector_event_source_cursor']) {
|
|
532
|
+
if (!this.indexExists(indexName)) {
|
|
533
|
+
throw new Error(`Migration 034 recovery failed: missing index ${indexName}`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
tableColumns(tableName) {
|
|
538
|
+
if (!SQLITE_IDENTIFIER_PATTERN.test(tableName)) {
|
|
539
|
+
throw new Error('Invalid SQLite table identifier');
|
|
540
|
+
}
|
|
541
|
+
const rows = this.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
542
|
+
return new Set(rows.map((row) => row.name));
|
|
543
|
+
}
|
|
544
|
+
tableExists(tableName) {
|
|
545
|
+
if (!SQLITE_IDENTIFIER_PATTERN.test(tableName)) {
|
|
546
|
+
throw new Error('Invalid SQLite table identifier');
|
|
547
|
+
}
|
|
548
|
+
const row = this.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name = ?").get(tableName);
|
|
549
|
+
return row?.name === tableName;
|
|
550
|
+
}
|
|
551
|
+
indexExists(indexName) {
|
|
552
|
+
const row = this.prepare("SELECT name FROM sqlite_master WHERE type='index' AND name = ?").get(indexName);
|
|
553
|
+
return row?.name === indexName;
|
|
554
|
+
}
|
|
348
555
|
migrateFromVssMemories() {
|
|
349
556
|
try {
|
|
350
557
|
const vssTables = this.prepare(`SELECT name FROM sqlite_master WHERE name='vss_memories'`).all();
|
package/dist/db-manager.d.ts
CHANGED
|
@@ -121,6 +121,7 @@ export interface SemanticEdges {
|
|
|
121
121
|
* @returns SQLite database connection
|
|
122
122
|
*/
|
|
123
123
|
export declare function initDB(): Promise<unknown>;
|
|
124
|
+
export declare function assertTestProcessIsNotUsingRealDb(effectivePath?: string, effectivePathSource?: string): void;
|
|
124
125
|
/**
|
|
125
126
|
* Get database connection (singleton pattern)
|
|
126
127
|
*
|
|
@@ -206,6 +207,12 @@ export interface DecisionInput {
|
|
|
206
207
|
event_date?: string | null;
|
|
207
208
|
/** Source event timestamp in milliseconds when known. */
|
|
208
209
|
event_datetime?: number | null;
|
|
210
|
+
agent_id?: string | null;
|
|
211
|
+
model_run_id?: string | null;
|
|
212
|
+
envelope_hash?: string | null;
|
|
213
|
+
gateway_call_id?: string | null;
|
|
214
|
+
source_refs_json?: string | null;
|
|
215
|
+
provenance_json?: string | null;
|
|
209
216
|
}
|
|
210
217
|
/**
|
|
211
218
|
* Insert decision with embedding
|
|
@@ -284,7 +291,7 @@ export declare function resetDBState(options?: {
|
|
|
284
291
|
/**
|
|
285
292
|
* Check if running in test mode
|
|
286
293
|
*
|
|
287
|
-
* Returns true if MAMA_TEST_MODE or
|
|
294
|
+
* Returns true if MAMA_TEST_MODE, VITEST, or NODE_ENV=test env vars are set
|
|
288
295
|
*/
|
|
289
296
|
export declare function isTestMode(): boolean;
|
|
290
297
|
/**
|