@rubytech/create-realagent 1.0.658 → 1.0.660

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 (86) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +115 -61
  3. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts +13 -1
  4. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.d.ts.map +1 -1
  5. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js +20 -5
  6. package/payload/platform/plugins/contacts/mcp/dist/lib/resolve-person.js.map +1 -1
  7. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  8. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +6 -1
  9. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  10. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.d.ts.map +1 -1
  11. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js +1 -1
  12. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-delete.js.map +1 -1
  13. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.d.ts.map +1 -1
  14. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js +1 -1
  15. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-erase.js.map +1 -1
  16. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
  17. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +8 -3
  18. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
  19. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts.map +1 -1
  20. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +14 -6
  21. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -1
  22. package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
  23. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts +7 -0
  24. package/payload/platform/plugins/email/mcp/dist/lib/credentials.d.ts.map +1 -1
  25. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js +7 -0
  26. package/payload/platform/plugins/email/mcp/dist/lib/credentials.js.map +1 -1
  27. package/payload/platform/plugins/memory/mcp/dist/index.js +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  29. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts +0 -16
  30. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts.map +1 -1
  31. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js +0 -39
  32. package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js.map +1 -1
  33. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +10 -0
  34. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -1
  35. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +20 -7
  36. package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  38. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +4 -1
  39. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  40. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  41. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +24 -5
  42. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  43. package/payload/platform/plugins/workflows/mcp/dist/index.js +6 -3
  44. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
  45. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.d.ts.map +1 -1
  46. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js +2 -1
  47. package/payload/platform/plugins/workflows/mcp/dist/lib/active-runs.js.map +1 -1
  48. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts +25 -3
  49. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.d.ts.map +1 -1
  50. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js +44 -12
  51. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-delete.js.map +1 -1
  52. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.d.ts.map +1 -1
  53. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js +19 -3
  54. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-execute.js.map +1 -1
  55. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.d.ts.map +1 -1
  56. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js +3 -1
  57. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-get.js.map +1 -1
  58. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.d.ts.map +1 -1
  59. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js +3 -2
  60. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-list.js.map +1 -1
  61. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.d.ts.map +1 -1
  62. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js +7 -2
  63. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-runs.js.map +1 -1
  64. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.d.ts.map +1 -1
  65. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js +5 -1
  66. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-update.js.map +1 -1
  67. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.d.ts.map +1 -1
  68. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js +3 -1
  69. package/payload/platform/plugins/workflows/mcp/dist/tools/workflow-validate.js.map +1 -1
  70. package/payload/server/public/assets/{admin-Czc-XCGo.js → admin-BFIYSS4u.js} +1 -1
  71. package/payload/server/public/assets/{data-CrSbmVOi.js → data-BmeeI1Ix.js} +1 -1
  72. package/payload/server/public/assets/{file-DJN8iDYd.js → file-D4Qulqz_.js} +1 -1
  73. package/payload/server/public/assets/graph-MoIys9Ub.js +49 -0
  74. package/payload/server/public/assets/{house-0e8rAfIV.js → house-B0Hukxjp.js} +1 -1
  75. package/payload/server/public/assets/{jsx-runtime-c8K7DPME.css → jsx-runtime-C6owBiFB.css} +1 -1
  76. package/payload/server/public/assets/{public-CWJExYI2.js → public-LvjJTLGn.js} +1 -1
  77. package/payload/server/public/assets/{share-2-DZ7p8QqC.js → share-2-BafZBLp9.js} +1 -1
  78. package/payload/server/public/assets/{useVoiceRecorder-BgiIIuSz.js → useVoiceRecorder-dLOpmcYJ.js} +1 -1
  79. package/payload/server/public/assets/{x-CknuyFAk.js → x-BOZIeV0f.js} +1 -1
  80. package/payload/server/public/data.html +6 -6
  81. package/payload/server/public/graph.html +6 -6
  82. package/payload/server/public/index.html +7 -7
  83. package/payload/server/public/public.html +4 -4
  84. package/payload/server/server.js +262 -160
  85. package/payload/server/public/assets/graph-BtnUfXRD.js +0 -49
  86. /package/payload/server/public/assets/{jsx-runtime-DHkQ2la3.js → jsx-runtime-CmCvZzVE.js} +0 -0
