@rubytech/create-realagent 1.0.829 → 1.0.831

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 (121) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/config/brand.json +1 -1
  3. package/payload/platform/lib/oauth-llm/dist/index.d.ts +9 -2
  4. package/payload/platform/lib/oauth-llm/dist/index.d.ts.map +1 -1
  5. package/payload/platform/lib/oauth-llm/dist/index.js +26 -1
  6. package/payload/platform/lib/oauth-llm/dist/index.js.map +1 -1
  7. package/payload/platform/lib/oauth-llm/src/index.ts +43 -4
  8. package/payload/platform/neo4j/migrations/007-conversation-archive-source.ts +116 -0
  9. package/payload/platform/neo4j/migrations/008-adminuser-accountid-backfill.ts +85 -0
  10. package/payload/platform/neo4j/schema.cypher +12 -3
  11. package/payload/platform/plugins/admin/hooks/__tests__/archive-ingest-surface-gate.test.sh +54 -39
  12. package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +64 -26
  13. package/payload/platform/plugins/admin/mcp/dist/index.js +25 -3
  14. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/contacts/mcp/dist/index.js +5 -5
  16. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  17. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +1 -1
  18. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  19. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +29 -23
  20. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  21. package/payload/platform/plugins/docs/references/internals.md +1 -1
  22. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  23. package/payload/platform/plugins/memory/PLUGIN.md +2 -1
  24. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.mjs +564 -0
  25. package/payload/platform/plugins/memory/bin/conversation-archive-ingest.sh +106 -0
  26. package/payload/platform/plugins/memory/mcp/dist/index.js +30 -16
  27. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js +4 -3
  29. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/llm-classifier.test.js.map +1 -1
  30. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +11 -6
  31. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts +5 -0
  33. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.d.ts.map +1 -0
  34. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js +30 -0
  35. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/index.js.map +1 -0
  36. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts +49 -0
  37. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.d.ts.map +1 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js +35 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/timestamp-scanner.js.map +1 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts +47 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.d.ts.map +1 -0
  42. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js +31 -0
  43. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/types.js.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts +3 -0
  45. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.d.ts.map +1 -0
  46. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js +155 -0
  47. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-normalisers/whatsapp-text.js.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts +11 -0
  49. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.d.ts.map +1 -0
  50. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js +20 -0
  51. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/delta-cursor.js.map +1 -0
  52. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts +14 -0
  53. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.d.ts.map +1 -0
  54. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js +38 -0
  55. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/derive-keys.js.map +1 -0
  56. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts +16 -0
  57. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.d.ts.map +1 -0
  58. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js +59 -0
  59. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sender-bind.js.map +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts +9 -0
  61. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.d.ts.map +1 -0
  62. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js +32 -0
  63. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/sessionize.js.map +1 -0
  64. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts +3 -0
  65. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.d.ts.map +1 -0
  66. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js +29 -0
  67. package/payload/platform/plugins/memory/mcp/dist/lib/conversation-pipeline/to-turn-text.js.map +1 -0
  68. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts +45 -0
  69. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.d.ts.map +1 -0
  70. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js +125 -0
  71. package/payload/platform/plugins/memory/mcp/dist/lib/document-chunker.js.map +1 -0
  72. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +34 -9
  73. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -1
  74. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +360 -35
  75. package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -1
  76. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts +3 -2
  77. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.d.ts.map +1 -1
  78. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js +46 -17
  79. package/payload/platform/plugins/memory/mcp/dist/lib/llm-ranker.js.map +1 -1
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts +2 -0
  81. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.d.ts.map +1 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js +73 -0
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-source-agnosticism.test.js.map +1 -0
  84. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts +2 -0
  85. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.d.ts.map +1 -0
  86. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js +109 -0
  87. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-normalisers-whatsapp-text.test.js.map +1 -0
  88. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js +34 -3
  89. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest.test.js.map +1 -1
  90. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts +17 -0
  91. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.d.ts.map +1 -1
  92. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js +34 -13
  93. package/payload/platform/plugins/memory/mcp/dist/tools/memory-archive-write.js.map +1 -1
  94. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +18 -7
  95. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  96. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +24 -8
  97. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  98. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js +2 -2
  99. package/payload/platform/plugins/memory/mcp/dist/tools/memory-rank.js.map +1 -1
  100. package/payload/platform/plugins/memory/references/schema-base.md +2 -2
  101. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +133 -0
  102. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +5 -2
  103. package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
  104. package/payload/platform/scripts/seed-neo4j.sh +15 -15
  105. package/payload/platform/templates/specialists/agents/database-operator.md +8 -9
  106. package/payload/server/chunk-7BO5HDJC.js +10093 -0
  107. package/payload/server/chunk-BCFM2UPH.js +2305 -0
  108. package/payload/server/chunk-CV3HPX46.js +10097 -0
  109. package/payload/server/chunk-EL4DZ56X.js +1116 -0
  110. package/payload/server/chunk-J6YWEJBN.js +1116 -0
  111. package/payload/server/chunk-OCPJGZ6S.js +654 -0
  112. package/payload/server/chunk-QOJ2D26Z.js +654 -0
  113. package/payload/server/chunk-RC46ZYGT.js +2305 -0
  114. package/payload/server/client-pool-7NTEFNVQ.js +32 -0
  115. package/payload/server/client-pool-ZNGN66GN.js +32 -0
  116. package/payload/server/cloudflare-task-tracker-MHALDN54.js +19 -0
  117. package/payload/server/cloudflare-task-tracker-WE77WXSI.js +19 -0
  118. package/payload/server/maxy-edge.js +3 -3
  119. package/payload/server/neo4j-migrations-4XPNJNM6.js +490 -0
  120. package/payload/server/neo4j-migrations-6RW423E2.js +530 -0
  121. package/payload/server/server.js +30 -19
