@rubytech/create-realagent 1.0.625 → 1.0.627

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.
Files changed (45) hide show
  1. package/dist/index.js +125 -3
  2. package/dist/uninstall.js +59 -2
  3. package/package.json +1 -1
  4. package/payload/platform/plugins/admin/mcp/dist/index.js +5 -8
  5. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  6. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +1 -1
  7. package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
  8. package/payload/platform/plugins/memory/PLUGIN.md +24 -0
  9. package/payload/platform/plugins/memory/mcp/dist/index.js +141 -0
  10. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  11. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts +66 -0
  12. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.d.ts.map +1 -0
  13. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js +411 -0
  14. package/payload/platform/plugins/memory/mcp/dist/lib/graph-prune.js.map +1 -0
  15. package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.d.ts +18 -0
  16. package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.d.ts.map +1 -0
  17. package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.js +80 -0
  18. package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.js.map +1 -0
  19. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts +8 -0
  20. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.d.ts.map +1 -0
  21. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js +7 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/conversation-memory-expunge.js.map +1 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts +7 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.d.ts.map +1 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js +28 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-add.js.map +1 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.d.ts +7 -0
  28. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.d.ts.map +1 -0
  29. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.js +7 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-list.js.map +1 -0
  31. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts +7 -0
  32. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.d.ts.map +1 -0
  33. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js +27 -0
  34. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-denylist-remove.js.map +1 -0
  35. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.d.ts +7 -0
  36. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.d.ts.map +1 -0
  37. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.js +10 -0
  38. package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.js.map +1 -0
  39. package/payload/platform/templates/agents/admin/IDENTITY.md +20 -4
  40. package/payload/platform/templates/agents/public/IDENTITY.md +18 -0
  41. package/payload/platform/templates/specialists/agents/content-producer.md +18 -0
  42. package/payload/platform/templates/specialists/agents/personal-assistant.md +18 -0
  43. package/payload/platform/templates/specialists/agents/project-manager.md +18 -0
  44. package/payload/platform/templates/specialists/agents/research-assistant.md +18 -0
  45. package/payload/server/server.js +20 -0
