@jungjaehoon/mama-core 1.6.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/db/migrations/037-create-context-packets.sql +32 -0
- package/dist/cases/wiki-page-index.js +6 -2
- 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 +3 -0
- package/dist/db-adapter/node-sqlite-adapter.js +76 -0
- package/dist/edges/ref-validation.js +55 -16
- package/dist/edges/types.d.ts +2 -0
- package/dist/embeddings.d.ts +3 -0
- package/dist/embeddings.js +37 -10
- 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 +1 -0
- package/dist/index.js +1 -0
- package/dist/memory/provenance.js +1 -0
- package/dist/memory/types.d.ts +1 -0
- package/package.json +3 -2
|
@@ -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,11 +55,14 @@ 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;
|
|
58
60
|
private recoverMemoryProvenanceMigration032;
|
|
59
61
|
private assertMigration032Complete;
|
|
60
62
|
private recoverConnectorEventScopeMigration034;
|
|
61
63
|
private assertMigration034Complete;
|
|
62
64
|
private tableColumns;
|
|
65
|
+
private tableExists;
|
|
63
66
|
private indexExists;
|
|
64
67
|
private migrateFromVssMemories;
|
|
65
68
|
}
|
|
@@ -350,12 +350,81 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
|
|
|
350
350
|
throw new Error(`Migration ${file} failed: ${message}`);
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
|
+
this.repairSkippedFeatureMigrations(migrationsDir);
|
|
353
354
|
const embeddingsTables = this.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='embeddings'`).all();
|
|
354
355
|
if (embeddingsTables.length === 0) {
|
|
355
356
|
throw new Error('Embeddings table is missing after migrations. Add the required embeddings migration.');
|
|
356
357
|
}
|
|
357
358
|
this.migrateFromVssMemories();
|
|
358
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
|
+
}
|
|
359
428
|
recoverMemoryProvenanceMigration032() {
|
|
360
429
|
this.transaction(() => {
|
|
361
430
|
const expectedDecisionColumns = [
|
|
@@ -472,6 +541,13 @@ class NodeSQLiteAdapter extends base_adapter_js_1.DatabaseAdapter {
|
|
|
472
541
|
const rows = this.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
473
542
|
return new Set(rows.map((row) => row.name));
|
|
474
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
|
+
}
|
|
475
551
|
indexExists(indexName) {
|
|
476
552
|
const row = this.prepare("SELECT name FROM sqlite_master WHERE type='index' AND name = ?").get(indexName);
|
|
477
553
|
return row?.name === indexName;
|
|
@@ -9,15 +9,15 @@ const TABLE_COLUMN_PRAGMAS = {
|
|
|
9
9
|
case_truth: 'PRAGMA table_info(case_truth)',
|
|
10
10
|
};
|
|
11
11
|
const tableColumnCache = new WeakMap();
|
|
12
|
+
// Keep memory-truth quarantine semantics excluded for legacy/backcompat rows even though the
|
|
13
|
+
// current decisions.status CHECK only admits superseded/contradicted/stale terminal states.
|
|
14
|
+
const EXCLUDED_MEMORY_STATUSES = new Set(['superseded', 'quarantined', 'contradicted', 'stale']);
|
|
12
15
|
function scopeKey(scope) {
|
|
13
16
|
return `${scope.kind}\0${scope.id}`;
|
|
14
17
|
}
|
|
15
18
|
function hasScopes(scopes) {
|
|
16
19
|
return Array.isArray(scopes) && scopes.length > 0;
|
|
17
20
|
}
|
|
18
|
-
function hasConnectors(connectors) {
|
|
19
|
-
return Array.isArray(connectors) && connectors.length > 0;
|
|
20
|
-
}
|
|
21
21
|
function hasProjectRefs(projectRefs) {
|
|
22
22
|
return Array.isArray(projectRefs) && projectRefs.length > 0;
|
|
23
23
|
}
|
|
@@ -27,16 +27,33 @@ function visibilityFromScopes(scopes) {
|
|
|
27
27
|
function asOfMs(visibility) {
|
|
28
28
|
return typeof visibility.asOfMs === 'number' ? visibility.asOfMs : null;
|
|
29
29
|
}
|
|
30
|
-
function
|
|
30
|
+
function startMs(visibility) {
|
|
31
|
+
return typeof visibility.startMs === 'number' ? visibility.startMs : null;
|
|
32
|
+
}
|
|
33
|
+
function isWithinVisibilityTime(value, visibility) {
|
|
34
|
+
const start = startMs(visibility);
|
|
31
35
|
const asOf = asOfMs(visibility);
|
|
32
|
-
|
|
36
|
+
if (start === null && asOf === null) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return (typeof value === 'number' &&
|
|
40
|
+
(start === null || value >= start) &&
|
|
41
|
+
(asOf === null || value <= asOf));
|
|
33
42
|
}
|
|
34
43
|
function parseTimestamp(value) {
|
|
35
44
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
36
45
|
return Math.floor(value);
|
|
37
46
|
}
|
|
38
47
|
if (typeof value === 'string') {
|
|
39
|
-
const
|
|
48
|
+
const trimmed = value.trim();
|
|
49
|
+
if (trimmed.length === 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const numeric = Number(trimmed);
|
|
53
|
+
if (Number.isFinite(numeric)) {
|
|
54
|
+
return Math.floor(numeric);
|
|
55
|
+
}
|
|
56
|
+
const parsed = Date.parse(trimmed);
|
|
40
57
|
return Number.isFinite(parsed) ? parsed : null;
|
|
41
58
|
}
|
|
42
59
|
return null;
|
|
@@ -61,9 +78,14 @@ function tableColumns(adapter, table) {
|
|
|
61
78
|
return columns;
|
|
62
79
|
}
|
|
63
80
|
function isMemoryVisible(adapter, id, visibility) {
|
|
81
|
+
const columns = tableColumns(adapter, 'decisions');
|
|
82
|
+
const statusSelect = columns.has('status') ? 'status' : 'NULL AS status';
|
|
83
|
+
const supersededBySelect = columns.has('superseded_by')
|
|
84
|
+
? 'superseded_by'
|
|
85
|
+
: 'NULL AS superseded_by';
|
|
64
86
|
const row = adapter
|
|
65
87
|
.prepare(`
|
|
66
|
-
SELECT created_at, event_datetime
|
|
88
|
+
SELECT created_at, event_datetime, ${statusSelect}, ${supersededBySelect}
|
|
67
89
|
FROM decisions
|
|
68
90
|
WHERE id = ?
|
|
69
91
|
LIMIT 1
|
|
@@ -72,7 +94,12 @@ function isMemoryVisible(adapter, id, visibility) {
|
|
|
72
94
|
if (!row) {
|
|
73
95
|
return false;
|
|
74
96
|
}
|
|
75
|
-
|
|
97
|
+
const status = typeof row.status === 'string' ? row.status : 'active';
|
|
98
|
+
if (EXCLUDED_MEMORY_STATUSES.has(status) ||
|
|
99
|
+
(typeof row.superseded_by === 'string' && row.superseded_by.length > 0)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (!isWithinVisibilityTime(row.event_datetime ?? row.created_at, visibility)) {
|
|
76
103
|
return false;
|
|
77
104
|
}
|
|
78
105
|
const scopes = visibility.scopes;
|
|
@@ -94,7 +121,6 @@ function isMemoryVisible(adapter, id, visibility) {
|
|
|
94
121
|
if (bindingRow?.ok) {
|
|
95
122
|
return true;
|
|
96
123
|
}
|
|
97
|
-
const columns = tableColumns(adapter, 'decisions');
|
|
98
124
|
if (columns.has('memory_scope_kind') && columns.has('memory_scope_id')) {
|
|
99
125
|
const row = adapter
|
|
100
126
|
.prepare(`
|
|
@@ -133,7 +159,7 @@ function isCaseVisible(adapter, id, visibility) {
|
|
|
133
159
|
return false;
|
|
134
160
|
}
|
|
135
161
|
const timestamp = parseTimestamp(row.last_activity_at) ?? parseTimestamp(row.updated_at);
|
|
136
|
-
if (!
|
|
162
|
+
if (!isWithinVisibilityTime(timestamp, visibility)) {
|
|
137
163
|
return false;
|
|
138
164
|
}
|
|
139
165
|
const scopes = visibility.scopes;
|
|
@@ -158,7 +184,7 @@ function isRawVisible(adapter, id, visibility) {
|
|
|
158
184
|
if (!row) {
|
|
159
185
|
return false;
|
|
160
186
|
}
|
|
161
|
-
if (
|
|
187
|
+
if (Array.isArray(visibility.connectors) &&
|
|
162
188
|
!visibility.connectors.includes(String(row.source_connector))) {
|
|
163
189
|
return false;
|
|
164
190
|
}
|
|
@@ -171,7 +197,12 @@ function isRawVisible(adapter, id, visibility) {
|
|
|
171
197
|
row.tenant_id !== visibility.tenantId) {
|
|
172
198
|
return false;
|
|
173
199
|
}
|
|
174
|
-
|
|
200
|
+
const rawTs = row.event_datetime ?? row.source_timestamp_ms;
|
|
201
|
+
if (rawTs === null || rawTs === undefined || (typeof rawTs === 'string' && rawTs.trim() === '')) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
const eventTsMs = parseTimestamp(rawTs);
|
|
205
|
+
if (eventTsMs === null || eventTsMs < 0 || !isWithinVisibilityTime(eventTsMs, visibility)) {
|
|
175
206
|
return false;
|
|
176
207
|
}
|
|
177
208
|
if (!hasScopes(visibility.scopes)) {
|
|
@@ -186,7 +217,7 @@ function isEntityVisible(adapter, id, visibility) {
|
|
|
186
217
|
if (!row) {
|
|
187
218
|
return false;
|
|
188
219
|
}
|
|
189
|
-
if (!
|
|
220
|
+
if (!isWithinVisibilityTime(row.created_at, visibility)) {
|
|
190
221
|
return false;
|
|
191
222
|
}
|
|
192
223
|
const scopes = visibility.scopes;
|
|
@@ -222,7 +253,7 @@ function isTwinRefVisible(adapter, ref, visibility, visitedEdges, edgeCache) {
|
|
|
222
253
|
if (!edge) {
|
|
223
254
|
return false;
|
|
224
255
|
}
|
|
225
|
-
if (!
|
|
256
|
+
if (!isWithinVisibilityTime(edge.created_at, visibility)) {
|
|
226
257
|
return false;
|
|
227
258
|
}
|
|
228
259
|
const pathWithCurrent = new Set(visitedEdges);
|
|
@@ -245,13 +276,21 @@ function listVisibleTwinEdgesForRefs(adapter, refs, options = {}) {
|
|
|
245
276
|
const edgeCache = new Map();
|
|
246
277
|
const edgeTypes = normalizeEdgeTypes(options.edgeTypes);
|
|
247
278
|
const edges = (0, store_js_1.listTwinEdgesForRefs)(adapter, refs).filter((edge) => (edgeTypes.size === 0 || edgeTypes.has(edge.edge_type)) &&
|
|
279
|
+
(typeof options.startMs !== 'number' || edge.created_at >= options.startMs) &&
|
|
248
280
|
(typeof options.asOfMs !== 'number' || edge.created_at <= options.asOfMs) &&
|
|
249
281
|
isTwinRefVisible(adapter, edge.subject_ref, options, new Set(), edgeCache) &&
|
|
250
282
|
isTwinRefVisible(adapter, edge.object_ref, options, new Set(), edgeCache));
|
|
251
283
|
const limit = typeof options.limit === 'number' && Number.isFinite(options.limit)
|
|
252
284
|
? Math.max(0, Math.floor(options.limit))
|
|
253
|
-
:
|
|
254
|
-
|
|
285
|
+
: null;
|
|
286
|
+
const sortedEdges = [...edges].sort((left, right) => {
|
|
287
|
+
const createdDiff = right.created_at - left.created_at;
|
|
288
|
+
return createdDiff !== 0 ? createdDiff : left.edge_id.localeCompare(right.edge_id);
|
|
289
|
+
});
|
|
290
|
+
if (limit === null) {
|
|
291
|
+
return sortedEdges;
|
|
292
|
+
}
|
|
293
|
+
return sortedEdges.slice(0, limit);
|
|
255
294
|
}
|
|
256
295
|
function normalizeEdgeTypes(edgeTypes) {
|
|
257
296
|
if (!Array.isArray(edgeTypes) || edgeTypes.length === 0) {
|
package/dist/edges/types.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export interface TwinVisibility {
|
|
|
27
27
|
connectors?: string[];
|
|
28
28
|
projectRefs?: TwinProjectRef[];
|
|
29
29
|
tenantId?: string | null;
|
|
30
|
+
startMs?: number | null;
|
|
30
31
|
asOfMs?: number | null;
|
|
31
32
|
}
|
|
32
33
|
export interface InsertTwinEdgeInput {
|
|
@@ -80,6 +81,7 @@ export interface ListVisibleTwinEdgesOptions {
|
|
|
80
81
|
projectRefs?: TwinProjectRef[];
|
|
81
82
|
tenantId?: string | null;
|
|
82
83
|
edgeTypes?: TwinEdgeType[];
|
|
84
|
+
startMs?: number | null;
|
|
83
85
|
asOfMs?: number | null;
|
|
84
86
|
limit?: number;
|
|
85
87
|
}
|
package/dist/embeddings.d.ts
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { embeddingCache } from './embedding-cache.js';
|
|
13
13
|
import { loadConfig, getModelName, getEmbeddingDim, getQuantized } from './config-loader.js';
|
|
14
|
+
export declare const EMBEDDING_MODEL_MAX_LENGTH = 512;
|
|
15
|
+
export declare const EMBEDDING_MAX_TOKENISH_SEGMENTS = 480;
|
|
16
|
+
export declare function prepareEmbeddingText(text: string): string;
|
|
14
17
|
/**
|
|
15
18
|
* Decision object for enhanced embedding generation
|
|
16
19
|
*/
|