@@ -0,0 +1,530 @@
1
+ import {
2
+ ACCOUNTS_DIR
3
+ } from "./chunk-5OG7TUQL.js";
4
+ import {
5
+ getSession,
6
+ projectAgent
7
+ } from "./chunk-BCFM2UPH.js";
8
+
9
+ // app/lib/neo4j-migrations.ts
10
+ import { readFileSync as readFileSync2 } from "fs";
11
+ import { resolve as resolve4 } from "path";
12
+
13
+ // ../neo4j/migrations/004-prune-alien-accounts.ts
14
+ import { readFileSync, readdirSync } from "fs";
15
+ import { resolve } from "path";
16
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17
+ async function pruneAlienAccounts(driver, platformRoot) {
18
+ const accountsDir = resolve(platformRoot, "..", "data", "accounts");
19
+ const validIds = enumerateValidAccountIds(accountsDir);
20
+ if (validIds.size === 0) {
21
+ throw new Error(
22
+ `refusing to prune: no valid accounts found under ${accountsDir} \u2014 corrupt install? not deleting anything to avoid wiping the entire graph.`
23
+ );
24
+ }
25
+ const valid = Array.from(validIds);
26
+ const session = driver.session();
27
+ try {
28
+ const peek = await session.run(
29
+ `MATCH (n)
30
+ WHERE n.accountId IS NOT NULL AND NOT n.accountId IN $valid
31
+ RETURN DISTINCT n.accountId AS aid`,
32
+ { valid }
33
+ );
34
+ const alienIds = [];
35
+ for (const record of peek.records) {
36
+ const aid = record.get("aid");
37
+ if (typeof aid === "string") alienIds.push(aid);
38
+ }
39
+ if (alienIds.length === 0) return;
40
+ const result = await session.run(
41
+ `MATCH (n)
42
+ WHERE n.accountId IS NOT NULL AND NOT n.accountId IN $valid
43
+ DETACH DELETE n
44
+ RETURN count(n) AS pruned`,
45
+ { valid }
46
+ );
47
+ const prunedRaw = result.records[0]?.get("pruned");
48
+ const pruned = typeof prunedRaw === "number" ? prunedRaw : prunedRaw?.toNumber?.() ?? 0;
49
+ console.error(
50
+ `[graph-invariant] alien-accounts pruned=${pruned} accountIds=${alienIds.join(",")}`
51
+ );
52
+ } finally {
53
+ await session.close();
54
+ }
55
+ }
56
+ function enumerateValidAccountIds(accountsDir) {
57
+ const valid = /* @__PURE__ */ new Set();
58
+ let names;
59
+ try {
60
+ names = readdirSync(accountsDir);
61
+ } catch (err) {
62
+ if (err.code === "ENOENT") return valid;
63
+ throw err;
64
+ }
65
+ for (const name of names) {
66
+ if (!UUID_RE.test(name)) continue;
67
+ const configPath = resolve(accountsDir, name, "account.json");
68
+ try {
69
+ JSON.parse(readFileSync(configPath, "utf-8"));
70
+ valid.add(name);
71
+ } catch (err) {
72
+ const code = err.code ?? "parse-error";
73
+ if (code === "ENOENT") continue;
74
+ console.error(
75
+ `[graph-invariant] account-json-skip uuid=${name} reason=${code}`
76
+ );
77
+ }
78
+ }
79
+ return valid;
80
+ }
81
+
82
+ // ../neo4j/migrations/004-project-admin-agent.ts
83
+ import { existsSync, readdirSync as readdirSync2 } from "fs";
84
+ import { resolve as resolve2 } from "path";
85
+ async function projectAccountAdmin(accountId, accountDir) {
86
+ const adminDir = resolve2(accountDir, "agents", "admin");
87
+ const configPath = resolve2(adminDir, "config.json");
88
+ if (!existsSync(adminDir) || !existsSync(configPath)) {
89
+ return { projected: 0, failed: 0 };
90
+ }
91
+ try {
92
+ await projectAgent(accountId, accountDir, "admin");
93
+ return { projected: 1, failed: 0 };
94
+ } catch (err) {
95
+ const msg = err instanceof Error ? err.message : String(err);
96
+ console.error(
97
+ `[admin-agent-graph-backfill] account=${accountId.slice(0, 8)} project FAILED error="${msg}"`
98
+ );
99
+ return { projected: 0, failed: 1 };
100
+ }
101
+ }
102
+ async function backfillAdminHandledBy(driver, accountId) {
103
+ const session = driver.session();
104
+ try {
105
+ const result = await session.run(
106
+ `MATCH (c:AdminConversation {accountId: $accountId})
107
+ WHERE NOT EXISTS((c)-[:HANDLED_BY]->(:Agent))
108
+ OPTIONAL MATCH (a:Agent {accountId: $accountId, slug: 'admin'})
109
+ FOREACH (_ IN CASE WHEN a IS NULL THEN [] ELSE [1] END | MERGE (c)-[:HANDLED_BY]->(a))
110
+ RETURN
111
+ count(c) AS candidates,
112
+ sum(CASE WHEN a IS NULL THEN 0 ELSE 1 END) AS edges`,
113
+ { accountId }
114
+ );
115
+ const toNum = (v) => {
116
+ if (typeof v === "number") return v;
117
+ if (v && typeof v.toNumber === "function") {
118
+ return v.toNumber();
119
+ }
120
+ return 0;
121
+ };
122
+ return {
123
+ candidates: toNum(result.records[0]?.get("candidates")),
124
+ edges: toNum(result.records[0]?.get("edges"))
125
+ };
126
+ } finally {
127
+ await session.close();
128
+ }
129
+ }
130
+ async function backfillChannel(driver, accountId) {
131
+ const session = driver.session();
132
+ try {
133
+ const result = await session.run(
134
+ `MATCH (c:Conversation {accountId: $accountId})
135
+ WHERE c.channel IS NULL
136
+ SET c.channel = 'webchat'
137
+ RETURN count(c) AS backfilled`,
138
+ { accountId }
139
+ );
140
+ const raw = result.records[0]?.get("backfilled");
141
+ if (typeof raw === "number") return raw;
142
+ if (raw && typeof raw.toNumber === "function") {
143
+ return raw.toNumber();
144
+ }
145
+ return 0;
146
+ } finally {
147
+ await session.close();
148
+ }
149
+ }
150
+ async function applyAdminAgentBackfill(driver, platformRoot) {
151
+ const accountsDir = resolve2(platformRoot, "..", "data", "accounts");
152
+ const start = Date.now();
153
+ if (!existsSync(accountsDir)) {
154
+ console.error(
155
+ `[admin-agent-graph-backfill] accounts-dir missing at ${accountsDir} \u2014 nothing to do`
156
+ );
157
+ return;
158
+ }
159
+ const accountEntries = readdirSync2(accountsDir, { withFileTypes: true }).filter((e) => e.isDirectory());
160
+ console.error(
161
+ `[admin-agent-graph-backfill] start accounts=${accountEntries.length}`
162
+ );
163
+ let totalProjected = 0;
164
+ let totalFailed = 0;
165
+ let totalHandledByCandidates = 0;
166
+ let totalHandledByEdges = 0;
167
+ let totalChannelBackfilled = 0;
168
+ const perAccount = [];
169
+ for (const entry of accountEntries) {
170
+ const accountDir = resolve2(accountsDir, entry.name);
171
+ const accountId = entry.name;
172
+ const accountStart = Date.now();
173
+ const { projected, failed } = await projectAccountAdmin(
174
+ accountId,
175
+ accountDir
176
+ );
177
+ totalProjected += projected;
178
+ totalFailed += failed;
179
+ let handledByStats = { candidates: 0, edges: 0 };
180
+ let channelBackfilled = 0;
181
+ try {
182
+ handledByStats = await backfillAdminHandledBy(driver, accountId);
183
+ totalHandledByCandidates += handledByStats.candidates;
184
+ totalHandledByEdges += handledByStats.edges;
185
+ } catch (err) {
186
+ const msg = err instanceof Error ? err.message : String(err);
187
+ console.error(
188
+ `[admin-agent-graph-backfill] account=${accountId.slice(0, 8)} handled-by-backfill FAILED error="${msg}"`
189
+ );
190
+ }
191
+ try {
192
+ channelBackfilled = await backfillChannel(driver, accountId);
193
+ totalChannelBackfilled += channelBackfilled;
194
+ } catch (err) {
195
+ const msg = err instanceof Error ? err.message : String(err);
196
+ console.error(
197
+ `[admin-agent-graph-backfill] account=${accountId.slice(0, 8)} channel-backfill FAILED error="${msg}"`
198
+ );
199
+ }
200
+ perAccount.push({
201
+ accountId,
202
+ projected,
203
+ failed,
204
+ handledByCandidates: handledByStats.candidates,
205
+ handledByEdges: handledByStats.edges,
206
+ channelBackfilled
207
+ });
208
+ const ms2 = Date.now() - accountStart;
209
+ console.error(
210
+ `[admin-agent-graph-backfill] account=${accountId.slice(0, 8)} projected=${projected} failed=${failed} handled-by-candidates=${handledByStats.candidates} handled-by-edges=${handledByStats.edges} channel-backfilled=${channelBackfilled} ms=${ms2}`
211
+ );
212
+ }
213
+ const ms = Date.now() - start;
214
+ console.error(
215
+ `[admin-agent-graph-backfill] done totals: projected=${totalProjected} failed=${totalFailed} handled-by-candidates=${totalHandledByCandidates} handled-by-edges=${totalHandledByEdges} channel-backfilled=${totalChannelBackfilled} ms=${ms}`
216
+ );
217
+ }
218
+ async function main() {
219
+ if (!existsSync(ACCOUNTS_DIR)) {
220
+ console.error(
221
+ `[admin-agent-graph-backfill] ACCOUNTS_DIR missing at ${ACCOUNTS_DIR} \u2014 nothing to do`
222
+ );
223
+ process.exit(0);
224
+ }
225
+ const driverShim = { session: () => getSession() };
226
+ const platformRoot = resolve2(ACCOUNTS_DIR, "..", "..", "platform");
227
+ try {
228
+ await applyAdminAgentBackfill(driverShim, platformRoot);
229
+ process.exit(0);
230
+ } catch (err) {
231
+ const msg = err instanceof Error ? err.message : String(err);
232
+ console.error(`[admin-agent-graph-backfill] fatal error="${msg}"`);
233
+ process.exit(2);
234
+ }
235
+ }
236
+ if (process.argv[1]?.endsWith("004-project-admin-agent.ts")) {
237
+ main().catch((err) => {
238
+ const msg = err instanceof Error ? err.message : String(err);
239
+ console.error(`[admin-agent-graph-backfill] fatal error="${msg}"`);
240
+ process.exit(2);
241
+ });
242
+ }
243
+
244
+ // ../neo4j/migrations/005-removed-review-feature.ts
245
+ import { resolve as resolve3 } from "path";
246
+ import { unlinkSync } from "fs";
247
+ import { homedir } from "os";
248
+ import { basename, dirname } from "path";
249
+ async function removeReviewFeature(driver, platformRoot) {
250
+ const session = driver.session();
251
+ let eventsDeleted = 0;
252
+ let alertsDeleted = 0;
253
+ try {
254
+ const eventResult = await session.run(
255
+ `MATCH (e:Event)
256
+ WHERE e.actionTool = "review-digest-compose"
257
+ OR e.eventId STARTS WITH "review-digest-"
258
+ DETACH DELETE e
259
+ RETURN count(e) AS deleted`
260
+ );
261
+ eventsDeleted = toNumber(eventResult.records[0]?.get("deleted"));
262
+ const alertResult = await session.run(
263
+ `MATCH (a:ReviewAlert)
264
+ DETACH DELETE a
265
+ RETURN count(a) AS deleted`
266
+ );
267
+ alertsDeleted = toNumber(alertResult.records[0]?.get("deleted"));
268
+ } finally {
269
+ await session.close();
270
+ }
271
+ const installDirName = basename(dirname(platformRoot));
272
+ const configDir = resolve3(homedir(), `.${installDirName}`);
273
+ const artefacts = [
274
+ resolve3(configDir, "logs", "review.log"),
275
+ resolve3(configDir, "review-rules.json"),
276
+ resolve3(configDir, "review-state.json"),
277
+ resolve3(configDir, "review-pending-alerts.jsonl")
278
+ ];
279
+ for (const path of artefacts) {
280
+ try {
281
+ unlinkSync(path);
282
+ } catch (err) {
283
+ const code = err.code;
284
+ if (code === "ENOENT") continue;
285
+ console.error(
286
+ `[migration] removed-review-feature unlink-skip path=${path} reason=${code ?? "unknown"}`
287
+ );
288
+ }
289
+ }
290
+ console.error(
291
+ `[migration] removed-review-feature events=${eventsDeleted} alerts=${alertsDeleted}`
292
+ );
293
+ }
294
+ function toNumber(v) {
295
+ if (typeof v === "number") return v;
296
+ if (v && typeof v.toNumber === "function") {
297
+ return v.toNumber();
298
+ }
299
+ return 0;
300
+ }
301
+
302
+ // ../neo4j/migrations/006-prune-bogus-whatsapp-persons.ts
303
+ var POLLUTED_NAME_REGEX = "(?s).*[\\n\\[].*";
304
+ async function pruneBogusWhatsappPersons(driver) {
305
+ const session = driver.session();
306
+ try {
307
+ const nonAutoRes = await session.run(
308
+ `MATCH (p:Person {source: 'whatsapp'})
309
+ WHERE coalesce(p.participantStatus, '') <> 'auto-created'
310
+ AND p.name =~ $regex
311
+ RETURN elementId(p) AS elementId, p.name AS name`,
312
+ { regex: POLLUTED_NAME_REGEX }
313
+ );
314
+ const skippedNonAuto = nonAutoRes.records.length;
315
+ for (const record of nonAutoRes.records) {
316
+ const elementId = record.get("elementId");
317
+ const name = record.get("name");
318
+ console.error(
319
+ `[migration:whatsapp-bogus-person-prune] non-auto-match elementId=${elementId} name=${JSON.stringify(name)}`
320
+ );
321
+ }
322
+ const matchedRes = await session.run(
323
+ `MATCH (p:Person {source: 'whatsapp', participantStatus: 'auto-created'})
324
+ WHERE p.name =~ $regex
325
+ RETURN count(p) AS matched`,
326
+ { regex: POLLUTED_NAME_REGEX }
327
+ );
328
+ const matched = toNumber2(matchedRes.records[0]?.get("matched"));
329
+ if (matched === 0) {
330
+ console.error(
331
+ `[migration:whatsapp-bogus-person-prune] matched=0 deleted=0 skipped-non-auto=${skippedNonAuto}`
332
+ );
333
+ return;
334
+ }
335
+ const deleteRes = await session.run(
336
+ `MATCH (p:Person {source: 'whatsapp', participantStatus: 'auto-created'})
337
+ WHERE p.name =~ $regex
338
+ DETACH DELETE p
339
+ RETURN count(p) AS deleted`,
340
+ { regex: POLLUTED_NAME_REGEX }
341
+ );
342
+ const deleted = toNumber2(deleteRes.records[0]?.get("deleted"));
343
+ console.error(
344
+ `[migration:whatsapp-bogus-person-prune] matched=${matched} deleted=${deleted} skipped-non-auto=${skippedNonAuto}`
345
+ );
346
+ } finally {
347
+ await session.close();
348
+ }
349
+ }
350
+ function toNumber2(v) {
351
+ if (typeof v === "number") return v;
352
+ if (v && typeof v.toNumber === "function") {
353
+ return v.toNumber();
354
+ }
355
+ return 0;
356
+ }
357
+
358
+ // ../neo4j/migrations/007-conversation-archive-source.ts
359
+ async function backfillConversationArchiveSource(driver) {
360
+ const session = driver.session();
361
+ try {
362
+ const archivesSourceSet = await runCount(
363
+ session,
364
+ `MATCH (a:ConversationArchive)
365
+ WHERE a.source IS NULL
366
+ SET a.source = 'whatsapp'
367
+ RETURN count(a) AS n`
368
+ );
369
+ const archivesAgentRenamed = await runCount(
370
+ session,
371
+ `MATCH (a:ConversationArchive)
372
+ WHERE a.createdByAgent = 'whatsapp-import'
373
+ SET a.createdByAgent = 'conversation-archive'
374
+ RETURN count(a) AS n`
375
+ );
376
+ const chunksSourceSet = await runCount(
377
+ session,
378
+ `MATCH (c:Section:Conversation)
379
+ WHERE c.source IS NULL
380
+ SET c.source = 'whatsapp'
381
+ RETURN count(c) AS n`
382
+ );
383
+ const chunksAgentRenamed = await runCount(
384
+ session,
385
+ `MATCH (c:Section:Conversation)
386
+ WHERE c.createdByAgent = 'whatsapp-import'
387
+ SET c.createdByAgent = 'conversation-archive'
388
+ RETURN count(c) AS n`
389
+ );
390
+ const edgesAgentRenamed = await runCount(
391
+ session,
392
+ `MATCH (a:ConversationArchive)-[r]-()
393
+ WHERE r.createdByAgent = 'whatsapp-import'
394
+ SET r.createdByAgent = 'conversation-archive'
395
+ RETURN count(r) AS n`
396
+ );
397
+ console.error(
398
+ `[migration:conversation-archive-source] archives-source-set=${archivesSourceSet} archives-agent-renamed=${archivesAgentRenamed} chunks-source-set=${chunksSourceSet} chunks-agent-renamed=${chunksAgentRenamed} edges-agent-renamed=${edgesAgentRenamed}`
399
+ );
400
+ } finally {
401
+ await session.close();
402
+ }
403
+ }
404
+ async function runCount(session, cypher) {
405
+ const res = await session.run(cypher);
406
+ const v = res.records[0]?.get("n");
407
+ if (typeof v === "number") return v;
408
+ if (v && typeof v.toNumber === "function") {
409
+ return v.toNumber();
410
+ }
411
+ return 0;
412
+ }
413
+
414
+ // ../neo4j/migrations/008-adminuser-accountid-backfill.ts
415
+ async function backfillAdminUserAccountId(driver) {
416
+ const session = driver.session();
417
+ try {
418
+ const backfilled = await runCount2(
419
+ session,
420
+ `MATCH (au:AdminUser) WHERE au.accountId IS NULL
421
+ OPTIONAL MATCH (au)-[:ADMIN_OF]->(b:LocalBusiness)
422
+ WITH au, b WHERE b IS NOT NULL AND b.accountId IS NOT NULL
423
+ SET au.accountId = b.accountId
424
+ RETURN count(au) AS n`
425
+ );
426
+ const residualNull = await runCount2(
427
+ session,
428
+ `MATCH (au:AdminUser) WHERE au.accountId IS NULL
429
+ RETURN count(au) AS n`
430
+ );
431
+ console.error(
432
+ `[migration:adminuser-accountid] backfilled=${backfilled} residual-null=${residualNull}`
433
+ );
434
+ if (residualNull > 0) {
435
+ throw new Error(
436
+ `migration:adminuser-accountid: ${residualNull} :AdminUser node(s) still carry a null accountId after backfill \u2014 no ADMIN_OF edge to derive from. Manual reconciliation required (cypher-shell into the graph; either DELETE the orphan or attach a valid ADMIN_OF edge and re-run boot).`
437
+ );
438
+ }
439
+ } finally {
440
+ await session.close();
441
+ }
442
+ }
443
+ async function runCount2(session, cypher) {
444
+ const res = await session.run(cypher);
445
+ const v = res.records[0]?.get("n");
446
+ if (typeof v === "number") return v;
447
+ if (v && typeof v.toNumber === "function") {
448
+ return v.toNumber();
449
+ }
450
+ return 0;
451
+ }
452
+
453
+ // app/lib/neo4j-migrations.ts
454
+ var BOOT_MIGRATIONS = [
455
+ { fileName: "003-person-name-eradicate.cypher", slug: "person-name-eradicate" }
456
+ ];
457
+ async function applyBootMigrations(driver, platformRoot) {
458
+ for (const migration of BOOT_MIGRATIONS) {
459
+ await applyOne(driver, platformRoot, migration);
460
+ }
461
+ try {
462
+ await pruneAlienAccounts(driver, platformRoot);
463
+ } catch (err) {
464
+ const msg = err instanceof Error ? err.message : String(err);
465
+ console.error(`[migration] failed prune-alien-accounts error="${msg}"`);
466
+ }
467
+ try {
468
+ await applyAdminAgentBackfill(driver, platformRoot);
469
+ } catch (err) {
470
+ const msg = err instanceof Error ? err.message : String(err);
471
+ console.error(`[migration] failed admin-agent-graph-backfill error="${msg}"`);
472
+ }
473
+ try {
474
+ await removeReviewFeature(driver, platformRoot);
475
+ } catch (err) {
476
+ const msg = err instanceof Error ? err.message : String(err);
477
+ console.error(`[migration] failed removed-review-feature error="${msg}"`);
478
+ }
479
+ try {
480
+ await pruneBogusWhatsappPersons(driver);
481
+ } catch (err) {
482
+ const msg = err instanceof Error ? err.message : String(err);
483
+ console.error(`[migration] failed prune-bogus-whatsapp-persons error="${msg}"`);
484
+ }
485
+ try {
486
+ await backfillConversationArchiveSource(driver);
487
+ } catch (err) {
488
+ const msg = err instanceof Error ? err.message : String(err);
489
+ console.error(`[migration] failed conversation-archive-source error="${msg}"`);
490
+ }
491
+ await backfillAdminUserAccountId(driver);
492
+ }
493
+ async function applyOne(driver, platformRoot, migration) {
494
+ const path = resolve4(platformRoot, "neo4j/migrations", migration.fileName);
495
+ let cypher;
496
+ try {
497
+ cypher = readFileSync2(path, "utf-8");
498
+ } catch (err) {
499
+ const msg = err instanceof Error ? err.message : String(err);
500
+ console.error(
501
+ `[migration] failed ${migration.slug} error=cannot-read-file path=${path} msg=${msg}`
502
+ );
503
+ return;
504
+ }
505
+ const session = driver.session();
506
+ try {
507
+ const result = await session.run(cypher);
508
+ const record = result.records[0];
509
+ const removedRaw = record?.get("removed");
510
+ const removed = toNumber3(removedRaw);
511
+ console.error(
512
+ `[migration] applied ${migration.slug} removed=${removed}`
513
+ );
514
+ } catch (err) {
515
+ const msg = err instanceof Error ? err.message : String(err);
516
+ console.error(`[migration] failed ${migration.slug} error="${msg}"`);
517
+ } finally {
518
+ await session.close();
519
+ }
520
+ }
521
+ function toNumber3(v) {
522
+ if (typeof v === "number") return v;
523
+ if (v && typeof v.toNumber === "function") {
524
+ return v.toNumber();
525
+ }
526
+ return 0;
527
+ }
528
+ export {
529
+ applyBootMigrations
530
+ };
@@ -51,7 +51,7 @@ import {
51
51
  vncLog,
52
52
  waitForExit,
53
53
  writeChromiumWrapper
54
- } from "./chunk-IWNDVGKT.js";
54
+ } from "./chunk-CV3HPX46.js";
55
55
  import {
56
56
  agentLogStream,
57
57
  clearSessionHistory,
@@ -79,7 +79,7 @@ import {
79
79
  sigtermFlushStreamLogs,
80
80
  unregisterSession,
81
81
  validateSession
82
- } from "./chunk-WUVXPZIV.js";
82
+ } from "./chunk-J6YWEJBN.js";
83
83
  import {
84
84
  ACCOUNTS_DIR,
85
85
  PLATFORM_ROOT,
@@ -97,7 +97,7 @@ import {
97
97
  openCloudflareTask,
98
98
  readTunnelState,
99
99
  resolveUnitGoneVerdict
100
- } from "./chunk-KC7NUABI.js";
100
+ } from "./chunk-OCPJGZ6S.js";
101
101
  import {
102
102
  GREETING_DIRECTIVE,
103
103
  HAIKU_MODEL,
@@ -129,7 +129,7 @@ import {
129
129
  verifyAndGetConversationUpdatedAt,
130
130
  verifyConversationOwnership,
131
131
  writeAdminUserAndPerson
132
- } from "./chunk-CUSH3UXP.js";
132
+ } from "./chunk-BCFM2UPH.js";
133
133
 