@@ -0,0 +1,411 @@
1
+ /**
2
+ * graph-prune — shared helpers for the daily graph-hygiene cron and on-demand tools.
3
+ *
4
+ * Three classes of action (per Task 549 scope-in):
5
+ * 1. Autonomous deletes — orphan nodes older than 7d (safety-ceiling gated)
6
+ * 2. Autonomous nulls — string properties containing a deny-list value (safety-ceiling gated, audit entry)
7
+ * 3. Flag-only — exact + near-duplicate detection, contradictions (no auto-merge per user decision 2026-04-19)
8
+ *
9
+ * Conversation-bound :Memory nodes get the same deny-list null treatment plus
10
+ * are the target of the conversation-memory-expunge manual tool.
11
+ *
12
+ * Per the user's "deterministic = shell script" doctrine, the cron is a
13
+ * Node script that runs Cypher queries with no LLM in the per-rule loop.
14
+ * Rule decisions are pure data: canonical keys, deny-list strings, age
15
+ * thresholds. The runPrune function is the same code path the cron and
16
+ * graph-prune-run MCP tool both invoke, so behaviour matches whether the
17
+ * trigger is a 03:00 cron or an operator chat command.
18
+ *
19
+ * Safety ceiling: any single rule that would touch more than 5 % of total
20
+ * account nodes aborts that rule and emits [graph:prune:ceiling-tripped].
21
+ * Other rules in the same run continue; the ceiling protects against a
22
+ * deny-list typo or schema drift nuking legitimate data.
23
+ */
24
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
25
+ import { resolve } from "node:path";
26
+ import { homedir } from "node:os";
27
+ const DEFAULT_CONFIG = {
28
+ enabled: true,
29
+ runHour: 3,
30
+ orphanAgeDays: 7,
31
+ safetyCeilingPercent: 5,
32
+ };
33
+ // ---------------------------------------------------------------------------
34
+ // Config + deny-list paths
35
+ //
36
+ // Both files live under ${configDir} (e.g. ~/.maxy/) so they are stable across
37
+ // upgrades and shared across accounts on the device. Per-account override via
38
+ // account-scoped subdirs is left for a future task — Phase 0 is single-account
39
+ // in practice and the deny-list is a coarse "this string is poison" instrument.
40
+ // ---------------------------------------------------------------------------
41
+ export function configPath(configDir) {
42
+ return resolve(configDir, "graph-prune.config.json");
43
+ }
44
+ export function denylistPath(configDir) {
45
+ return resolve(configDir, "graph-prune-denylist.json");
46
+ }
47
+ export function loadConfig(configDir) {
48
+ const path = configPath(configDir);
49
+ if (!existsSync(path))
50
+ return { ...DEFAULT_CONFIG };
51
+ try {
52
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
53
+ return { ...DEFAULT_CONFIG, ...raw };
54
+ }
55
+ catch (err) {
56
+ // Malformed config falls back to defaults but the failure is loud.
57
+ process.stderr.write(`[graph-prune:config] failed to parse ${path}: ${err instanceof Error ? err.message : String(err)} — using defaults\n`);
58
+ return { ...DEFAULT_CONFIG };
59
+ }
60
+ }
61
+ export function saveConfig(configDir, config) {
62
+ mkdirSync(configDir, { recursive: true });
63
+ writeFileSync(configPath(configDir), JSON.stringify(config, null, 2) + "\n", "utf-8");
64
+ }
65
+ export function loadDenylist(configDir) {
66
+ const path = denylistPath(configDir);
67
+ if (!existsSync(path)) {
68
+ return { values: [], updatedAt: new Date(0).toISOString() };
69
+ }
70
+ try {
71
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
72
+ return {
73
+ values: Array.isArray(raw.values) ? raw.values.filter((v) => typeof v === "string") : [],
74
+ updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : new Date(0).toISOString(),
75
+ };
76
+ }
77
+ catch (err) {
78
+ process.stderr.write(`[graph-prune:denylist] failed to parse ${path}: ${err instanceof Error ? err.message : String(err)} — treating as empty\n`);
79
+ return { values: [], updatedAt: new Date(0).toISOString() };
80
+ }
81
+ }
82
+ export function saveDenylist(configDir, list) {
83
+ mkdirSync(configDir, { recursive: true });
84
+ writeFileSync(denylistPath(configDir), JSON.stringify(list, null, 2) + "\n", "utf-8");
85
+ }
86
+ export function resolveConfigDir() {
87
+ const platformRoot = process.env.PLATFORM_ROOT;
88
+ if (!platformRoot) {
89
+ throw new Error("PLATFORM_ROOT environment variable is required");
90
+ }
91
+ const brandPath = resolve(platformRoot, "config", "brand.json");
92
+ if (!existsSync(brandPath)) {
93
+ throw new Error(`brand.json not found at ${brandPath} — platform not properly installed`);
94
+ }
95
+ const brand = JSON.parse(readFileSync(brandPath, "utf-8"));
96
+ if (!brand.configDir) {
97
+ throw new Error(`brand.json at ${brandPath} is missing the configDir field`);
98
+ }
99
+ return resolve(homedir(), brand.configDir);
100
+ }
101
+ const CANONICAL_KEYS = [
102
+ { label: "Person", keyTuples: [["email"], ["phone"]] },
103
+ { label: "LocalBusiness", keyTuples: [["name"]] },
104
+ { label: "Tunnel", keyTuples: [["tunnelId"]] },
105
+ { label: "Account", keyTuples: [["accountId"]] },
106
+ { label: "Conversation", keyTuples: [["conversationId"]] },
107
+ ];
108
+ // ---------------------------------------------------------------------------
109
+ // Helpers
110
+ // ---------------------------------------------------------------------------
111
+ function logLine(line) {
112
+ process.stderr.write(line.endsWith("\n") ? line : line + "\n");
113
+ }
114
+ function tripsCeiling(wouldAffect, totalNodes, ceilingPercent) {
115
+ if (totalNodes === 0)
116
+ return false;
117
+ return (wouldAffect / totalNodes) * 100 > ceilingPercent;
118
+ }
119
+ export async function runPrune(driver, accountId, config, denylist, opts = {}) {
120
+ const dryRun = opts.dryRun ?? false;
121
+ const t0 = Date.now();
122
+ const summary = {
123
+ accountId,
124
+ totalNodes: 0,
125
+ orphansDeleted: 0,
126
+ denylistNulled: 0,
127
+ conversationMemoryNulled: 0,
128
+ candidateMergesFlagged: 0,
129
+ contradictionsFlagged: 0,
130
+ ceilingTripped: [],
131
+ elapsedMs: 0,
132
+ dryRun,
133
+ };
134
+ const session = driver.session();
135
+ try {
136
+ // Total node count for safety ceiling.
137
+ const totalRes = await session.run("MATCH (n) WHERE n.accountId = $accountId RETURN count(n) AS c", { accountId });
138
+ summary.totalNodes = Number(totalRes.records[0]?.get("c") ?? 0);
139
+ logLine(`[graph:prune:start] account=${accountId} totalNodes=${summary.totalNodes} dryRun=${dryRun} denylistSize=${denylist.values.length}`);
140
+ if (summary.totalNodes === 0) {
141
+ logLine(`[graph:prune:summary] account=${accountId} deleted=0 nulled=0 convNulled=0 flagged=0 contradictions=0 elapsed=${Date.now() - t0}ms (empty graph)`);
142
+ summary.elapsedMs = Date.now() - t0;
143
+ return summary;
144
+ }
145
+ // -----------------------------------------------------------------------
146
+ // Rule 1: orphan delete (no relations, age > config.orphanAgeDays)
147
+ // Excludes UserProfile, Account, LocalBusiness — those legitimately exist
148
+ // without relations on a fresh install.
149
+ // -----------------------------------------------------------------------
150
+ const orphanCutoff = new Date(Date.now() - config.orphanAgeDays * 86_400_000).toISOString();
151
+ const orphanCountRes = await session.run(`MATCH (n)
152
+ WHERE n.accountId = $accountId
153
+ AND NOT (n)--()
154
+ AND coalesce(n.createdAt, n.updatedAt) < $cutoff
155
+ AND NOT (n:UserProfile OR n:Account OR n:LocalBusiness)
156
+ RETURN count(n) AS c`, { accountId, cutoff: orphanCutoff });
157
+ const orphanCount = Number(orphanCountRes.records[0]?.get("c") ?? 0);
158
+ if (orphanCount > 0) {
159
+ if (tripsCeiling(orphanCount, summary.totalNodes, config.safetyCeilingPercent)) {
160
+ logLine(`[graph:prune:ceiling-tripped] rule=orphan-delete wouldAffect=${orphanCount} totalNodes=${summary.totalNodes} ceiling=${config.safetyCeilingPercent}%`);
161
+ summary.ceilingTripped.push("orphan-delete");
162
+ }
163
+ else {
164
+ logLine(`[graph:prune:rule-start] rule=orphan-delete count=${orphanCount}`);
165
+ if (!dryRun) {
166
+ // Audit each delete before the bulk DETACH DELETE — without per-node
167
+ // ids in the log, recovery from a misconfiguration is impossible.
168
+ const idsRes = await session.run(`MATCH (n)
169
+ WHERE n.accountId = $accountId
170
+ AND NOT (n)--()
171
+ AND coalesce(n.createdAt, n.updatedAt) < $cutoff
172
+ AND NOT (n:UserProfile OR n:Account OR n:LocalBusiness)
173
+ RETURN id(n) AS nid, labels(n) AS labels LIMIT 1000`, { accountId, cutoff: orphanCutoff });
174
+ for (const r of idsRes.records) {
175
+ logLine(`[graph:prune:action] rule=orphan-delete op=delete nodeId=${r.get("nid")} labels=${r.get("labels").join(",")}`);
176
+ }
177
+ await session.run(`MATCH (n)
178
+ WHERE n.accountId = $accountId
179
+ AND NOT (n)--()
180
+ AND coalesce(n.createdAt, n.updatedAt) < $cutoff
181
+ AND NOT (n:UserProfile OR n:Account OR n:LocalBusiness)
182
+ DELETE n`, { accountId, cutoff: orphanCutoff });
183
+ }
184
+ summary.orphansDeleted = orphanCount;
185
+ }
186
+ }
187
+ // -----------------------------------------------------------------------
188
+ // Rule 2: deny-list null
189
+ //
190
+ // Scan every node for string properties whose value contains any deny-list
191
+ // entry (substring match). Null those properties, append an audit entry to
192
+ // a per-node `_denylistAudit` JSON-encoded property. Excludes the audit
193
+ // property itself from the scan to avoid runaway re-matching on stored
194
+ // history.
195
+ // -----------------------------------------------------------------------
196
+ if (denylist.values.length > 0) {
197
+ const matchingRes = await session.run(`MATCH (n)
198
+ WHERE n.accountId = $accountId
199
+ WITH n, [k IN keys(n) WHERE k <> '_denylistAudit'
200
+ AND k <> 'embedding'
201
+ AND apoc.meta.cypher.isType(n[k], 'STRING')
202
+ AND any(deny IN $denylist WHERE n[k] CONTAINS deny)] AS hits
203
+ WHERE size(hits) > 0
204
+ RETURN id(n) AS nid, labels(n) AS labels, hits LIMIT 5000`, { accountId, denylist: denylist.values }).catch(async (apocErr) => {
205
+ // APOC may be unavailable on minimal Neo4j installs; fall back to a
206
+ // pure-Cypher type check that is slower but functional.
207
+ process.stderr.write(`[graph:prune:denylist] APOC unavailable (${apocErr instanceof Error ? apocErr.message : String(apocErr)}); using pure-Cypher fallback\n`);
208
+ return session.run(`MATCH (n)
209
+ WHERE n.accountId = $accountId
210
+ WITH n, [k IN keys(n) WHERE k <> '_denylistAudit'
211
+ AND k <> 'embedding'
212
+ AND any(deny IN $denylist WHERE
213
+ CASE
214
+ WHEN n[k] IS NULL THEN false
215
+ WHEN toString(n[k]) = n[k] THEN n[k] CONTAINS deny
216
+ ELSE false
217
+ END)] AS hits
218
+ WHERE size(hits) > 0
219
+ RETURN id(n) AS nid, labels(n) AS labels, hits LIMIT 5000`, { accountId, denylist: denylist.values });
220
+ });
221
+ const matchCount = matchingRes.records.length;
222
+ if (matchCount > 0) {
223
+ if (tripsCeiling(matchCount, summary.totalNodes, config.safetyCeilingPercent)) {
224
+ logLine(`[graph:prune:ceiling-tripped] rule=denylist-null wouldAffect=${matchCount} totalNodes=${summary.totalNodes} ceiling=${config.safetyCeilingPercent}%`);
225
+ summary.ceilingTripped.push("denylist-null");
226
+ }
227
+ else {
228
+ logLine(`[graph:prune:rule-start] rule=denylist-null nodes=${matchCount}`);
229
+ for (const r of matchingRes.records) {
230
+ const nid = Number(r.get("nid"));
231
+ const labels = r.get("labels").join(",");
232
+ const hits = r.get("hits");
233
+ for (const propKey of hits) {
234
+ logLine(`[graph:prune:action] rule=denylist-null op=null nodeId=${nid} labels=${labels} property=${propKey}`);
235
+ }
236
+ if (!dryRun) {
237
+ const auditEntry = JSON.stringify({
238
+ ts: new Date().toISOString(),
239
+ properties: hits,
240
+ denylistMatches: denylist.values,
241
+ });
242
+ const setClauses = hits.map((k) => `n.\`${k.replace(/`/g, "")}\` = null`).join(", ");
243
+ await session.run(`MATCH (n) WHERE id(n) = $nid
244
+ SET ${setClauses},
245
+ n._denylistAudit = coalesce(n._denylistAudit, '') + $entry + '\n'`, { nid, entry: auditEntry });
246
+ summary.denylistNulled += hits.length;
247
+ }
248
+ else {
249
+ summary.denylistNulled += hits.length;
250
+ }
251
+ }
252
+ }
253
+ }
254
+ }
255
+ // -----------------------------------------------------------------------
256
+ // Rule 3: conversation-memory deny-list null
257
+ //
258
+ // Pollution surface: :Memory nodes tied to :Conversation. Same scan as
259
+ // rule 2 but scoped to that label so the cost stays bounded even when
260
+ // the deny-list grows.
261
+ // -----------------------------------------------------------------------
262
+ if (denylist.values.length > 0) {
263
+ const memRes = await session.run(`MATCH (m:Memory)-[:PART_OF|:ABOUT]->(c:Conversation)
264
+ WHERE m.accountId = $accountId
265
+ WITH m, [k IN keys(m) WHERE k <> '_denylistAudit'
266
+ AND k <> 'embedding'
267
+ AND any(deny IN $denylist WHERE
268
+ CASE
269
+ WHEN m[k] IS NULL THEN false
270
+ WHEN toString(m[k]) = m[k] THEN m[k] CONTAINS deny
271
+ ELSE false
272
+ END)] AS hits
273
+ WHERE size(hits) > 0
274
+ RETURN id(m) AS nid, hits LIMIT 5000`, { accountId, denylist: denylist.values });
275
+ const memCount = memRes.records.length;
276
+ if (memCount > 0) {
277
+ if (tripsCeiling(memCount, summary.totalNodes, config.safetyCeilingPercent)) {
278
+ logLine(`[graph:prune:ceiling-tripped] rule=conversation-memory-null wouldAffect=${memCount} totalNodes=${summary.totalNodes} ceiling=${config.safetyCeilingPercent}%`);
279
+ summary.ceilingTripped.push("conversation-memory-null");
280
+ }
281
+ else {
282
+ logLine(`[graph:prune:rule-start] rule=conversation-memory-null nodes=${memCount}`);
283
+ for (const r of memRes.records) {
284
+ const nid = Number(r.get("nid"));
285
+ const hits = r.get("hits");
286
+ for (const k of hits) {
287
+ logLine(`[graph:prune:action] rule=conversation-memory-null op=null nodeId=${nid} property=${k}`);
288
+ }
289
+ if (!dryRun) {
290
+ const setClauses = hits.map((k) => `m.\`${k.replace(/`/g, "")}\` = null`).join(", ");
291
+ const auditEntry = JSON.stringify({
292
+ ts: new Date().toISOString(),
293
+ properties: hits,
294
+ denylistMatches: denylist.values,
295
+ });
296
+ await session.run(`MATCH (m) WHERE id(m) = $nid
297
+ SET ${setClauses},
298
+ m._denylistAudit = coalesce(m._denylistAudit, '') + $entry + '\n'`, { nid, entry: auditEntry });
299
+ }
300
+ summary.conversationMemoryNulled += hits.length;
301
+ }
302
+ }
303
+ }
304
+ }
305
+ // -----------------------------------------------------------------------
306
+ // Rule 4: candidate-merge flagging (per the 2026-04-19 user decision —
307
+ // never auto-merge; admin agent reviews next turn).
308
+ //
309
+ // For each canonical-key spec, find pairs of nodes sharing the key value.
310
+ // Emit a flag line per pair; stored as :PruneFlag nodes so the
311
+ // review-detector pattern (graph:prune:candidate-merge) can surface them.
312
+ // -----------------------------------------------------------------------
313
+ for (const spec of CANONICAL_KEYS) {
314
+ for (const tuple of spec.keyTuples) {
315
+ const matchProps = tuple.map((k) => `a.\`${k}\` IS NOT NULL AND a.\`${k}\` = b.\`${k}\``).join(" AND ");
316
+ const returnProps = tuple.map((k) => `a.\`${k}\` AS \`${k}\``).join(", ");
317
+ const dupRes = await session.run(`MATCH (a:${spec.label}), (b:${spec.label})
318
+ WHERE a.accountId = $accountId AND b.accountId = $accountId
319
+ AND id(a) < id(b)
320
+ AND ${matchProps}
321
+ RETURN id(a) AS aid, id(b) AS bid, ${returnProps} LIMIT 500`, { accountId });
322
+ for (const r of dupRes.records) {
323
+ const aid = Number(r.get("aid"));
324
+ const bid = Number(r.get("bid"));
325
+ const keyValues = tuple.map((k) => `${k}=${r.get(k)}`).join(",");
326
+ logLine(`[graph:prune:candidate-merge] label=${spec.label} keys=${keyValues} sourceId=${aid} targetId=${bid}`);
327
+ summary.candidateMergesFlagged += 1;
328
+ if (!dryRun) {
329
+ await session.run(`MERGE (f:PruneFlag {accountId: $accountId, kind: 'candidate-merge', sourceId: $aid, targetId: $bid})
330
+ ON CREATE SET f.label = $label, f.keys = $keys, f.createdAt = datetime()`, { accountId, aid, bid, label: spec.label, keys: keyValues });
331
+ }
332
+ }
333
+ }
334
+ }
335
+ // -----------------------------------------------------------------------
336
+ // Rule 5: contradiction detection
337
+ //
338
+ // Same node, same atomic property, multiple recent writes with diverging
339
+ // values. Detected via the audit trail Task 343 lays down on ToolCall
340
+ // records — but those are append-only, so the simpler signal here is
341
+ // Preference nodes whose status indicates contradiction (set by
342
+ // profile-update mode=contradict).
343
+ // -----------------------------------------------------------------------
344
+ const contraRes = await session.run(`MATCH (p:Preference)
345
+ WHERE p.accountId = $accountId AND p.status = 'contradicted'
346
+ AND p.flaggedByPrune IS NULL
347
+ RETURN id(p) AS pid, p.category AS category, p.key AS key LIMIT 200`, { accountId });
348
+ for (const r of contraRes.records) {
349
+ const pid = Number(r.get("pid"));
350
+ logLine(`[graph:prune:contradiction] nodeId=${pid} category=${r.get("category")} key=${r.get("key")}`);
351
+ summary.contradictionsFlagged += 1;
352
+ if (!dryRun) {
353
+ await session.run(`MATCH (p) WHERE id(p) = $pid SET p.flaggedByPrune = datetime()`, { pid });
354
+ }
355
+ }
356
+ summary.elapsedMs = Date.now() - t0;
357
+ logLine(`[graph:prune:summary] account=${accountId} deleted=${summary.orphansDeleted} nulled=${summary.denylistNulled} convNulled=${summary.conversationMemoryNulled} flagged=${summary.candidateMergesFlagged} contradictions=${summary.contradictionsFlagged} ceilingTripped=${summary.ceilingTripped.join(",") || "none"} elapsed=${summary.elapsedMs}ms dryRun=${dryRun}`);
358
+ return summary;
359
+ }
360
+ finally {
361
+ await session.close();
362
+ }
363
+ }
364
+ export async function expungeConversationMemory(driver, accountId, conversationId, pattern) {
365
+ const session = driver.session();
366
+ try {
367
+ const matchRes = await session.run(`MATCH (m:Memory)-[:PART_OF|:ABOUT]->(c:Conversation {conversationId: $conversationId})
368
+ WHERE m.accountId = $accountId
369
+ WITH m, [k IN keys(m) WHERE k <> '_denylistAudit'
370
+ AND k <> 'embedding'
371
+ AND CASE
372
+ WHEN m[k] IS NULL THEN false
373
+ WHEN toString(m[k]) = m[k] THEN m[k] CONTAINS $pattern
374
+ ELSE false
375
+ END] AS hits
376
+ RETURN id(m) AS nid, hits, size(collect(m)) AS scanned`, { accountId, conversationId, pattern });
377
+ let propertiesNulled = 0;
378
+ const affectedNodeIds = [];
379
+ let nodesScanned = 0;
380
+ for (const r of matchRes.records) {
381
+ nodesScanned += 1;
382
+ const nid = Number(r.get("nid"));
383
+ const hits = r.get("hits");
384
+ if (hits.length === 0)
385
+ continue;
386
+ affectedNodeIds.push(nid);
387
+ const setClauses = hits.map((k) => `m.\`${k.replace(/`/g, "")}\` = null`).join(", ");
388
+ const auditEntry = JSON.stringify({
389
+ ts: new Date().toISOString(),
390
+ properties: hits,
391
+ manualPattern: pattern,
392
+ conversationId,
393
+ });
394
+ await session.run(`MATCH (m) WHERE id(m) = $nid
395
+ SET ${setClauses},
396
+ m._denylistAudit = coalesce(m._denylistAudit, '') + $entry + '\n'`, { nid, entry: auditEntry });
397
+ propertiesNulled += hits.length;
398
+ }
399
+ return {
400
+ conversationId,
401
+ pattern,
402
+ nodesScanned,
403
+ propertiesNulled,
404
+ affectedNodeIds,
405
+ };
406
+ }
407
+ finally {
408
+ await session.close();
409
+ }
410
+ }
411
+ //# sourceMappingURL=graph-prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune.js","sourceRoot":"","sources":["../../src/lib/graph-prune.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Cron script: graph-prune
3
+ *
4
+ * Daily Neo4j hygiene runner. Triggered by crontab at the configured local
5
+ * hour (default 03:00). Reads ${configDir}/graph-prune.config.json for the
6
+ * disable switch; reads ${configDir}/graph-prune-denylist.json for the
7
+ * operator-curated bad-string list. All actual rule logic lives in
8
+ * lib/graph-prune.ts so the on-demand graph-prune-run MCP tool exercises the
9
+ * same code path.
10
+ *
11
+ * Exit codes are advisory only — cron does not surface them. The script logs
12
+ * its outcome via [graph:prune:*] lines on stderr; logs-read ${configDir}/logs/graph-prune.log
13
+ * is the canonical reader.
14
+ *
15
+ * PLATFORM_ROOT=... ACCOUNT_ID=... node dist/scripts/graph-prune.js
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=graph-prune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune.d.ts","sourceRoot":"","sources":["../../src/scripts/graph-prune.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Cron script: graph-prune
3
+ *
4
+ * Daily Neo4j hygiene runner. Triggered by crontab at the configured local
5
+ * hour (default 03:00). Reads ${configDir}/graph-prune.config.json for the
6
+ * disable switch; reads ${configDir}/graph-prune-denylist.json for the
7
+ * operator-curated bad-string list. All actual rule logic lives in
8
+ * lib/graph-prune.ts so the on-demand graph-prune-run MCP tool exercises the
9
+ * same code path.
10
+ *
11
+ * Exit codes are advisory only — cron does not surface them. The script logs
12
+ * its outcome via [graph:prune:*] lines on stderr; logs-read ${configDir}/logs/graph-prune.log
13
+ * is the canonical reader.
14
+ *
15
+ * PLATFORM_ROOT=... ACCOUNT_ID=... node dist/scripts/graph-prune.js
16
+ */
17
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
18
+ import { resolve } from "node:path";
19
+ import { closeDriver, getDriver } from "../lib/neo4j.js";
20
+ import { loadConfig, loadDenylist, resolveConfigDir, runPrune, } from "../lib/graph-prune.js";
21
+ const CONFIG_DIR = resolveConfigDir();
22
+ const LOCK_FILE = resolve(CONFIG_DIR, "graph-prune.lock");
23
+ function acquireLock() {
24
+ mkdirSync(CONFIG_DIR, { recursive: true });
25
+ if (existsSync(LOCK_FILE)) {
26
+ const pid = readFileSync(LOCK_FILE, "utf-8").trim();
27
+ try {
28
+ process.kill(Number(pid), 0);
29
+ return false; // a previous run is still alive
30
+ }
31
+ catch {
32
+ unlinkSync(LOCK_FILE); // stale lock from a crashed run
33
+ }
34
+ }
35
+ writeFileSync(LOCK_FILE, String(process.pid));
36
+ return true;
37
+ }
38
+ function releaseLock() {
39
+ try {
40
+ if (existsSync(LOCK_FILE)) {
41
+ const pid = readFileSync(LOCK_FILE, "utf-8").trim();
42
+ if (pid === String(process.pid))
43
+ unlinkSync(LOCK_FILE);
44
+ }
45
+ }
46
+ catch {
47
+ /* best-effort */
48
+ }
49
+ }
50
+ async function main() {
51
+ const accountId = process.env.ACCOUNT_ID;
52
+ if (!accountId) {
53
+ process.stderr.write("[graph:prune:error] ACCOUNT_ID environment variable is required\n");
54
+ process.exit(1);
55
+ }
56
+ if (!acquireLock()) {
57
+ process.stderr.write("[graph:prune:skip] another run holds the lock — exiting\n");
58
+ process.exit(0);
59
+ }
60
+ try {
61
+ const config = loadConfig(CONFIG_DIR);
62
+ if (!config.enabled) {
63
+ process.stderr.write(`[graph:prune:disabled] account=${accountId} — graph-prune.config.json has enabled=false\n`);
64
+ return;
65
+ }
66
+ const denylist = loadDenylist(CONFIG_DIR);
67
+ const driver = getDriver();
68
+ await runPrune(driver, accountId, config, denylist, { dryRun: false });
69
+ }
70
+ catch (err) {
71
+ process.stderr.write(`[graph:prune:error] ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
72
+ process.exit(1);
73
+ }
74
+ finally {
75
+ releaseLock();
76
+ await closeDriver();
77
+ }
78
+ }
79
+ main();
80
+ //# sourceMappingURL=graph-prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune.js","sourceRoot":"","sources":["../../src/scripts/graph-prune.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { type ExpungeResult } from "../lib/graph-prune.js";
2
+ export interface ConversationMemoryExpungeParams {
3
+ accountId: string;
4
+ conversationId: string;
5
+ pattern: string;
6
+ }
7
+ export declare function conversationMemoryExpunge(params: ConversationMemoryExpungeParams): Promise<ExpungeResult>;
8
+ //# sourceMappingURL=conversation-memory-expunge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-memory-expunge.d.ts","sourceRoot":"","sources":["../../src/tools/conversation-memory-expunge.ts"],"names":[],"mappings":"AACA,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtF,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,+BAA+B,GACtC,OAAO,CAAC,aAAa,CAAC,CAGxB"}
@@ -0,0 +1,7 @@
1
+ import { getDriver } from "../lib/neo4j.js";
2
+ import { expungeConversationMemory } from "../lib/graph-prune.js";
3
+ export async function conversationMemoryExpunge(params) {
4
+ const driver = getDriver();
5
+ return expungeConversationMemory(driver, params.accountId, params.conversationId, params.pattern);
6
+ }
7
+ //# sourceMappingURL=conversation-memory-expunge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-memory-expunge.js","sourceRoot":"","sources":["../../src/tools/conversation-memory-expunge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAsB,MAAM,uBAAuB,CAAC;AAQtF,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAuC;IAEvC,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,OAAO,yBAAyB,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;AACpG,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface DenylistAddResult {
2
+ added: string[];
3
+ skipped: string[];
4
+ totalAfter: number;
5
+ }
6
+ export declare function graphPruneDenylistAdd(values: string[]): DenylistAddResult;
7
+ //# sourceMappingURL=graph-prune-denylist-add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune-denylist-add.d.ts","sourceRoot":"","sources":["../../src/tools/graph-prune-denylist-add.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,iBAAiB,CA0BzE"}
@@ -0,0 +1,28 @@
1
+ import { loadDenylist, resolveConfigDir, saveDenylist } from "../lib/graph-prune.js";
2
+ export function graphPruneDenylistAdd(values) {
3
+ const configDir = resolveConfigDir();
4
+ const list = loadDenylist(configDir);
5
+ const before = new Set(list.values);
6
+ const added = [];
7
+ const skipped = [];
8
+ for (const raw of values) {
9
+ const v = raw.trim();
10
+ if (!v)
11
+ continue;
12
+ if (before.has(v)) {
13
+ skipped.push(v);
14
+ }
15
+ else {
16
+ before.add(v);
17
+ added.push(v);
18
+ }
19
+ }
20
+ if (added.length > 0) {
21
+ saveDenylist(configDir, {
22
+ values: [...before].sort(),
23
+ updatedAt: new Date().toISOString(),
24
+ });
25
+ }
26
+ return { added, skipped, totalAfter: before.size };
27
+ }
28
+ //# sourceMappingURL=graph-prune-denylist-add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune-denylist-add.js","sourceRoot":"","sources":["../../src/tools/graph-prune-denylist-add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQrF,MAAM,UAAU,qBAAqB,CAAC,MAAgB;IACpD,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,YAAY,CAAC,SAAS,EAAE;YACtB,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE;YAC1B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface DenylistListResult {
2
+ values: string[];
3
+ updatedAt: string;
4
+ count: number;
5
+ }
6
+ export declare function graphPruneDenylistList(): DenylistListResult;
7
+ //# sourceMappingURL=graph-prune-denylist-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune-denylist-list.d.ts","sourceRoot":"","sources":["../../src/tools/graph-prune-denylist-list.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,sBAAsB,IAAI,kBAAkB,CAI3D"}
@@ -0,0 +1,7 @@
1
+ import { loadDenylist, resolveConfigDir } from "../lib/graph-prune.js";
2
+ export function graphPruneDenylistList() {
3
+ const configDir = resolveConfigDir();
4
+ const list = loadDenylist(configDir);
5
+ return { values: list.values, updatedAt: list.updatedAt, count: list.values.length };
6
+ }
7
+ //# sourceMappingURL=graph-prune-denylist-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-prune-denylist-list.js","sourceRoot":"","sources":["../../src/tools/graph-prune-denylist-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAQvE,MAAM,UAAU,sBAAsB;IACpC,MAAM,SAAS,GAAG,gBAAgB,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;AACvF,CAAC"}