@@ -1 +1 @@
1
- import{r as e}from"./jsx-runtime-DHkQ2la3.js";var t=e(`rotate-ccw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]),n=e(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);export{t as n,n as t};
1
+ import{r as e}from"./jsx-runtime-CmCvZzVE.js";var t=e(`rotate-ccw`,[[`path`,{d:`M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8`,key:`1357e3`}],[`path`,{d:`M3 3v5h5`,key:`1xhq8a`}]]),n=e(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);export{t as n,n as t};
@@ -5,13 +5,13 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Data — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/data-CrSbmVOi.js"></script>
8
+ <script type="module" crossorigin src="/assets/data-BmeeI1Ix.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
10
- <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-DHkQ2la3.js">
11
- <link rel="modulepreload" crossorigin href="/assets/share-2-DZ7p8QqC.js">
12
- <link rel="modulepreload" crossorigin href="/assets/file-DJN8iDYd.js">
13
- <link rel="modulepreload" crossorigin href="/assets/house-0e8rAfIV.js">
14
- <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-c8K7DPME.css">
10
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-CmCvZzVE.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/share-2-BafZBLp9.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/file-D4Qulqz_.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/house-B0Hukxjp.js">
14
+ <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-C6owBiFB.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -5,13 +5,13 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Graph — Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/graph-BtnUfXRD.js"></script>
8
+ <script type="module" crossorigin src="/assets/graph-MoIys9Ub.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
10
- <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-DHkQ2la3.js">
11
- <link rel="modulepreload" crossorigin href="/assets/share-2-DZ7p8QqC.js">
12
- <link rel="modulepreload" crossorigin href="/assets/house-0e8rAfIV.js">
13
- <link rel="modulepreload" crossorigin href="/assets/x-CknuyFAk.js">
14
- <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-c8K7DPME.css">
10
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-CmCvZzVE.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/share-2-BafZBLp9.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/house-B0Hukxjp.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/x-BOZIeV0f.js">
14
+ <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-C6owBiFB.css">
15
15
  </head>
16
16
  <body>
17
17
  <div id="root"></div>
@@ -5,15 +5,15 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Real Agent</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/admin-Czc-XCGo.js"></script>
8
+ <script type="module" crossorigin src="/assets/admin-BFIYSS4u.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
10
- <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-DHkQ2la3.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-CmCvZzVE.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-qlgyTAkD.js">
12
- <link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-BgiIIuSz.js">
13
- <link rel="modulepreload" crossorigin href="/assets/share-2-DZ7p8QqC.js">
14
- <link rel="modulepreload" crossorigin href="/assets/file-DJN8iDYd.js">
15
- <link rel="modulepreload" crossorigin href="/assets/x-CknuyFAk.js">
16
- <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-c8K7DPME.css">
12
+ <link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-dLOpmcYJ.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/share-2-BafZBLp9.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/file-D4Qulqz_.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/x-BOZIeV0f.js">
16
+ <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-C6owBiFB.css">
17
17
  <link rel="stylesheet" crossorigin href="/assets/admin-kHJ-D0s7.css">
18
18
  <link rel="stylesheet" href="/brand-defaults.css">
19
19
  </head>
@@ -5,12 +5,12 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Real Agent</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/public-CWJExYI2.js"></script>
8
+ <script type="module" crossorigin src="/assets/public-LvjJTLGn.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
10
- <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-DHkQ2la3.js">
10
+ <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-CmCvZzVE.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-qlgyTAkD.js">
12
- <link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-BgiIIuSz.js">
13
- <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-c8K7DPME.css">
12
+ <link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-dLOpmcYJ.js">
13
+ <link rel="stylesheet" crossorigin href="/assets/jsx-runtime-C6owBiFB.css">
14
14
  <link rel="stylesheet" href="/brand-defaults.css">
15
15
  </head>
16
16
  <body>
@@ -20254,8 +20254,236 @@ function resolveDataPath(raw2) {
20254
20254
  return { ok: true, absolute: resolvedReal, dataRootReal, relative: relPath };
20255
20255
  }
20256
20256
 
20257
- // server/routes/admin/files.ts
20257
+ // ../lib/graph-trash/src/index.ts
20258
+ var UNIQUE_KEYS_BY_LABEL = {
20259
+ Person: ["email", "telephone"],
20260
+ Service: ["serviceId"],
20261
+ LocalBusiness: ["accountId"],
20262
+ Task: ["taskId"],
20263
+ Event: ["eventId"],
20264
+ KnowledgeDocument: ["attachmentId"],
20265
+ DigitalDocument: ["attachmentId"],
20266
+ Conversation: ["conversationId", "sessionKey"],
20267
+ Message: ["messageId"],
20268
+ OnboardingState: ["accountId"],
20269
+ Workflow: ["workflowId"],
20270
+ WorkflowStep: ["stepId"],
20271
+ WorkflowRun: ["runId"],
20272
+ Preference: ["preferenceId"],
20273
+ Email: ["emailId", "messageId"],
20274
+ AdminUser: ["userId"],
20275
+ ToolCall: ["callId"],
20276
+ // Composite component nulls — frees the composite constraint:
20277
+ AccessGrant: ["contactValue"],
20278
+ // composite (contactValue, agentSlug, accountId)
20279
+ UserProfile: ["userId"]
20280
+ // composite (accountId, userId)
20281
+ };
20282
+ async function trashNode(params) {
20283
+ const { session, accountId, elementId, by, reason } = params;
20284
+ const lookup = await session.run(
20285
+ `MATCH (n) WHERE elementId(n) = $eid AND n.accountId = $accountId
20286
+ RETURN labels(n) AS labels, properties(n) AS props`,
20287
+ { eid: elementId, accountId }
20288
+ );
20289
+ if (lookup.records.length === 0) {
20290
+ throw new Error(
20291
+ `trashNode: node not found (elementId=${elementId} accountId=${accountId.slice(0, 8)}\u2026)`
20292
+ );
20293
+ }
20294
+ const allLabels = lookup.records[0].get("labels");
20295
+ const props = lookup.records[0].get("props");
20296
+ const baseLabels = allLabels.filter((l) => l !== "Trashed");
20297
+ if (allLabels.includes("Trashed")) {
20298
+ return {
20299
+ trashed: false,
20300
+ alreadyTrashed: true,
20301
+ nodeId: elementId,
20302
+ labels: baseLabels,
20303
+ trashedAt: String(props.trashedAt ?? ""),
20304
+ originalKeys: {}
20305
+ };
20306
+ }
20307
+ const uniqueKeys = /* @__PURE__ */ new Set();
20308
+ for (const label of baseLabels) {
20309
+ for (const key of UNIQUE_KEYS_BY_LABEL[label] ?? []) uniqueKeys.add(key);
20310
+ }
20311
+ const originalKeys = {};
20312
+ for (const k of uniqueKeys) {
20313
+ if (props[k] !== void 0 && props[k] !== null) originalKeys[k] = props[k];
20314
+ }
20315
+ const setNullClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = null`).join(", ");
20316
+ const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
20317
+ const trashedAt = (/* @__PURE__ */ new Date()).toISOString();
20318
+ await session.run(
20319
+ `MATCH (n) WHERE elementId(n) = $eid
20320
+ SET n:Trashed,
20321
+ n.trashedAt = datetime($trashedAt),
20322
+ n.trashedBy = $by,
20323
+ n.trashReason = $reason,
20324
+ n._trashedKeys = $trashedKeysJson${setNullSuffix}`,
20325
+ {
20326
+ eid: elementId,
20327
+ trashedAt,
20328
+ by,
20329
+ reason: reason ?? null,
20330
+ trashedKeysJson: JSON.stringify(originalKeys)
20331
+ }
20332
+ );
20333
+ process.stderr.write(
20334
+ `[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}
20335
+ `
20336
+ );
20337
+ return {
20338
+ trashed: true,
20339
+ alreadyTrashed: false,
20340
+ nodeId: elementId,
20341
+ labels: baseLabels,
20342
+ trashedAt,
20343
+ originalKeys
20344
+ };
20345
+ }
20346
+ async function restoreNode(params) {
20347
+ const { session, accountId, elementId } = params;
20348
+ const lookup = await session.run(
20349
+ `MATCH (n:Trashed) WHERE elementId(n) = $eid
20350
+ RETURN labels(n) AS labels, n._trashedKeys AS keysJson`,
20351
+ { eid: elementId }
20352
+ );
20353
+ if (lookup.records.length === 0) {
20354
+ throw new Error(
20355
+ `restoreNode: trashed node not found (elementId=${elementId})`
20356
+ );
20357
+ }
20358
+ const allLabels = lookup.records[0].get("labels");
20359
+ const baseLabels = allLabels.filter((l) => l !== "Trashed");
20360
+ const keysJson = lookup.records[0].get("keysJson");
20361
+ const originalKeys = keysJson ? JSON.parse(keysJson) : {};
20362
+ for (const label of baseLabels) {
20363
+ const uniqueKeys = UNIQUE_KEYS_BY_LABEL[label] ?? [];
20364
+ for (const k of uniqueKeys) {
20365
+ const v = originalKeys[k];
20366
+ if (v === void 0 || v === null) continue;
20367
+ const conflict = await session.run(
20368
+ `MATCH (other:\`${label}\`)
20369
+ WHERE elementId(other) <> $eid
20370
+ AND NOT other:Trashed
20371
+ AND other.\`${k}\` = $val
20372
+ RETURN elementId(other) AS otherId LIMIT 1`,
20373
+ { eid: elementId, val: v }
20374
+ );
20375
+ if (conflict.records.length > 0) {
20376
+ const otherId = conflict.records[0].get("otherId");
20377
+ throw new Error(
20378
+ `restoreNode: cannot restore ${label} elementId=${elementId} \u2014 active node elementId=${otherId} already holds ${k}=${JSON.stringify(v)}`
20379
+ );
20380
+ }
20381
+ }
20382
+ }
20383
+ const setClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = $val_${k}`).join(", ");
20384
+ const setSuffix = setClauses ? `, ${setClauses}` : "";
20385
+ const setParams = { eid: elementId };
20386
+ for (const [k, v] of Object.entries(originalKeys)) setParams[`val_${k}`] = v;
20387
+ await session.run(
20388
+ `MATCH (n:Trashed) WHERE elementId(n) = $eid
20389
+ REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
20390
+ SET n.restoredAt = datetime()${setSuffix}`,
20391
+ setParams
20392
+ );
20393
+ process.stderr.write(
20394
+ `[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}
20395
+ `
20396
+ );
20397
+ return {
20398
+ restored: true,
20399
+ nodeId: elementId,
20400
+ labels: baseLabels,
20401
+ restoredKeys: originalKeys
20402
+ };
20403
+ }
20404
+
20405
+ // app/lib/file-delete-cascade.ts
20258
20406
  var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20407
+ function parseAttachmentPath(relPath) {
20408
+ const segments = relPath.split("/").filter(Boolean);
20409
+ if (segments.length !== 4) return null;
20410
+ if (segments[0] !== "uploads") return null;
20411
+ const accountId = segments[1];
20412
+ const attachmentId = segments[2];
20413
+ const filename = segments[3];
20414
+ if (!UUID_RE2.test(accountId) || !UUID_RE2.test(attachmentId)) return null;
20415
+ const dot = filename.lastIndexOf(".");
20416
+ if (dot === -1) return null;
20417
+ const stem = filename.slice(0, dot);
20418
+ const ext = filename.slice(dot + 1);
20419
+ if (stem !== attachmentId) return null;
20420
+ if (ext === "meta" || filename.endsWith(".meta.json")) return null;
20421
+ return { accountId, attachmentId };
20422
+ }
20423
+ async function cascadeDeleteDocument(params) {
20424
+ const { accountId, attachmentId } = params;
20425
+ const session = getSession();
20426
+ try {
20427
+ const lookup = await session.run(
20428
+ `MATCH (d:KnowledgeDocument { accountId: $accountId, attachmentId: $attachmentId })
20429
+ WHERE NOT d:Trashed
20430
+ RETURN elementId(d) AS eid LIMIT 1`,
20431
+ { accountId, attachmentId }
20432
+ );
20433
+ if (lookup.records.length === 0) return { nodes: 0 };
20434
+ const docElementId = lookup.records[0].get("eid");
20435
+ const childResult = await session.run(
20436
+ `MATCH (d) WHERE elementId(d) = $eid
20437
+ OPTIONAL MATCH (d)-[:HAS_SECTION]->(s:Section)
20438
+ OPTIONAL MATCH (s)-[:HAS_CHUNK]->(c:Chunk)
20439
+ WITH collect(DISTINCT s) AS sections, collect(DISTINCT c) AS chunks
20440
+ RETURN [s IN sections WHERE s IS NOT NULL | elementId(s)] AS sectionIds,
20441
+ [c IN chunks WHERE c IS NOT NULL | elementId(c)] AS chunkIds`,
20442
+ { eid: docElementId }
20443
+ );
20444
+ if (childResult.records.length === 0) {
20445
+ throw new Error(
20446
+ `cascadeDeleteDocument: child lookup returned zero rows for elementId=${docElementId} \u2014 concurrent trash or connection drop`
20447
+ );
20448
+ }
20449
+ const sectionIds = childResult.records[0].get("sectionIds") ?? [];
20450
+ const chunkIds = childResult.records[0].get("chunkIds") ?? [];
20451
+ await trashNode({
20452
+ session,
20453
+ accountId,
20454
+ elementId: docElementId,
20455
+ by: "file-delete-cascade",
20456
+ reason: "file deleted via /data"
20457
+ });
20458
+ for (const sid of sectionIds) {
20459
+ await trashNode({
20460
+ session,
20461
+ accountId,
20462
+ elementId: sid,
20463
+ by: `file-delete-cascade:from-${docElementId}`,
20464
+ reason: `cascade from KnowledgeDocument ${docElementId}`
20465
+ });
20466
+ }
20467
+ for (const cid of chunkIds) {
20468
+ await trashNode({
20469
+ session,
20470
+ accountId,
20471
+ elementId: cid,
20472
+ by: `file-delete-cascade:from-${docElementId}`,
20473
+ reason: `cascade from KnowledgeDocument ${docElementId}`
20474
+ });
20475
+ }
20476
+ return { nodes: 1 };
20477
+ } finally {
20478
+ try {
20479
+ await session.close();
20480
+ } catch {
20481
+ }
20482
+ }
20483
+ }
20484
+
20485
+ // server/routes/admin/files.ts
20486
+ var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20259
20487
  async function readMeta(absDir, baseName) {
20260
20488
  try {
20261
20489
  const raw2 = await readFile4(join12(absDir, `${baseName}.meta.json`), "utf8");
@@ -20277,7 +20505,7 @@ async function readAccountNames() {
20277
20505
  return map;
20278
20506
  }
20279
20507
  for (const name of names) {
20280
- if (!UUID_RE2.test(name)) continue;
20508
+ if (!UUID_RE3.test(name)) continue;
20281
20509
  const configPath2 = resolve29(accountsDir, name, "account.json");
20282
20510
  try {
20283
20511
  const raw2 = await readFile4(configPath2, "utf8");
@@ -20295,7 +20523,7 @@ async function readAccountNames() {
20295
20523
  return map;
20296
20524
  }
20297
20525
  async function enrich(absolute, entry, accountNames) {
20298
- if (entry.kind === "directory" && UUID_RE2.test(entry.name)) {
20526
+ if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
20299
20527
  const meta = await readMeta(join12(absolute, entry.name), entry.name);
20300
20528
  if (meta?.filename) {
20301
20529
  entry.displayName = meta.filename;
@@ -20311,7 +20539,7 @@ async function enrich(absolute, entry, accountNames) {
20311
20539
  if (entry.kind === "file") {
20312
20540
  const dot = entry.name.lastIndexOf(".");
20313
20541
  const base = dot === -1 ? entry.name : entry.name.slice(0, dot);
20314
- if (UUID_RE2.test(base)) {
20542
+ if (UUID_RE3.test(base)) {
20315
20543
  const meta = await readMeta(absolute, base);
20316
20544
  if (meta?.filename) {
20317
20545
  entry.displayName = meta.filename;
@@ -20323,7 +20551,7 @@ async function enrich(absolute, entry, accountNames) {
20323
20551
  function buildDisplayPath(relPath, accountNames) {
20324
20552
  if (relPath === "." || relPath === "") return [];
20325
20553
  return relPath.split("/").filter(Boolean).map((seg) => {
20326
- const dn = UUID_RE2.test(seg) ? accountNames.get(seg) : void 0;
20554
+ const dn = UUID_RE3.test(seg) ? accountNames.get(seg) : void 0;
20327
20555
  return dn ? { name: seg, displayName: dn } : { name: seg };
20328
20556
  });
20329
20557
  }
@@ -20351,7 +20579,7 @@ app24.get("/", requireAdminSession, async (c) => {
20351
20579
  const names = await readdir2(absolute);
20352
20580
  const entries = [];
20353
20581
  for (const name of names) {
20354
- if (UUID_RE2.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
20582
+ if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
20355
20583
  continue;
20356
20584
  }
20357
20585
  try {
@@ -20497,7 +20725,8 @@ app24.post("/upload", requireAdminSession, async (c) => {
20497
20725
  });
20498
20726
  app24.delete("/", requireAdminSession, async (c) => {
20499
20727
  const sessionKey = c.var.sessionKey;
20500
- if (!getAccountIdForSession(sessionKey)) {
20728
+ const accountId = getAccountIdForSession(sessionKey);
20729
+ if (!accountId) {
20501
20730
  console.error(`[data] auth-rejected endpoint="DELETE /api/admin/files" reason="no account for session"`);
20502
20731
  return c.json({ error: "Account not found for session" }, 401);
20503
20732
  }
@@ -20527,7 +20756,7 @@ app24.delete("/", requireAdminSession, async (c) => {
20527
20756
  }
20528
20757
  const dot = base.lastIndexOf(".");
20529
20758
  const stem = dot === -1 ? base : base.slice(0, dot);
20530
- const sidecarPath = UUID_RE2.test(stem) && base !== `${stem}.meta.json` ? join12(dirname10(absolute), `${stem}.meta.json`) : null;
20759
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join12(dirname10(absolute), `${stem}.meta.json`) : null;
20531
20760
  await unlink2(absolute);
20532
20761
  if (sidecarPath) {
20533
20762
  try {
@@ -20536,6 +20765,19 @@ app24.delete("/", requireAdminSession, async (c) => {
20536
20765
  }
20537
20766
  }
20538
20767
  console.error(`[data] file-delete path="${relPath}" bytes=${info.size}`);
20768
+ const parsed = parseAttachmentPath(relPath);
20769
+ if (parsed) {
20770
+ try {
20771
+ const { nodes } = await cascadeDeleteDocument({
20772
+ accountId,
20773
+ attachmentId: parsed.attachmentId
20774
+ });
20775
+ console.error(`[data] file-delete graph-cascade path="${relPath}" nodes=${nodes}`);
20776
+ } catch (err) {
20777
+ const message = err instanceof Error ? err.message : String(err);
20778
+ console.error(`[data] file-delete graph-cascade-failed path="${relPath}" err="${message}"`);
20779
+ }
20780
+ }
20539
20781
  return c.json({ ok: true });
20540
20782
  } catch (err) {
20541
20783
  const code = err.code;
@@ -20884,155 +21126,8 @@ function pruneNode(node) {
20884
21126
  }
20885
21127
  var graph_subgraph_default = app26;
20886
21128
 
20887
- // ../lib/graph-trash/src/index.ts
20888
- var UNIQUE_KEYS_BY_LABEL = {
20889
- Person: ["email", "telephone"],
20890
- Service: ["serviceId"],
20891
- LocalBusiness: ["accountId"],
20892
- Task: ["taskId"],
20893
- Event: ["eventId"],
20894
- KnowledgeDocument: ["attachmentId"],
20895
- DigitalDocument: ["attachmentId"],
20896
- Conversation: ["conversationId", "sessionKey"],
20897
- Message: ["messageId"],
20898
- OnboardingState: ["accountId"],
20899
- Workflow: ["workflowId"],
20900
- WorkflowStep: ["stepId"],
20901
- WorkflowRun: ["runId"],
20902
- Preference: ["preferenceId"],
20903
- Email: ["emailId", "messageId"],
20904
- AdminUser: ["userId"],
20905
- ToolCall: ["callId"],
20906
- // Composite component nulls — frees the composite constraint:
20907
- AccessGrant: ["contactValue"],
20908
- // composite (contactValue, agentSlug, accountId)
20909
- UserProfile: ["userId"]
20910
- // composite (accountId, userId)
20911
- };
20912
- async function trashNode(params) {
20913
- const { session, accountId, elementId, by, reason } = params;
20914
- const lookup = await session.run(
20915
- `MATCH (n) WHERE elementId(n) = $eid AND n.accountId = $accountId
20916
- RETURN labels(n) AS labels, properties(n) AS props`,
20917
- { eid: elementId, accountId }
20918
- );
20919
- if (lookup.records.length === 0) {
20920
- throw new Error(
20921
- `trashNode: node not found (elementId=${elementId} accountId=${accountId.slice(0, 8)}\u2026)`
20922
- );
20923
- }
20924
- const allLabels = lookup.records[0].get("labels");
20925
- const props = lookup.records[0].get("props");
20926
- const baseLabels = allLabels.filter((l) => l !== "Trashed");
20927
- if (allLabels.includes("Trashed")) {
20928
- return {
20929
- trashed: false,
20930
- alreadyTrashed: true,
20931
- nodeId: elementId,
20932
- labels: baseLabels,
20933
- trashedAt: String(props.trashedAt ?? ""),
20934
- originalKeys: {}
20935
- };
20936
- }
20937
- const uniqueKeys = /* @__PURE__ */ new Set();
20938
- for (const label of baseLabels) {
20939
- for (const key of UNIQUE_KEYS_BY_LABEL[label] ?? []) uniqueKeys.add(key);
20940
- }
20941
- const originalKeys = {};
20942
- for (const k of uniqueKeys) {
20943
- if (props[k] !== void 0 && props[k] !== null) originalKeys[k] = props[k];
20944
- }
20945
- const setNullClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = null`).join(", ");
20946
- const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
20947
- const trashedAt = (/* @__PURE__ */ new Date()).toISOString();
20948
- await session.run(
20949
- `MATCH (n) WHERE elementId(n) = $eid
20950
- SET n:Trashed,
20951
- n.trashedAt = datetime($trashedAt),
20952
- n.trashedBy = $by,
20953
- n.trashReason = $reason,
20954
- n._trashedKeys = $trashedKeysJson${setNullSuffix}`,
20955
- {
20956
- eid: elementId,
20957
- trashedAt,
20958
- by,
20959
- reason: reason ?? null,
20960
- trashedKeysJson: JSON.stringify(originalKeys)
20961
- }
20962
- );
20963
- process.stderr.write(
20964
- `[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}
20965
- `
20966
- );
20967
- return {
20968
- trashed: true,
20969
- alreadyTrashed: false,
20970
- nodeId: elementId,
20971
- labels: baseLabels,
20972
- trashedAt,
20973
- originalKeys
20974
- };
20975
- }
20976
- async function restoreNode(params) {
20977
- const { session, accountId, elementId } = params;
20978
- const lookup = await session.run(
20979
- `MATCH (n:Trashed) WHERE elementId(n) = $eid
20980
- RETURN labels(n) AS labels, n._trashedKeys AS keysJson`,
20981
- { eid: elementId }
20982
- );
20983
- if (lookup.records.length === 0) {
20984
- throw new Error(
20985
- `restoreNode: trashed node not found (elementId=${elementId})`
20986
- );
20987
- }
20988
- const allLabels = lookup.records[0].get("labels");
20989
- const baseLabels = allLabels.filter((l) => l !== "Trashed");
20990
- const keysJson = lookup.records[0].get("keysJson");
20991
- const originalKeys = keysJson ? JSON.parse(keysJson) : {};
20992
- for (const label of baseLabels) {
20993
- const uniqueKeys = UNIQUE_KEYS_BY_LABEL[label] ?? [];
20994
- for (const k of uniqueKeys) {
20995
- const v = originalKeys[k];
20996
- if (v === void 0 || v === null) continue;
20997
- const conflict = await session.run(
20998
- `MATCH (other:\`${label}\`)
20999
- WHERE elementId(other) <> $eid
21000
- AND NOT other:Trashed
21001
- AND other.\`${k}\` = $val
21002
- RETURN elementId(other) AS otherId LIMIT 1`,
21003
- { eid: elementId, val: v }
21004
- );
21005
- if (conflict.records.length > 0) {
21006
- const otherId = conflict.records[0].get("otherId");
21007
- throw new Error(
21008
- `restoreNode: cannot restore ${label} elementId=${elementId} \u2014 active node elementId=${otherId} already holds ${k}=${JSON.stringify(v)}`
21009
- );
21010
- }
21011
- }
21012
- }
21013
- const setClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = $val_${k}`).join(", ");
21014
- const setSuffix = setClauses ? `, ${setClauses}` : "";
21015
- const setParams = { eid: elementId };
21016
- for (const [k, v] of Object.entries(originalKeys)) setParams[`val_${k}`] = v;
21017
- await session.run(
21018
- `MATCH (n:Trashed) WHERE elementId(n) = $eid
21019
- REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
21020
- SET n.restoredAt = datetime()${setSuffix}`,
21021
- setParams
21022
- );
21023
- process.stderr.write(
21024
- `[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}
21025
- `
21026
- );
21027
- return {
21028
- restored: true,
21029
- nodeId: elementId,
21030
- labels: baseLabels,
21031
- restoredKeys: originalKeys
21032
- };
21033
- }
21034
-
21035
21129
  // server/routes/admin/graph-delete.ts
21130
+ var ALLOWED_BY = ["graph-page", "graph-drag-trash"];
21036
21131
  var app27 = new Hono2();
21037
21132
  app27.post("/", requireAdminSession, async (c) => {
21038
21133
  const sessionKey = c.var.sessionKey;
@@ -21049,6 +21144,13 @@ app27.post("/", requireAdminSession, async (c) => {
21049
21144
  if (!elementId) {
21050
21145
  return c.json({ error: "elementId required" }, 400);
21051
21146
  }
21147
+ let by = "graph-page";
21148
+ if (body.by !== void 0) {
21149
+ if (typeof body.by !== "string" || !ALLOWED_BY.includes(body.by)) {
21150
+ return c.json({ error: `by must be one of: ${ALLOWED_BY.join(", ")}` }, 400);
21151
+ }
21152
+ by = body.by;
21153
+ }
21052
21154
  const started = Date.now();
21053
21155
  const session = getSession();
21054
21156
  try {
@@ -21061,7 +21163,7 @@ app27.post("/", requireAdminSession, async (c) => {
21061
21163
  if (lookup.records.length === 0) {
21062
21164
  const elapsed2 = Date.now() - started;
21063
21165
  console.error(
21064
- `[graph-page] delete account=${accountId} elementId=${elementId} labels= result=not-found ms=${elapsed2}`
21166
+ `[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels= result=not-found ms=${elapsed2}`
21065
21167
  );
21066
21168
  return c.json({ error: "Node not found" }, 404);
21067
21169
  }
@@ -21070,12 +21172,12 @@ app27.post("/", requireAdminSession, async (c) => {
21070
21172
  session,
21071
21173
  accountId,
21072
21174
  elementId,
21073
- by: "graph-page"
21175
+ by
21074
21176
  });
21075
21177
  const elapsed = Date.now() - started;
21076
21178
  const labelSummary = (result.labels.length > 0 ? result.labels : preflightLabels).join(",");
21077
21179
  console.error(
21078
- `[graph-page] delete account=${accountId} elementId=${elementId} labels=${labelSummary} result=ok ms=${elapsed}`
21180
+ `[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels=${labelSummary} result=ok ms=${elapsed}`
21079
21181
  );
21080
21182
  const response = {
21081
21183
  elementId: result.nodeId,
@@ -21088,7 +21190,7 @@ app27.post("/", requireAdminSession, async (c) => {
21088
21190
  const elapsed = Date.now() - started;
21089
21191
  const message = err instanceof Error ? err.message : String(err);
21090
21192
  console.error(
21091
- `[graph-page] delete account=${accountId} elementId=${elementId} labels= result=error ms=${elapsed} err="${message}"`
21193
+ `[graph-page] delete account=${accountId} elementId=${elementId} by=${by} labels= result=error ms=${elapsed} err="${message}"`
21092
21194
  );
21093
21195
  return c.json({ error: `Delete failed: ${message}` }, 503);
21094
21196
  } finally {