@rubytech/create-maxy-code 0.1.441 → 0.1.443

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 (95) hide show
  1. package/dist/__tests__/converge-client-admins.test.js +50 -0
  2. package/dist/converge-client-admins.js +67 -0
  3. package/dist/index.js +107 -1
  4. package/package.json +1 -1
  5. package/payload/platform/docs/superpowers/plans/2026-07-13-account-schema-ontology-projection.md +547 -0
  6. package/payload/platform/docs/superpowers/plans/2026-07-14-graph-top-level-labels-ontology-single-source.md +458 -0
  7. package/payload/platform/docs/superpowers/specs/2026-07-13-account-schema-ontology-projection-design.md +178 -0
  8. package/payload/platform/docs/superpowers/specs/2026-07-14-graph-top-level-labels-ontology-single-source-design.md +119 -0
  9. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  10. package/payload/platform/plugins/admin/PLUGIN.md +7 -1
  11. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +19 -2
  12. package/payload/platform/plugins/admin/mcp/dist/index.js +126 -1
  13. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  14. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +30 -0
  15. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -1
  16. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +78 -0
  17. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -1
  18. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +37 -2
  19. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  20. package/payload/platform/plugins/docs/references/troubleshooting.md +35 -0
  21. package/payload/platform/plugins/memory/PLUGIN.md +2 -0
  22. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.js +12 -0
  23. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/resolve-active-vertical.test.js.map +1 -1
  24. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.d.ts +9 -1
  25. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.d.ts.map +1 -1
  26. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.js +19 -2
  27. package/payload/platform/plugins/memory/mcp/dist/lib/resolve-active-vertical.js.map +1 -1
  28. package/payload/platform/plugins/memory/references/schema-estate-agent.md +8 -3
  29. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js +37 -4
  30. package/payload/platform/plugins/outlook/mcp/dist/__tests__/device-flow.test.js.map +1 -1
  31. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts +27 -3
  32. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.d.ts.map +1 -1
  33. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js +52 -11
  34. package/payload/platform/plugins/outlook/mcp/dist/auth/device-flow.js.map +1 -1
  35. package/payload/platform/plugins/outlook/mcp/dist/index.js +1 -1
  36. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  37. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts +5 -0
  38. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.d.ts.map +1 -1
  39. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js +32 -0
  40. package/payload/platform/plugins/outlook/mcp/dist/tools/account-register.js.map +1 -1
  41. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +20 -22
  42. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  43. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -1
  44. package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +58 -0
  45. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +6 -0
  46. package/payload/platform/scripts/lib/account-schema-owned-dirs.py +143 -10
  47. package/payload/platform/scripts/lib/provision-account-dir.sh +5 -0
  48. package/payload/platform/scripts/logs-read.sh +24 -6
  49. package/payload/platform/scripts/logs-read.test.sh +42 -0
  50. package/payload/platform/scripts/name-glsmith-owner.sh +239 -0
  51. package/payload/platform/scripts/rss-sampler.sh +107 -0
  52. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  53. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  54. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  55. package/payload/server/public/assets/{AdminLoginScreens-CukwQufW.js → AdminLoginScreens-BJVwtpLH.js} +1 -1
  56. package/payload/server/public/assets/{AdminShell-BwEBH6tT.js → AdminShell-CMu8_Xwn.js} +1 -1
  57. package/payload/server/public/assets/{Checkbox-B629mOPp.js → Checkbox-CzD0ORqA.js} +1 -1
  58. package/payload/server/public/assets/{admin-CdFRCUUZ.js → admin-D0yk2LMq.js} +1 -1
  59. package/payload/server/public/assets/{browser-BWR59IkB.js → browser-CvfbgznO.js} +1 -1
  60. package/payload/server/public/assets/{calendar-kuTcxX1t.js → calendar-fw4WethJ.js} +1 -1
  61. package/payload/server/public/assets/chat-DhD-NES_.js +1 -0
  62. package/payload/server/public/assets/chevron-left-B3xSeEDR.js +1 -0
  63. package/payload/server/public/assets/data-CUnDzDox.js +1 -0
  64. package/payload/server/public/assets/{file-download-4mKJxUi-.js → file-download-Px4s0Bhp.js} +1 -1
  65. package/payload/server/public/assets/{graph-CboWM2_-.js → graph-DbT8nA6R.js} +1 -1
  66. package/payload/server/public/assets/{graph-labels-B8YAdo3Z.js → graph-labels-C1YIyLiA.js} +1 -1
  67. package/payload/server/public/assets/{maximize-2-CxstTgoK.js → maximize-2-BwphzI9S.js} +1 -1
  68. package/payload/server/public/assets/{operator-oSaB1mya.js → operator-zcIfeB14.js} +1 -1
  69. package/payload/server/public/assets/{page-DMeJHVfA.js → page-6fCnIb45.js} +2 -2
  70. package/payload/server/public/assets/page-CfQFAJia.js +1 -0
  71. package/payload/server/public/assets/{public-C54tl6V4.js → public-C0ehZCdE.js} +1 -1
  72. package/payload/server/public/assets/{rotate-ccw-COGQTi7n.js → rotate-ccw-C7BqkMec.js} +1 -1
  73. package/payload/server/public/assets/{tasks-B97VpwEJ.js → tasks-DlgiCafL.js} +1 -1
  74. package/payload/server/public/assets/{time-entry-format-C09rDHo9.js → time-entry-format-CyNYVDkq.js} +1 -1
  75. package/payload/server/public/assets/{useCopyFeedback-DHoYHiK9.js → useCopyFeedback-ByGUMN94.js} +1 -1
  76. package/payload/server/public/assets/useSubAccountSwitcher-1izQvPiD.css +1 -0
  77. package/payload/server/public/assets/useVoiceRecorder-XgpWvXpk.js +2 -0
  78. package/payload/server/public/browser.html +5 -5
  79. package/payload/server/public/calendar.html +6 -6
  80. package/payload/server/public/chat.html +12 -12
  81. package/payload/server/public/data.html +10 -10
  82. package/payload/server/public/graph.html +11 -11
  83. package/payload/server/public/index.html +13 -13
  84. package/payload/server/public/operator.html +14 -14
  85. package/payload/server/public/public.html +12 -12
  86. package/payload/server/public/tasks.html +5 -5
  87. package/payload/server/server.js +742 -321
  88. package/payload/platform/plugins/memory/references/schema-trades.md +0 -34
  89. package/payload/server/public/assets/chat-ejnq2rck.js +0 -1
  90. package/payload/server/public/assets/chevron-left-u4tjq7fI.js +0 -1
  91. package/payload/server/public/assets/data-dZbOyLdT.js +0 -1
  92. package/payload/server/public/assets/page-DEkI-Gl1.js +0 -1
  93. package/payload/server/public/assets/useSubAccountSwitcher-Dzmd7Aip.css +0 -1
  94. package/payload/server/public/assets/useVoiceRecorder-b7HzTFs0.js +0 -2
  95. /package/payload/server/public/assets/{useSubAccountSwitcher-DLJFISqf.js → useSubAccountSwitcher-s6WlRn52.js} +0 -0
@@ -1413,10 +1413,10 @@ var serveStatic = (options = { root: "" }) => {
1413
1413
  };
1414
1414
 
1415
1415
  // server/index.ts
1416
- import { readFileSync as readFileSync35, existsSync as existsSync35, watchFile } from "fs";
1416
+ import { readFileSync as readFileSync37, existsSync as existsSync35, watchFile } from "fs";
1417
1417
  import { spawn as spawn3 } from "child_process";
1418
1418
  import { createHash as createHash7 } from "crypto";
1419
- import { resolve as resolve33, join as join36, basename as basename12 } from "path";
1419
+ import { resolve as resolve34, join as join37, basename as basename12 } from "path";
1420
1420
  import { homedir as homedir4 } from "os";
1421
1421
  import { monitorEventLoopDelay } from "perf_hooks";
1422
1422
 
@@ -1793,7 +1793,7 @@ async function ensureAuth() {
1793
1793
  }
1794
1794
 
1795
1795
  // server/routes/health.ts
1796
- import { existsSync as existsSync5, readFileSync as readFileSync9 } from "fs";
1796
+ import { existsSync as existsSync5, readFileSync as readFileSync10 } from "fs";
1797
1797
  import { createConnection as createConnection2 } from "net";
1798
1798
 
1799
1799
  // app/lib/network.ts
@@ -2129,7 +2129,7 @@ var credsSaveQueue = Promise.resolve();
2129
2129
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2130
2130
  console.error(`${TAG2} draining credential save queue\u2026`);
2131
2131
  const timer2 = new Promise(
2132
- (resolve34) => setTimeout(() => resolve34("timeout"), timeoutMs)
2132
+ (resolve35) => setTimeout(() => resolve35("timeout"), timeoutMs)
2133
2133
  );
