nexus-agents 2.77.13 → 2.79.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/dist/{child-mcp-config-MJMUF7TL.js → child-mcp-config-CTO2MBRM.js} +3 -4
- package/dist/{child-mcp-config-MJMUF7TL.js.map → child-mcp-config-CTO2MBRM.js.map} +1 -1
- package/dist/{chunk-YJ2IGAD2.js → chunk-2UYTFLMO.js} +2 -2
- package/dist/{chunk-6AY5DK4E.js → chunk-2YPG6PDG.js} +3 -3
- package/dist/{chunk-3VWMM6UF.js → chunk-3NIPH6UP.js} +2 -2
- package/dist/{chunk-GFKGL2GQ.js → chunk-3ULMVOIF.js} +171 -83
- package/dist/chunk-3ULMVOIF.js.map +1 -0
- package/dist/{chunk-JN6UWGHH.js → chunk-5O6XLBPP.js} +2 -2
- package/dist/{chunk-ERWXGXV2.js → chunk-6TFTVW77.js} +3 -3
- package/dist/{chunk-GOT7OAL5.js → chunk-7BMOZJYS.js} +29 -5
- package/dist/chunk-7BMOZJYS.js.map +1 -0
- package/dist/{chunk-C2LLQ6TW.js → chunk-7XCUZI4G.js} +4 -4
- package/dist/chunk-7XCUZI4G.js.map +1 -0
- package/dist/{chunk-TDV5ALHY.js → chunk-D6TM2VHX.js} +3 -3
- package/dist/{chunk-PWTJGGKB.js → chunk-DLXT23AC.js} +2 -2
- package/dist/chunk-DNO2INX5.js +276 -0
- package/dist/chunk-DNO2INX5.js.map +1 -0
- package/dist/{chunk-G2CSKBY5.js → chunk-FJWWSVWB.js} +29 -6
- package/dist/chunk-FJWWSVWB.js.map +1 -0
- package/dist/{chunk-DSQ5XM4O.js → chunk-FVPYP5DD.js} +4 -4
- package/dist/{chunk-MGLWPN2I.js → chunk-GONMG4NM.js} +2 -2
- package/dist/{chunk-YQMQSJQK.js → chunk-HYU4GZY6.js} +2 -2
- package/dist/{chunk-3DH5SLFH.js → chunk-K2QILJG4.js} +6 -6
- package/dist/{chunk-5WHWKY32.js → chunk-KT5FIBWS.js} +2 -2
- package/dist/{chunk-DIB6V67T.js → chunk-L6SCKLGO.js} +3 -3
- package/dist/{chunk-IPWCD22D.js → chunk-PLX6FCFC.js} +2 -2
- package/dist/chunk-PQHVC4BD.js +639 -0
- package/dist/chunk-PQHVC4BD.js.map +1 -0
- package/dist/chunk-Q5CFPIJ5.js +5581 -0
- package/dist/chunk-Q5CFPIJ5.js.map +1 -0
- package/dist/{chunk-G6ZPVADX.js → chunk-SD76JZBG.js} +2 -2
- package/dist/{chunk-Y2CP4Z5B.js → chunk-SWFJU3W2.js} +220 -4580
- package/dist/chunk-SWFJU3W2.js.map +1 -0
- package/dist/{chunk-3MRM53T4.js → chunk-WDYCIJWN.js} +640 -470
- package/dist/chunk-WDYCIJWN.js.map +1 -0
- package/dist/{chunk-RBZ4CDMY.js → chunk-YMMYYAZT.js} +5 -5
- package/dist/{chunk-CM3TORGV.js → chunk-YXWGEIQR.js} +2 -2
- package/dist/{chunk-JX6OI4FS.js → chunk-ZI6G7U7Y.js} +125 -33
- package/dist/chunk-ZI6G7U7Y.js.map +1 -0
- package/dist/{chunk-7NK7BTWP.js → chunk-ZVCED4Z4.js} +2 -2
- package/dist/cli-circuit-breaker-I74ZQ44Q.js +13 -0
- package/dist/cli.js +109 -58
- package/dist/cli.js.map +1 -1
- package/dist/{composite-router-S6E26BCI.js → composite-router-V3OC57IE.js} +3 -4
- package/dist/consensus-vote-ESFPGEJE.js +30 -0
- package/dist/context-retriever-MB3D7KS6.js +18 -0
- package/dist/dist-NIXVXYIH.js +42 -0
- package/dist/doctor-deep-KQ765XZA.js +12 -0
- package/dist/expert-bridge-JKLC57IC.js +10 -0
- package/dist/factory-BUUXNGIB.js +14 -0
- package/dist/{factory-X3VKIGKP.js → factory-LHHYDVZX.js} +5 -6
- package/dist/index.d.ts +72 -8
- package/dist/index.js +208 -316
- package/dist/index.js.map +1 -1
- package/dist/{init-opencode-CFE7M6XA.js → init-opencode-GXZN2W5S.js} +6 -7
- package/dist/{init-opencode-CFE7M6XA.js.map → init-opencode-GXZN2W5S.js.map} +1 -1
- package/dist/issue-triage-RMXPDZ2K.js +15 -0
- package/dist/{learning-persistence-N6ILD2HX.js → learning-persistence-Q3HTOGTU.js} +2 -3
- package/dist/outcome-store-adapter-QRFJJIKB.js +57 -0
- package/dist/outcome-store-adapter-QRFJJIKB.js.map +1 -0
- package/dist/{registry-command-RPPC7N2K.js → registry-command-6E4YKAMT.js} +3 -4
- package/dist/{registry-command-RPPC7N2K.js.map → registry-command-6E4YKAMT.js.map} +1 -1
- package/dist/{repo-security-plan-7ZCDVH5O.js → repo-security-plan-AGRU72DL.js} +4 -5
- package/dist/research-helpers-synthesize-K2UCJQQG.js +13 -0
- package/dist/{routing-memory-5VTX7LQX.js → routing-memory-3B6DDZ76.js} +3 -4
- package/dist/{session-memory-7XBV6BMY.js → session-memory-L7EQIY2O.js} +4 -5
- package/dist/{setup-command-W6UKPODL.js → setup-command-R4BOEMLZ.js} +11 -12
- package/dist/setup-config-EQT24DD4.js +10 -0
- package/dist/{setup-custom-api-WM5W5AY5.js → setup-custom-api-IBDV654K.js} +5 -6
- package/dist/{setup-custom-api-WM5W5AY5.js.map → setup-custom-api-IBDV654K.js.map} +1 -1
- package/dist/tool-memory-6HCHQLAN.js +19 -0
- package/dist/{weather-report-YJMVKJGA.js → weather-report-ER3WUZ7S.js} +3 -4
- package/package.json +3 -2
- package/dist/adaptive-memory-EI564K4C.js +0 -16
- package/dist/chunk-3MRM53T4.js.map +0 -1
- package/dist/chunk-BJ2OMC7P.js +0 -944
- package/dist/chunk-BJ2OMC7P.js.map +0 -1
- package/dist/chunk-C2LLQ6TW.js.map +0 -1
- package/dist/chunk-G2CSKBY5.js.map +0 -1
- package/dist/chunk-GFKGL2GQ.js.map +0 -1
- package/dist/chunk-GOT7OAL5.js.map +0 -1
- package/dist/chunk-I7ORMAO7.js +0 -32
- package/dist/chunk-I7ORMAO7.js.map +0 -1
- package/dist/chunk-JX6OI4FS.js.map +0 -1
- package/dist/chunk-Y2CP4Z5B.js.map +0 -1
- package/dist/cli-circuit-breaker-YX4BWZD5.js +0 -14
- package/dist/consensus-vote-MUQ4HPIF.js +0 -30
- package/dist/doctor-deep-BRU5ZUJI.js +0 -13
- package/dist/expert-bridge-ZPNVLJVN.js +0 -11
- package/dist/factory-A7DTCCUY.js +0 -15
- package/dist/issue-triage-6XD6CVPB.js +0 -16
- package/dist/mobimem-CG2MNS7V.js +0 -14
- package/dist/nexus-data-dir-77UO7N6J.js +0 -12
- package/dist/research-helpers-synthesize-36TUTUUA.js +0 -14
- package/dist/setup-config-EI5KROA3.js +0 -11
- /package/dist/{chunk-YJ2IGAD2.js.map → chunk-2UYTFLMO.js.map} +0 -0
- /package/dist/{chunk-6AY5DK4E.js.map → chunk-2YPG6PDG.js.map} +0 -0
- /package/dist/{chunk-3VWMM6UF.js.map → chunk-3NIPH6UP.js.map} +0 -0
- /package/dist/{chunk-JN6UWGHH.js.map → chunk-5O6XLBPP.js.map} +0 -0
- /package/dist/{chunk-ERWXGXV2.js.map → chunk-6TFTVW77.js.map} +0 -0
- /package/dist/{chunk-TDV5ALHY.js.map → chunk-D6TM2VHX.js.map} +0 -0
- /package/dist/{chunk-PWTJGGKB.js.map → chunk-DLXT23AC.js.map} +0 -0
- /package/dist/{chunk-DSQ5XM4O.js.map → chunk-FVPYP5DD.js.map} +0 -0
- /package/dist/{chunk-MGLWPN2I.js.map → chunk-GONMG4NM.js.map} +0 -0
- /package/dist/{chunk-YQMQSJQK.js.map → chunk-HYU4GZY6.js.map} +0 -0
- /package/dist/{chunk-3DH5SLFH.js.map → chunk-K2QILJG4.js.map} +0 -0
- /package/dist/{chunk-5WHWKY32.js.map → chunk-KT5FIBWS.js.map} +0 -0
- /package/dist/{chunk-DIB6V67T.js.map → chunk-L6SCKLGO.js.map} +0 -0
- /package/dist/{chunk-IPWCD22D.js.map → chunk-PLX6FCFC.js.map} +0 -0
- /package/dist/{chunk-G6ZPVADX.js.map → chunk-SD76JZBG.js.map} +0 -0
- /package/dist/{chunk-RBZ4CDMY.js.map → chunk-YMMYYAZT.js.map} +0 -0
- /package/dist/{chunk-CM3TORGV.js.map → chunk-YXWGEIQR.js.map} +0 -0
- /package/dist/{chunk-7NK7BTWP.js.map → chunk-ZVCED4Z4.js.map} +0 -0
- /package/dist/{adaptive-memory-EI564K4C.js.map → cli-circuit-breaker-I74ZQ44Q.js.map} +0 -0
- /package/dist/{cli-circuit-breaker-YX4BWZD5.js.map → composite-router-V3OC57IE.js.map} +0 -0
- /package/dist/{composite-router-S6E26BCI.js.map → consensus-vote-ESFPGEJE.js.map} +0 -0
- /package/dist/{consensus-vote-MUQ4HPIF.js.map → context-retriever-MB3D7KS6.js.map} +0 -0
- /package/dist/{doctor-deep-BRU5ZUJI.js.map → dist-NIXVXYIH.js.map} +0 -0
- /package/dist/{expert-bridge-ZPNVLJVN.js.map → doctor-deep-KQ765XZA.js.map} +0 -0
- /package/dist/{factory-A7DTCCUY.js.map → expert-bridge-JKLC57IC.js.map} +0 -0
- /package/dist/{factory-X3VKIGKP.js.map → factory-BUUXNGIB.js.map} +0 -0
- /package/dist/{issue-triage-6XD6CVPB.js.map → factory-LHHYDVZX.js.map} +0 -0
- /package/dist/{learning-persistence-N6ILD2HX.js.map → issue-triage-RMXPDZ2K.js.map} +0 -0
- /package/dist/{mobimem-CG2MNS7V.js.map → learning-persistence-Q3HTOGTU.js.map} +0 -0
- /package/dist/{nexus-data-dir-77UO7N6J.js.map → repo-security-plan-AGRU72DL.js.map} +0 -0
- /package/dist/{repo-security-plan-7ZCDVH5O.js.map → research-helpers-synthesize-K2UCJQQG.js.map} +0 -0
- /package/dist/{research-helpers-synthesize-36TUTUUA.js.map → routing-memory-3B6DDZ76.js.map} +0 -0
- /package/dist/{routing-memory-5VTX7LQX.js.map → session-memory-L7EQIY2O.js.map} +0 -0
- /package/dist/{session-memory-7XBV6BMY.js.map → setup-command-R4BOEMLZ.js.map} +0 -0
- /package/dist/{setup-command-W6UKPODL.js.map → setup-config-EQT24DD4.js.map} +0 -0
- /package/dist/{setup-config-EI5KROA3.js.map → tool-memory-6HCHQLAN.js.map} +0 -0
- /package/dist/{weather-report-YJMVKJGA.js.map → weather-report-ER3WUZ7S.js.map} +0 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
// ../nexus-memory/dist/index.js
|
|
2
|
+
import Database from "better-sqlite3";
|
|
3
|
+
import Database2 from "better-sqlite3";
|
|
4
|
+
import { existsSync, mkdirSync, renameSync, writeFileSync } from "fs";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
var KEY_SUMMARY_LIMIT = 120;
|
|
7
|
+
var PAYLOAD_SUMMARY_LIMIT = 240;
|
|
8
|
+
var counters = /* @__PURE__ */ new Map();
|
|
9
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
10
|
+
function isAuditMode() {
|
|
11
|
+
return process.env["NEXUS_MEMORY_AUDIT_MODE"] === "audit";
|
|
12
|
+
}
|
|
13
|
+
function counterKey(domain, op) {
|
|
14
|
+
return `${domain}::${op}`;
|
|
15
|
+
}
|
|
16
|
+
function truncate(value, limit) {
|
|
17
|
+
let s;
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
s = value;
|
|
20
|
+
} else if (value === void 0) {
|
|
21
|
+
s = "<undefined>";
|
|
22
|
+
} else if (value === null) {
|
|
23
|
+
s = "<null>";
|
|
24
|
+
} else {
|
|
25
|
+
try {
|
|
26
|
+
s = JSON.stringify(value);
|
|
27
|
+
} catch {
|
|
28
|
+
s = "<unserializable>";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return s.length > limit ? `${s.slice(0, limit - 1)}\u2026` : s;
|
|
32
|
+
}
|
|
33
|
+
function updateCounter(event) {
|
|
34
|
+
const ck = counterKey(event.domain, event.op);
|
|
35
|
+
const existing = counters.get(ck);
|
|
36
|
+
const hitDelta = event.hit === true ? 1 : 0;
|
|
37
|
+
counters.set(ck, {
|
|
38
|
+
domain: event.domain,
|
|
39
|
+
op: event.op,
|
|
40
|
+
count: (existing?.count ?? 0) + 1,
|
|
41
|
+
hitCount: (existing?.hitCount ?? 0) + hitDelta,
|
|
42
|
+
totalDurationMs: (existing?.totalDurationMs ?? 0) + event.durationMs,
|
|
43
|
+
maxDurationMs: Math.max(existing?.maxDurationMs ?? 0, event.durationMs)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function buildPublicEvent(event, audit) {
|
|
47
|
+
return {
|
|
48
|
+
domain: event.domain,
|
|
49
|
+
op: event.op,
|
|
50
|
+
durationMs: event.durationMs,
|
|
51
|
+
...event.cli !== void 0 && { cli: event.cli },
|
|
52
|
+
...event.hit !== void 0 && { hit: event.hit },
|
|
53
|
+
...audit && event.key !== void 0 && {
|
|
54
|
+
keySummary: truncate(event.key, KEY_SUMMARY_LIMIT)
|
|
55
|
+
},
|
|
56
|
+
...audit && event.payload !== void 0 && {
|
|
57
|
+
payloadSummary: truncate(event.payload, PAYLOAD_SUMMARY_LIMIT)
|
|
58
|
+
},
|
|
59
|
+
...audit && event.result !== void 0 && {
|
|
60
|
+
resultSummary: truncate(event.result, PAYLOAD_SUMMARY_LIMIT)
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function recordMemoryEvent(event) {
|
|
65
|
+
updateCounter(event);
|
|
66
|
+
const publicEvent = buildPublicEvent(event, isAuditMode());
|
|
67
|
+
for (const listener of listeners) {
|
|
68
|
+
try {
|
|
69
|
+
listener(publicEvent);
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function getMemoryEventCounters() {
|
|
75
|
+
return [...counters.values()];
|
|
76
|
+
}
|
|
77
|
+
function subscribeToMemoryEvents(listener) {
|
|
78
|
+
listeners.add(listener);
|
|
79
|
+
return () => {
|
|
80
|
+
listeners.delete(listener);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function resetMemoryTelemetry() {
|
|
84
|
+
counters.clear();
|
|
85
|
+
listeners.clear();
|
|
86
|
+
}
|
|
87
|
+
var MemoryValidationError = class extends Error {
|
|
88
|
+
constructor(domain, cause) {
|
|
89
|
+
super(`nexus-memory: write rejected for domain "${domain}": ${String(cause)}`);
|
|
90
|
+
this.name = "MemoryValidationError";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
function buildInMemoryRow(value, meta) {
|
|
94
|
+
return {
|
|
95
|
+
value,
|
|
96
|
+
...meta?.cli !== void 0 && { cli: meta.cli },
|
|
97
|
+
...meta?.source !== void 0 && { source: meta.source },
|
|
98
|
+
timestamp: meta?.timestamp ?? Date.now(),
|
|
99
|
+
...meta?.trustTier !== void 0 && { trustTier: meta.trustTier }
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function applyInMemoryFilter(values, filter) {
|
|
103
|
+
let out = values;
|
|
104
|
+
if (filter?.where !== void 0) {
|
|
105
|
+
const where = filter.where;
|
|
106
|
+
out = out.filter((v) => {
|
|
107
|
+
for (const [k, expected] of Object.entries(where)) {
|
|
108
|
+
if (v[k] !== expected) return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
if (filter?.orderBy !== void 0) {
|
|
114
|
+
const orderBy = filter.orderBy;
|
|
115
|
+
const dir = filter.orderDir === "desc" ? -1 : 1;
|
|
116
|
+
out = [...out].sort((a, b) => {
|
|
117
|
+
const av = a[orderBy];
|
|
118
|
+
const bv = b[orderBy];
|
|
119
|
+
if (av === bv) return 0;
|
|
120
|
+
if (av === void 0 || av === null) return 1;
|
|
121
|
+
if (bv === void 0 || bv === null) return -1;
|
|
122
|
+
return (av < bv ? -1 : 1) * dir;
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (filter?.limit !== void 0) {
|
|
126
|
+
out = out.slice(0, filter.limit);
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
var InMemoryBackend = class {
|
|
131
|
+
domain;
|
|
132
|
+
rows = /* @__PURE__ */ new Map();
|
|
133
|
+
schema;
|
|
134
|
+
closed = false;
|
|
135
|
+
constructor(options) {
|
|
136
|
+
this.domain = options.domain;
|
|
137
|
+
if (options.schema !== void 0) {
|
|
138
|
+
this.schema = options.schema;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async read(key) {
|
|
142
|
+
this.assertOpen();
|
|
143
|
+
const start = Date.now();
|
|
144
|
+
const row = this.rows.get(key);
|
|
145
|
+
recordMemoryEvent({
|
|
146
|
+
domain: this.domain,
|
|
147
|
+
op: "read",
|
|
148
|
+
hit: row !== void 0,
|
|
149
|
+
durationMs: Date.now() - start,
|
|
150
|
+
key,
|
|
151
|
+
result: row?.value
|
|
152
|
+
});
|
|
153
|
+
return Promise.resolve(row?.value);
|
|
154
|
+
}
|
|
155
|
+
validate(value) {
|
|
156
|
+
if (this.schema === void 0) return;
|
|
157
|
+
const result = this.schema.safeParse(value);
|
|
158
|
+
if (!result.success) {
|
|
159
|
+
throw new MemoryValidationError(this.domain, result.error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async write(key, value, meta) {
|
|
163
|
+
this.assertOpen();
|
|
164
|
+
const start = Date.now();
|
|
165
|
+
this.validate(value);
|
|
166
|
+
this.rows.set(key, buildInMemoryRow(value, meta));
|
|
167
|
+
recordMemoryEvent({
|
|
168
|
+
domain: this.domain,
|
|
169
|
+
op: "write",
|
|
170
|
+
...meta?.cli !== void 0 && { cli: meta.cli },
|
|
171
|
+
durationMs: Date.now() - start,
|
|
172
|
+
key,
|
|
173
|
+
payload: value
|
|
174
|
+
});
|
|
175
|
+
return Promise.resolve();
|
|
176
|
+
}
|
|
177
|
+
async query(filter) {
|
|
178
|
+
this.assertOpen();
|
|
179
|
+
const start = Date.now();
|
|
180
|
+
let rows = [...this.rows.values()];
|
|
181
|
+
if (filter?.cli !== void 0) {
|
|
182
|
+
rows = rows.filter((r) => r.cli === filter.cli);
|
|
183
|
+
}
|
|
184
|
+
const values = applyInMemoryFilter(
|
|
185
|
+
rows.map((r) => r.value),
|
|
186
|
+
filter
|
|
187
|
+
);
|
|
188
|
+
recordMemoryEvent({
|
|
189
|
+
domain: this.domain,
|
|
190
|
+
op: "query",
|
|
191
|
+
hit: values.length > 0,
|
|
192
|
+
...filter?.cli !== void 0 && { cli: filter.cli },
|
|
193
|
+
durationMs: Date.now() - start,
|
|
194
|
+
key: filter,
|
|
195
|
+
result: { count: values.length }
|
|
196
|
+
});
|
|
197
|
+
return Promise.resolve(values);
|
|
198
|
+
}
|
|
199
|
+
async delete(key) {
|
|
200
|
+
this.assertOpen();
|
|
201
|
+
const start = Date.now();
|
|
202
|
+
const removed = this.rows.delete(key);
|
|
203
|
+
recordMemoryEvent({
|
|
204
|
+
domain: this.domain,
|
|
205
|
+
op: "delete",
|
|
206
|
+
hit: removed,
|
|
207
|
+
durationMs: Date.now() - start,
|
|
208
|
+
key
|
|
209
|
+
});
|
|
210
|
+
return Promise.resolve(removed);
|
|
211
|
+
}
|
|
212
|
+
async stats() {
|
|
213
|
+
this.assertOpen();
|
|
214
|
+
const start = Date.now();
|
|
215
|
+
const timestamps = [...this.rows.values()].map((r) => r.timestamp);
|
|
216
|
+
const result = {
|
|
217
|
+
domain: this.domain,
|
|
218
|
+
count: this.rows.size,
|
|
219
|
+
oldestTimestamp: timestamps.length > 0 ? Math.min(...timestamps) : null,
|
|
220
|
+
newestTimestamp: timestamps.length > 0 ? Math.max(...timestamps) : null
|
|
221
|
+
};
|
|
222
|
+
recordMemoryEvent({
|
|
223
|
+
domain: this.domain,
|
|
224
|
+
op: "stats",
|
|
225
|
+
durationMs: Date.now() - start,
|
|
226
|
+
result
|
|
227
|
+
});
|
|
228
|
+
return Promise.resolve(result);
|
|
229
|
+
}
|
|
230
|
+
async close() {
|
|
231
|
+
this.closed = true;
|
|
232
|
+
this.rows.clear();
|
|
233
|
+
return Promise.resolve();
|
|
234
|
+
}
|
|
235
|
+
assertOpen() {
|
|
236
|
+
if (this.closed) {
|
|
237
|
+
throw new Error(`nexus-memory: backend "${this.domain}" is closed`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
function buildWriteRow(key, value, meta) {
|
|
242
|
+
return {
|
|
243
|
+
key: keyToString(key),
|
|
244
|
+
value: JSON.stringify(value),
|
|
245
|
+
cli: meta?.cli ?? null,
|
|
246
|
+
source: meta?.source ?? null,
|
|
247
|
+
timestamp: meta?.timestamp ?? Date.now(),
|
|
248
|
+
trust_tier: meta?.trustTier ?? null
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function keyToString(key) {
|
|
252
|
+
if (typeof key === "string") return key;
|
|
253
|
+
if (typeof key === "number" || typeof key === "boolean") return String(key);
|
|
254
|
+
return JSON.stringify(key);
|
|
255
|
+
}
|
|
256
|
+
function applyQueryFilter(values, filter) {
|
|
257
|
+
let out = values;
|
|
258
|
+
if (filter?.where !== void 0) {
|
|
259
|
+
const where = filter.where;
|
|
260
|
+
out = out.filter((v) => {
|
|
261
|
+
for (const [k, expected] of Object.entries(where)) {
|
|
262
|
+
if (v[k] !== expected) return false;
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (filter?.orderBy !== void 0) {
|
|
268
|
+
const orderBy = filter.orderBy;
|
|
269
|
+
const dir = filter.orderDir === "desc" ? -1 : 1;
|
|
270
|
+
out = [...out].sort((a, b) => {
|
|
271
|
+
const av = a[orderBy];
|
|
272
|
+
const bv = b[orderBy];
|
|
273
|
+
if (av === bv) return 0;
|
|
274
|
+
if (av === void 0 || av === null) return 1;
|
|
275
|
+
if (bv === void 0 || bv === null) return -1;
|
|
276
|
+
return (av < bv ? -1 : 1) * dir;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
if (filter?.limit !== void 0) {
|
|
280
|
+
out = out.slice(0, filter.limit);
|
|
281
|
+
}
|
|
282
|
+
return out;
|
|
283
|
+
}
|
|
284
|
+
var SqliteBackend = class {
|
|
285
|
+
domain;
|
|
286
|
+
db;
|
|
287
|
+
ownsDb;
|
|
288
|
+
schema;
|
|
289
|
+
stmts;
|
|
290
|
+
closed = false;
|
|
291
|
+
constructor(options) {
|
|
292
|
+
this.domain = options.domain;
|
|
293
|
+
if (options.db !== void 0) {
|
|
294
|
+
this.db = options.db;
|
|
295
|
+
this.ownsDb = false;
|
|
296
|
+
} else {
|
|
297
|
+
const db = new Database(options.dbPath);
|
|
298
|
+
db.pragma("journal_mode = WAL");
|
|
299
|
+
this.db = db;
|
|
300
|
+
this.ownsDb = true;
|
|
301
|
+
}
|
|
302
|
+
if (options.schema !== void 0) {
|
|
303
|
+
this.schema = options.schema;
|
|
304
|
+
}
|
|
305
|
+
this.ensureTable();
|
|
306
|
+
this.stmts = this.prepareStatements();
|
|
307
|
+
}
|
|
308
|
+
ensureTable() {
|
|
309
|
+
if (!/^[a-z][a-z0-9_]{0,63}$/i.test(this.domain)) {
|
|
310
|
+
throw new Error(
|
|
311
|
+
`nexus-memory: invalid domain "${this.domain}" \u2014 must match [a-zA-Z][a-zA-Z0-9_]{0,63}`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
this.db.exec(`
|
|
315
|
+
CREATE TABLE IF NOT EXISTS ${this.domain} (
|
|
316
|
+
key TEXT PRIMARY KEY,
|
|
317
|
+
value TEXT NOT NULL,
|
|
318
|
+
cli TEXT,
|
|
319
|
+
source TEXT,
|
|
320
|
+
timestamp INTEGER NOT NULL,
|
|
321
|
+
trust_tier INTEGER
|
|
322
|
+
);
|
|
323
|
+
CREATE INDEX IF NOT EXISTS ${this.domain}_cli ON ${this.domain}(cli);
|
|
324
|
+
CREATE INDEX IF NOT EXISTS ${this.domain}_timestamp ON ${this.domain}(timestamp);
|
|
325
|
+
`);
|
|
326
|
+
}
|
|
327
|
+
prepareStatements() {
|
|
328
|
+
return {
|
|
329
|
+
read: this.db.prepare(
|
|
330
|
+
`SELECT key, value, cli, source, timestamp, trust_tier FROM ${this.domain} WHERE key = ?`
|
|
331
|
+
),
|
|
332
|
+
write: this.db.prepare(
|
|
333
|
+
`INSERT INTO ${this.domain} (key, value, cli, source, timestamp, trust_tier)
|
|
334
|
+
VALUES (@key, @value, @cli, @source, @timestamp, @trust_tier)
|
|
335
|
+
ON CONFLICT(key) DO UPDATE SET
|
|
336
|
+
value = excluded.value,
|
|
337
|
+
cli = excluded.cli,
|
|
338
|
+
source = excluded.source,
|
|
339
|
+
timestamp = excluded.timestamp,
|
|
340
|
+
trust_tier = excluded.trust_tier`
|
|
341
|
+
),
|
|
342
|
+
delete: this.db.prepare(`DELETE FROM ${this.domain} WHERE key = ?`),
|
|
343
|
+
count: this.db.prepare(`SELECT COUNT(*) AS count FROM ${this.domain}`),
|
|
344
|
+
bounds: this.db.prepare(
|
|
345
|
+
`SELECT MIN(timestamp) AS oldest, MAX(timestamp) AS newest FROM ${this.domain}`
|
|
346
|
+
),
|
|
347
|
+
queryAll: this.db.prepare(
|
|
348
|
+
`SELECT key, value, cli, source, timestamp, trust_tier FROM ${this.domain}`
|
|
349
|
+
)
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
async read(key) {
|
|
353
|
+
this.assertOpen();
|
|
354
|
+
const start = Date.now();
|
|
355
|
+
const row = this.stmts.read.get(keyToString(key));
|
|
356
|
+
const value = row !== void 0 ? JSON.parse(row.value) : void 0;
|
|
357
|
+
recordMemoryEvent({
|
|
358
|
+
domain: this.domain,
|
|
359
|
+
op: "read",
|
|
360
|
+
hit: value !== void 0,
|
|
361
|
+
...row?.cli !== null && row?.cli !== void 0 && { cli: row.cli },
|
|
362
|
+
durationMs: Date.now() - start,
|
|
363
|
+
key,
|
|
364
|
+
result: value
|
|
365
|
+
});
|
|
366
|
+
return Promise.resolve(value);
|
|
367
|
+
}
|
|
368
|
+
validate(value) {
|
|
369
|
+
if (this.schema === void 0) return;
|
|
370
|
+
const result = this.schema.safeParse(value);
|
|
371
|
+
if (!result.success) {
|
|
372
|
+
throw new MemoryValidationError(this.domain, result.error);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
async write(key, value, meta) {
|
|
376
|
+
this.assertOpen();
|
|
377
|
+
const start = Date.now();
|
|
378
|
+
this.validate(value);
|
|
379
|
+
this.stmts.write.run(buildWriteRow(key, value, meta));
|
|
380
|
+
recordMemoryEvent({
|
|
381
|
+
domain: this.domain,
|
|
382
|
+
op: "write",
|
|
383
|
+
...meta?.cli !== void 0 && { cli: meta.cli },
|
|
384
|
+
durationMs: Date.now() - start,
|
|
385
|
+
key,
|
|
386
|
+
payload: value
|
|
387
|
+
});
|
|
388
|
+
return Promise.resolve();
|
|
389
|
+
}
|
|
390
|
+
async query(filter) {
|
|
391
|
+
this.assertOpen();
|
|
392
|
+
const start = Date.now();
|
|
393
|
+
let rows = this.stmts.queryAll.all();
|
|
394
|
+
if (filter?.cli !== void 0) {
|
|
395
|
+
rows = rows.filter((r) => r.cli === filter.cli);
|
|
396
|
+
}
|
|
397
|
+
let values = rows.map((r) => JSON.parse(r.value));
|
|
398
|
+
values = applyQueryFilter(values, filter);
|
|
399
|
+
recordMemoryEvent({
|
|
400
|
+
domain: this.domain,
|
|
401
|
+
op: "query",
|
|
402
|
+
hit: values.length > 0,
|
|
403
|
+
...filter?.cli !== void 0 && { cli: filter.cli },
|
|
404
|
+
durationMs: Date.now() - start,
|
|
405
|
+
key: filter,
|
|
406
|
+
result: { count: values.length }
|
|
407
|
+
});
|
|
408
|
+
return Promise.resolve(values);
|
|
409
|
+
}
|
|
410
|
+
async delete(key) {
|
|
411
|
+
this.assertOpen();
|
|
412
|
+
const start = Date.now();
|
|
413
|
+
const result = this.stmts.delete.run(keyToString(key));
|
|
414
|
+
const removed = result.changes > 0;
|
|
415
|
+
recordMemoryEvent({
|
|
416
|
+
domain: this.domain,
|
|
417
|
+
op: "delete",
|
|
418
|
+
hit: removed,
|
|
419
|
+
durationMs: Date.now() - start,
|
|
420
|
+
key
|
|
421
|
+
});
|
|
422
|
+
return Promise.resolve(removed);
|
|
423
|
+
}
|
|
424
|
+
async stats() {
|
|
425
|
+
this.assertOpen();
|
|
426
|
+
const start = Date.now();
|
|
427
|
+
const countRow = this.stmts.count.get();
|
|
428
|
+
const boundsRow = this.stmts.bounds.get();
|
|
429
|
+
const result = {
|
|
430
|
+
domain: this.domain,
|
|
431
|
+
count: countRow.count,
|
|
432
|
+
oldestTimestamp: boundsRow.oldest,
|
|
433
|
+
newestTimestamp: boundsRow.newest
|
|
434
|
+
};
|
|
435
|
+
recordMemoryEvent({
|
|
436
|
+
domain: this.domain,
|
|
437
|
+
op: "stats",
|
|
438
|
+
durationMs: Date.now() - start,
|
|
439
|
+
result
|
|
440
|
+
});
|
|
441
|
+
return Promise.resolve(result);
|
|
442
|
+
}
|
|
443
|
+
async close() {
|
|
444
|
+
if (this.closed) return Promise.resolve();
|
|
445
|
+
this.closed = true;
|
|
446
|
+
if (this.ownsDb) this.db.close();
|
|
447
|
+
return Promise.resolve();
|
|
448
|
+
}
|
|
449
|
+
assertOpen() {
|
|
450
|
+
if (this.closed) {
|
|
451
|
+
throw new Error(`nexus-memory: backend "${this.domain}" is closed`);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
var MemoryRegistry = class {
|
|
456
|
+
backends = /* @__PURE__ */ new Map();
|
|
457
|
+
db;
|
|
458
|
+
closed = false;
|
|
459
|
+
constructor(options = {}) {
|
|
460
|
+
if (options.dbPath !== void 0) {
|
|
461
|
+
const db = new Database2(options.dbPath);
|
|
462
|
+
db.pragma("journal_mode = WAL");
|
|
463
|
+
this.db = db;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Register a backend for `domain`. Throws on duplicate domain.
|
|
468
|
+
*
|
|
469
|
+
* When the registry has a `dbPath`, the new backend is SQLite-backed
|
|
470
|
+
* and shares the registry's connection. Otherwise, an `InMemoryBackend`
|
|
471
|
+
* is created (used for tests).
|
|
472
|
+
*/
|
|
473
|
+
register(options) {
|
|
474
|
+
this.assertOpen();
|
|
475
|
+
if (this.backends.has(options.domain)) {
|
|
476
|
+
throw new Error(`nexus-memory: domain "${options.domain}" already registered`);
|
|
477
|
+
}
|
|
478
|
+
const backend = this.db !== void 0 ? new SqliteBackend({
|
|
479
|
+
domain: options.domain,
|
|
480
|
+
dbPath: "<shared>",
|
|
481
|
+
// unused when db is supplied
|
|
482
|
+
db: this.db,
|
|
483
|
+
...options.schema !== void 0 && { schema: options.schema }
|
|
484
|
+
}) : new InMemoryBackend({
|
|
485
|
+
domain: options.domain,
|
|
486
|
+
...options.schema !== void 0 && { schema: options.schema }
|
|
487
|
+
});
|
|
488
|
+
this.backends.set(options.domain, backend);
|
|
489
|
+
return backend;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Attach an externally-managed {@link IMemoryBackend}. Use this when the
|
|
493
|
+
* backend owns its own storage (e.g., a pre-existing SQLite file under
|
|
494
|
+
* `~/.nexus-agents/memory/agentic.db`) and you want it discoverable
|
|
495
|
+
* through the registry without changing its persistence.
|
|
496
|
+
*
|
|
497
|
+
* Phase 5–7 of the memory-unification epic use this to bring the
|
|
498
|
+
* tool-memory backends (`agentic`, `adaptive`, `typed`, `belief`),
|
|
499
|
+
* OutcomeStore, SICA, skills, etc. under a unified observability
|
|
500
|
+
* contract without rewriting their internals. Each attached backend
|
|
501
|
+
* still owns its own `.db` (or JSONL, etc.) until a follow-up migration
|
|
502
|
+
* folds the storage in fully.
|
|
503
|
+
*/
|
|
504
|
+
attach(domain, backend) {
|
|
505
|
+
this.assertOpen();
|
|
506
|
+
if (this.backends.has(domain)) {
|
|
507
|
+
throw new Error(`nexus-memory: domain "${domain}" already registered`);
|
|
508
|
+
}
|
|
509
|
+
this.backends.set(domain, backend);
|
|
510
|
+
return backend;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Get a previously-registered backend. Returns `undefined` if the
|
|
514
|
+
* domain isn't registered — callers should treat that as "not yet
|
|
515
|
+
* migrated to the unified contract."
|
|
516
|
+
*/
|
|
517
|
+
get(domain) {
|
|
518
|
+
this.assertOpen();
|
|
519
|
+
const backend = this.backends.get(domain);
|
|
520
|
+
return backend;
|
|
521
|
+
}
|
|
522
|
+
/** List all registered domains. Useful for `memory_stats`-style readers. */
|
|
523
|
+
domains() {
|
|
524
|
+
return [...this.backends.keys()];
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Close every registered backend and the shared SQLite handle. After
|
|
528
|
+
* `close()` the registry rejects all further operations.
|
|
529
|
+
*/
|
|
530
|
+
async close() {
|
|
531
|
+
if (this.closed) return;
|
|
532
|
+
this.closed = true;
|
|
533
|
+
for (const backend of this.backends.values()) {
|
|
534
|
+
await backend.close();
|
|
535
|
+
}
|
|
536
|
+
this.backends.clear();
|
|
537
|
+
if (this.db !== void 0) this.db.close();
|
|
538
|
+
}
|
|
539
|
+
assertOpen() {
|
|
540
|
+
if (this.closed) {
|
|
541
|
+
throw new Error("nexus-memory: registry is closed");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
var sharedRegistry = null;
|
|
546
|
+
function getMemoryRegistry() {
|
|
547
|
+
if (sharedRegistry === null) {
|
|
548
|
+
const dbPath = resolveDefaultDbPath();
|
|
549
|
+
sharedRegistry = new MemoryRegistry({ dbPath });
|
|
550
|
+
}
|
|
551
|
+
return sharedRegistry;
|
|
552
|
+
}
|
|
553
|
+
function setMemoryRegistry(registry) {
|
|
554
|
+
sharedRegistry = registry;
|
|
555
|
+
}
|
|
556
|
+
async function closeMemoryRegistry() {
|
|
557
|
+
if (sharedRegistry !== null) {
|
|
558
|
+
await sharedRegistry.close();
|
|
559
|
+
sharedRegistry = null;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function resolveDefaultDbPath() {
|
|
563
|
+
const root = process.env["NEXUS_DATA_DIR"] ?? `${process.env["HOME"] ?? "/tmp"}/.nexus-agents`;
|
|
564
|
+
return `${root}/memory/memory.db`;
|
|
565
|
+
}
|
|
566
|
+
function createInMemoryMemoryRegistry() {
|
|
567
|
+
return new MemoryRegistry({
|
|
568
|
+
/* no dbPath → InMemoryBackend per domain */
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function createSqliteMemoryRegistry(dbPath) {
|
|
572
|
+
return new MemoryRegistry({ dbPath });
|
|
573
|
+
}
|
|
574
|
+
var importers = /* @__PURE__ */ new Map();
|
|
575
|
+
function registerImporter(importer) {
|
|
576
|
+
if (importers.has(importer.id)) {
|
|
577
|
+
throw new Error(`nexus-memory: importer "${importer.id}" already registered`);
|
|
578
|
+
}
|
|
579
|
+
importers.set(importer.id, importer);
|
|
580
|
+
}
|
|
581
|
+
function resetImporters() {
|
|
582
|
+
importers.clear();
|
|
583
|
+
}
|
|
584
|
+
function listImporters() {
|
|
585
|
+
return [...importers.keys()];
|
|
586
|
+
}
|
|
587
|
+
async function runImporters(registry, options) {
|
|
588
|
+
mkdirSync(options.markerDir, { recursive: true });
|
|
589
|
+
const runs = [];
|
|
590
|
+
const errors = [];
|
|
591
|
+
for (const importer of importers.values()) {
|
|
592
|
+
const marker = join(options.markerDir, `.imported-${importer.id}`);
|
|
593
|
+
if (options.force !== true && existsSync(marker)) continue;
|
|
594
|
+
try {
|
|
595
|
+
const run = await importer.run(registry);
|
|
596
|
+
writeMarker(marker, run);
|
|
597
|
+
runs.push(run);
|
|
598
|
+
} catch (err) {
|
|
599
|
+
errors.push({ id: importer.id, error: err instanceof Error ? err : new Error(String(err)) });
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return { runs, errors };
|
|
603
|
+
}
|
|
604
|
+
function writeMarker(path, run) {
|
|
605
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
606
|
+
writeFileSync(
|
|
607
|
+
path,
|
|
608
|
+
JSON.stringify({ ...run, completedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2),
|
|
609
|
+
"utf-8"
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
function backupSourceFile(sourcePath) {
|
|
613
|
+
if (!existsSync(sourcePath)) return null;
|
|
614
|
+
const backup = `${sourcePath}.bak.${String(Date.now())}`;
|
|
615
|
+
renameSync(sourcePath, backup);
|
|
616
|
+
return backup;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export {
|
|
620
|
+
recordMemoryEvent,
|
|
621
|
+
getMemoryEventCounters,
|
|
622
|
+
subscribeToMemoryEvents,
|
|
623
|
+
resetMemoryTelemetry,
|
|
624
|
+
MemoryValidationError,
|
|
625
|
+
InMemoryBackend,
|
|
626
|
+
SqliteBackend,
|
|
627
|
+
MemoryRegistry,
|
|
628
|
+
getMemoryRegistry,
|
|
629
|
+
setMemoryRegistry,
|
|
630
|
+
closeMemoryRegistry,
|
|
631
|
+
createInMemoryMemoryRegistry,
|
|
632
|
+
createSqliteMemoryRegistry,
|
|
633
|
+
registerImporter,
|
|
634
|
+
resetImporters,
|
|
635
|
+
listImporters,
|
|
636
|
+
runImporters,
|
|
637
|
+
backupSourceFile
|
|
638
|
+
};
|
|
639
|
+
//# sourceMappingURL=chunk-PQHVC4BD.js.map
|