134
134
  // ../lib/graph-trash/dist/index.js
135
135
  var require_dist = __commonJS({
@@ -7285,7 +7285,7 @@ var app11 = new Hono();
7285
7285
  app11.post("/cancel", requireAdminSession, async (c) => {
7286
7286
  const session_key = c.var.sessionKey;
7287
7287
  try {
7288
- const { interruptClient: interruptClient2 } = await import("./client-pool-3TM3SRIA.js");
7288
+ const { interruptClient: interruptClient2 } = await import("./client-pool-ZNGN66GN.js");
7289
7289
  await interruptClient2(session_key);
7290
7290
  return c.json({ ok: true });
7291
7291
  } catch (err) {
@@ -7777,25 +7777,36 @@ app13.get("/", async (c) => {
7777
7777
  );
7778
7778
  }
7779
7779
  let sessionKey = sessionKeyParam ?? null;
7780
- if (!sessionKey && conversationIdParam) {
7780
+ let conversationId = conversationIdParam ?? null;
7781
+ if (!sessionKey && conversationId || sessionKey && !conversationId) {
7781
7782
  const neoSession = getSession();
7782
7783
  try {
7783
- const result = await neoSession.run(
7784
- `MATCH (c:Conversation {conversationId: $conversationId})
7785
- RETURN c.sessionKey AS sessionKey LIMIT 1`,
7786
- { conversationId: conversationIdParam }
7787
- );
7788
- const sk = result.records[0]?.get("sessionKey");
7789
- if (typeof sk === "string" && sk.length > 0) sessionKey = sk;
7784
+ if (!sessionKey && conversationId) {
7785
+ const result = await neoSession.run(
7786
+ `MATCH (c:Conversation {conversationId: $conversationId})
7787
+ RETURN c.sessionKey AS sessionKey LIMIT 1`,
7788
+ { conversationId }
7789
+ );
7790
+ const sk = result.records[0]?.get("sessionKey");
7791
+ if (typeof sk === "string" && sk.length > 0) sessionKey = sk;
7792
+ } else if (sessionKey && !conversationId) {
7793
+ const result = await neoSession.run(
7794
+ `MATCH (c:Conversation {sessionKey: $sessionKey})
7795
+ RETURN c.conversationId AS conversationId LIMIT 1`,
7796
+ { sessionKey }
7797
+ );
7798
+ const cid = result.records[0]?.get("conversationId");
7799
+ if (typeof cid === "string" && cid.length > 0) conversationId = cid;
7800
+ }
7790
7801
  } catch (err) {
7791
7802
  const reason2 = err instanceof Error ? err.message : String(err);
7792
- console.warn(`[admin/logs] sessionKey-lookup-neo4j-error conversationId=${conversationIdParam} reason=${reason2}`);
7803
+ console.warn(`[admin/logs] id-lookup-neo4j-error sessionKey=${sessionKey ?? "none"} conversationId=${conversationId ?? "none"} reason=${reason2}`);
7793
7804
  } finally {
7794
7805
  await neoSession.close();
7795
7806
  }
7796
7807
  }
7797
7808
  const MISSING_SENTINEL = ".task702-identifier-not-supplied.log";
7798
- const fullFilename = conversationIdParam ? `${prefix}-${conversationIdParam}.log` : MISSING_SENTINEL;
7809
+ const fullFilename = conversationId ? `${prefix}-${conversationId}.log` : MISSING_SENTINEL;
7799
7810
  const preflushFilename = sessionKey ? `${prefix}-${preflushSliceOf(sessionKey)}.log` : MISSING_SENTINEL;
7800
7811
  const { hits, stalePreflushPaths, tried } = resolveConversationLogPaths(
7801
7812
  fullFilename,
@@ -7804,7 +7815,7 @@ app13.get("/", async (c) => {
7804
7815
  );
7805
7816
  const stalePreflushCount = stalePreflushPaths.length;
7806
7817
  const sessionKeySlice = sessionKey ? sessionKey.slice(0, 12) : "none";
7807
- const conversationIdSlice = conversationIdParam ? conversationIdParam.slice(0, 12) : "none";
7818
+ const conversationIdSlice = conversationId ? conversationId.slice(0, 12) : "none";
7808
7819
  if (hits.length > 0) {
7809
7820
  const hit = hits[0];
7810
7821
  console.info(`[admin/logs] resolved sessionKey=${sessionKeySlice} conversationId=${conversationIdSlice} shape=${hit.shape} stalePreflushCount=${stalePreflushCount}`);
@@ -7837,7 +7848,7 @@ app13.get("/", async (c) => {
7837
7848
  );
7838
7849
  }
7839
7850
  }
7840
- const reason = !conversationIdParam ? "no preflush log on disk for sessionKey" : !sessionKey ? "no full log on disk and sessionKey not derivable" : "no preflush log, no full log";
7851
+ const reason = !conversationId ? "no preflush log on disk for sessionKey and conversationId not derivable" : !sessionKey ? "no full log on disk and sessionKey not derivable" : "no preflush log, no full log";
7841
7852
  console.warn(`[admin/logs] not-found tried=[${tried.join(",")}] sessionKey=${sessionKeySlice} conversationId=${conversationIdSlice} reason=${JSON.stringify(reason)}`);
7842
7853
  return c.json(
7843
7854
  {
@@ -7845,7 +7856,7 @@ app13.get("/", async (c) => {
7845
7856
  code: "NOT_FOUND",
7846
7857
  reason,
7847
7858
  sessionKey: sessionKey ?? null,
7848
- conversationId: conversationIdParam ?? null
7859
+ conversationId: conversationId ?? null
7849
7860
  },
7850
7861
  404
7851
7862
  );
@@ -12832,7 +12843,7 @@ autoDeliverPremiumPlugins(bootEntitlement?.purchasedPlugins ?? void 0);
12832
12843
  (async () => {
12833
12844
  if (!bootAccount) return;
12834
12845
  try {
12835
- const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-4NIODMGL.js");
12846
+ const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-MHALDN54.js");
12836
12847
  const result = await recoverRunningCloudflareTasks(
12837
12848
  bootAccount.accountId,
12838
12849
  configDirForWhatsApp,