2134
2134
  const result = await Promise.race([
2135
2135
  credsSaveQueue.then(() => "drained"),
@@ -2292,11 +2292,11 @@ async function createWaSocket(opts) {
2292
2292
  return sock;
2293
2293
  }
2294
2294
  async function waitForConnection(sock) {
2295
- return new Promise((resolve34, reject) => {
2295
+ return new Promise((resolve35, reject) => {
2296
2296
  const handler = (update) => {
2297
2297
  if (update.connection === "open") {
2298
2298
  sock.ev.off("connection.update", handler);
2299
- resolve34();
2299
+ resolve35();
2300
2300
  }
2301
2301
  if (update.connection === "close") {
2302
2302
  sock.ev.off("connection.update", handler);
@@ -2401,7 +2401,7 @@ import { inspect as inspect2 } from "util";
2401
2401
 
2402
2402
  // app/lib/whatsapp/with-timeout.ts
2403
2403
  function withTimeout(label, promise, timeoutMs) {
2404
- return new Promise((resolve34, reject) => {
2404
+ return new Promise((resolve35, reject) => {
2405
2405
  const timer2 = setTimeout(() => {
2406
2406
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
2407
2407
  }, timeoutMs);
@@ -2409,7 +2409,7 @@ function withTimeout(label, promise, timeoutMs) {
2409
2409
  promise.then(
2410
2410
  (value) => {
2411
2411
  clearTimeout(timer2);
2412
- resolve34(value);
2412
+ resolve35(value);
2413
2413
  },
2414
2414
  (err) => {
2415
2415
  clearTimeout(timer2);
@@ -3489,10 +3489,10 @@ function readConversationSummaries(platformAccountId) {
3489
3489
  } catch {
3490
3490
  continue;
3491
3491
  }
3492
- const cached2 = summaryCache.get(file);
3493
- if (cached2 && cached2.size === size && cached2.mtimeMs === mtimeMs) {
3492
+ const cached3 = summaryCache.get(file);
3493
+ if (cached3 && cached3.size === size && cached3.mtimeMs === mtimeMs) {
3494
3494
  cacheHits++;
3495
- out.set(remoteJid, cached2.summary);
3495
+ out.set(remoteJid, cached3.summary);
3496
3496
  continue;
3497
3497
  }
3498
3498
  let summary;
@@ -3556,8 +3556,8 @@ async function persistWhatsAppMessage(input) {
3556
3556
  const { givenName, familyName } = splitName(input.pushName);
3557
3557
  const prev = sessionWriteLocks.get(input.cacheKey);
3558
3558
  let release;
3559
- const mine = new Promise((resolve34) => {
3560
- release = resolve34;
3559
+ const mine = new Promise((resolve35) => {
3560
+ release = resolve35;
3561
3561
  });
3562
3562
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3563
3563
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -3700,8 +3700,8 @@ import { resolve as resolve3 } from "path";
3700
3700
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3701
3701
  var cache = /* @__PURE__ */ new Map();
3702
3702
  function enumerateValidAccountIds(accountsDir) {
3703
- const cached2 = cache.get(accountsDir);
3704
- if (cached2 !== void 0) return cached2;
3703
+ const cached3 = cache.get(accountsDir);
3704
+ if (cached3 !== void 0) return cached3;
3705
3705
  let names;
3706
3706
  try {
3707
3707
  names = readdirSync2(accountsDir);
@@ -3843,7 +3843,7 @@ import {
3843
3843
  // app/lib/attachments.ts
3844
3844
  import { randomUUID as randomUUID3 } from "crypto";
3845
3845
  import { mkdir, writeFile } from "fs/promises";
3846
- import { realpathSync as realpathSync2, existsSync as existsSync4 } from "fs";
3846
+ import { realpathSync as realpathSync2, existsSync as existsSync4, readFileSync as readFileSync8, readdirSync as readdirSync3 } from "fs";
3847
3847
  import { resolve as resolve6, extname } from "path";
3848
3848
 
3849
3849
  // app/lib/data-path.ts
@@ -3926,11 +3926,15 @@ function resolveScratchpadPath(raw, scratchpadRootAbs) {
3926
3926
  import { unzipSync, strFromU8 } from "fflate";
3927
3927
  var DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
3928
3928
  var ODT = "application/vnd.oasis.opendocument.text";
3929
+ var XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
3930
+ var ODS = "application/vnd.oasis.opendocument.spreadsheet";
3929
3931
  var CONVERTIBLE_DOC_MIME = /* @__PURE__ */ new Set([
3930
3932
  DOCX,
3931
3933
  ODT,
3932
3934
  "text/rtf",
3933
- "application/rtf"
3935
+ "application/rtf",
3936
+ XLSX,
3937
+ ODS
3934
3938
  ]);
3935
3939
  function bareMime(mimeType) {
3936
3940
  return mimeType.split(";")[0].trim().toLowerCase();
@@ -3964,6 +3968,80 @@ function odtToText(buffer) {
3964
3968
  const stripped = withBreaks.replace(/<[^>]+>/g, "");
3965
3969
  return unescapeXml(stripped).replace(/\n{3,}/g, "\n\n").trim();
3966
3970
  }
3971
+ function sheetOrdinal(name) {
3972
+ const m = name.match(/sheet(\d+)\.xml$/);
3973
+ return m ? parseInt(m[1], 10) : 0;
3974
+ }
3975
+ function xlsxToText(buffer) {
3976
+ const files = unzipSync(new Uint8Array(buffer));
3977
+ const shared = [];
3978
+ const sharedBytes = files["xl/sharedStrings.xml"];
3979
+ if (sharedBytes) {
3980
+ for (const si of strFromU8(sharedBytes).split("</si>")) {
3981
+ if (!si.includes("<si")) continue;
3982
+ const base = si.replace(/<rPh\b[\s\S]*?<\/rPh>/g, "");
3983
+ let s = "";
3984
+ for (const m of base.matchAll(/<t\b[^>]*>([\s\S]*?)<\/t>/g)) s += m[1];
3985
+ shared.push(unescapeXml(s));
3986
+ }
3987
+ }
3988
+ const sheetKeys = Object.keys(files).filter((n) => /^xl\/worksheets\/sheet\d+\.xml$/.test(n)).sort((a, b) => sheetOrdinal(a) - sheetOrdinal(b));
3989
+ const sheets = [];
3990
+ for (const key of sheetKeys) {
3991
+ const xml = strFromU8(files[key]);
3992
+ const rows = [];
3993
+ for (const rowXml of xml.split("</row>")) {
3994
+ if (!rowXml.includes("<row")) continue;
3995
+ const cells = [];
3996
+ for (const c of rowXml.matchAll(/<c\b([^>]*)>([\s\S]*?)<\/c>/g)) {
3997
+ const attrs = c[1];
3998
+ const body = c[2];
3999
+ if (/\bt="inlineStr"/.test(attrs)) {
4000
+ let s = "";
4001
+ for (const m of body.matchAll(/<t\b[^>]*>([\s\S]*?)<\/t>/g)) s += m[1];
4002
+ cells.push(unescapeXml(s));
4003
+ continue;
4004
+ }
4005
+ const v = body.match(/<v\b[^>]*>([\s\S]*?)<\/v>/);
4006
+ if (!v) {
4007
+ cells.push("");
4008
+ continue;
4009
+ }
4010
+ if (/\bt="s"/.test(attrs)) {
4011
+ const idx = parseInt(v[1], 10);
4012
+ cells.push(Number.isInteger(idx) && idx >= 0 && idx < shared.length ? shared[idx] : "");
4013
+ } else {
4014
+ cells.push(unescapeXml(v[1]));
4015
+ }
4016
+ }
4017
+ while (cells.length && cells[cells.length - 1] === "") cells.pop();
4018
+ if (cells.length) rows.push(cells.join(" "));
4019
+ }
4020
+ if (rows.length) sheets.push(rows.join("\n"));
4021
+ }
4022
+ return sheets.join("\n\n");
4023
+ }
4024
+ function odsToText(buffer) {
4025
+ const xml = unzipEntry(buffer, "content.xml");
4026
+ const tables = [];
4027
+ for (const tableXml of xml.split(/<table:table(?=[\s>])/).slice(1)) {
4028
+ const rows = [];
4029
+ for (const rowXml of tableXml.split(/<table:table-row\b/).slice(1)) {
4030
+ const cells = rowXml.split(/<table:(?:covered-)?table-cell\b/).slice(1).map((frag) => {
4031
+ const withBreaks = frag.replace(/<text:line-break\b[^>]*\/?>/g, "\n").replace(/<text:tab\b[^>]*\/?>/g, " ");
4032
+ let s = "";
4033
+ for (const m of withBreaks.matchAll(/<text:p\b[^>]*>([\s\S]*?)<\/text:p>/g)) {
4034
+ s += (s ? "\n" : "") + m[1].replace(/<[^>]+>/g, "");
4035
+ }
4036
+ return unescapeXml(s);
4037
+ });
4038
+ while (cells.length && cells[cells.length - 1] === "") cells.pop();
4039
+ if (cells.length) rows.push(cells.join(" "));
4040
+ }
4041
+ if (rows.length) tables.push(rows.join("\n"));
4042
+ }
4043
+ return tables.join("\n\n");
4044
+ }
3967
4045
  var RTF_DROP_DEST = /^\\(?:\*|fonttbl|colortbl|stylesheet|listtable|info|pict)\b/;
3968
4046
  function stripRtfDestinations(s) {
3969
4047
  let out = "";
@@ -4004,6 +4082,8 @@ function docToText(buffer, mimeType) {
4004
4082
  const mime = bareMime(mimeType);
4005
4083
  if (mime === DOCX) return docxToText(buffer);
4006
4084
  if (mime === ODT) return odtToText(buffer);
4085
+ if (mime === XLSX) return xlsxToText(buffer);
4086
+ if (mime === ODS) return odsToText(buffer);
4007
4087
  if (mime === "text/rtf" || mime === "application/rtf") return rtfToText(buffer.toString("latin1"));
4008
4088
  return null;
4009
4089
  }
@@ -4033,6 +4113,21 @@ var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
4033
4113
  "application/vnd.oasis.opendocument.text",
4034
4114
  "text/rtf",
4035
4115
  "application/rtf",
4116
+ // Task 1607 — spreadsheets + presentations operators send. These land
4117
+ // download-only in the reader; the Read tool cannot parse the binary
4118
+ // containers and agent-readable text extraction for them is task 1608.
4119
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
4120
+ // xlsx
4121
+ "application/vnd.ms-excel",
4122
+ // xls
4123
+ "application/vnd.oasis.opendocument.spreadsheet",
4124
+ // ods
4125
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
4126
+ // pptx
4127
+ "application/vnd.ms-powerpoint",
4128
+ // ppt
4129
+ "application/vnd.oasis.opendocument.presentation",
4130
+ // odp
4036
4131
  // Task 1240 — voice recordings persist as playable clips in the admin bubble.
4037
4132
  // These match AUDIO_MIME_TYPES in app/lib/stt/audio-mime.ts exactly.
4038
4133
  "audio/ogg",
@@ -4049,6 +4144,7 @@ var MAX_FILES_PER_MESSAGE = 5;
4049
4144
  var MAX_DATA_UPLOAD_BYTES = 2 * 1024 * 1024 * 1024;
4050
4145
  var MAX_ZIP_UNCOMPRESSED_BYTES = 100 * 1024 * 1024;
4051
4146
  var EXTRACTED_TEXT_SUFFIX = ".extracted.txt";
4147
+ var ATTACHMENT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4052
4148
  function assertSupportedMime(mimeType) {
4053
4149
  const bare = mimeType.split(";")[0].trim().toLowerCase();
4054
4150
  if (!SUPPORTED_MIME_TYPES.has(bare)) {
@@ -4057,22 +4153,47 @@ function assertSupportedMime(mimeType) {
4057
4153
  );
4058
4154
  }
4059
4155
  }
4060
- function isSupportedMime(mimeType) {
4061
- return SUPPORTED_MIME_TYPES.has(mimeType.split(";")[0].trim().toLowerCase());
4156
+ function isInlineSafeMime(mimeType) {
4157
+ const bare = mimeType.split(";")[0].trim().toLowerCase();
4158
+ if (bare === "image/svg+xml") return false;
4159
+ return bare.startsWith("image/") || bare === "application/pdf" || bare.startsWith("audio/") || bare === "video/mp4";
4062
4160
  }
4063
4161
  async function storeMediaBuffer(accountId, attachmentId, filename, mimeType, buffer) {
4064
- assertSupportedMime(mimeType);
4065
4162
  if (buffer.byteLength > MAX_FILE_SIZE_BYTES) {
4066
4163
  throw new Error(
4067
4164
  `Media exceeds the 50 MB limit (${(buffer.byteLength / 1024 / 1024).toFixed(1)} MB).`
4068
4165
  );
4069
4166
  }
4070
- return writeAttachment(accountId, "", filename, mimeType, buffer.byteLength, buffer, attachmentId, true);
4167
+ return writeAttachment(accountId, "", filename, mimeType, buffer.byteLength, buffer, attachmentId);
4071
4168
  }
4072
4169
  function attachmentExists(accountId, attachmentId) {
4073
4170
  return existsSync4(resolve6(uploadsDirFor(accountId, "", attachmentId), `${attachmentId}.meta.json`));
4074
4171
  }
4075
- async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes, buffer, explicitId, skipDocToText = false) {
4172
+ function resolveStoredAttachment(accountId, attachmentId) {
4173
+ if (!ATTACHMENT_ID_RE.test(attachmentId)) return null;
4174
+ const dir = uploadsDirFor(accountId, "", attachmentId);
4175
+ let meta;
4176
+ try {
4177
+ meta = JSON.parse(readFileSync8(resolve6(dir, `${attachmentId}.meta.json`), "utf8"));
4178
+ } catch {
4179
+ return null;
4180
+ }
4181
+ if (!meta || typeof meta !== "object") return null;
4182
+ const { filename, mimeType } = meta;
4183
+ if (typeof filename !== "string" || typeof mimeType !== "string") return null;
4184
+ let entries;
4185
+ try {
4186
+ entries = readdirSync3(dir, { withFileTypes: true });
4187
+ } catch {
4188
+ return null;
4189
+ }
4190
+ const dataFile = entries.find((e) => e.isFile() && !e.name.endsWith(".meta.json") && !e.name.endsWith(EXTRACTED_TEXT_SUFFIX));
4191
+ if (!dataFile) return null;
4192
+ const readableSibling = `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`;
4193
+ const readableTextPath = entries.some((e) => e.isFile() && e.name === readableSibling) ? resolve6(dir, readableSibling) : void 0;
4194
+ return { attachmentId, filename, mimeType, path: resolve6(dir, dataFile.name), readableTextPath };
4195
+ }
4196
+ async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes, buffer, explicitId) {
4076
4197
  const attachmentId = explicitId ?? randomUUID3();
4077
4198
  const dir = uploadsDirFor(accountId, subPath, attachmentId);
4078
4199
  await mkdir(dir, { recursive: true });
@@ -4090,11 +4211,23 @@ async function writeAttachment(accountId, subPath, filename, mimeType, sizeBytes
4090
4211
  await writeFile(storagePath, buffer);
4091
4212
  await writeFile(metaPath, JSON.stringify(meta, null, 2));
4092
4213
  let readableTextPath;
4093
- if (!skipDocToText && CONVERTIBLE_DOC_MIME.has(mimeType.split(";")[0].trim().toLowerCase())) {
4094
- const text = docToText(buffer, mimeType);
4214
+ const bare = mimeType.split(";")[0].trim().toLowerCase();
4215
+ if (CONVERTIBLE_DOC_MIME.has(bare)) {
4216
+ let text = null;
4217
+ try {
4218
+ text = docToText(buffer, mimeType);
4219
+ } catch {
4220
+ text = null;
4221
+ }
4222
+ console.error(`[doc-to-text] op=convert mime=${bare} ok=${text !== null} chars=${text?.length ?? 0}`);
4095
4223
  if (text) {
4096
- readableTextPath = resolve6(dir, `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`);
4097
- await writeFile(readableTextPath, text);
4224
+ const siblingPath = resolve6(dir, `${attachmentId}${EXTRACTED_TEXT_SUFFIX}`);
4225
+ try {
4226
+ await writeFile(siblingPath, text);
4227
+ readableTextPath = siblingPath;
4228
+ } catch (err) {
4229
+ console.error(`[doc-to-text] op=sibling-write mime=${bare} ok=false reason=${String(err).slice(0, 80)}`);
4230
+ }
4098
4231
  }
4099
4232
  }
4100
4233
  return { attachmentId, filename, storagePath, metaPath, mimeType, sizeBytes, readableTextPath };
@@ -4141,6 +4274,13 @@ var MIME_BY_EXT = {
4141
4274
  ".odt": "application/vnd.oasis.opendocument.text",
4142
4275
  ".rtf": "text/rtf",
4143
4276
  ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
4277
+ // Task 1607 — legacy + OpenDocument office extensions, so a device-stored file
4278
+ // attached to a WhatsApp reply (detectMimeType → isMimeAdmitted, whatsapp-reader.ts)
4279
+ // resolves to the mime now admitted in SUPPORTED_MIME_TYPES rather than octet-stream.
4280
+ ".xls": "application/vnd.ms-excel",
4281
+ ".ods": "application/vnd.oasis.opendocument.spreadsheet",
4282
+ ".ppt": "application/vnd.ms-powerpoint",
4283
+ ".odp": "application/vnd.oasis.opendocument.presentation",
4144
4284
  ".ics": "text/calendar"
4145
4285
  };
4146
4286
  function detectMimeType(filePath) {
@@ -4978,20 +5118,6 @@ function attachSelfIdRefreshOnCredsUpdate(conn) {
4978
5118
  function reloadConfig(accountConfig) {
4979
5119
  loadConfig(accountConfig);
4980
5120
  }
4981
- function getMessages(accountId, jid, limit) {
4982
- const entries = messageStore.get(messageStoreKey(accountId, jid)) ?? [];
4983
- return limit ? entries.slice(-limit) : entries.slice();
4984
- }
4985
- function listConversationJids(accountId) {
4986
- const prefix = messageStoreKey(accountId, "");
4987
- const jids = [];
4988
- for (const key of messageStore.keys()) {
4989
- if (key.startsWith(prefix)) {
4990
- jids.push(key.slice(prefix.length));
4991
- }
4992
- }
4993
- return jids;
4994
- }
4995
5121
  function listStoreAccountKeys() {
4996
5122
  const accounts = /* @__PURE__ */ new Set();
4997
5123
  for (const key of messageStore.keys()) {
@@ -5133,11 +5259,11 @@ async function connectWithReconnect(conn) {
5133
5259
  console.error(
5134
5260
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5135
5261
  );
5136
- await new Promise((resolve34) => {
5137
- const timer2 = setTimeout(resolve34, delay);
5262
+ await new Promise((resolve35) => {
5263
+ const timer2 = setTimeout(resolve35, delay);
5138
5264
  conn.abortController.signal.addEventListener("abort", () => {
5139
5265
  clearTimeout(timer2);
5140
- resolve34();
5266
+ resolve35();
5141
5267
  }, { once: true });
5142
5268
  });
5143
5269
  }
@@ -5145,16 +5271,16 @@ async function connectWithReconnect(conn) {
5145
5271
  }
5146
5272
  }
5147
5273
  function waitForDisconnectEvent(conn) {
5148
- return new Promise((resolve34) => {
5274
+ return new Promise((resolve35) => {
5149
5275
  if (!conn.sock) {
5150
- resolve34();
5276
+ resolve35();
5151
5277
  return;
5152
5278
  }
5153
5279
  const sock = conn.sock;
5154
5280
  const handler = (update) => {
5155
5281
  if (update.connection === "close") {
5156
5282
  sock.ev.off("connection.update", handler);
5157
- resolve34();
5283
+ resolve35();
5158
5284
  }
5159
5285
  };
5160
5286
  sock.ev.on("connection.update", handler);
@@ -5205,7 +5331,7 @@ function monitorInbound(conn) {
5205
5331
  if (!msg.message) continue;
5206
5332
  let receiptDeferredToWorker = false;
5207
5333
  const extracted = extractMessage(msg);
5208
- const mediaServable = !!(extracted.mediaType && extracted.mimetype && isSupportedMime(extracted.mimetype));
5334
+ const mediaServable = !!(extracted.mediaType && extracted.mimetype);
5209
5335
  if (extracted.text || mediaServable) {
5210
5336
  const isGroup = isGroupJid(remoteJid);
5211
5337
  const senderJid = isGroup ? msg.key.participant ?? remoteJid : remoteJid;
@@ -5476,8 +5602,8 @@ async function handleInboundMessage(conn, msg) {
5476
5602
  const conversationKey = isGroup ? remoteJid : senderPhone;
5477
5603
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
5478
5604
  let resolvePending;
5479
- const sttPending = new Promise((resolve34) => {
5480
- resolvePending = resolve34;
5605
+ const sttPending = new Promise((resolve35) => {
5606
+ resolvePending = resolve35;
5481
5607
  });
5482
5608
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
5483
5609
  try {
@@ -5602,7 +5728,7 @@ async function handleInboundMessage(conn, msg) {
5602
5728
  // app/lib/vnc.ts
5603
5729
  import { spawnSync, execFileSync } from "child_process";
5604
5730
  import { createConnection } from "net";
5605
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
5731
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
5606
5732
  import { resolve as resolve7 } from "path";
5607
5733
  var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "..");
5608
5734
  var VNC_SCRIPT = resolve7(PLATFORM_ROOT4, "scripts/vnc.sh");
@@ -5660,7 +5786,7 @@ function discoverNativeDisplay() {
5660
5786
  const leaderPid = leaderResult.stdout?.trim();
5661
5787
  if (leaderPid) {
5662
5788
  try {
5663
- const environ = readFileSync8(`/proc/${leaderPid}/environ`, "utf8");
5789
+ const environ = readFileSync9(`/proc/${leaderPid}/environ`, "utf8");
5664
5790
  const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
5665
5791
  if (match) waylandDisplay = match.split("=")[1];
5666
5792
  } catch {
@@ -5905,20 +6031,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
5905
6031
 
5906
6032
  // server/routes/health.ts
5907
6033
  function checkPort(port2, timeoutMs = 500) {
5908
- return new Promise((resolve34) => {
6034
+ return new Promise((resolve35) => {
5909
6035
  const socket = createConnection2(port2, "127.0.0.1");
5910
6036
  socket.setTimeout(timeoutMs);
5911
6037
  socket.once("connect", () => {
5912
6038
  socket.destroy();
5913
- resolve34(true);
6039
+ resolve35(true);
5914
6040
  });
5915
6041
  socket.once("error", () => {
5916
6042
  socket.destroy();
5917
- resolve34(false);
6043
+ resolve35(false);
5918
6044
  });
5919
6045
  socket.once("timeout", () => {
5920
6046
  socket.destroy();
5921
- resolve34(false);
6047
+ resolve35(false);
5922
6048
  });
5923
6049
  });
5924
6050
  }
@@ -5928,7 +6054,7 @@ app.get("/", async (c) => {
5928
6054
  let pinConfigured = false;
5929
6055
  try {
5930
6056
  if (existsSync5(USERS_FILE)) {
5931
- const raw = readFileSync9(USERS_FILE, "utf-8").trim();
6057
+ const raw = readFileSync10(USERS_FILE, "utf-8").trim();
5932
6058
  if (raw) {
5933
6059
  const users = JSON.parse(raw);
5934
6060
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -6600,13 +6726,13 @@ async function sendTelegramText(botToken, chatId, text) {
6600
6726
 
6601
6727
  // server/routes/whatsapp.ts
6602
6728
  import { join as join10, resolve as resolve10 } from "path";
6603
- import { readdirSync as readdirSync4, readFileSync as readFileSync11, existsSync as existsSync7 } from "fs";
6729
+ import { readdirSync as readdirSync5, readFileSync as readFileSync12, existsSync as existsSync7 } from "fs";
6604
6730
 
6605
6731
  // app/lib/whatsapp/login.ts
6606
6732
  import { randomUUID as randomUUID6 } from "crypto";
6607
6733
 
6608
6734
  // app/lib/whatsapp/config-persist.ts
6609
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
6735
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
6610
6736
  import { resolve as resolve8, join as join8, dirname as dirname2, basename as basename2 } from "path";
6611
6737
  var TAG17 = "[whatsapp:config]";
6612
6738
  function configPath(accountDir) {
@@ -6615,7 +6741,7 @@ function configPath(accountDir) {
6615
6741
  function readConfig(accountDir) {
6616
6742
  const path2 = configPath(accountDir);
6617
6743
  if (!existsSync6(path2)) throw new Error(`account.json not found at ${path2}`);
6618
- return JSON.parse(readFileSync10(path2, "utf-8"));
6744
+ return JSON.parse(readFileSync11(path2, "utf-8"));
6619
6745
  }
6620
6746
  function writeConfig(accountDir, config) {
6621
6747
  const path2 = configPath(accountDir);
@@ -7195,8 +7321,8 @@ async function startLogin(opts) {
7195
7321
  await clearAuth(authDir);
7196
7322
  let resolveCode = null;
7197
7323
  let rejectCode = null;
7198
- const codePromise = new Promise((resolve34, reject) => {
7199
- resolveCode = resolve34;
7324
+ const codePromise = new Promise((resolve35, reject) => {
7325
+ resolveCode = resolve35;
7200
7326
  rejectCode = reject;
7201
7327
  });
7202
7328
  const codeTimer = setTimeout(
@@ -7536,13 +7662,13 @@ function serializeWhatsAppSchema() {
7536
7662
  }
7537
7663
 
7538
7664
  // app/lib/whatsapp/status-reconcile.ts
7539
- import { readdirSync as readdirSync3 } from "fs";
7665
+ import { readdirSync as readdirSync4 } from "fs";
7540
7666
  import { join as join9 } from "path";
7541
7667
  var TAG20 = "[whatsapp:reconcile]";
7542
7668
  var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
7543
7669
  function listCredsAccountIds(credsRoot) {
7544
7670
  try {
7545
- return readdirSync3(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join9(credsRoot, e.name))).map((e) => e.name);
7671
+ return readdirSync4(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join9(credsRoot, e.name))).map((e) => e.name);
7546
7672
  } catch {
7547
7673
  return [];
7548
7674
  }
@@ -7689,6 +7815,15 @@ function authorizeRecallRead(input) {
7689
7815
  // server/routes/whatsapp.ts
7690
7816
  var TAG21 = "[whatsapp:api]";
7691
7817
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
7818
+ function recallAttachment(storeAccountId, attachmentId) {
7819
+ if (!attachmentId) return void 0;
7820
+ const a = resolveStoredAttachment(storeAccountId, attachmentId);
7821
+ if (!a) {
7822
+ console.error(`[whatsapp-read-tool] op=attachment-miss attachmentId=${attachmentId.slice(0, 8)} storeAccount=${storeAccountId}`);
7823
+ return void 0;
7824
+ }
7825
+ return a;
7826
+ }
7692
7827
  function projectionFor(requestedAccountId) {
7693
7828
  const validAccounts = listValidAccounts();
7694
7829
  if (validAccounts.length <= 1) return { storeAccountId: requestedAccountId, managerPhones: null };
@@ -7880,13 +8015,13 @@ app2.post("/config", async (c) => {
7880
8015
  const agents = [];
7881
8016
  if (existsSync7(agentsDir)) {
7882
8017
  try {
7883
- const entries = readdirSync4(agentsDir, { withFileTypes: true });
8018
+ const entries = readdirSync5(agentsDir, { withFileTypes: true });
7884
8019
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
7885
8020
  if (!entry.isDirectory() || entry.name === "admin") continue;
7886
8021
  const configPath2 = resolve10(agentsDir, entry.name, "config.json");
7887
8022
  if (!existsSync7(configPath2)) continue;
7888
8023
  try {
7889
- const config = JSON.parse(readFileSync11(configPath2, "utf-8"));
8024
+ const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
7890
8025
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
7891
8026
  } catch {
7892
8027
  console.error(`${TAG21} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
@@ -8031,20 +8166,23 @@ app2.get("/conversations", (c) => {
8031
8166
  const requestedAccountId = validateAccountId(c.req.query("accountId") ?? null);
8032
8167
  const denial = callerScopeDenial(c, requestedAccountId);
8033
8168
  if (denial) return denial;
8034
- const accountId = resolveReadStoreKey(requestedAccountId);
8035
- const jids = listConversationJids(accountId);
8036
- const conversations = jids.map((jid) => {
8037
- const messages = getMessages(accountId, jid);
8038
- const lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
8169
+ const projection = projectionFor(requestedAccountId);
8170
+ const storeAccountId = resolveReadStoreKey(projection.storeAccountId);
8171
+ const projecting = projection.managerPhones != null;
8172
+ const managerPhonesBound = projection.managerPhones?.length ?? 0;
8173
+ const allConversations = readAllConversations(storeAccountId);
8174
+ const scoped = projection.managerPhones ? filterConversationsForSubAccount(allConversations, projection.managerPhones) : allConversations;
8175
+ const conversations = [...scoped.entries()].map(([jid, messages]) => {
8176
+ const last = messages.length > 0 ? messages[messages.length - 1] : null;
8039
8177
  return {
8040
8178
  jid,
8041
8179
  type: isGroupJid(jid) ? "group" : "dm",
8042
- lastMessageTimestamp: lastMessage?.timestamp ?? null,
8180
+ lastMessageTimestamp: last ? Math.floor(Date.parse(last.dateSent) / 1e3) : null,
8043
8181
  messageCount: messages.length
8044
8182
  };
8045
8183
  });
8046
8184
  conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
8047
- console.error(`${TAG21} conversations requested=${requestedAccountId} account=${accountId} storeKeyFound=${jids.length > 0} count=${conversations.length}`);
8185
+ console.error(`${TAG21} conversations requested=${requestedAccountId} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
8048
8186
  return c.json({ conversations });
8049
8187
  } catch (err) {
8050
8188
  console.error(`${TAG21} conversations error: ${String(err)}`);
@@ -8056,7 +8194,6 @@ app2.get("/messages", (c) => {
8056
8194
  const requestedAccountId = validateAccountId(c.req.query("accountId") ?? null);
8057
8195
  const denial = callerScopeDenial(c, requestedAccountId);
8058
8196
  if (denial) return denial;
8059
- const accountId = resolveReadStoreKey(requestedAccountId);
8060
8197
  const jid = c.req.query("jid");
8061
8198
  if (!jid) {
8062
8199
  return c.json({ error: "Missing required parameter: jid" }, 400);
@@ -8064,9 +8201,33 @@ app2.get("/messages", (c) => {
8064
8201
  const limitParam = c.req.query("limit");
8065
8202
  const limit = limitParam ? parseInt(limitParam, 10) : void 0;
8066
8203
  const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
8067
- const messages = getMessages(accountId, jid, effectiveLimit);
8068
- const storeKeyFound = listConversationJids(accountId).length > 0;
8069
- console.error(`${TAG21} messages requested=${requestedAccountId} account=${accountId} jid=${jid} storeKeyFound=${storeKeyFound} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
8204
+ const projection = projectionFor(requestedAccountId);
8205
+ const storeAccountId = resolveReadStoreKey(projection.storeAccountId);
8206
+ const projecting = projection.managerPhones != null;
8207
+ const managerPhonesBound = projection.managerPhones?.length ?? 0;
8208
+ const storeMessages = readConversation(storeAccountId, jid);
8209
+ const counterpartyBound = !projecting || counterpartyBoundToSubAccount(
8210
+ projection.managerPhones,
8211
+ conversationCounterpartyPhone(jid, storeMessages[0]?.senderTelephone ?? "")
8212
+ );
8213
+ const bounded = counterpartyBound ? storeMessages : [];
8214
+ const limited = effectiveLimit ? bounded.slice(-effectiveLimit) : bounded;
8215
+ let withAttachments = 0;
8216
+ const messages = limited.map((m) => {
8217
+ const attachment = recallAttachment(storeAccountId, m.attachmentId);
8218
+ if (attachment) withAttachments += 1;
8219
+ return {
8220
+ id: m.messageId,
8221
+ sender: m.senderTelephone,
8222
+ senderName: m.senderName ?? void 0,
8223
+ body: m.body,
8224
+ timestamp: Math.floor(Date.parse(m.dateSent) / 1e3),
8225
+ fromMe: m.fromMe,
8226
+ quoted: m.quotedId ? { id: m.quotedId, sender: m.quotedSender ?? void 0 } : void 0,
8227
+ attachment
8228
+ };
8229
+ });
8230
+ console.error(`${TAG21} messages requested=${requestedAccountId} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
8070
8231
  return c.json({ messages });
8071
8232
  } catch (err) {
8072
8233
  console.error(`${TAG21} messages error: ${String(err)}`);
@@ -8090,6 +8251,7 @@ app2.get("/conversation-graph-state", async (c) => {
8090
8251
  const rows2 = [];
8091
8252
  const nameCache = /* @__PURE__ */ new Map();
8092
8253
  let unresolvedSenders = 0;
8254
+ let withAttachments2 = 0;
8093
8255
  let session = null;
8094
8256
  try {
8095
8257
  session = getSession();
@@ -8105,6 +8267,8 @@ app2.get("/conversation-graph-state", async (c) => {
8105
8267
  nameCache.set(m.senderTelephone, resolved);
8106
8268
  if (!resolved) unresolvedSenders += 1;
8107
8269
  }
8270
+ const attachment = recallAttachment(projection2.storeAccountId, m.attachmentId);
8271
+ if (attachment) withAttachments2 += 1;
8108
8272
  rows2.push({
8109
8273
  remoteJid: remoteJid2,
8110
8274
  messageId: m.messageId,
@@ -8112,7 +8276,8 @@ app2.get("/conversation-graph-state", async (c) => {
8112
8276
  fromMe: m.fromMe,
8113
8277
  senderTelephone: m.senderTelephone,
8114
8278
  senderName: nameCache.get(m.senderTelephone) ?? m.senderName,
8115
- body: m.body
8279
+ body: m.body,
8280
+ attachment
8116
8281
  });
8117
8282
  }
8118
8283
  }
@@ -8120,7 +8285,7 @@ app2.get("/conversation-graph-state", async (c) => {
8120
8285
  if (session) await session.close();
8121
8286
  }
8122
8287
  const ms2 = Date.now() - t0;
8123
- console.error(`[whatsapp-read-tool] op=query scope=${requestedAccountId} storeAccount=${projection2.storeAccountId} managerPhonesBound=${managerPhonesBound} resultCount=${rows2.length} unresolvedSenders=${unresolvedSenders} ms=${ms2}`);
8288
+ console.error(`[whatsapp-read-tool] op=query scope=${requestedAccountId} storeAccount=${projection2.storeAccountId} managerPhonesBound=${managerPhonesBound} resultCount=${rows2.length} unresolvedSenders=${unresolvedSenders} withAttachments=${withAttachments2} ms=${ms2}`);
8124
8289
  return c.json({ accountId: requestedAccountId, rows: rows2, unresolvedSenders, ms: ms2, managerPhonesBound });
8125
8290
  }
8126
8291
  let remoteJid;
@@ -8144,15 +8309,21 @@ app2.get("/conversation-graph-state", async (c) => {
8144
8309
  );
8145
8310
  const messages = counterpartyBound ? storeMessages : [];
8146
8311
  const sessionId = messages[0]?.sessionId ?? null;
8147
- const rows = messages.map((m) => ({
8148
- messageId: m.messageId,
8149
- dateSent: m.dateSent,
8150
- fromMe: m.fromMe,
8151
- senderTelephone: m.senderTelephone,
8152
- body: m.body
8153
- }));
8312
+ let withAttachments = 0;
8313
+ const rows = messages.map((m) => {
8314
+ const attachment = recallAttachment(projection.storeAccountId, m.attachmentId);
8315
+ if (attachment) withAttachments += 1;
8316
+ return {
8317
+ messageId: m.messageId,
8318
+ dateSent: m.dateSent,
8319
+ fromMe: m.fromMe,
8320
+ senderTelephone: m.senderTelephone,
8321
+ body: m.body,
8322
+ attachment
8323
+ };
8324
+ });
8154
8325
  const ms = Date.now() - t0;
8155
- console.error(`[whatsapp-read-tool] op=query mode=single scope=${accountId} storeAccount=${projection.storeAccountId} graphRows=${rows.length} sessionId=${sessionId ?? "null"} ms=${ms}`);
8326
+ console.error(`[whatsapp-read-tool] op=query mode=single scope=${accountId} storeAccount=${projection.storeAccountId} graphRows=${rows.length} sessionId=${sessionId ?? "null"} withAttachments=${withAttachments} ms=${ms}`);
8156
8327
  return c.json({
8157
8328
  cacheKey,
8158
8329
  sessionId,
@@ -8201,15 +8372,15 @@ app2.get("/group-info", async (c) => {
8201
8372
  var whatsapp_default = app2;
8202
8373
 
8203
8374
  // server/routes/whatsapp-reader.ts
8204
- import { readFileSync as readFileSync15, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync10, readdirSync as readdirSync9, realpathSync as realpathSync4 } from "fs";
8375
+ import { readFileSync as readFileSync16, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync10, readdirSync as readdirSync10, realpathSync as realpathSync4 } from "fs";
8205
8376
  import { basename as basename4, dirname as dirname4, isAbsolute, join as join15, relative as relative2, resolve as resolve12, sep as sep3 } from "path";
8206
8377
 
8207
8378
  // server/routes/admin/sidebar-sessions.ts
8208
- import { readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
8379
+ import { readdirSync as readdirSync8, readFileSync as readFileSync14, statSync as statSync4 } from "fs";
8209
8380
  import { dirname as dirname3, join as join13, resolve as resolve11 } from "path";
8210
8381
 
8211
8382
  // ../services/claude-session-manager/src/jsonl-path.ts
8212
- import { existsSync as existsSync8, readdirSync as readdirSync5 } from "fs";
8383
+ import { existsSync as existsSync8, readdirSync as readdirSync6 } from "fs";
8213
8384
  import { homedir } from "os";
8214
8385
  import { join as join11 } from "path";
8215
8386
  function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
@@ -8217,7 +8388,7 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
8217
8388
  if (!existsSync8(projectsRoot)) return null;
8218
8389
  let slugs;
8219
8390
  try {
8220
- slugs = readdirSync5(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
8391
+ slugs = readdirSync6(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
8221
8392
  } catch {
8222
8393
  return null;
8223
8394
  }
@@ -8233,14 +8404,14 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
8233
8404
 
8234
8405
  // app/lib/admin-identity/pin-validator.ts
8235
8406
  import { createHash as createHash3 } from "crypto";
8236
- import { existsSync as existsSync9, readFileSync as readFileSync12, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
8407
+ import { existsSync as existsSync9, readFileSync as readFileSync13, readdirSync as readdirSync7, statSync as statSync3 } from "fs";
8237
8408
  import { join as join12 } from "path";
8238
8409
  function hashPin(pin) {
8239
8410
  return createHash3("sha256").update(pin).digest("hex");
8240
8411
  }
8241
8412
  function readUsersFile(usersFilePath) {
8242
8413
  if (!existsSync9(usersFilePath)) return null;
8243
- const raw = readFileSync12(usersFilePath, "utf-8").trim();
8414
+ const raw = readFileSync13(usersFilePath, "utf-8").trim();
8244
8415
  if (!raw) return [];
8245
8416
  return JSON.parse(raw);
8246
8417
  }
@@ -8359,7 +8530,7 @@ function enumerateJsonls(projectsRoot) {
8359
8530
  const out = [];
8360
8531
  let slugs;
8361
8532
  try {
8362
- slugs = readdirSync7(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
8533
+ slugs = readdirSync8(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
8363
8534
  } catch (err) {
8364
8535
  if (err.code === "ENOENT") return out;
8365
8536
  throw err;
@@ -8368,7 +8539,7 @@ function enumerateJsonls(projectsRoot) {
8368
8539
  const slugDir = join13(projectsRoot, slug);
8369
8540
  let entries;
8370
8541
  try {
8371
- entries = readdirSync7(slugDir, { withFileTypes: true });
8542
+ entries = readdirSync8(slugDir, { withFileTypes: true });
8372
8543
  } catch (err) {
8373
8544
  const code = err.code ?? "unknown";
8374
8545
  console.error(`[admin-sessions-list] slug-skipped slug=${slug} code=${code}`);
@@ -8381,7 +8552,7 @@ function enumerateJsonls(projectsRoot) {
8381
8552
  const subDir = join13(slugDir, entry.name);
8382
8553
  let subEntries;
8383
8554
  try {
8384
- subEntries = readdirSync7(subDir, { withFileTypes: true });
8555
+ subEntries = readdirSync8(subDir, { withFileTypes: true });
8385
8556
  } catch (err) {
8386
8557
  const code = err.code ?? "unknown";
8387
8558
  console.error(`[admin-sessions-list] subagents-skipped slug=${slug} code=${code}`);
@@ -8396,7 +8567,7 @@ function enumerateJsonls(projectsRoot) {
8396
8567
  const archiveDir = join13(slugDir, entry.name);
8397
8568
  let archiveEntries;
8398
8569
  try {
8399
- archiveEntries = readdirSync7(archiveDir, { withFileTypes: true });
8570
+ archiveEntries = readdirSync8(archiveDir, { withFileTypes: true });
8400
8571
  } catch (err) {
8401
8572
  const code = err.code ?? "unknown";
8402
8573
  console.error(`[admin-sessions-list] archive-skipped slug=${slug} code=${code}`);
@@ -8440,7 +8611,7 @@ function loadUserTitles(accountDir) {
8440
8611
  const path2 = join13(accountDir, "session-titles.json");
8441
8612
  let raw;
8442
8613
  try {
8443
- raw = readFileSync13(path2, "utf8");
8614
+ raw = readFileSync14(path2, "utf8");
8444
8615
  } catch {
8445
8616
  return out;
8446
8617
  }
@@ -8466,7 +8637,7 @@ function readSidecarMeta(metaPath) {
8466
8637
  const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null, accountId: null };
8467
8638
  let raw;
8468
8639
  try {
8469
- raw = readFileSync13(metaPath, "utf8");
8640
+ raw = readFileSync14(metaPath, "utf8");
8470
8641
  } catch {
8471
8642
  return empty;
8472
8643
  }
@@ -8608,7 +8779,7 @@ app3.get("/", requireAdminSession, async (c) => {
8608
8779
  let body;
8609
8780
  let mtimeMs;
8610
8781
  try {
8611
- body = readFileSync13(path2, "utf8");
8782
+ body = readFileSync14(path2, "utf8");
8612
8783
  mtimeMs = statSync4(path2).mtimeMs;
8613
8784
  } catch {
8614
8785
  continue;
@@ -8968,7 +9139,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
8968
9139
  }
8969
9140
 
8970
9141
  // app/lib/whatsapp-reader/enrich-attachments.ts
8971
- import { readFileSync as readFileSync14, readdirSync as readdirSync8 } from "fs";
9142
+ import { readFileSync as readFileSync15, readdirSync as readdirSync9 } from "fs";
8972
9143
  import { join as join14 } from "path";
8973
9144
  var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
8974
9145
  function adminAttachmentIds(text) {
@@ -8990,7 +9161,7 @@ function adminAttachmentsFromText(text, uploadsBase) {
8990
9161
  function readSidecar(dir, attachmentId) {
8991
9162
  let parsed;
8992
9163
  try {
8993
- parsed = JSON.parse(readFileSync14(join14(dir, `${attachmentId}.meta.json`), "utf8"));
9164
+ parsed = JSON.parse(readFileSync15(join14(dir, `${attachmentId}.meta.json`), "utf8"));
8994
9165
  } catch {
8995
9166
  return null;
8996
9167
  }
@@ -9013,7 +9184,7 @@ function readAttachmentMeta(dir) {
9013
9184
  function listSessionAttachmentsInDir(dir) {
9014
9185
  let entries;
9015
9186
  try {
9016
- entries = readdirSync8(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
9187
+ entries = readdirSync9(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
9017
9188
  } catch {
9018
9189
  return [];
9019
9190
  }
@@ -9039,6 +9210,24 @@ function splitNewLines(buf, baseOffset) {
9039
9210
  };
9040
9211
  }
9041
9212
 
9213
+ // app/lib/whatsapp-reader/authorize-reply-file-ref.ts
9214
+ var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9215
+ function accountPartitionOwner(relative7) {
9216
+ const segs = relative7.split("/").filter(Boolean);
9217
+ if (segs.length < 2 || segs[0] !== "accounts") return null;
9218
+ return ACCOUNT_UUID_RE2.test(segs[1]) ? segs[1] : null;
9219
+ }
9220
+ function authorizeReplyFileRef(input) {
9221
+ const { relative: relative7, sendAccountId, sessionAccountId } = input;
9222
+ const segs = relative7.split("/").filter(Boolean);
9223
+ const inAccounts = segs.length >= 2 && segs[0] === "accounts";
9224
+ if (inAccounts && segs[1] === sendAccountId) return { allow: true, via: "own" };
9225
+ if (inAccounts && sessionAccountId && segs[1] === sessionAccountId) return { allow: true, via: "session" };
9226
+ if (accountPartitionOwner(relative7)) return { allow: false, reason: "cross-account" };
9227
+ if (segs[0] === "public" || inAccounts && segs[1] === "public") return { allow: false, reason: "public" };
9228
+ return { allow: false, reason: "foreign" };
9229
+ }
9230
+
9042
9231
  // server/routes/admin/manager-client.ts
9043
9232
  var SESSION_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9044
9233
  function isSessionId(value) {
@@ -9060,7 +9249,7 @@ function pickActivity(entries, grown, nowMs, concurrencyWindowMs) {
9060
9249
  }
9061
9250
 
9062
9251
  // app/admin-types.ts
9063
- var ACCEPT_MIME = "image/jpeg,image/png,image/gif,image/webp,application/pdf,text/plain,text/markdown,text/csv,text/html,text/calendar,application/zip,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.text,text/rtf,application/rtf,audio/ogg,audio/opus,audio/mp4,audio/x-m4a,audio/mpeg,audio/webm,audio/wav,video/mp4,.docx,.odt,.rtf,.opus,.ogg,.m4a,.mp4,.mp3,.wav,.webm";
9252
+ var ACCEPT_MIME = "image/jpeg,image/png,image/gif,image/webp,application/pdf,text/plain,text/markdown,text/csv,text/html,text/calendar,application/zip,application/x-zip-compressed,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.oasis.opendocument.text,text/rtf,application/rtf,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel,application/vnd.oasis.opendocument.spreadsheet,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.ms-powerpoint,application/vnd.oasis.opendocument.presentation,audio/ogg,audio/opus,audio/mp4,audio/x-m4a,audio/mpeg,audio/webm,audio/wav,video/mp4,.docx,.odt,.rtf,.xlsx,.xls,.ods,.pptx,.ppt,.odp,.opus,.ogg,.m4a,.mp4,.mp3,.wav,.webm";
9064
9253
  var SUPPORTED_MIME_SET = new Set(ACCEPT_MIME.split(",").filter((t) => !t.startsWith(".")));
9065
9254
  var MAX_ATTACHMENTS = 5;
9066
9255
  var MAX_FILE_BYTES = 50 * 1024 * 1024;
@@ -9073,6 +9262,11 @@ function isMimeAdmitted(mimeType, supportedSet) {
9073
9262
 
9074
9263
  // server/routes/whatsapp-reader.ts
9075
9264
  var app4 = new Hono();
9265
+ var DENY_COPY = {
9266
+ "cross-account": "This file belongs to another account. Open that account to send it.",
9267
+ public: "This file cannot be sent to a WhatsApp contact.",
9268
+ foreign: "This file cannot be sent to a WhatsApp contact."
9269
+ };
9076
9270
  app4.get("/conversations", requireAdminSession, async (c) => {
9077
9271
  const cacheKey = c.var.cacheKey;
9078
9272
  const scopeAccountId = getAccountIdForSession(cacheKey) ?? null;
@@ -9099,7 +9293,7 @@ app4.get("/conversations", requireAdminSession, async (c) => {
9099
9293
  }
9100
9294
  let body = "";
9101
9295
  try {
9102
- body = readFileSync15(path2, "utf8");
9296
+ body = readFileSync16(path2, "utf8");
9103
9297
  } catch {
9104
9298
  }
9105
9299
  const { title } = resolveTitle(body, sessionId, userTitles);
@@ -9268,7 +9462,7 @@ function openTaskKey(turns) {
9268
9462
  }
9269
9463
  function readSubagentMeta(dir, hex) {
9270
9464
  try {
9271
- const m = JSON.parse(readFileSync15(join15(dir, `agent-${hex}.meta.json`), "utf8"));
9465
+ const m = JSON.parse(readFileSync16(join15(dir, `agent-${hex}.meta.json`), "utf8"));
9272
9466
  if (typeof m.agentType === "string" && typeof m.description === "string" && typeof m.toolUseId === "string") {
9273
9467
  return { agentType: m.agentType, description: m.description, toolUseId: m.toolUseId };
9274
9468
  }
@@ -9291,7 +9485,7 @@ function runActivityTick(c) {
9291
9485
  c.parentGrewThisTick = false;
9292
9486
  let names = [];
9293
9487
  try {
9294
- names = readdirSync9(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
9488
+ names = readdirSync10(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
9295
9489
  } catch {
9296
9490
  }
9297
9491
  const entries = [];
@@ -9486,7 +9680,7 @@ app4.get("/directives", requireAdminSession, (c) => {
9486
9680
  if (!dir || !existsSync10(dir)) return c.json({ entries: [] });
9487
9681
  let names;
9488
9682
  try {
9489
- names = readdirSync9(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
9683
+ names = readdirSync10(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
9490
9684
  } catch {
9491
9685
  return c.json({ entries: [] });
9492
9686
  }
@@ -9515,7 +9709,7 @@ app4.get("/directive", requireAdminSession, (c) => {
9515
9709
  if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
9516
9710
  let body;
9517
9711
  try {
9518
- body = readFileSync15(path2, "utf8");
9712
+ body = readFileSync16(path2, "utf8");
9519
9713
  } catch {
9520
9714
  return c.json({ error: "not found" }, 404);
9521
9715
  }
@@ -9720,7 +9914,9 @@ app4.post("/reply", requireAdminSession, async (c) => {
9720
9914
  return c.json({ error: "invalid accountId" }, 400);
9721
9915
  }
9722
9916
  const validAccount = isValidAccountId(accountId);
9723
- console.log(`${MANUAL_TAG} op=scope sendId=${sendId} accountId=${accountId} validAccount=${validAccount}`);
9917
+ const cacheKey = c.var.cacheKey;
9918
+ const sessionAccountId = getAccountIdForSession(cacheKey) ?? null;
9919
+ console.log(`${MANUAL_TAG} op=scope sendId=${sendId} accountId=${accountId} validAccount=${validAccount} sessionAccount=${sessionAccountId ?? "null"} src=session`);
9724
9920
  if (!validAccount) {
9725
9921
  exit("rejected");
9726
9922
  return c.json({ error: "unknown account" }, 400);
@@ -9758,26 +9954,37 @@ app4.post("/reply", requireAdminSession, async (c) => {
9758
9954
  }
9759
9955
  }
9760
9956
  const resolvedRefs = [];
9957
+ const acct8 = accountId.slice(0, 8);
9958
+ const sess8 = sessionAccountId ? sessionAccountId.slice(0, 8) : "";
9761
9959
  for (const ref of fileRefs) {
9762
9960
  const resolved = resolveDataPath(ref);
9763
- const withinOwn = resolved.ok && !crossesForeignAccountPartition(resolved.relative, accountId) && isWithinOwnAccountPartition(resolved.relative, accountId);
9764
- let absolute = "";
9961
+ if (!resolved.ok) {
9962
+ console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner= sendAccount=${acct8} sessionAccount=${sess8} via=deny reason=unresolved ok=false`);
9963
+ exit("rejected");
9964
+ return c.json({ error: `device file not found: ${ref}`, reason: "file-ref-unresolved" }, 404);
9965
+ }
9966
+ const owner = accountPartitionOwner(resolved.relative);
9967
+ const owner8 = owner ? owner.slice(0, 8) : "";
9968
+ const decision = authorizeReplyFileRef({ relative: resolved.relative, sendAccountId: accountId, sessionAccountId });
9969
+ if (!decision.allow) {
9970
+ console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner=${owner8} sendAccount=${acct8} sessionAccount=${sess8} via=deny reason=${decision.reason} ok=false`);
9971
+ exit("rejected");
9972
+ return c.json({ error: DENY_COPY[decision.reason], reason: `file-ref-${decision.reason}` }, 403);
9973
+ }
9974
+ const absolute = resolved.absolute;
9765
9975
  let size = -1;
9766
- if (withinOwn) {
9767
- absolute = resolved.absolute;
9768
- try {
9769
- const st = statSync5(absolute);
9770
- size = st.isFile() ? st.size : -1;
9771
- } catch {
9772
- size = -1;
9773
- }
9976
+ try {
9977
+ const st = statSync5(absolute);
9978
+ size = st.isFile() ? st.size : -1;
9979
+ } catch {
9980
+ size = -1;
9774
9981
  }
9775
- const ok = size >= 0;
9776
- console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} resolved=${absolute} ok=${ok}`);
9777
- if (!ok) {
9982
+ if (size < 0) {
9983
+ console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner=${owner8} sendAccount=${acct8} sessionAccount=${sess8} via=${decision.via} reason=missing ok=false`);
9778
9984
  exit("rejected");
9779
9985
  return c.json({ error: `device file not found: ${ref}`, reason: "file-ref-unresolved" }, 404);
9780
9986
  }
9987
+ console.log(`${MANUAL_TAG} op=reply-file-ref sendId=${sendId} path=${ref} owner=${owner8} sendAccount=${acct8} sessionAccount=${sess8} via=${decision.via} reason=none ok=true`);
9781
9988
  if (size > MAX_FILE_BYTES) {
9782
9989
  exit("rejected");
9783
9990
  return c.json({ error: `"${basename4(absolute)}" exceeds the 50 MB limit` }, 400);
@@ -9872,7 +10079,7 @@ app4.post("/reply", requireAdminSession, async (c) => {
9872
10079
  }
9873
10080
  for (let i = 0; i < resolvedRefs.length; i++) {
9874
10081
  const { absolute, mime } = resolvedRefs[i];
9875
- const file = new File([readFileSync15(absolute)], basename4(absolute), { type: mime });
10082
+ const file = new File([readFileSync16(absolute)], basename4(absolute), { type: mime });
9876
10083
  const out = await sendStagedFile(file, `ref=${i}`);
9877
10084
  if (!out.ok) {
9878
10085
  exit("error");
@@ -9891,7 +10098,7 @@ app4.post("/reply", requireAdminSession, async (c) => {
9891
10098
  var whatsapp_reader_default = app4;
9892
10099
 
9893
10100
  // server/routes/public-reader.ts
9894
- import { statSync as statSync6, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync16, readdirSync as readdirSync10 } from "fs";
10101
+ import { statSync as statSync6, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync17, readdirSync as readdirSync11 } from "fs";
9895
10102
  import { basename as basename5, dirname as dirname5, join as join16, resolve as resolve13, sep as sep4 } from "path";
9896
10103
 
9897
10104
  // app/lib/whatsapp-reader/delivered-kinds.ts
@@ -10125,7 +10332,7 @@ data: ${JSON.stringify(turn)}
10125
10332
  });
10126
10333
  });
10127
10334
  var ATT_TAG = "[public-attachment]";
10128
- var ATTACHMENT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10335
+ var ATTACHMENT_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10129
10336
  app5.get("/attachment/:attachmentId", (c) => {
10130
10337
  const visitor = resolveVisitor(c);
10131
10338
  const attachmentId = c.req.param("attachmentId");
@@ -10137,7 +10344,7 @@ app5.get("/attachment/:attachmentId", (c) => {
10137
10344
  return new Response("Unauthorized", { status: 401 });
10138
10345
  }
10139
10346
  console.log(`${ATT_TAG} op=request session=${ses} att=${at}`);
10140
- if (!ATTACHMENT_ID_RE.test(attachmentId) || !SESSION_ID_RE2.test(sessionId)) {
10347
+ if (!ATTACHMENT_ID_RE2.test(attachmentId) || !SESSION_ID_RE2.test(sessionId)) {
10141
10348
  console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=bad-id`);
10142
10349
  return new Response("Not found", { status: 404 });
10143
10350
  }
@@ -10166,19 +10373,22 @@ app5.get("/attachment/:attachmentId", (c) => {
10166
10373
  let dataFile;
10167
10374
  let buffer;
10168
10375
  try {
10169
- dataFile = readdirSync10(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
10376
+ dataFile = readdirSync11(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
10170
10377
  if (!dataFile) throw new Error("no data file");
10171
- buffer = readFileSync16(resolve13(dir, dataFile));
10378
+ buffer = readFileSync17(resolve13(dir, dataFile));
10172
10379
  } catch {
10173
10380
  console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
10174
10381
  return new Response("Not found", { status: 404 });
10175
10382
  }
10176
- console.log(`${ATT_TAG} op=served session=${ses} att=${at} bytes=${buffer.length} mime=${meta.mimeType}`);
10383
+ const inlineSafe = isInlineSafeMime(meta.mimeType);
10384
+ const disposition = inlineSafe ? "inline" : "attachment";
10385
+ console.log(`${ATT_TAG} op=served session=${ses} att=${at} bytes=${buffer.length} mime=${meta.mimeType} disposition=${disposition}${inlineSafe ? "" : " reason=non-preview-mime"}`);
10177
10386
  const safeName = meta.filename.replace(/["\\\r\n]/g, "_");
10178
10387
  return new Response(new Uint8Array(buffer), {
10179
10388
  headers: {
10180
10389
  "Content-Type": meta.mimeType,
10181
- "Content-Disposition": `inline; filename="${safeName}"`,
10390
+ "Content-Disposition": `${disposition}; filename="${safeName}"`,
10391
+ "X-Content-Type-Options": "nosniff",
10182
10392
  "Cache-Control": "private, max-age=3600"
10183
10393
  }
10184
10394
  });
@@ -10188,10 +10398,10 @@ var public_reader_default = app5;
10188
10398
  // server/routes/webchat.ts
10189
10399
  import { basename as basename6, dirname as dirname6 } from "path";
10190
10400
  import { join as join19 } from "path";
10191
- import { existsSync as existsSync12, readdirSync as readdirSync12, readFileSync as readFileSync19, renameSync as renameSync4, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
10401
+ import { existsSync as existsSync12, readdirSync as readdirSync13, readFileSync as readFileSync20, renameSync as renameSync4, statSync as statSync7, writeFileSync as writeFileSync7 } from "fs";
10192
10402
 
10193
10403
  // server/canonical-webchat-override.ts
10194
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync5, renameSync as renameSync2 } from "fs";
10404
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync5, renameSync as renameSync2 } from "fs";
10195
10405
  import { join as join17 } from "path";
10196
10406
  var FILE = "canonical-webchat-session.json";
10197
10407
  var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
@@ -10200,7 +10410,7 @@ function canonicalOverridePath(accountDir) {
10200
10410
  }
10201
10411
  function readRaw(accountDir) {
10202
10412
  try {
10203
- return JSON.parse(readFileSync17(canonicalOverridePath(accountDir), "utf8"));
10413
+ return JSON.parse(readFileSync18(canonicalOverridePath(accountDir), "utf8"));
10204
10414
  } catch {
10205
10415
  return null;
10206
10416
  }
@@ -10275,7 +10485,7 @@ function isNewSessionEffortLevel(value) {
10275
10485
  }
10276
10486
 
10277
10487
  // ../services/claude-session-manager/src/sidecar-store.ts
10278
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readdirSync as readdirSync11, readFileSync as readFileSync18, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
10488
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readdirSync as readdirSync12, readFileSync as readFileSync19, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
10279
10489
  import { join as join18 } from "path";
10280
10490
  function escapeRegExp(s) {
10281
10491
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -10305,7 +10515,7 @@ function createSidecarStore(config) {
10305
10515
  function readFileAt(path2) {
10306
10516
  let raw;
10307
10517
  try {
10308
- raw = readFileSync18(path2, "utf8");
10518
+ raw = readFileSync19(path2, "utf8");
10309
10519
  } catch {
10310
10520
  return null;
10311
10521
  }
@@ -10321,7 +10531,7 @@ function createSidecarStore(config) {
10321
10531
  function readAll(sessionsDir, onSkip) {
10322
10532
  let names;
10323
10533
  try {
10324
- names = readdirSync11(sessionsDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
10534
+ names = readdirSync12(sessionsDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
10325
10535
  } catch {
10326
10536
  return [];
10327
10537
  }
@@ -10331,7 +10541,7 @@ function createSidecarStore(config) {
10331
10541
  const path2 = join18(sessionsDir, name);
10332
10542
  let raw;
10333
10543
  try {
10334
- raw = readFileSync18(path2, "utf8");
10544
+ raw = readFileSync19(path2, "utf8");
10335
10545
  } catch {
10336
10546
  onSkip?.(name, "unreadable");
10337
10547
  continue;
@@ -10414,7 +10624,7 @@ function locateSidecar(sessionId) {
10414
10624
  const projectsRoot = join19(cfg, "projects");
10415
10625
  let slugs;
10416
10626
  try {
10417
- slugs = readdirSync12(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
10627
+ slugs = readdirSync13(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
10418
10628
  } catch {
10419
10629
  return null;
10420
10630
  }
@@ -10897,7 +11107,7 @@ ${note}` : note;
10897
11107
  }
10898
11108
  const accountJsonPath = join19(account.accountDir, "account.json");
10899
11109
  try {
10900
- const parsed = JSON.parse(readFileSync19(accountJsonPath, "utf8"));
11110
+ const parsed = JSON.parse(readFileSync20(accountJsonPath, "utf8"));
10901
11111
  if (op === "model") parsed.adminModel = value;
10902
11112
  else if (op === "effort") parsed.effort = value;
10903
11113
  else parsed.adminPermissionMode = value;
@@ -11015,7 +11225,7 @@ ${note}` : note;
11015
11225
  import { resolve as resolve14 } from "path";
11016
11226
 
11017
11227
  // app/lib/claude-agent/specialist-roster.ts
11018
- import { existsSync as existsSync13, readFileSync as readFileSync20, readdirSync as readdirSync13 } from "fs";
11228
+ import { existsSync as existsSync13, readFileSync as readFileSync21, readdirSync as readdirSync14 } from "fs";
11019
11229
  import { join as join20 } from "path";
11020
11230
  function field(fm, name) {
11021
11231
  const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
@@ -11028,14 +11238,14 @@ function field(fm, name) {
11028
11238
  }
11029
11239
  function readSpecialistRoster(specialistsDir) {
11030
11240
  if (!existsSync13(specialistsDir)) return { specialists: [], skipped: [] };
11031
- const entries = readdirSync13(specialistsDir);
11241
+ const entries = readdirSync14(specialistsDir);
11032
11242
  const specialists = [];
11033
11243
  const skipped = [];
11034
11244
  for (const file of entries.sort()) {
11035
11245
  if (!file.endsWith(".md")) continue;
11036
11246
  let raw;
11037
11247
  try {
11038
- raw = readFileSync20(join20(specialistsDir, file), "utf-8");
11248
+ raw = readFileSync21(join20(specialistsDir, file), "utf-8");
11039
11249
  } catch (err) {
11040
11250
  skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
11041
11251
  continue;
@@ -11153,7 +11363,7 @@ var webchat_greeting_default = app6;
11153
11363
  // server/routes/telegram.ts
11154
11364
  import { homedir as homedir2 } from "os";
11155
11365
  import { resolve as resolve15, join as join21 } from "path";
11156
- import { existsSync as existsSync14, readFileSync as readFileSync21 } from "fs";
11366
+ import { existsSync as existsSync14, readFileSync as readFileSync22 } from "fs";
11157
11367
 
11158
11368
  // app/lib/telegram/gateway/instance.ts
11159
11369
  var instance = null;
@@ -11215,7 +11425,7 @@ function configDirName() {
11215
11425
  const brandPath = join21(platformRoot3, "config", "brand.json");
11216
11426
  if (existsSync14(brandPath)) {
11217
11427
  try {
11218
- return JSON.parse(readFileSync21(brandPath, "utf-8")).configDir ?? ".maxy";
11428
+ return JSON.parse(readFileSync22(brandPath, "utf-8")).configDir ?? ".maxy";
11219
11429
  } catch {
11220
11430
  }
11221
11431
  }
@@ -11238,7 +11448,7 @@ app7.post("/", async (c) => {
11238
11448
  console.error(`${TAG23} op=reject reason=no-secret-file botType=${botType}`);
11239
11449
  return c.json({ ok: false }, 401);
11240
11450
  }
11241
- const expected = readFileSync21(sp, "utf-8").trim();
11451
+ const expected = readFileSync22(sp, "utf-8").trim();
11242
11452
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
11243
11453
  if (got !== expected) {
11244
11454
  console.error(`${TAG23} op=reject reason=bad-secret botType=${botType}`);
@@ -11302,7 +11512,7 @@ var telegram_default = app7;
11302
11512
 
11303
11513
  // server/routes/quickbooks.ts
11304
11514
  import { join as join22 } from "path";
11305
- import { existsSync as existsSync15, readFileSync as readFileSync22, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
11515
+ import { existsSync as existsSync15, readFileSync as readFileSync23, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
11306
11516
  var TAG24 = "[quickbooks]";
11307
11517
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
11308
11518
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
@@ -11329,7 +11539,7 @@ async function completeConsent(args) {
11329
11539
  try {
11330
11540
  let pending;
11331
11541
  try {
11332
- pending = JSON.parse(readFileSync22(claimFile, "utf-8"));
11542
+ pending = JSON.parse(readFileSync23(claimFile, "utf-8"));
11333
11543
  } catch {
11334
11544
  console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
11335
11545
  return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
@@ -11345,7 +11555,7 @@ async function completeConsent(args) {
11345
11555
  if (!existsSync15(storePath(accountDir))) {
11346
11556
  return { ok: false, status: 500, message: "No QuickBooks credentials are stored for this account.", stateValid: true };
11347
11557
  }
11348
- const store2 = JSON.parse(readFileSync22(storePath(accountDir), "utf-8"));
11558
+ const store2 = JSON.parse(readFileSync23(storePath(accountDir), "utf-8"));
11349
11559
  const form = new URLSearchParams({
11350
11560
  grant_type: "authorization_code",
11351
11561
  code,
@@ -11379,7 +11589,7 @@ async function completeConsent(args) {
11379
11589
  };
11380
11590
  mkdirSync4(join22(accountDir, "secrets"), { recursive: true });
11381
11591
  writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
11382
- const persisted = JSON.parse(readFileSync22(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
11592
+ const persisted = JSON.parse(readFileSync23(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
11383
11593
  console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=${persisted}`);
11384
11594
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
11385
11595
  } finally {
@@ -11410,7 +11620,7 @@ var quickbooks_default = app8;
11410
11620
 
11411
11621
  // server/routes/onboarding.ts
11412
11622
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync3 } from "child_process";
11413
- import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync17, readFileSync as readFileSync24, unlinkSync as unlinkSync3, renameSync as renameSync7 } from "fs";
11623
+ import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync17, readFileSync as readFileSync25, unlinkSync as unlinkSync3, renameSync as renameSync7 } from "fs";
11414
11624
  import { createHash as createHash4, randomUUID as randomUUID7 } from "crypto";
11415
11625
 
11416
11626
  // app/lib/claude-spawn-env.ts
@@ -11548,7 +11758,7 @@ function readHostCredsBlob(service) {
11548
11758
  }
11549
11759
 
11550
11760
  // ../lib/admins-write/src/index.ts
11551
- import { existsSync as existsSync16, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync14, statSync as statSync8, appendFileSync as appendFileSync2 } from "fs";
11761
+ import { existsSync as existsSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync15, statSync as statSync8, appendFileSync as appendFileSync2 } from "fs";
11552
11762
  import { dirname as dirname7, join as join23 } from "path";
11553
11763
  function id8(value) {
11554
11764
  return value.slice(0, 8);
@@ -11591,7 +11801,7 @@ function writeAdminEntry(input) {
11591
11801
  try {
11592
11802
  let users = [];
11593
11803
  if (existsSync16(input.usersFile)) {
11594
- const raw = readFileSync23(input.usersFile, "utf-8").trim();
11804
+ const raw = readFileSync24(input.usersFile, "utf-8").trim();
11595
11805
  if (raw) {
11596
11806
  const parsed = JSON.parse(raw);
11597
11807
  if (!Array.isArray(parsed)) {
@@ -11626,7 +11836,7 @@ function writeAdminEntry(input) {
11626
11836
  if (!existsSync16(accountJsonPath)) {
11627
11837
  throw new Error(`account.json not found at ${accountJsonPath}`);
11628
11838
  }
11629
- const config = JSON.parse(readFileSync23(accountJsonPath, "utf-8"));
11839
+ const config = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
11630
11840
  const admins = config.admins ?? [];
11631
11841
  if (admins.some((a) => a.userId === input.userId)) {
11632
11842
  result.accountJsonResult = "noop";
@@ -11648,7 +11858,7 @@ function computeAdminStoreDivergence(input) {
11648
11858
  const usersUserIds = /* @__PURE__ */ new Set();
11649
11859
  if (existsSync16(input.usersFile)) {
11650
11860
  try {
11651
- const raw = readFileSync23(input.usersFile, "utf-8").trim();
11861
+ const raw = readFileSync24(input.usersFile, "utf-8").trim();
11652
11862
  if (raw) {
11653
11863
  const users = JSON.parse(raw);
11654
11864
  for (const u of users) {
@@ -11663,7 +11873,7 @@ function computeAdminStoreDivergence(input) {
11663
11873
  if (existsSync16(input.accountsDir)) {
11664
11874
  let entries;
11665
11875
  try {
11666
- entries = readdirSync14(input.accountsDir);
11876
+ entries = readdirSync15(input.accountsDir);
11667
11877
  } catch (err) {
11668
11878
  result.errors.push({ source: input.accountsDir, detail: err instanceof Error ? err.message : String(err) });
11669
11879
  return result;
@@ -11680,7 +11890,7 @@ function computeAdminStoreDivergence(input) {
11680
11890
  if (!existsSync16(accountJsonPath)) continue;
11681
11891
  let admins = [];
11682
11892
  try {
11683
- const config = JSON.parse(readFileSync23(accountJsonPath, "utf-8"));
11893
+ const config = JSON.parse(readFileSync24(accountJsonPath, "utf-8"));
11684
11894
  admins = config.admins ?? [];
11685
11895
  } catch (err) {
11686
11896
  result.errors.push({ source: accountJsonPath, detail: err instanceof Error ? err.message : String(err) });
@@ -11729,7 +11939,7 @@ function hashPin2(pin) {
11729
11939
  }
11730
11940
  function readUsersFile2() {
11731
11941
  if (!existsSync17(USERS_FILE)) return null;
11732
- const raw = readFileSync24(USERS_FILE, "utf-8").trim();
11942
+ const raw = readFileSync25(USERS_FILE, "utf-8").trim();
11733
11943
  if (!raw) return [];
11734
11944
  return JSON.parse(raw);
11735
11945
  }
@@ -11939,7 +12149,7 @@ app9.post("/set-pin", async (c) => {
11939
12149
  let installOwner = null;
11940
12150
  if (existsSync17(INSTALL_OWNER_FILE)) {
11941
12151
  try {
11942
- const raw = readFileSync24(INSTALL_OWNER_FILE, "utf-8").trim();
12152
+ const raw = readFileSync25(INSTALL_OWNER_FILE, "utf-8").trim();
11943
12153
  if (raw.length > 0) installOwner = raw;
11944
12154
  } catch (err) {
11945
12155
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -12087,7 +12297,7 @@ app9.put("/set-pin", requireAdminSession, async (c) => {
12087
12297
  let installOwner = null;
12088
12298
  if (existsSync17(INSTALL_OWNER_FILE)) {
12089
12299
  try {
12090
- const raw = readFileSync24(INSTALL_OWNER_FILE, "utf-8").trim();
12300
+ const raw = readFileSync25(INSTALL_OWNER_FILE, "utf-8").trim();
12091
12301
  if (raw.length > 0) installOwner = raw;
12092
12302
  } catch (err) {
12093
12303
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -12489,7 +12699,7 @@ app12.get("/", requireAdminSession, async (c) => {
12489
12699
  var accounts_default = app12;
12490
12700
 
12491
12701
  // server/routes/admin/logs.ts
12492
- import { existsSync as existsSync20, readdirSync as readdirSync15, readFileSync as readFileSync25, statSync as statSync10 } from "fs";
12702
+ import { existsSync as existsSync20, readdirSync as readdirSync16, readFileSync as readFileSync26, statSync as statSync10 } from "fs";
12493
12703
  import { resolve as resolve16, basename as basename7 } from "path";
12494
12704
 
12495
12705
  // app/lib/logs-read-resolve.ts
@@ -12528,7 +12738,7 @@ app13.get("/", async (c) => {
12528
12738
  const filePath = resolve16(dir, safe);
12529
12739
  searched.push(filePath);
12530
12740
  try {
12531
- const buffer = readFileSync25(filePath);
12741
+ const buffer = readFileSync26(filePath);
12532
12742
  const onDiskBytes = statSync10(filePath).size;
12533
12743
  const headers = {
12534
12744
  "Content-Type": "text/plain; charset=utf-8",
@@ -12593,7 +12803,7 @@ app13.get("/", async (c) => {
12593
12803
  console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
12594
12804
  try {
12595
12805
  const filename = basename7(hit.path);
12596
- const buffer = readFileSync25(hit.path);
12806
+ const buffer = readFileSync26(hit.path);
12597
12807
  const onDiskBytes = statSync10(hit.path).size;
12598
12808
  const headers = {
12599
12809
  "Content-Type": "text/plain; charset=utf-8",
@@ -12631,7 +12841,7 @@ app13.get("/", async (c) => {
12631
12841
  if (!existsSync20(dir)) continue;
12632
12842
  let files;
12633
12843
  try {
12634
- files = readdirSync15(dir).filter((f) => f.endsWith(".log"));
12844
+ files = readdirSync16(dir).filter((f) => f.endsWith(".log"));
12635
12845
  } catch (err) {
12636
12846
  const reason = err instanceof Error ? err.message : String(err);
12637
12847
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
@@ -12640,7 +12850,7 @@ app13.get("/", async (c) => {
12640
12850
  files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync10(resolve16(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
12641
12851
  seen.add(name);
12642
12852
  try {
12643
- const content = readFileSync25(resolve16(dir, name));
12853
+ const content = readFileSync26(resolve16(dir, name));
12644
12854
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
12645
12855
  logs[name] = tail.trim() || "(empty)";
12646
12856
  } catch (err) {
@@ -12683,7 +12893,7 @@ import { readFile as readFile2, readdir } from "fs/promises";
12683
12893
  import { existsSync as existsSync21 } from "fs";
12684
12894
  import { resolve as resolve17 } from "path";
12685
12895
  var app15 = new Hono();
12686
- var ATTACHMENT_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12896
+ var ATTACHMENT_ID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12687
12897
  function uploadsDirFor2(accountId, attachmentId) {
12688
12898
  return resolve17(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
12689
12899
  }
@@ -12697,7 +12907,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
12697
12907
  console.log(`[admin-attachment] op=miss attachmentId=${shortId2} session=${shortSession} status=401 reason=no-account`);
12698
12908
  return new Response("Unauthorized", { status: 401 });
12699
12909
  }
12700
- if (!ATTACHMENT_ID_RE2.test(attachmentId)) {
12910
+ if (!ATTACHMENT_ID_RE3.test(attachmentId)) {
12701
12911
  console.log(`[admin-attachment] op=miss attachmentId=${shortId2} session=${shortSession} status=404 reason=bad-id`);
12702
12912
  return new Response("Not found", { status: 404 });
12703
12913
  }
@@ -12739,10 +12949,16 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
12739
12949
  }
12740
12950
  const filePath = resolve17(dir, dataFile);
12741
12951
  const buffer = await readFile2(filePath);
12952
+ const inlineSafe = isInlineSafeMime(meta.mimeType);
12953
+ const safeName = meta.filename.replace(/["\\\r\n]/g, "_");
12954
+ if (!inlineSafe) {
12955
+ console.log(`[admin-attachment] op=served attachmentId=${shortId2} disposition=attachment reason=non-preview-mime mime=${meta.mimeType}`);
12956
+ }
12742
12957
  return new Response(new Uint8Array(buffer), {
12743
12958
  headers: {
12744
12959
  "Content-Type": meta.mimeType,
12745
- "Content-Disposition": `inline; filename="${meta.filename}"`,
12960
+ "Content-Disposition": `${inlineSafe ? "inline" : "attachment"}; filename="${safeName}"`,
12961
+ "X-Content-Type-Options": "nosniff",
12746
12962
  "Cache-Control": "private, max-age=3600"
12747
12963
  }
12748
12964
  });
@@ -12751,7 +12967,7 @@ var attachment_default = app15;
12751
12967
 
12752
12968
  // server/routes/admin/agents.ts
12753
12969
  import { resolve as resolve18 } from "path";
12754
- import { readdirSync as readdirSync16, readFileSync as readFileSync26, existsSync as existsSync22, rmSync as rmSync2 } from "fs";
12970
+ import { readdirSync as readdirSync17, readFileSync as readFileSync27, existsSync as existsSync22, rmSync as rmSync2 } from "fs";
12755
12971
  var app16 = new Hono();
12756
12972
  app16.get("/", (c) => {
12757
12973
  const account = resolveAccount();
@@ -12760,14 +12976,14 @@ app16.get("/", (c) => {
12760
12976
  if (!existsSync22(agentsDir)) return c.json({ agents: [] });
12761
12977
  const agents = [];
12762
12978
  try {
12763
- const entries = readdirSync16(agentsDir, { withFileTypes: true });
12979
+ const entries = readdirSync17(agentsDir, { withFileTypes: true });
12764
12980
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
12765
12981
  if (!entry.isDirectory()) continue;
12766
12982
  if (entry.name === "admin") continue;
12767
12983
  const configPath2 = resolve18(agentsDir, entry.name, "config.json");
12768
12984
  if (!existsSync22(configPath2)) continue;
12769
12985
  try {
12770
- const config = JSON.parse(readFileSync26(configPath2, "utf-8"));
12986
+ const config = JSON.parse(readFileSync27(configPath2, "utf-8"));
12771
12987
  agents.push({
12772
12988
  slug: entry.name,
12773
12989
  displayName: config.displayName ?? entry.name,
@@ -13526,7 +13742,7 @@ app17.put("/:id/label", requireAdminSession, async (c) => {
13526
13742
  var sessions_default = app17;
13527
13743
 
13528
13744
  // app/lib/claude-agent/spawn-context.ts
13529
- import { existsSync as existsSync24, readFileSync as readFileSync27, readdirSync as readdirSync17, statSync as statSync11 } from "fs";
13745
+ import { existsSync as existsSync24, readFileSync as readFileSync28, readdirSync as readdirSync18, statSync as statSync11 } from "fs";
13530
13746
  import { dirname as dirname9, resolve as resolve19, join as join27 } from "path";
13531
13747
  async function resolveOwnerProfileBlock(accountId, userId) {
13532
13748
  if (!userId) return { ok: false, reason: "missing-user-id" };
@@ -13575,7 +13791,7 @@ function extractPluginToolDescriptions(pluginDir) {
13575
13791
  for (const path2 of candidates) {
13576
13792
  if (!existsSync24(path2)) continue;
13577
13793
  try {
13578
- raw = readFileSync27(path2, "utf-8");
13794
+ raw = readFileSync28(path2, "utf-8");
13579
13795
  break;
13580
13796
  } catch {
13581
13797
  }
@@ -13626,7 +13842,7 @@ function listPluginDirs() {
13626
13842
  const out = /* @__PURE__ */ new Map();
13627
13843
  const platformPlugins = resolve19(PLATFORM_ROOT, "plugins");
13628
13844
  if (existsSync24(platformPlugins)) {
13629
- for (const name of readdirSync17(platformPlugins)) {
13845
+ for (const name of readdirSync18(platformPlugins)) {
13630
13846
  const dir = join27(platformPlugins, name);
13631
13847
  try {
13632
13848
  if (statSync11(dir).isDirectory()) out.set(name, dir);
@@ -13636,11 +13852,11 @@ function listPluginDirs() {
13636
13852
  }
13637
13853
  const premiumRoot = resolve19(dirname9(PLATFORM_ROOT), "premium-plugins");
13638
13854
  if (existsSync24(premiumRoot)) {
13639
- for (const bundle of readdirSync17(premiumRoot)) {
13855
+ for (const bundle of readdirSync18(premiumRoot)) {
13640
13856
  const bundlePlugins = join27(premiumRoot, bundle, "plugins");
13641
13857
  if (!existsSync24(bundlePlugins)) continue;
13642
13858
  try {
13643
- for (const name of readdirSync17(bundlePlugins)) {
13859
+ for (const name of readdirSync18(bundlePlugins)) {
13644
13860
  const dir = join27(bundlePlugins, name);
13645
13861
  try {
13646
13862
  if (statSync11(dir).isDirectory()) out.set(name, dir);
@@ -13657,7 +13873,7 @@ function readEnabledPlugins(accountId) {
13657
13873
  const accountFile = resolve19(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
13658
13874
  if (!existsSync24(accountFile)) return /* @__PURE__ */ new Set();
13659
13875
  try {
13660
- const parsed = JSON.parse(readFileSync27(accountFile, "utf-8"));
13876
+ const parsed = JSON.parse(readFileSync28(accountFile, "utf-8"));
13661
13877
  return new Set(
13662
13878
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
13663
13879
  );
@@ -13669,7 +13885,7 @@ function readFrontmatter(path2) {
13669
13885
  if (!existsSync24(path2)) return null;
13670
13886
  let raw;
13671
13887
  try {
13672
- raw = readFileSync27(path2, "utf-8");
13888
+ raw = readFileSync28(path2, "utf-8");
13673
13889
  } catch {
13674
13890
  return null;
13675
13891
  }
@@ -13731,7 +13947,7 @@ function computeSpecialistDomains(accountId) {
13731
13947
  if (!existsSync24(specialistsDir)) return [];
13732
13948
  let entries;
13733
13949
  try {
13734
- entries = readdirSync17(specialistsDir);
13950
+ entries = readdirSync18(specialistsDir);
13735
13951
  } catch {
13736
13952
  return [];
13737
13953
  }
@@ -13777,7 +13993,7 @@ function computeDormantPlugins(accountId) {
13777
13993
  if (!existsSync24(accountFile)) return [];
13778
13994
  let enabled;
13779
13995
  try {
13780
- const raw = readFileSync27(accountFile, "utf-8");
13996
+ const raw = readFileSync28(accountFile, "utf-8");
13781
13997
  const parsed = JSON.parse(raw);
13782
13998
  enabled = new Set(
13783
13999
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
@@ -13792,7 +14008,7 @@ function computeDormantPlugins(accountId) {
13792
14008
  if (!existsSync24(pluginsDir)) return [];
13793
14009
  let entries;
13794
14010
  try {
13795
- entries = readdirSync17(pluginsDir);
14011
+ entries = readdirSync18(pluginsDir);
13796
14012
  } catch {
13797
14013
  return [];
13798
14014
  }
@@ -13803,7 +14019,7 @@ function computeDormantPlugins(accountId) {
13803
14019
  if (!existsSync24(manifestPath)) continue;
13804
14020
  let manifest;
13805
14021
  try {
13806
- manifest = readFileSync27(manifestPath, "utf-8");
14022
+ manifest = readFileSync28(manifestPath, "utf-8");
13807
14023
  } catch {
13808
14024
  continue;
13809
14025
  }
@@ -13817,13 +14033,13 @@ function computeDormantPlugins(accountId) {
13817
14033
  }
13818
14034
 
13819
14035
  // server/routes/admin/claude-sessions.ts
13820
- import { existsSync as existsSync25, readFileSync as readFileSync28 } from "fs";
14036
+ import { existsSync as existsSync25, readFileSync as readFileSync29 } from "fs";
13821
14037
  import { resolve as resolve20 } from "path";
13822
14038
  function readTunnelState(brandConfigDir) {
13823
14039
  const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
13824
14040
  if (!existsSync25(statePath)) return null;
13825
14041
  try {
13826
- const parsed = JSON.parse(readFileSync28(statePath, "utf-8"));
14042
+ const parsed = JSON.parse(readFileSync29(statePath, "utf-8"));
13827
14043
  const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
13828
14044
  const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
13829
14045
  const domain = typeof parsed.domain === "string" ? parsed.domain : null;
@@ -13837,7 +14053,7 @@ function resolveTunnelUrl() {
13837
14053
  const platformRoot3 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
13838
14054
  let brand;
13839
14055
  try {
13840
- brand = JSON.parse(readFileSync28(resolve20(platformRoot3, "config", "brand.json"), "utf-8"));
14056
+ brand = JSON.parse(readFileSync29(resolve20(platformRoot3, "config", "brand.json"), "utf-8"));
13841
14057
  } catch {
13842
14058
  return null;
13843
14059
  }
@@ -14080,7 +14296,7 @@ var events_default = app20;
14080
14296
 
14081
14297
  // server/routes/admin/files.ts
14082
14298
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync26 } from "fs";
14083
- import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, unlink as unlink2, rename } from "fs/promises";
14299
+ import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, unlink as unlink2, rename, rmdir } from "fs/promises";
14084
14300
  import { realpathSync as realpathSync5 } from "fs";
14085
14301
  import { randomUUID as randomUUID9 } from "crypto";
14086
14302
  import { basename as basename9, dirname as dirname10, join as join29, relative as relative4, resolve as resolve22, sep as sep6 } from "path";
@@ -14958,19 +15174,55 @@ async function readAccountNames() {
14958
15174
  }
14959
15175
  return map;
14960
15176
  }
15177
+ async function servableFilesIn(dirAbs, uuid) {
15178
+ const metaName = `${uuid}.meta.json`;
15179
+ let dirents;
15180
+ try {
15181
+ dirents = await readdir3(dirAbs, { withFileTypes: true });
15182
+ } catch {
15183
+ return [];
15184
+ }
15185
+ const out = [];
15186
+ for (const d of dirents) {
15187
+ if (!d.isFile()) continue;
15188
+ if (d.name === metaName) continue;
15189
+ if (d.name.endsWith(EXTRACTED_TEXT_SUFFIX)) continue;
15190
+ out.push(d.name);
15191
+ }
15192
+ return out;
15193
+ }
14961
15194
  async function enrich(absolute, entry, accountNames) {
14962
15195
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
14963
- const meta = await readMeta(join29(absolute, entry.name), entry.name);
15196
+ const dirAbs = join29(absolute, entry.name);
15197
+ const meta = await readMeta(dirAbs, entry.name);
14964
15198
  if (meta?.filename) {
15199
+ const servable = await servableFilesIn(dirAbs, entry.name);
15200
+ if (servable.length === 1) {
15201
+ const innerName = servable[0];
15202
+ let size = null;
15203
+ let modifiedAt = entry.modifiedAt;
15204
+ try {
15205
+ const st = await stat4(join29(dirAbs, innerName));
15206
+ size = st.size;
15207
+ modifiedAt = st.mtime.toISOString();
15208
+ } catch {
15209
+ }
15210
+ entry.kind = "file";
15211
+ entry.displayName = meta.filename;
15212
+ entry.mimeType = meta.mimeType;
15213
+ entry.sizeBytes = size;
15214
+ entry.modifiedAt = modifiedAt;
15215
+ entry.entryPath = `${entry.name}/${innerName}`;
15216
+ return "flattened";
15217
+ }
14965
15218
  entry.displayName = meta.filename;
14966
- entry.mimeType = meta.mimeType;
14967
- return;
15219
+ return "kept-as-dir";
14968
15220
  }
14969
15221
  const accountName = accountNames.get(entry.name);
14970
15222
  if (accountName) {
14971
15223
  entry.displayName = accountName;
14972
15224
  }
14973
- return;
15225
+ return null;
14974
15226
  }
14975
15227
  if (entry.kind === "file") {
14976
15228
  const dot = entry.name.lastIndexOf(".");
@@ -14983,6 +15235,7 @@ async function enrich(absolute, entry, accountNames) {
14983
15235
  }
14984
15236
  }
14985
15237
  }
15238
+ return null;
14986
15239
  }
14987
15240
  function buildDisplayPath(relPath, accountNames) {
14988
15241
  if (relPath === "." || relPath === "") return [];
@@ -15138,8 +15391,18 @@ app21.get("/", requireAdminSession, async (c) => {
15138
15391
  }
15139
15392
  }
15140
15393
  const accountNames = await readAccountNames();
15141
- await Promise.all(entries.map((e) => enrich(absolute, e, accountNames)));
15394
+ const classes = await Promise.all(entries.map((e) => enrich(absolute, e, accountNames)));
15142
15395
  sortDirEntries(entries);
15396
+ let flattened = 0;
15397
+ let keptAsDir = 0;
15398
+ for (const cls of classes) {
15399
+ if (cls === "flattened") flattened++;
15400
+ else if (cls === "kept-as-dir") keptAsDir++;
15401
+ }
15402
+ const containers = flattened + keptAsDir;
15403
+ if (containers > 0) {
15404
+ console.error(`[data] op=uploads-flatten containers=${containers} flattened=${flattened} kept-as-dir=${keptAsDir}`);
15405
+ }
15143
15406
  const displayPath = buildDisplayPath(relPath, accountNames);
15144
15407
  console.error(`[data] file-list path="${relPath}" entries=${entries.length}`);
15145
15408
  return c.json({ path: relPath, displayPath, entries });
@@ -15228,8 +15491,16 @@ app21.get("/download", requireAdminSession, async (c) => {
15228
15491
  });
15229
15492
  }
15230
15493
  console.error(`[data] file-download root="${root}" path="${relPath}" size=${info.size}`);
15231
- const safeQuotedName = filename.replace(/[\r\n"\\]/g, "_");
15232
- const encodedName = encodeURIComponent(filename);
15494
+ let downloadName = filename;
15495
+ if (root === "data") {
15496
+ const parsed = parseAttachmentPath(relPath);
15497
+ if (parsed) {
15498
+ const meta = await readMeta(dirname10(absolute), parsed.attachmentId);
15499
+ if (meta?.filename) downloadName = meta.filename;
15500
+ }
15501
+ }
15502
+ const safeQuotedName = downloadName.replace(/[\r\n"\\]/g, "_");
15503
+ const encodedName = encodeURIComponent(downloadName);
15233
15504
  return new Response(webStream, {
15234
15505
  status: 200,
15235
15506
  headers: {
@@ -15324,7 +15595,13 @@ app21.get("/download-zip", requireAdminSession, async (c) => {
15324
15595
  if (over) return over;
15325
15596
  }
15326
15597
  } else if (info.isFile()) {
15327
- const over = await addFile(absolute, info.size, basename9(absolute));
15598
+ let member = basename9(absolute);
15599
+ const parsed = parseAttachmentPath(relPath);
15600
+ if (parsed) {
15601
+ const meta = await readMeta(dirname10(absolute), parsed.attachmentId);
15602
+ if (meta?.filename) member = meta.filename;
15603
+ }
15604
+ const over = await addFile(absolute, info.size, member);
15328
15605
  if (over) return over;
15329
15606
  } else {
15330
15607
  return c.json({ error: "Path is not a file or directory" }, 400);
@@ -15744,7 +16021,9 @@ app21.delete("/", requireAdminSession, async (c) => {
15744
16021
  }
15745
16022
  const dot = base.lastIndexOf(".");
15746
16023
  const stem = dot === -1 ? base : base.slice(0, dot);
15747
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join29(dirname10(absolute), `${stem}.meta.json`) : null;
16024
+ const containerDir = dirname10(absolute);
16025
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join29(containerDir, `${stem}.meta.json`) : null;
16026
+ const isAttachmentContainer = UUID_RE3.test(stem) && basename9(containerDir) === stem;
15748
16027
  await unlink2(absolute);
15749
16028
  if (sidecarPath) {
15750
16029
  try {
@@ -15752,6 +16031,12 @@ app21.delete("/", requireAdminSession, async (c) => {
15752
16031
  } catch {
15753
16032
  }
15754
16033
  }
16034
+ if (isAttachmentContainer) {
16035
+ await unlink2(join29(containerDir, `${stem}${EXTRACTED_TEXT_SUFFIX}`)).catch(() => {
16036
+ });
16037
+ await rmdir(containerDir).catch(() => {
16038
+ });
16039
+ }
15755
16040
  console.error(`[data] file-delete path="${relPath}" bytes=${info.size}`);
15756
16041
  try {
15757
16042
  await dropFileIndex(accountId, relPath);
@@ -16850,6 +17135,142 @@ function isHiddenByDefault(label) {
16850
17135
  return HIDDEN_BY_DEFAULT_LABELS.has(label);
16851
17136
  }
16852
17137
 
17138
+ // server/lib/top-level-labels.ts
17139
+ import { readdirSync as readdirSync19, readFileSync as readFileSync30 } from "fs";
17140
+ import { join as join30, resolve as resolve23 } from "path";
17141
+ var STATIC_TOP_LEVEL_LABELS = Object.freeze(
17142
+ /* @__PURE__ */ new Set([
17143
+ // Base-schema infra (schema-base.md has no top-level section).
17144
+ "LocalBusiness",
17145
+ "Service",
17146
+ "PriceSpecification",
17147
+ "OpeningHoursSpecification",
17148
+ "Organization",
17149
+ "Person",
17150
+ "UserProfile",
17151
+ "Preference",
17152
+ "AdminUser",
17153
+ "AccessGrant",
17154
+ "KnowledgeDocument",
17155
+ "DigitalDocument",
17156
+ "CreativeWork",
17157
+ "Question",
17158
+ "FAQPage",
17159
+ "DefinedTerm",
17160
+ "Review",
17161
+ "ImageObject",
17162
+ "Invoice",
17163
+ "Task",
17164
+ "Project",
17165
+ "Event",
17166
+ "Workflow",
17167
+ "Email",
17168
+ "EmailAccount",
17169
+ "Agent",
17170
+ // Knowledge-work vertical: schema-knowledge-work.md has no top-level
17171
+ // section yet.
17172
+ "Objective",
17173
+ "KeyResult",
17174
+ "Decision",
17175
+ "Risk",
17176
+ "Source",
17177
+ "Finding",
17178
+ "Hypothesis"
17179
+ ])
17180
+ );
17181
+ function splitPipeRow(row) {
17182
+ let trimmed = row.trim();
17183
+ if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
17184
+ if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
17185
+ const cells = [];
17186
+ let inBacktick = false;
17187
+ let start = 0;
17188
+ for (let i = 0; i < trimmed.length; i++) {
17189
+ const ch = trimmed[i];
17190
+ if (ch === "`") inBacktick = !inBacktick;
17191
+ else if (ch === "|" && !inBacktick) {
17192
+ cells.push(trimmed.slice(start, i).trim());
17193
+ start = i + 1;
17194
+ }
17195
+ }
17196
+ cells.push(trimmed.slice(start).trim());
17197
+ return cells;
17198
+ }
17199
+ var stripBackticks = (s) => s.replace(/`/g, "").trim();
17200
+ function parseTopLevelLabels(content) {
17201
+ const lines = content.split(/\r?\n/);
17202
+ let i = 0;
17203
+ while (i < lines.length) {
17204
+ if (lines[i].toLowerCase().startsWith("## top-level node types")) break;
17205
+ i++;
17206
+ }
17207
+ if (i >= lines.length) return [];
17208
+ i++;
17209
+ for (; i < lines.length - 1; i++) {
17210
+ const line = lines[i];
17211
+ if (line.startsWith("## ")) return [];
17212
+ if (!line.includes("|")) continue;
17213
+ const sep9 = lines[i + 1];
17214
+ if (!/^\s*\|?[\s:|-]+\|?\s*$/.test(sep9) || !sep9.includes("-")) continue;
17215
+ const headers = splitPipeRow(line).map((h) => stripBackticks(h).toLowerCase());
17216
+ const labelCol = headers.findIndex((h) => h === "neo4j label");
17217
+ if (labelCol === -1) return [];
17218
+ const out = [];
17219
+ let j = i + 2;
17220
+ for (; j < lines.length; j++) {
17221
+ const row = lines[j];
17222
+ if (!row.trim() || !row.includes("|")) break;
17223
+ const cells = splitPipeRow(row);
17224
+ const label = stripBackticks(cells[labelCol] ?? "");
17225
+ if (label) out.push(label);
17226
+ }
17227
+ return out;
17228
+ }
17229
+ return [];
17230
+ }
17231
+ function resolveReferencesDir() {
17232
+ if (process.env.MAXY_PLATFORM_ROOT) {
17233
+ return join30(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
17234
+ }
17235
+ return resolve23(import.meta.dirname, "../../../plugins/memory/references");
17236
+ }
17237
+ function parseTableTopLevelLabels(referencesDir) {
17238
+ const labels = /* @__PURE__ */ new Set();
17239
+ const contributingFiles = [];
17240
+ try {
17241
+ const fileNames = readdirSync19(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
17242
+ for (const fileName of fileNames) {
17243
+ const content = readFileSync30(join30(referencesDir, fileName), "utf-8");
17244
+ const parsed = parseTopLevelLabels(content);
17245
+ if (parsed.length > 0) contributingFiles.push(fileName);
17246
+ for (const label of parsed) labels.add(label);
17247
+ }
17248
+ } catch (err) {
17249
+ const message = err instanceof Error ? err.message : String(err);
17250
+ process.stderr.write(
17251
+ `[graph-page] op=top-level-labels source=ontology status=error dir=${referencesDir} reason="${message}"
17252
+ `
17253
+ );
17254
+ throw err;
17255
+ }
17256
+ return { labels, contributingFiles };
17257
+ }
17258
+ var cached = null;
17259
+ function getTopLevelLabelAllowlist(opts = {}) {
17260
+ if (!opts.referencesDir && cached) return cached;
17261
+ const referencesDir = opts.referencesDir ?? resolveReferencesDir();
17262
+ const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
17263
+ const derivedCount = derived.size;
17264
+ for (const label of STATIC_TOP_LEVEL_LABELS) derived.add(label);
17265
+ const result = Object.freeze(derived);
17266
+ process.stderr.write(
17267
+ `[graph-page] op=top-level-labels source=ontology count=${derived.size} files=${contributingFiles.join("|") || "none"} derived=${derivedCount} static=${STATIC_TOP_LEVEL_LABELS.size}
17268
+ `
17269
+ );
17270
+ if (!opts.referencesDir) cached = result;
17271
+ return result;
17272
+ }
17273
+
16853
17274
  // server/routes/admin/graph-subgraph.ts
16854
17275
  var CEILING = 2e3;
16855
17276
  var TOP_LEVEL_LIMIT = 1e3;
@@ -16971,7 +17392,7 @@ async function handleDefault(c, accountId) {
16971
17392
  }
16972
17393
  async function handleDefaultTopLevel(c, accountId, flags) {
16973
17394
  const { includeTrashed, includeAgentActions } = flags;
16974
- const labels = [...FILTER_TOP_LEVEL_LABELS].filter((l) => !HIDDEN_BY_DEFAULT_LABELS.has(l));
17395
+ const labels = [...getTopLevelLabelAllowlist()].filter((l) => !HIDDEN_BY_DEFAULT_LABELS.has(l));
16975
17396
  const agentActionLabels = [...AGENT_ACTION_LABELS];
16976
17397
  const started = Date.now();
16977
17398
  const session = getSession();
@@ -17782,7 +18203,7 @@ app26.get("/", requireAdminSession, async (c) => {
17782
18203
  const started = Date.now();
17783
18204
  const session = getSession();
17784
18205
  try {
17785
- const allowedSet = new Set(FILTER_TOP_LEVEL_LABELS);
18206
+ const allowedSet = new Set(getTopLevelLabelAllowlist());
17786
18207
  if (includeAgentActions) {
17787
18208
  for (const label of AGENT_ACTION_LABELS) allowedSet.add(label);
17788
18209
  }
@@ -17944,12 +18365,12 @@ function validateBody(body) {
17944
18365
  if (typeof lbl !== "string") {
17945
18366
  return { ok: false, code: "shape", reason: "labels must be an array of strings" };
17946
18367
  }
17947
- if (!FILTER_TOP_LEVEL_LABELS.has(lbl)) {
18368
+ if (!getTopLevelLabelAllowlist().has(lbl)) {
17948
18369
  return {
17949
18370
  ok: false,
17950
18371
  code: "not-eligible-chip",
17951
18372
  label: lbl,
17952
- reason: `label "${lbl}" is not eligible for default view \u2014 outside the chip allowlist (FILTER_TOP_LEVEL_LABELS)`
18373
+ reason: `label "${lbl}" is not eligible for default view \u2014 outside the chip allowlist`
17953
18374
  };
17954
18375
  }
17955
18376
  seen.add(lbl);
@@ -17972,7 +18393,7 @@ var graph_default_view_default = app27;
17972
18393
 
17973
18394
  // server/routes/admin/sidebar-artefacts.ts
17974
18395
  import { readdir as readdir4, stat as stat5 } from "fs/promises";
17975
- import { resolve as resolve23, relative as relative5, isAbsolute as isAbsolute2, sep as sep7, basename as basename10 } from "path";
18396
+ import { resolve as resolve24, relative as relative5, isAbsolute as isAbsolute2, sep as sep7, basename as basename10 } from "path";
17976
18397
  import { existsSync as existsSync27 } from "fs";
17977
18398
  var LIMIT = 50;
17978
18399
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
@@ -17992,7 +18413,7 @@ app28.get("/", requireAdminSession, async (c) => {
17992
18413
  if (accountFiles === null) {
17993
18414
  return c.json({ error: "Failed to load artefacts" }, 500);
17994
18415
  }
17995
- const accountDir = resolve23(ACCOUNTS_DIR, accountId);
18416
+ const accountDir = resolve24(ACCOUNTS_DIR, accountId);
17996
18417
  const agents = await fetchAgentTemplateRows(accountDir);
17997
18418
  const artefacts = [...accountFiles, ...agents].sort(
17998
18419
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
@@ -18044,8 +18465,8 @@ async function fetchAccountFileArtefacts(accountId) {
18044
18465
  async function fetchAgentTemplateRows(accountDir) {
18045
18466
  const rows = [];
18046
18467
  for (const filename of ADMIN_AGENT_FILES) {
18047
- const overridePath = resolve23(accountDir, "agents", "admin", filename);
18048
- const bundledPath = resolve23(PLATFORM_ROOT, "templates", "agents", "admin", filename);
18468
+ const overridePath = resolve24(accountDir, "agents", "admin", filename);
18469
+ const bundledPath = resolve24(PLATFORM_ROOT, "templates", "agents", "admin", filename);
18049
18470
  const labelStem = filename.replace(/\.md$/, "");
18050
18471
  const row = await readAgentTemplateRow({
18051
18472
  id: `agent-template:admin:${filename}`,
@@ -18058,12 +18479,12 @@ async function fetchAgentTemplateRows(accountDir) {
18058
18479
  });
18059
18480
  if (row) rows.push(row);
18060
18481
  }
18061
- const overrideDir = resolve23(accountDir, "specialists", "agents");
18062
- const bundledDir = resolve23(PLATFORM_ROOT, "templates", "specialists", "agents");
18482
+ const overrideDir = resolve24(accountDir, "specialists", "agents");
18483
+ const bundledDir = resolve24(PLATFORM_ROOT, "templates", "specialists", "agents");
18063
18484
  const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
18064
18485
  for (const filename of specialistNames) {
18065
- const overridePath = resolve23(overrideDir, filename);
18066
- const bundledPath = resolve23(bundledDir, filename);
18486
+ const overridePath = resolve24(overrideDir, filename);
18487
+ const bundledPath = resolve24(bundledDir, filename);
18067
18488
  const row = await readAgentTemplateRow({
18068
18489
  id: `agent-template:specialist:${filename}`,
18069
18490
  displayName: filename.replace(/\.md$/, ""),
@@ -18345,7 +18766,7 @@ var session_usage_default = app33;
18345
18766
 
18346
18767
  // server/routes/admin/session-rc-spawn.ts
18347
18768
  import { randomUUID as randomUUID10 } from "crypto";
18348
- var ACCOUNT_UUID_RE2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
18769
+ var ACCOUNT_UUID_RE3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
18349
18770
  function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, adminUserId, authenticatedName, callerAccountId) {
18350
18771
  const operator = isOperatorHost(host, operatorDomains);
18351
18772
  const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
@@ -18356,8 +18777,8 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, ad
18356
18777
  const pickedModel = kind === "new" && typeof body.model === "string" && isSelectableModel(body.model) ? body.model : null;
18357
18778
  const pickedEffort = kind === "new" && typeof body.effort === "string" && isNewSessionEffortLevel(body.effort) ? body.effort : null;
18358
18779
  const paramsSource = pickedModel !== null && pickedEffort !== null ? "picker" : "default";
18359
- const callerTargetAccountId = typeof callerAccountId === "string" && ACCOUNT_UUID_RE2.test(callerAccountId) ? callerAccountId : void 0;
18360
- const bodyTargetAccountId = typeof body.targetAccountId === "string" && ACCOUNT_UUID_RE2.test(body.targetAccountId) ? body.targetAccountId : void 0;
18780
+ const callerTargetAccountId = typeof callerAccountId === "string" && ACCOUNT_UUID_RE3.test(callerAccountId) ? callerAccountId : void 0;
18781
+ const bodyTargetAccountId = typeof body.targetAccountId === "string" && ACCOUNT_UUID_RE3.test(body.targetAccountId) ? body.targetAccountId : void 0;
18361
18782
  const targetAccountId = kind === "new" ? callerTargetAccountId : bodyTargetAccountId;
18362
18783
  const payload = {};
18363
18784
  let sessionId;
@@ -18708,24 +19129,24 @@ app36.get("/", async (c) => {
18708
19129
  var system_stats_default = app36;
18709
19130
 
18710
19131
  // server/routes/admin/health.ts
18711
- import { existsSync as existsSync28, readFileSync as readFileSync29 } from "fs";
18712
- import { resolve as resolve24, join as join30 } from "path";
18713
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
19132
+ import { existsSync as existsSync28, readFileSync as readFileSync31 } from "fs";
19133
+ import { resolve as resolve25, join as join31 } from "path";
19134
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
18714
19135
  var brandHostname = "maxy";
18715
- var brandJsonPath = join30(PLATFORM_ROOT6, "config", "brand.json");
19136
+ var brandJsonPath = join31(PLATFORM_ROOT6, "config", "brand.json");
18716
19137
  if (existsSync28(brandJsonPath)) {
18717
19138
  try {
18718
- const brand = JSON.parse(readFileSync29(brandJsonPath, "utf-8"));
19139
+ const brand = JSON.parse(readFileSync31(brandJsonPath, "utf-8"));
18719
19140
  if (brand.hostname) brandHostname = brand.hostname;
18720
19141
  } catch {
18721
19142
  }
18722
19143
  }
18723
- var VERSION_FILE = resolve24(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
19144
+ var VERSION_FILE = resolve25(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
18724
19145
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
18725
19146
  var PROBE_TIMEOUT_MS = 1e3;
18726
19147
  function readVersion() {
18727
19148
  if (!existsSync28(VERSION_FILE)) return "unknown";
18728
- return readFileSync29(VERSION_FILE, "utf-8").trim() || "unknown";
19149
+ return readFileSync31(VERSION_FILE, "utf-8").trim() || "unknown";
18729
19150
  }
18730
19151
  async function probeConversationDb() {
18731
19152
  let session;
@@ -19502,7 +19923,7 @@ var browser_default = app44;
19502
19923
 
19503
19924
  // server/routes/admin/calendar.ts
19504
19925
  import { existsSync as existsSync29 } from "fs";
19505
- import { join as join32 } from "path";
19926
+ import { join as join33 } from "path";
19506
19927
 
19507
19928
  // server/lib/calendar-ics.ts
19508
19929
  var CRLF = "\r\n";
@@ -19601,8 +20022,8 @@ function countVEvents(ics) {
19601
20022
  }
19602
20023
 
19603
20024
  // server/lib/calendar-availability.ts
19604
- import { readFileSync as readFileSync30 } from "fs";
19605
- import { join as join31 } from "path";
20025
+ import { readFileSync as readFileSync32 } from "fs";
20026
+ import { join as join32 } from "path";
19606
20027
  var AVAILABILITY_FILENAME = "calendar-availability.json";
19607
20028
  var REQUIRED_KEYS = [
19608
20029
  "timezone",
@@ -19611,10 +20032,10 @@ var REQUIRED_KEYS = [
19611
20032
  "weekly"
19612
20033
  ];
19613
20034
  function readAvailabilityConfig(accountDir) {
19614
- const path2 = join31(accountDir, AVAILABILITY_FILENAME);
20035
+ const path2 = join32(accountDir, AVAILABILITY_FILENAME);
19615
20036
  let raw;
19616
20037
  try {
19617
- raw = readFileSync30(path2, "utf-8");
20038
+ raw = readFileSync32(path2, "utf-8");
19618
20039
  } catch {
19619
20040
  throw new Error(`availability not configured: ${path2} not found`);
19620
20041
  }
@@ -19973,7 +20394,7 @@ app45.get("/booking-link", requireAdminSession, (c) => {
19973
20394
  console.error('[calendar] op=booking-link-fail err="no account configured"');
19974
20395
  return c.json({ bookingDomain: null });
19975
20396
  }
19976
- if (!existsSync29(join32(account.accountDir, AVAILABILITY_FILENAME))) {
20397
+ if (!existsSync29(join33(account.accountDir, AVAILABILITY_FILENAME))) {
19977
20398
  console.log("[calendar] op=booking-link bookingDomain=none source=availability-config");
19978
20399
  return c.json({ bookingDomain: null });
19979
20400
  }
@@ -20447,9 +20868,9 @@ var verify_token_default = app52;
20447
20868
 
20448
20869
  // app/lib/access-email.ts
20449
20870
  import { spawn as spawn2 } from "child_process";
20450
- import { resolve as resolve25 } from "path";
20451
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
20452
- var SEND_SCRIPT = resolve25(
20871
+ import { resolve as resolve26 } from "path";
20872
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
20873
+ var SEND_SCRIPT = resolve26(
20453
20874
  PLATFORM_ROOT7,
20454
20875
  "plugins",
20455
20876
  "email",
@@ -20591,8 +21012,8 @@ app54.route("/request-magic-link", request_magic_link_default);
20591
21012
  var access_default = app54;
20592
21013
 
20593
21014
  // server/routes/sites.ts
20594
- import { existsSync as existsSync30, readFileSync as readFileSync31, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
20595
- import { resolve as resolve26 } from "path";
21015
+ import { existsSync as existsSync30, readFileSync as readFileSync33, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
21016
+ import { resolve as resolve27 } from "path";
20596
21017
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
20597
21018
  var MIME = {
20598
21019
  ".html": "text/html; charset=utf-8",
@@ -20649,8 +21070,8 @@ app55.get("/:rel{.*}", (c) => {
20649
21070
  }
20650
21071
  segments.push(seg);
20651
21072
  }
20652
- const rootDir = resolve26(account.accountDir, "sites");
20653
- let filePath = segments.length === 0 ? rootDir : resolve26(rootDir, ...segments);
21073
+ const rootDir = resolve27(account.accountDir, "sites");
21074
+ let filePath = segments.length === 0 ? rootDir : resolve27(rootDir, ...segments);
20654
21075
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
20655
21076
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
20656
21077
  return c.text("Forbidden", 403);
@@ -20670,7 +21091,7 @@ app55.get("/:rel{.*}", (c) => {
20670
21091
  return c.redirect(target, 301);
20671
21092
  }
20672
21093
  if (stat8?.isDirectory()) {
20673
- filePath = resolve26(filePath, "index.html");
21094
+ filePath = resolve27(filePath, "index.html");
20674
21095
  }
20675
21096
  if (!filePath.startsWith(rootDir + "/")) {
20676
21097
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
@@ -20695,7 +21116,7 @@ app55.get("/:rel{.*}", (c) => {
20695
21116
  }
20696
21117
  let body;
20697
21118
  try {
20698
- body = readFileSync31(realPath);
21119
+ body = readFileSync33(realPath);
20699
21120
  } catch (err) {
20700
21121
  const code = err?.code;
20701
21122
  if (code === "EISDIR") {
@@ -20731,7 +21152,7 @@ var sites_default = app55;
20731
21152
 
20732
21153
  // app/lib/visitor-token.ts
20733
21154
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
20734
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync32, writeFileSync as writeFileSync11 } from "fs";
21155
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync34, writeFileSync as writeFileSync11 } from "fs";
20735
21156
  import { dirname as dirname11 } from "path";
20736
21157
  var TOKEN_PREFIX = "v1.";
20737
21158
  var SECRET_BYTES = 32;
@@ -20740,7 +21161,7 @@ var cachedSecret = null;
20740
21161
  function getSecret() {
20741
21162
  if (cachedSecret) return cachedSecret;
20742
21163
  try {
20743
- const hex2 = readFileSync32(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
21164
+ const hex2 = readFileSync34(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
20744
21165
  if (hex2.length === SECRET_BYTES * 2) {
20745
21166
  cachedSecret = Buffer.from(hex2, "hex");
20746
21167
  return cachedSecret;
@@ -20754,7 +21175,7 @@ function getSecret() {
20754
21175
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
20755
21176
  } catch {
20756
21177
  }
20757
- const hex = readFileSync32(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
21178
+ const hex = readFileSync34(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
20758
21179
  cachedSecret = Buffer.from(hex, "hex");
20759
21180
  return cachedSecret;
20760
21181
  }
@@ -20807,20 +21228,20 @@ var VISITOR_COOKIE_NAME = "mxy_v";
20807
21228
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
20808
21229
 
20809
21230
  // app/lib/brand-config.ts
20810
- import { existsSync as existsSync31, readFileSync as readFileSync33 } from "fs";
20811
- import { join as join33 } from "path";
20812
- var cached = null;
21231
+ import { existsSync as existsSync31, readFileSync as readFileSync35 } from "fs";
21232
+ import { join as join34 } from "path";
21233
+ var cached2 = null;
20813
21234
  var cachedAttempted = false;
20814
21235
  function readBrandConfig() {
20815
- if (cachedAttempted) return cached;
21236
+ if (cachedAttempted) return cached2;
20816
21237
  cachedAttempted = true;
20817
21238
  const platformRoot3 = process.env.MAXY_PLATFORM_ROOT;
20818
21239
  if (!platformRoot3) return null;
20819
- const brandPath = join33(platformRoot3, "config", "brand.json");
21240
+ const brandPath = join34(platformRoot3, "config", "brand.json");
20820
21241
  if (!existsSync31(brandPath)) return null;
20821
21242
  try {
20822
- cached = JSON.parse(readFileSync33(brandPath, "utf-8"));
20823
- return cached;
21243
+ cached2 = JSON.parse(readFileSync35(brandPath, "utf-8"));
21244
+ return cached2;
20824
21245
  } catch {
20825
21246
  return null;
20826
21247
  }
@@ -21307,14 +21728,14 @@ async function writeEvent(opts) {
21307
21728
  var visitor_event_default = app58;
21308
21729
 
21309
21730
  // server/routes/session.ts
21310
- import { resolve as resolve27 } from "path";
21731
+ import { resolve as resolve28 } from "path";
21311
21732
  import { existsSync as existsSync32, writeFileSync as writeFileSync12, mkdirSync as mkdirSync8 } from "fs";
21312
21733
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
21313
21734
  function writeBrandingCache(accountId, agentSlug, branding) {
21314
21735
  try {
21315
- const cacheDir = resolve27(MAXY_DIR, "branding-cache", accountId);
21736
+ const cacheDir = resolve28(MAXY_DIR, "branding-cache", accountId);
21316
21737
  mkdirSync8(cacheDir, { recursive: true });
21317
- writeFileSync12(resolve27(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
21738
+ writeFileSync12(resolve28(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
21318
21739
  } catch (err) {
21319
21740
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
21320
21741
  }
@@ -21391,8 +21812,8 @@ app59.post("/", async (c) => {
21391
21812
  }
21392
21813
  let agentConfig = null;
21393
21814
  if (account) {
21394
- const agentDir = resolve27(account.accountDir, "agents", agentSlug);
21395
- if (!existsSync32(agentDir) || !existsSync32(resolve27(agentDir, "config.json"))) {
21815
+ const agentDir = resolve28(account.accountDir, "agents", agentSlug);
21816
+ if (!existsSync32(agentDir) || !existsSync32(resolve28(agentDir, "config.json"))) {
21396
21817
  return c.json({ error: "Agent not found" }, 404);
21397
21818
  }
21398
21819
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -21838,8 +22259,8 @@ function startGraphHealthTimer() {
21838
22259
 
21839
22260
  // app/lib/file-watcher.ts
21840
22261
  import * as fsp2 from "fs/promises";
21841
- import { resolve as resolve28, sep as sep8 } from "path";
21842
- var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
22262
+ import { resolve as resolve29, sep as sep8 } from "path";
22263
+ var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
21843
22264
  var DEFAULT_COALESCE_MS = 500;
21844
22265
  var ROOTS = ["accounts"];
21845
22266
  function _routeEvent(rootName, filename) {
@@ -21847,7 +22268,7 @@ function _routeEvent(rootName, filename) {
21847
22268
  const segs = filename.split(sep8).filter(Boolean);
21848
22269
  if (segs.length < 2) return null;
21849
22270
  const accountId = segs[0];
21850
- if (!ACCOUNT_UUID_RE3.test(accountId)) return null;
22271
+ if (!ACCOUNT_UUID_RE4.test(accountId)) return null;
21851
22272
  return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
21852
22273
  }
21853
22274
  async function startFileWatcher(opts = {}) {
@@ -21857,7 +22278,7 @@ async function startFileWatcher(opts = {}) {
21857
22278
  const dropFn = opts.drop ?? dropFileIndex;
21858
22279
  for (const r of ROOTS) {
21859
22280
  try {
21860
- await fsp2.mkdir(resolve28(dataRoot, r), { recursive: true });
22281
+ await fsp2.mkdir(resolve29(dataRoot, r), { recursive: true });
21861
22282
  } catch (err) {
21862
22283
  console.error(
21863
22284
  `[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
@@ -21878,7 +22299,7 @@ async function startFileWatcher(opts = {}) {
21878
22299
  timers.set(relativePath, t);
21879
22300
  }
21880
22301
  async function runHook(relativePath, accountId) {
21881
- const absolute = resolve28(dataRoot, relativePath);
22302
+ const absolute = resolve29(dataRoot, relativePath);
21882
22303
  let exists = false;
21883
22304
  try {
21884
22305
  const st = await fsp2.stat(absolute);
@@ -21902,7 +22323,7 @@ async function startFileWatcher(opts = {}) {
21902
22323
  }
21903
22324
  }
21904
22325
  async function watchRoot(rootName) {
21905
- const absRoot = resolve28(dataRoot, rootName);
22326
+ const absRoot = resolve29(dataRoot, rootName);
21906
22327
  try {
21907
22328
  const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
21908
22329
  for await (const event of iter) {
@@ -21941,8 +22362,8 @@ async function startFileWatcher(opts = {}) {
21941
22362
 
21942
22363
  // app/lib/migrate-uploads.ts
21943
22364
  import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
21944
- import { dirname as dirname12, relative as relative6, resolve as resolve29 } from "path";
21945
- var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
22365
+ import { dirname as dirname12, relative as relative6, resolve as resolve30 } from "path";
22366
+ var ACCOUNT_UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
21946
22367
  async function walkFiles(dir) {
21947
22368
  const out = [];
21948
22369
  let entries;
@@ -21952,7 +22373,7 @@ async function walkFiles(dir) {
21952
22373
  return out;
21953
22374
  }
21954
22375
  for (const e of entries) {
21955
- const abs = resolve29(dir, e.name);
22376
+ const abs = resolve30(dir, e.name);
21956
22377
  if (e.isDirectory()) {
21957
22378
  out.push(...await walkFiles(abs));
21958
22379
  } else if (e.isFile()) {
@@ -21972,7 +22393,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
21972
22393
  let moved = 0;
21973
22394
  for (const oldAbs of files) {
21974
22395
  const suffix = relative6(srcDir, oldAbs);
21975
- const newAbs = resolve29(destDir, suffix);
22396
+ const newAbs = resolve30(destDir, suffix);
21976
22397
  await mkdir5(dirname12(newAbs), { recursive: true });
21977
22398
  await rename2(oldAbs, newAbs);
21978
22399
  moved++;
@@ -21999,7 +22420,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
21999
22420
  }
22000
22421
  async function migrateUploads(opts = {}) {
22001
22422
  const dataRoot = opts.dataRoot ?? DATA_ROOT;
22002
- const oldRoot = resolve29(dataRoot, "uploads");
22423
+ const oldRoot = resolve30(dataRoot, "uploads");
22003
22424
  let topEntries;
22004
22425
  try {
22005
22426
  topEntries = await readdir5(oldRoot, { withFileTypes: true });
@@ -22018,10 +22439,10 @@ async function migrateUploads(opts = {}) {
22018
22439
  try {
22019
22440
  for (const entry of topEntries) {
22020
22441
  const name = entry.name;
22021
- if (ACCOUNT_UUID_RE4.test(name)) {
22442
+ if (ACCOUNT_UUID_RE5.test(name)) {
22022
22443
  moved += await relocateTree(
22023
- resolve29(oldRoot, name),
22024
- resolve29(dataRoot, "accounts", name, "uploads"),
22444
+ resolve30(oldRoot, name),
22445
+ resolve30(dataRoot, "accounts", name, "uploads"),
22025
22446
  dataRoot,
22026
22447
  name,
22027
22448
  session
@@ -22033,8 +22454,8 @@ async function migrateUploads(opts = {}) {
22033
22454
  continue;
22034
22455
  }
22035
22456
  moved += await relocateTree(
22036
- resolve29(oldRoot, "public"),
22037
- resolve29(dataRoot, "accounts", installAccountId, "uploads", "public"),
22457
+ resolve30(oldRoot, "public"),
22458
+ resolve30(dataRoot, "accounts", installAccountId, "uploads", "public"),
22038
22459
  dataRoot,
22039
22460
  null,
22040
22461
  // public uploads carry no graph nodes
@@ -22065,7 +22486,7 @@ async function migrateUploads(opts = {}) {
22065
22486
  // app/lib/migrate-admin-webchat-sidecars.ts
22066
22487
  import { readdir as readdir6, readFile as readFile6, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
22067
22488
  import { existsSync as existsSync33 } from "fs";
22068
- import { join as join34 } from "path";
22489
+ import { join as join35 } from "path";
22069
22490
  var ADMIN_ROLE2 = "admin";
22070
22491
  var WEBCHAT_CHANNEL2 = "webchat";
22071
22492
  var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
@@ -22110,7 +22531,7 @@ async function collectLiveSidecars(projectsRoot) {
22110
22531
  }
22111
22532
  for (const slug of slugs) {
22112
22533
  if (!slug.isDirectory()) continue;
22113
- const slugDir = join34(projectsRoot, slug.name);
22534
+ const slugDir = join35(projectsRoot, slug.name);
22114
22535
  let entries;
22115
22536
  try {
22116
22537
  entries = await readdir6(slugDir, { withFileTypes: true });
@@ -22119,9 +22540,9 @@ async function collectLiveSidecars(projectsRoot) {
22119
22540
  }
22120
22541
  for (const entry of entries) {
22121
22542
  if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
22122
- out.push(join34(slugDir, entry.name));
22543
+ out.push(join35(slugDir, entry.name));
22123
22544
  } else if (entry.isDirectory() && entry.name === "subagents") {
22124
- const subDir = join34(slugDir, entry.name);
22545
+ const subDir = join35(slugDir, entry.name);
22125
22546
  let subs;
22126
22547
  try {
22127
22548
  subs = await readdir6(subDir, { withFileTypes: true });
@@ -22129,7 +22550,7 @@ async function collectLiveSidecars(projectsRoot) {
22129
22550
  continue;
22130
22551
  }
22131
22552
  for (const s of subs) {
22132
- if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join34(subDir, s.name));
22553
+ if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join35(subDir, s.name));
22133
22554
  }
22134
22555
  }
22135
22556
  }
@@ -22141,7 +22562,7 @@ function shortId(path2) {
22141
22562
  return base.slice(0, 8);
22142
22563
  }
22143
22564
  async function migrateAdminWebchatSidecars(opts = {}) {
22144
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join34(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
22565
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join35(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
22145
22566
  const usersFile = opts.usersFile ?? USERS_FILE;
22146
22567
  const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
22147
22568
  const resolveName = opts.resolveName ?? defaultResolveName;
@@ -23120,16 +23541,16 @@ var WebchatGateway = class _WebchatGateway {
23120
23541
  * The public /api/chat route awaits this to return the reply on the same SSE
23121
23542
  * response, preserving the pre-756 POST→single-blob browser contract. */
23122
23543
  awaitReply(key, timeoutMs) {
23123
- return new Promise((resolve34) => {
23544
+ return new Promise((resolve35) => {
23124
23545
  const timer2 = setTimeout(() => {
23125
23546
  this.replyAwaiters.delete(key);
23126
- resolve34({ timeout: true });
23547
+ resolve35({ timeout: true });
23127
23548
  }, timeoutMs);
23128
23549
  if (timer2.unref) timer2.unref();
23129
23550
  this.replyAwaiters.set(key, (r) => {
23130
23551
  clearTimeout(timer2);
23131
23552
  this.replyAwaiters.delete(key);
23132
- resolve34(r);
23553
+ resolve35(r);
23133
23554
  });
23134
23555
  });
23135
23556
  }
@@ -23140,11 +23561,11 @@ var WebchatGateway = class _WebchatGateway {
23140
23561
  * terminal dialog stays answerable). */
23141
23562
  awaitPermissionVerdict(p, timeoutMs) {
23142
23563
  const k = _WebchatGateway.promptKey(p.key, p.requestId);
23143
- return new Promise((resolve34) => {
23564
+ return new Promise((resolve35) => {
23144
23565
  this.pendingPrompts.get(k)?.resolve({ timeout: true });
23145
23566
  const timer2 = setTimeout(() => {
23146
23567
  this.pendingPrompts.delete(k);
23147
- resolve34({ timeout: true });
23568
+ resolve35({ timeout: true });
23148
23569
  }, timeoutMs);
23149
23570
  if (timer2.unref) timer2.unref();
23150
23571
  this.pendingPrompts.set(k, {
@@ -23153,7 +23574,7 @@ var WebchatGateway = class _WebchatGateway {
23153
23574
  resolve: (r) => {
23154
23575
  clearTimeout(timer2);
23155
23576
  this.pendingPrompts.delete(k);
23156
- resolve34(r);
23577
+ resolve35(r);
23157
23578
  }
23158
23579
  });
23159
23580
  console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
@@ -24149,7 +24570,7 @@ function buildTelegramSpawnRequest(input) {
24149
24570
  // app/lib/telegram/outbound/send-document.ts
24150
24571
  import { realpathSync as realpathSync7 } from "fs";
24151
24572
  import { readFile as readFile7, stat as stat7 } from "fs/promises";
24152
- import { resolve as resolve30, basename as basename11 } from "path";
24573
+ import { resolve as resolve31, basename as basename11 } from "path";
24153
24574
  var TAG42 = "[telegram:outbound]";
24154
24575
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
24155
24576
  async function sendTelegramDocument(input) {
@@ -24160,7 +24581,7 @@ async function sendTelegramDocument(input) {
24160
24581
  if (!maxyAccountId || !platformRoot3) {
24161
24582
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
24162
24583
  }
24163
- const accountDir = resolve30(platformRoot3, "..", "data/accounts", maxyAccountId);
24584
+ const accountDir = resolve31(platformRoot3, "..", "data/accounts", maxyAccountId);
24164
24585
  let resolvedPath;
24165
24586
  try {
24166
24587
  resolvedPath = realpathSync7(filePath);
@@ -24287,12 +24708,12 @@ function startTelegramNativeFileFollower(input) {
24287
24708
  }
24288
24709
 
24289
24710
  // server/telegram-descriptor.ts
24290
- import { resolve as resolve31, join as join35 } from "path";
24711
+ import { resolve as resolve32, join as join36 } from "path";
24291
24712
  function telegramGatewayUrl() {
24292
24713
  return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
24293
24714
  }
24294
24715
  function telegramServerPath() {
24295
- return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ?? join35(__dirname, ".."), "services/telegram-channel/dist/server.js");
24716
+ return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve32(process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, ".."), "services/telegram-channel/dist/server.js");
24296
24717
  }
24297
24718
 
24298
24719
  // app/lib/channel-pty-bridge/public-session-end-review.ts
@@ -24554,8 +24975,8 @@ function broadcastAdminShutdown(reason) {
24554
24975
 
24555
24976
  // ../lib/entitlement/src/index.ts
24556
24977
  import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
24557
- import { existsSync as existsSync34, readFileSync as readFileSync34, statSync as statSync13 } from "fs";
24558
- import { resolve as resolve32 } from "path";
24978
+ import { existsSync as existsSync34, readFileSync as readFileSync36, statSync as statSync13 } from "fs";
24979
+ import { resolve as resolve33 } from "path";
24559
24980
 
24560
24981
  // ../lib/entitlement/src/canonicalize.ts
24561
24982
  function canonicalize(value) {
@@ -24590,7 +25011,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
24590
25011
  var GRACE_DAYS = 7;
24591
25012
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
24592
25013
  function pubkeyPath(brand) {
24593
- return resolve32(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
25014
+ return resolve33(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
24594
25015
  }
24595
25016
  var memo = null;
24596
25017
  function memoKey(mtimeMs, account) {
@@ -24602,7 +25023,7 @@ function resolveEntitlement(brand, account) {
24602
25023
  if (brand.commercialMode !== true) {
24603
25024
  return logResolved(implicitTrust(account), null);
24604
25025
  }
24605
- const entitlementPath = resolve32(brand.configDir, "entitlement.json");
25026
+ const entitlementPath = resolve33(brand.configDir, "entitlement.json");
24606
25027
  if (!existsSync34(entitlementPath)) {
24607
25028
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
24608
25029
  }
@@ -24618,7 +25039,7 @@ function resolveEntitlement(brand, account) {
24618
25039
  function verifyAndResolve(brand, entitlementPath, account) {
24619
25040
  let pubkeyPem;
24620
25041
  try {
24621
- pubkeyPem = readFileSync34(pubkeyPath(brand), "utf-8");
25042
+ pubkeyPem = readFileSync36(pubkeyPath(brand), "utf-8");
24622
25043
  } catch (err) {
24623
25044
  return logResolved(anonymousFallback("pubkey-missing"), {
24624
25045
  reason: "pubkey-missing"
@@ -24632,7 +25053,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
24632
25053
  }
24633
25054
  let envelope;
24634
25055
  try {
24635
- envelope = JSON.parse(readFileSync34(entitlementPath, "utf-8"));
25056
+ envelope = JSON.parse(readFileSync36(entitlementPath, "utf-8"));
24636
25057
  } catch {
24637
25058
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
24638
25059
  }
@@ -24793,14 +25214,14 @@ function clientFrom(c) {
24793
25214
  );
24794
25215
  }
24795
25216
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
24796
- var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join36(PLATFORM_ROOT8, "config", "brand.json") : "";
25217
+ var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join37(PLATFORM_ROOT8, "config", "brand.json") : "";
24797
25218
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
24798
25219
  if (BRAND_JSON_PATH && !existsSync35(BRAND_JSON_PATH)) {
24799
25220
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
24800
25221
  }
24801
25222
  if (BRAND_JSON_PATH && existsSync35(BRAND_JSON_PATH)) {
24802
25223
  try {
24803
- const parsed = JSON.parse(readFileSync35(BRAND_JSON_PATH, "utf-8"));
25224
+ const parsed = JSON.parse(readFileSync37(BRAND_JSON_PATH, "utf-8"));
24804
25225
  BRAND = { ...BRAND, ...parsed };
24805
25226
  } catch (err) {
24806
25227
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -24832,11 +25253,11 @@ var brandLoginOpts = {
24832
25253
  appleTouchIconPath: brandAppIcon512Path,
24833
25254
  themeColor: BRAND.defaultColors?.primary
24834
25255
  };
24835
- var ALIAS_DOMAINS_PATH = join36(homedir4(), BRAND.configDir, "alias-domains.json");
25256
+ var ALIAS_DOMAINS_PATH = join37(homedir4(), BRAND.configDir, "alias-domains.json");
24836
25257
  function loadAliasDomains() {
24837
25258
  try {
24838
25259
  if (!existsSync35(ALIAS_DOMAINS_PATH)) return null;
24839
- const parsed = JSON.parse(readFileSync35(ALIAS_DOMAINS_PATH, "utf-8"));
25260
+ const parsed = JSON.parse(readFileSync37(ALIAS_DOMAINS_PATH, "utf-8"));
24840
25261
  if (!Array.isArray(parsed)) {
24841
25262
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
24842
25263
  return null;
@@ -24899,7 +25320,7 @@ async function recordPassiveDispatch(input) {
24899
25320
  var waGateway = new WaGateway({
24900
25321
  fetchStandingRules: fetchAccountStandingRules,
24901
25322
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
24902
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
25323
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
24903
25324
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
24904
25325
  // path-validation scope) is the sender's effective SESSION account, resolved
24905
25326
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -24914,7 +25335,7 @@ var waGateway = new WaGateway({
24914
25335
  caption,
24915
25336
  accountId,
24916
25337
  maxyAccountId,
24917
- platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, ".."))
25338
+ platformRoot: resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."))
24918
25339
  });
24919
25340
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
24920
25341
  },
@@ -25512,8 +25933,8 @@ app61.get("/agent-assets/:slug/:filename", (c) => {
25512
25933
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
25513
25934
  return c.text("Not found", 404);
25514
25935
  }
25515
- const filePath = resolve33(account.accountDir, "agents", slug, "assets", filename);
25516
- const expectedDir = resolve33(account.accountDir, "agents", slug, "assets");
25936
+ const filePath = resolve34(account.accountDir, "agents", slug, "assets", filename);
25937
+ const expectedDir = resolve34(account.accountDir, "agents", slug, "assets");
25517
25938
  if (!filePath.startsWith(expectedDir + "/")) {
25518
25939
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
25519
25940
  return c.text("Forbidden", 403);
@@ -25525,7 +25946,7 @@ app61.get("/agent-assets/:slug/:filename", (c) => {
25525
25946
  const ext = "." + filename.split(".").pop()?.toLowerCase();
25526
25947
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
25527
25948
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
25528
- const body = readFileSync35(filePath);
25949
+ const body = readFileSync37(filePath);
25529
25950
  return c.body(body, 200, {
25530
25951
  "Content-Type": contentType,
25531
25952
  "Cache-Control": "public, max-age=3600"
@@ -25542,8 +25963,8 @@ app61.get("/generated/:filename", (c) => {
25542
25963
  console.error(`[generated] serve file=${filename} status=404`);
25543
25964
  return c.text("Not found", 404);
25544
25965
  }
25545
- const filePath = resolve33(account.accountDir, "generated", filename);
25546
- const expectedDir = resolve33(account.accountDir, "generated");
25966
+ const filePath = resolve34(account.accountDir, "generated", filename);
25967
+ const expectedDir = resolve34(account.accountDir, "generated");
25547
25968
  if (!filePath.startsWith(expectedDir + "/")) {
25548
25969
  console.error(`[generated] serve file=${filename} status=403`);
25549
25970
  return c.text("Forbidden", 403);
@@ -25555,7 +25976,7 @@ app61.get("/generated/:filename", (c) => {
25555
25976
  const ext = "." + filename.split(".").pop()?.toLowerCase();
25556
25977
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
25557
25978
  console.log(`[generated] serve file=${filename} status=200`);
25558
- const body = readFileSync35(filePath);
25979
+ const body = readFileSync37(filePath);
25559
25980
  return c.body(body, 200, {
25560
25981
  "Content-Type": contentType,
25561
25982
  "Cache-Control": "public, max-age=86400"
@@ -25570,7 +25991,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
25570
25991
  var brandIconPath = "/brand/maxy-monochrome.png";
25571
25992
  if (BRAND_JSON_PATH && existsSync35(BRAND_JSON_PATH)) {
25572
25993
  try {
25573
- const fullBrand = JSON.parse(readFileSync35(BRAND_JSON_PATH, "utf-8"));
25994
+ const fullBrand = JSON.parse(readFileSync37(BRAND_JSON_PATH, "utf-8"));
25574
25995
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
25575
25996
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
25576
25997
  } catch {
@@ -25597,11 +26018,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
25597
26018
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
25598
26019
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
25599
26020
  var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
25600
- (p) => existsSync35(resolve33(process.cwd(), "public", p.replace(/^\//, "")))
26021
+ (p) => existsSync35(resolve34(process.cwd(), "public", p.replace(/^\//, "")))
25601
26022
  );
25602
26023
  var SW_SOURCE = (() => {
25603
26024
  try {
25604
- return readFileSync35(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
26025
+ return readFileSync37(resolve34(process.cwd(), "public", "sw.js"), "utf-8");
25605
26026
  } catch {
25606
26027
  return null;
25607
26028
  }
@@ -25609,9 +26030,9 @@ var SW_SOURCE = (() => {
25609
26030
  function readInstalledVersion() {
25610
26031
  try {
25611
26032
  if (!PLATFORM_ROOT8) return "unknown";
25612
- const versionFile = join36(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
26033
+ const versionFile = join37(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
25613
26034
  if (!existsSync35(versionFile)) return "unknown";
25614
- const content = readFileSync35(versionFile, "utf-8").trim();
26035
+ const content = readFileSync37(versionFile, "utf-8").trim();
25615
26036
  return content || "unknown";
25616
26037
  } catch {
25617
26038
  return "unknown";
@@ -25652,7 +26073,7 @@ var clientErrorReporterScript = `<script>
25652
26073
  function cachedHtml(file) {
25653
26074
  let html = htmlCache.get(file);
25654
26075
  if (!html) {
25655
- html = readFileSync35(resolve33(process.cwd(), "public", file), "utf-8");
26076
+ html = readFileSync37(resolve34(process.cwd(), "public", file), "utf-8");
25656
26077
  const productNameEsc = escapeHtml(BRAND.productName);
25657
26078
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
25658
26079
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -25669,13 +26090,13 @@ ${clientErrorReporterScript}
25669
26090
  return html;
25670
26091
  }
25671
26092
  function loadBrandingCache(agentSlug) {
25672
- const configDir2 = join36(homedir4(), BRAND.configDir);
26093
+ const configDir2 = join37(homedir4(), BRAND.configDir);
25673
26094
  try {
25674
26095
  const accountId = getDefaultAccountId();
25675
26096
  if (!accountId) return null;
25676
- const cachePath = join36(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
26097
+ const cachePath = join37(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
25677
26098
  if (!existsSync35(cachePath)) return null;
25678
- return JSON.parse(readFileSync35(cachePath, "utf-8"));
26099
+ return JSON.parse(readFileSync37(cachePath, "utf-8"));
25679
26100
  } catch {
25680
26101
  return null;
25681
26102
  }
@@ -25692,11 +26113,11 @@ var brandedHtmlCache = /* @__PURE__ */ new Map();
25692
26113
  function brandedPublicHtml(agentSlug) {
25693
26114
  const baseHtml = cachedHtml("public.html");
25694
26115
  if (!agentSlug) return baseHtml;
25695
- const cached2 = brandedHtmlCache.get(agentSlug);
26116
+ const cached3 = brandedHtmlCache.get(agentSlug);
25696
26117
  const branding = loadBrandingCache(agentSlug);
25697
26118
  if (!branding) return baseHtml;
25698
26119
  const brandHash = JSON.stringify(branding).length;
25699
- if (cached2 && cached2.mtime === brandHash) return cached2.html;
26120
+ if (cached3 && cached3.mtime === brandHash) return cached3.html;
25700
26121
  const title = escapeHtml(branding.name);
25701
26122
  const description = branding.tagline ? escapeHtml(branding.tagline) : "";
25702
26123
  const themeColor = branding.primaryColor || "";
@@ -25770,7 +26191,7 @@ app61.use("/vnc-popout.html", logViewerFetch);
25770
26191
  app61.get("/vnc-popout.html", (c) => {
25771
26192
  let html = htmlCache.get("vnc-popout.html");
25772
26193
  if (!html) {
25773
- html = readFileSync35(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
26194
+ html = readFileSync37(resolve34(process.cwd(), "public", "vnc-popout.html"), "utf-8");
25774
26195
  const name = escapeHtml(BRAND.productName);
25775
26196
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
25776
26197
  html = html.replace("</head>", ` ${brandScript}
@@ -25902,8 +26323,8 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
25902
26323
  var httpServer = serve({ fetch: app61.fetch, port, hostname });
25903
26324
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
25904
26325
  {
25905
- const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, "..");
25906
- const reconcileScript = resolve33(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
26326
+ const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..");
26327
+ const reconcileScript = resolve34(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
25907
26328
  const RECONCILE_INTERVAL_MS = 12e4;
25908
26329
  const runReconcile = () => {
25909
26330
  if (!existsSync35(reconcileScript)) return;
@@ -25924,8 +26345,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
25924
26345
  loop.unref();
25925
26346
  }
25926
26347
  {
25927
- const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, "..");
25928
- const publishScript = resolve33(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
26348
+ const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..");
26349
+ const publishScript = resolve34(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
25929
26350
  const PUBLISH_INTERVAL_MS = 3e5;
25930
26351
  const currentAccount = () => {
25931
26352
  try {
@@ -25954,21 +26375,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
25954
26375
  }
25955
26376
  };
25956
26377
  const auditSnapshotAge = (account) => {
25957
- const availPath = resolve33(account.accountDir, "calendar-availability.json");
26378
+ const availPath = resolve34(account.accountDir, "calendar-availability.json");
25958
26379
  let hasBookingSite = false;
25959
26380
  try {
25960
26381
  if (existsSync35(availPath)) {
25961
- const cfg = JSON.parse(readFileSync35(availPath, "utf-8"));
26382
+ const cfg = JSON.parse(readFileSync37(availPath, "utf-8"));
25962
26383
  hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
25963
26384
  }
25964
26385
  } catch {
25965
26386
  }
25966
26387
  if (!hasBookingSite) return;
25967
- const statePath = resolve33(account.accountDir, "state", "booking-availability", "last-publish.json");
26388
+ const statePath = resolve34(account.accountDir, "state", "booking-availability", "last-publish.json");
25968
26389
  let lastSuccessAt = null;
25969
26390
  if (existsSync35(statePath)) {
25970
26391
  try {
25971
- const rec = JSON.parse(readFileSync35(statePath, "utf-8"));
26392
+ const rec = JSON.parse(readFileSync37(statePath, "utf-8"));
25972
26393
  lastSuccessAt = rec.lastSuccessAt ?? null;
25973
26394
  } catch {
25974
26395
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
@@ -25992,8 +26413,8 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
25992
26413
  }
25993
26414
  startTimeEntryCensus(() => getSession());
25994
26415
  {
25995
- const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, "..");
25996
- const auditScript = resolve33(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
26416
+ const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..");
26417
+ const auditScript = resolve34(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
25997
26418
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
25998
26419
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
25999
26420
  const runConnectorAudit = () => {
@@ -26111,7 +26532,7 @@ async function runAdminUserReconcileTick() {
26111
26532
  console.error("[adminuser-self-heal] skip reason=no-users-file");
26112
26533
  return;
26113
26534
  }
26114
- const usersRaw = readFileSync35(USERS_FILE, "utf-8").trim();
26535
+ const usersRaw = readFileSync37(USERS_FILE, "utf-8").trim();
26115
26536
  if (!usersRaw) {
26116
26537
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
26117
26538
  return;
@@ -26141,7 +26562,7 @@ if (typeof adminUserReconcileTimer.unref === "function") adminUserReconcileTimer
26141
26562
  var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
26142
26563
  function countUsersRows() {
26143
26564
  if (!existsSync35(USERS_FILE)) return 0;
26144
- const raw = readFileSync35(USERS_FILE, "utf-8").trim();
26565
+ const raw = readFileSync37(USERS_FILE, "utf-8").trim();
26145
26566
  if (!raw) return 0;
26146
26567
  const users = JSON.parse(raw);
26147
26568
  return users.filter((u) => typeof u.userId === "string").length;
@@ -26261,7 +26682,7 @@ if (bootAccountConfig?.whatsapp) {
26261
26682
  }
26262
26683
  init({
26263
26684
  configDir: configDirForWhatsApp,
26264
- platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join36(__dirname, "..")),
26685
+ platformRoot: resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, "..")),
26265
26686
  accountConfig: bootAccountConfig,
26266
26687
  onMessage: async (msg) => {
26267
26688
  if (msg.isOwnerMirror) {