@rubytech/create-maxy-code 0.1.461 → 0.1.462

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 (148) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-mcp/dist/index.d.ts +2 -1
  3. package/payload/platform/lib/graph-mcp/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/graph-mcp/dist/index.js +14 -22
  5. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  6. package/payload/platform/lib/graph-mcp/src/index.ts +15 -22
  7. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +8 -7
  8. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts.map +1 -1
  9. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +19 -19
  10. package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
  11. package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +34 -5
  12. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +22 -19
  13. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +35 -17
  14. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -1
  15. package/payload/platform/lib/mcp-stderr-tee/dist/index.js +41 -48
  16. package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -1
  17. package/payload/platform/lib/mcp-stderr-tee/src/index.ts +42 -51
  18. package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js +27 -0
  19. package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js.map +1 -1
  20. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +49 -0
  21. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
  22. package/payload/platform/lib/storage-broker/dist/audit.d.ts +11 -0
  23. package/payload/platform/lib/storage-broker/dist/audit.d.ts.map +1 -1
  24. package/payload/platform/lib/storage-broker/dist/audit.js +16 -0
  25. package/payload/platform/lib/storage-broker/dist/audit.js.map +1 -1
  26. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +18 -0
  27. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
  28. package/payload/platform/lib/storage-broker/dist/cf-exec.js +77 -0
  29. package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
  30. package/payload/platform/lib/storage-broker/dist/house-scoped-token.js +3 -0
  31. package/payload/platform/lib/storage-broker/dist/house-scoped-token.js.map +1 -1
  32. package/payload/platform/lib/storage-broker/dist/registry.d.ts +1 -1
  33. package/payload/platform/lib/storage-broker/dist/registry.d.ts.map +1 -1
  34. package/payload/platform/lib/storage-broker/dist/registry.js.map +1 -1
  35. package/payload/platform/lib/storage-broker/src/__tests__/audit.test.ts +32 -1
  36. package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +61 -0
  37. package/payload/platform/lib/storage-broker/src/audit.ts +45 -0
  38. package/payload/platform/lib/storage-broker/src/cf-exec.ts +91 -0
  39. package/payload/platform/lib/storage-broker/src/house-scoped-token.ts +3 -0
  40. package/payload/platform/lib/storage-broker/src/registry.ts +5 -1
  41. package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +19 -19
  42. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +22 -11
  43. package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +19 -19
  44. package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +19 -19
  45. package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
  46. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +5 -5
  47. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
  48. package/payload/platform/plugins/cloudflare/references/hosting-sites.md +2 -0
  49. package/payload/platform/plugins/cloudflare/skills/calendar-site/SKILL.md +1 -1
  50. package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +2 -0
  51. package/payload/platform/plugins/connector/lib/mcp-spawn-tee/index.js +19 -19
  52. package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +19 -19
  53. package/payload/platform/plugins/docs/references/admin-session.md +3 -3
  54. package/payload/platform/plugins/docs/references/cloudflare.md +2 -0
  55. package/payload/platform/plugins/docs/references/outlook-guide.md +11 -4
  56. package/payload/platform/plugins/docs/references/plugins-guide.md +8 -6
  57. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +19 -19
  58. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +19 -19
  59. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +19 -19
  60. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  61. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +19 -19
  62. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +2 -2
  63. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -1
  64. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  65. package/payload/platform/plugins/outlook/PLUGIN.md +2 -2
  66. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +19 -19
  67. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.d.ts +2 -0
  68. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.d.ts.map +1 -0
  69. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.js +238 -0
  70. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.js.map +1 -0
  71. package/payload/platform/plugins/outlook/mcp/dist/index.js +9 -5
  72. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  73. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +58 -9
  74. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
  75. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +113 -19
  76. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
  77. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +5 -2
  78. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +19 -19
  79. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +19 -19
  80. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +19 -19
  81. package/payload/platform/plugins/storage-broker/PLUGIN.md +9 -1
  82. package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +19 -19
  83. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts.map +1 -1
  84. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js +16 -5
  85. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js.map +1 -1
  86. package/payload/platform/plugins/storage-broker/mcp/dist/index.js +49 -0
  87. package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
  88. package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +19 -19
  89. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +19 -19
  90. package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +19 -19
  91. package/payload/platform/plugins/web-designer/PLUGIN.md +1 -1
  92. package/payload/platform/plugins/web-designer/commands/website.md +6 -0
  93. package/payload/platform/plugins/web-designer/skills/web-design/references/deploy.md +12 -0
  94. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +19 -19
  95. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +19 -19
  96. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +19 -19
  97. package/payload/platform/scripts/__tests__/logs-rotate.test.sh +24 -20
  98. package/payload/platform/scripts/logs-read.sh +31 -0
  99. package/payload/platform/scripts/logs-rotate.sh +21 -18
  100. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  101. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  102. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  104. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +37 -0
  105. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  106. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +5 -3
  107. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  109. package/payload/server/{chunk-JXWFVE5X.js → chunk-BBSPFN45.js} +65 -0
  110. package/payload/server/public/assets/{AdminLoginScreens-BFbpkEUS.js → AdminLoginScreens-Bed8SDxY.js} +1 -1
  111. package/payload/server/public/assets/{AdminShell-CdOGObaZ.js → AdminShell-BLh9O6dn.js} +1 -1
  112. package/payload/server/public/assets/{Checkbox-RFD_F0c0.js → Checkbox-BC_KSbpS.js} +1 -1
  113. package/payload/server/public/assets/{admin-CYGdL-1O.js → admin-DfWgJVI3.js} +1 -1
  114. package/payload/server/public/assets/{browser-DVfa_plj.js → browser-C0vkv-hH.js} +1 -1
  115. package/payload/server/public/assets/{calendar-JC_Dxa4n.js → calendar-_pfHw0Hp.js} +1 -1
  116. package/payload/server/public/assets/chat-CufidiBQ.js +1 -0
  117. package/payload/server/public/assets/chevron-left-BnpVBu51.js +1 -0
  118. package/payload/server/public/assets/data-CR-fbPWr.js +1 -0
  119. package/payload/server/public/assets/{graph-hBUrRSkr.js → graph-BZiudUGr.js} +1 -1
  120. package/payload/server/public/assets/{graph-labels-CHh6zs8t.js → graph-labels-BzmmJwCR.js} +1 -1
  121. package/payload/server/public/assets/{maximize-2-DjxE4rm9.js → maximize-2-Bhdsv0U-.js} +1 -1
  122. package/payload/server/public/assets/{operator-BqU9SQuu.js → operator-Cj0B_usT.js} +1 -1
  123. package/payload/server/public/assets/page-84Fdwiu_.js +32 -0
  124. package/payload/server/public/assets/{page-DiQhU6lD.js → page-8AB3jUa_.js} +1 -1
  125. package/payload/server/public/assets/{public-Cu_eALgY.js → public-BkNW9e9T.js} +1 -1
  126. package/payload/server/public/assets/{rotate-ccw-F9DsSTSJ.js → rotate-ccw-D_5cETGt.js} +1 -1
  127. package/payload/server/public/assets/{tasks-CE-D0f50.js → tasks-EyKLXINi.js} +1 -1
  128. package/payload/server/public/assets/{time-entry-format-D2rsWhov.js → time-entry-format-DAwmmR3i.js} +1 -1
  129. package/payload/server/public/assets/{useCopyFeedback-QxOmoobV.js → useCopyFeedback-rOWd_lzl.js} +1 -1
  130. package/payload/server/public/assets/{useSelectionMode-RIzNqFjV.js → useSelectionMode-DDmLmPph.js} +1 -1
  131. package/payload/server/public/assets/{useSubAccountSwitcher-BW_fK1Nt.css → useSubAccountSwitcher-pu0jjLCz.css} +1 -1
  132. package/payload/server/public/assets/{useVoiceRecorder-WrWurSYI.js → useVoiceRecorder-azG8zSUI.js} +1 -1
  133. package/payload/server/public/browser.html +5 -5
  134. package/payload/server/public/calendar.html +6 -6
  135. package/payload/server/public/chat.html +12 -12
  136. package/payload/server/public/data.html +10 -10
  137. package/payload/server/public/graph.html +11 -11
  138. package/payload/server/public/index.html +13 -13
  139. package/payload/server/public/operator.html +14 -14
  140. package/payload/server/public/public.html +12 -12
  141. package/payload/server/public/tasks.html +5 -5
  142. package/payload/server/server.js +526 -174
  143. package/payload/server/{src-4F37OHLK.js → src-YLGAZY36.js} +5 -1
  144. package/payload/server/public/assets/chat-Yf8Nkop8.js +0 -1
  145. package/payload/server/public/assets/chevron-left-CAiAUBxO.js +0 -1
  146. package/payload/server/public/assets/data-hqdYXaEn.js +0 -1
  147. package/payload/server/public/assets/page-B4c4_4HP.js +0 -32
  148. /package/payload/server/public/assets/{useSubAccountSwitcher-wo_qGW-I.js → useSubAccountSwitcher-CU-lOxTd.js} +0 -0
@@ -122,10 +122,11 @@ import {
122
122
  authorizeAccess,
123
123
  listResources,
124
124
  makeHouseCfExec,
125
+ makeHousePagesExec,
125
126
  registerResource,
126
127
  resolveOwner,
127
128
  tooLargeMessage
128
- } from "./chunk-JXWFVE5X.js";
129
+ } from "./chunk-BBSPFN45.js";
129
130
  import {
130
131
  __commonJS,
131
132
  __toESM
@@ -1535,7 +1536,7 @@ var serveStatic = (options = { root: "" }) => {
1535
1536
  import { readFileSync as readFileSync38, existsSync as existsSync36, watchFile, readdirSync as readdirSync21, statSync as statSync14 } from "fs";
1536
1537
  import { spawn as spawn3 } from "child_process";
1537
1538
  import { createHash as createHash7 } from "crypto";
1538
- import { resolve as resolve35, join as join38, basename as basename12 } from "path";
1539
+ import { resolve as resolve36, join as join38, basename as basename12 } from "path";
1539
1540
  import { homedir as homedir4 } from "os";
1540
1541
  import { monitorEventLoopDelay } from "perf_hooks";
1541
1542
 
@@ -2248,7 +2249,7 @@ var credsSaveQueue = Promise.resolve();
2248
2249
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2249
2250
  console.error(`${TAG2} draining credential save queue\u2026`);
2250
2251
  const timer2 = new Promise(
2251
- (resolve36) => setTimeout(() => resolve36("timeout"), timeoutMs)
2252
+ (resolve37) => setTimeout(() => resolve37("timeout"), timeoutMs)
2252
2253
  );
2253
2254
  const result = await Promise.race([
2254
2255
  credsSaveQueue.then(() => "drained"),
@@ -2411,11 +2412,11 @@ async function createWaSocket(opts) {
2411
2412
  return sock;
2412
2413
  }
2413
2414
  async function waitForConnection(sock) {
2414
- return new Promise((resolve36, reject) => {
2415
+ return new Promise((resolve37, reject) => {
2415
2416
  const handler = (update) => {
2416
2417
  if (update.connection === "open") {
2417
2418
  sock.ev.off("connection.update", handler);
2418
- resolve36();
2419
+ resolve37();
2419
2420
  }
2420
2421
  if (update.connection === "close") {
2421
2422
  sock.ev.off("connection.update", handler);
@@ -2520,7 +2521,7 @@ import { inspect as inspect2 } from "util";
2520
2521
 
2521
2522
  // app/lib/whatsapp/with-timeout.ts
2522
2523
  function withTimeout(label, promise, timeoutMs) {
2523
- return new Promise((resolve36, reject) => {
2524
+ return new Promise((resolve37, reject) => {
2524
2525
  const timer2 = setTimeout(() => {
2525
2526
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
2526
2527
  }, timeoutMs);
@@ -2528,7 +2529,7 @@ function withTimeout(label, promise, timeoutMs) {
2528
2529
  promise.then(
2529
2530
  (value) => {
2530
2531
  clearTimeout(timer2);
2531
- resolve36(value);
2532
+ resolve37(value);
2532
2533
  },
2533
2534
  (err) => {
2534
2535
  clearTimeout(timer2);
@@ -3779,8 +3780,8 @@ async function persistWhatsAppMessage(input) {
3779
3780
  const { givenName, familyName } = splitName(input.pushName);
3780
3781
  const prev = sessionWriteLocks.get(input.cacheKey);
3781
3782
  let release;
3782
- const mine = new Promise((resolve36) => {
3783
- release = resolve36;
3783
+ const mine = new Promise((resolve37) => {
3784
+ release = resolve37;
3784
3785
  });
3785
3786
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3786
3787
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -5484,11 +5485,11 @@ async function connectWithReconnect(conn) {
5484
5485
  console.error(
5485
5486
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5486
5487
  );
5487
- await new Promise((resolve36) => {
5488
- const timer2 = setTimeout(resolve36, delay);
5488
+ await new Promise((resolve37) => {
5489
+ const timer2 = setTimeout(resolve37, delay);
5489
5490
  conn.abortController.signal.addEventListener("abort", () => {
5490
5491
  clearTimeout(timer2);
5491
- resolve36();
5492
+ resolve37();
5492
5493
  }, { once: true });
5493
5494
  });
5494
5495
  }
@@ -5496,16 +5497,16 @@ async function connectWithReconnect(conn) {
5496
5497
  }
5497
5498
  }
5498
5499
  function waitForDisconnectEvent(conn) {
5499
- return new Promise((resolve36) => {
5500
+ return new Promise((resolve37) => {
5500
5501
  if (!conn.sock) {
5501
- resolve36();
5502
+ resolve37();
5502
5503
  return;
5503
5504
  }
5504
5505
  const sock = conn.sock;
5505
5506
  const handler = (update) => {
5506
5507
  if (update.connection === "close") {
5507
5508
  sock.ev.off("connection.update", handler);
5508
- resolve36();
5509
+ resolve37();
5509
5510
  }
5510
5511
  };
5511
5512
  sock.ev.on("connection.update", handler);
@@ -5873,8 +5874,8 @@ async function handleInboundMessage(conn, msg) {
5873
5874
  const conversationKey = isGroup ? remoteJid : senderPhone;
5874
5875
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
5875
5876
  let resolvePending;
5876
- const sttPending = new Promise((resolve36) => {
5877
- resolvePending = resolve36;
5877
+ const sttPending = new Promise((resolve37) => {
5878
+ resolvePending = resolve37;
5878
5879
  });
5879
5880
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
5880
5881
  try {
@@ -6302,20 +6303,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
6302
6303
 
6303
6304
  // server/routes/health.ts
6304
6305
  function checkPort(port2, timeoutMs = 500) {
6305
- return new Promise((resolve36) => {
6306
+ return new Promise((resolve37) => {
6306
6307
  const socket = createConnection2(port2, "127.0.0.1");
6307
6308
  socket.setTimeout(timeoutMs);
6308
6309
  socket.once("connect", () => {
6309
6310
  socket.destroy();
6310
- resolve36(true);
6311
+ resolve37(true);
6311
6312
  });
6312
6313
  socket.once("error", () => {
6313
6314
  socket.destroy();
6314
- resolve36(false);
6315
+ resolve37(false);
6315
6316
  });
6316
6317
  socket.once("timeout", () => {
6317
6318
  socket.destroy();
6318
- resolve36(false);
6319
+ resolve37(false);
6319
6320
  });
6320
6321
  });
6321
6322
  }
@@ -7593,8 +7594,8 @@ async function startLogin(opts) {
7593
7594
  await clearAuth(authDir);
7594
7595
  let resolveCode = null;
7595
7596
  let rejectCode = null;
7596
- const codePromise = new Promise((resolve36, reject) => {
7597
- resolveCode = resolve36;
7597
+ const codePromise = new Promise((resolve37, reject) => {
7598
+ resolveCode = resolve37;
7598
7599
  rejectCode = reject;
7599
7600
  });
7600
7601
  const codeTimer = setTimeout(
@@ -7766,14 +7767,14 @@ function routeDocumentOutboundAt(to, filePath) {
7766
7767
  return lastRouteDocumentOutboundAt.get(routeKey(to, filePath));
7767
7768
  }
7768
7769
  async function sendWhatsAppDocument(input) {
7769
- const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot4 } = input;
7770
+ const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot5 } = input;
7770
7771
  if (!to || !filePath) {
7771
7772
  return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
7772
7773
  }
7773
- if (!maxyAccountId || !platformRoot4) {
7774
+ if (!maxyAccountId || !platformRoot5) {
7774
7775
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
7775
7776
  }
7776
- const accountDir = resolve9(platformRoot4, "..", "data/accounts", maxyAccountId);
7777
+ const accountDir = resolve9(platformRoot5, "..", "data/accounts", maxyAccountId);
7777
7778
  let resolvedPath;
7778
7779
  try {
7779
7780
  resolvedPath = realpathSync3(filePath);
@@ -8734,7 +8735,8 @@ var bodyLimit = (options) => {
8734
8735
 
8735
8736
  // server/routes/storage-broker.ts
8736
8737
  import { join as join11, resolve as resolve11 } from "path";
8737
- import { readdirSync as readdirSync6, readFileSync as readFileSync13, existsSync as existsSync8 } from "fs";
8738
+ import { readdirSync as readdirSync6, readFileSync as readFileSync13, existsSync as existsSync8, realpathSync as realpathSync4 } from "fs";
8739
+ import { randomUUID as randomUUID7 } from "crypto";
8738
8740
  var app3 = new Hono();
8739
8741
  function platformRoot() {
8740
8742
  return process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? join11(__dirname, "..", "..");
@@ -8793,6 +8795,125 @@ var d1QueryBodyLimit = bodyLimit({
8793
8795
  );
8794
8796
  }
8795
8797
  });
8798
+ var pagesDeployBodyLimit = bodyLimit({
8799
+ maxSize: STORAGE_RESOURCE_NAME_MAX_BODY_BYTES,
8800
+ onError: (c) => {
8801
+ log("write", "pages", caller(c) ?? "none", "*", null, "deny", "publish-body-too-large");
8802
+ return c.json(
8803
+ {
8804
+ error: `request body exceeds the ${(STORAGE_RESOURCE_NAME_MAX_BODY_BYTES / 1024).toFixed(
8805
+ 1
8806
+ )} KiB (${STORAGE_RESOURCE_NAME_MAX_BODY_BYTES} bytes) limit for a Pages publish, which carries only a project name, a site folder name and an optional branch.`
8807
+ },
8808
+ 413
8809
+ );
8810
+ }
8811
+ });
8812
+ function resolveSiteDir(account, site) {
8813
+ if (!site || site === "." || site === "..") return null;
8814
+ if (site.includes("/") || site.includes("\\") || site.includes("\0")) return null;
8815
+ const accountsDir = resolve11(platformRoot(), "..", "data", "accounts");
8816
+ const base = resolve11(accountsDir, account, "sites");
8817
+ const dir = resolve11(base, site);
8818
+ if (dir !== base && !dir.startsWith(base + "/")) return null;
8819
+ try {
8820
+ const realBase = realpathSync4(base);
8821
+ const realDir = realpathSync4(dir);
8822
+ if (!realDir.startsWith(realBase + "/")) return null;
8823
+ return realDir;
8824
+ } catch {
8825
+ return null;
8826
+ }
8827
+ }
8828
+ async function verifyDeployed(url) {
8829
+ try {
8830
+ const res = await fetch(`${url}?cb=${Date.now()}`, { method: "GET", redirect: "follow" });
8831
+ return res.status;
8832
+ } catch {
8833
+ return null;
8834
+ }
8835
+ }
8836
+ app3.post("/pages/deploy", pagesDeployBodyLimit, async (c) => {
8837
+ const pub = randomUUID7().slice(0, 8);
8838
+ const account = caller(c);
8839
+ if (!account) {
8840
+ log("write", "pages", "none", "*", null, "deny", "no-caller");
8841
+ return c.json({ error: "no-caller" }, 403);
8842
+ }
8843
+ const parsed = await readJsonBody(c);
8844
+ if (!parsed.ok) return parsed.res;
8845
+ const { project, site, branch } = parsed.body;
8846
+ if (!project || typeof project !== "string") return c.json({ error: "project-required" }, 400);
8847
+ if (!site || typeof site !== "string") return c.json({ error: "site-required" }, 400);
8848
+ if (branch !== void 0 && typeof branch !== "string") return c.json({ error: "branch-invalid" }, 400);
8849
+ console.error(
8850
+ `[pages-broker] op=request pub=${pub} caller=${account} project=${project} site=${site} branch=${branch ?? "default"}`
8851
+ );
8852
+ const dir = resolveSiteDir(account, site);
8853
+ if (dir === null) {
8854
+ console.error(`[pages-broker] op=path pub=${pub} site=${site} inside=false`);
8855
+ log("write", "pages", account, String(site), null, "deny", "invalid-site");
8856
+ return c.json({ error: "invalid-site" }, 400);
8857
+ }
8858
+ console.error(`[pages-broker] op=path pub=${pub} resolved=${dir} inside=true`);
8859
+ const session = getSession();
8860
+ try {
8861
+ const owner = await resolveOwner(session, "pages", project);
8862
+ const decision = authorizeAccess({ caller: account, owner, action: "write" });
8863
+ console.error(
8864
+ `[pages-broker] op=authorize pub=${pub} caller=${account} owner=${owner ?? "none"} decision=${decision.allow ? "allow" : "deny"} reason=${decision.reason}`
8865
+ );
8866
+ log("write", "pages", account, project, owner, decision.allow ? "allow" : "deny", decision.allow ? void 0 : decision.reason);
8867
+ if (!decision.allow) return c.json({ error: decision.reason }, 403);
8868
+ const started = Date.now();
8869
+ const { url } = await (await makeHousePagesExec(platformRoot())).pagesDeploy(dir, project, branch);
8870
+ console.error(`[pages-broker] op=deploy pub=${pub} ranMs=${Date.now() - started} url=${url}`);
8871
+ const httpStatus = await verifyDeployed(url);
8872
+ const verified = httpStatus === 200;
8873
+ console.error(
8874
+ `[pages-broker] op=complete pub=${pub} httpStatus=${httpStatus ?? "unreachable"} verified=${verified}`
8875
+ );
8876
+ if (!verified) {
8877
+ return c.json({ error: "deploy-unverified", url, httpStatus }, 502);
8878
+ }
8879
+ return c.json({ url });
8880
+ } catch (err) {
8881
+ console.error(`[pages-broker] op=exit pub=${pub} result=error detail="${String(err)}"`);
8882
+ return c.json({ error: String(err) }, 500);
8883
+ } finally {
8884
+ await session.close();
8885
+ }
8886
+ });
8887
+ app3.post("/pages/adopt", nameBodyLimit("pages"), async (c) => {
8888
+ const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
8889
+ const account = caller(c);
8890
+ if (!houseAdminScope) {
8891
+ log("write", "pages", account ?? "none", "*", null, "deny", "not-house-admin");
8892
+ return c.json({ error: "not-house-admin" }, 403);
8893
+ }
8894
+ const parsed = await readJsonBody(c);
8895
+ if (!parsed.ok) return parsed.res;
8896
+ const { project, account: target } = parsed.body;
8897
+ if (!project || typeof project !== "string") return c.json({ error: "project-required" }, 400);
8898
+ if (!target || typeof target !== "string") return c.json({ error: "account-required" }, 400);
8899
+ const session = getSession();
8900
+ try {
8901
+ const owner = await resolveOwner(session, "pages", project);
8902
+ if (owner !== null && owner !== target) {
8903
+ log("write", "pages", target, project, owner, "deny", "already-owned");
8904
+ return c.json({ error: "already-owned", owner }, 409);
8905
+ }
8906
+ if (owner === null) {
8907
+ await registerResource(session, { accountId: target, kind: "pages", name: project, cfResourceId: project });
8908
+ }
8909
+ log("write", "pages", target, project, target, "allow");
8910
+ return c.json({ project, account: target });
8911
+ } catch (err) {
8912
+ return c.json({ error: String(err) }, 500);
8913
+ } finally {
8914
+ await session.close();
8915
+ }
8916
+ });
8796
8917
  app3.get("/d1/list", async (c) => {
8797
8918
  const account = caller(c);
8798
8919
  if (!account) return c.json({ error: "no-caller" }, 403);
@@ -9062,7 +9183,7 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
9062
9183
  });
9063
9184
  var storage_broker_default = app3;
9064
9185
  async function runStorageAudit(root) {
9065
- const { reconcileStorage } = await import("./src-4F37OHLK.js");
9186
+ const { reconcileStorage } = await import("./src-YLGAZY36.js");
9066
9187
  const session = getSession();
9067
9188
  try {
9068
9189
  const cf = await makeHouseCfExec(root);
@@ -9094,9 +9215,31 @@ async function runStorageAudit(root) {
9094
9215
  await session.close();
9095
9216
  }
9096
9217
  }
9218
+ async function runPagesAudit(root) {
9219
+ const { reconcilePages } = await import("./src-YLGAZY36.js");
9220
+ const session = getSession();
9221
+ try {
9222
+ const cf = await makeHousePagesExec(root);
9223
+ const r = await reconcilePages({
9224
+ cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
9225
+ registeredProjects: async () => {
9226
+ const res = await session.run(
9227
+ "MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
9228
+ { kind: "pages" }
9229
+ );
9230
+ return res.records.map((rec) => rec.get("name"));
9231
+ }
9232
+ });
9233
+ console.error(
9234
+ `[pages-audit] projects=${r.projects} registered=${r.registered} orphan=${r.orphans.length} phantom=${r.phantoms.length}` + r.orphans.map((n) => ` orphan:${n}`).join("") + r.phantoms.map((n) => ` phantom:${n}`).join("")
9235
+ );
9236
+ } finally {
9237
+ await session.close();
9238
+ }
9239
+ }
9097
9240
 
9098
9241
  // server/routes/whatsapp-reader.ts
9099
- import { readFileSync as readFileSync17, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync11, readdirSync as readdirSync11, realpathSync as realpathSync4 } from "fs";
9242
+ import { readFileSync as readFileSync17, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync11, readdirSync as readdirSync11, realpathSync as realpathSync5 } from "fs";
9100
9243
  import { basename as basename4, dirname as dirname5, isAbsolute, join as join16, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
9101
9244
 
9102
9245
  // server/routes/admin/sidebar-sessions.ts
@@ -9728,7 +9871,31 @@ function joinTextBlocks(content) {
9728
9871
  }
9729
9872
  return parts.join("");
9730
9873
  }
9731
- function assistantTurns(content, ts, promoteFinal = false) {
9874
+ function erroredToolUseIds(lines) {
9875
+ const ids = /* @__PURE__ */ new Set();
9876
+ for (const line of lines) {
9877
+ if (!line || line[0] !== "{" || !line.includes("is_error")) continue;
9878
+ let row;
9879
+ try {
9880
+ const parsed = JSON.parse(line);
9881
+ if (!parsed || typeof parsed !== "object") continue;
9882
+ row = parsed;
9883
+ } catch {
9884
+ continue;
9885
+ }
9886
+ const content = row.message?.content;
9887
+ if (!Array.isArray(content)) continue;
9888
+ for (const block of content) {
9889
+ if (!block || typeof block !== "object") continue;
9890
+ const b = block;
9891
+ if (b.type === "tool_result" && b.is_error === true && typeof b.tool_use_id === "string") {
9892
+ ids.add(b.tool_use_id);
9893
+ }
9894
+ }
9895
+ }
9896
+ return ids;
9897
+ }
9898
+ function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set()) {
9732
9899
  if (!Array.isArray(content)) return [];
9733
9900
  const out = [];
9734
9901
  for (const block of content) {
@@ -9748,7 +9915,12 @@ function assistantTurns(content, ts, promoteFinal = false) {
9748
9915
  const input = b.input ?? {};
9749
9916
  const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
9750
9917
  const caption = typeof input.caption === "string" ? input.caption : null;
9751
- out.push({ kind: "agent-file", files, caption, ts });
9918
+ const refused = typeof b.id === "string" && erroredIds.has(b.id);
9919
+ if (files.length === 0 || refused) {
9920
+ out.push({ kind: "tool-call", name: SEND_USER_FILE_TOOL, input: b.input ?? null, ts });
9921
+ } else {
9922
+ out.push({ kind: "agent-file", files, caption, ts });
9923
+ }
9752
9924
  } else if (b.type === "tool_use" && typeof b.name === "string") {
9753
9925
  out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
9754
9926
  }
@@ -9788,6 +9960,7 @@ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress) {
9788
9960
  }
9789
9961
  function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}) {
9790
9962
  const out = [];
9963
+ const erroredIds = erroredToolUseIds(lines);
9791
9964
  for (const line of lines) {
9792
9965
  if (!line || line[0] !== "{") continue;
9793
9966
  let row;
@@ -9812,7 +9985,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
9812
9985
  if (hasWebchatReply(msg?.content)) turnCtx.repliedThisTurn = true;
9813
9986
  const isEnd = msg?.stop_reason === "end_turn";
9814
9987
  const promoteFinal = isEnd && turnCtx.lastChannelSource === "webchat" && !turnCtx.repliedThisTurn;
9815
- out.push(...assistantTurns(msg?.content, ts, promoteFinal));
9988
+ out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds));
9816
9989
  continue;
9817
9990
  }
9818
9991
  if (row.type === "queue-operation" && row.operation === "enqueue") {
@@ -10209,7 +10382,7 @@ function enrichDownloads(turn, scratchpadRoot) {
10209
10382
  if (turn.kind !== "agent-file") return turn;
10210
10383
  let scratchpadReal = null;
10211
10384
  try {
10212
- scratchpadReal = realpathSync4(scratchpadRoot);
10385
+ scratchpadReal = realpathSync5(scratchpadRoot);
10213
10386
  } catch {
10214
10387
  scratchpadReal = null;
10215
10388
  }
@@ -10220,7 +10393,7 @@ function enrichDownloads(turn, scratchpadRoot) {
10220
10393
  }
10221
10394
  if (scratchpadReal) {
10222
10395
  try {
10223
- const fileReal = realpathSync4(f);
10396
+ const fileReal = realpathSync5(f);
10224
10397
  if (fileReal === scratchpadReal || fileReal.startsWith(scratchpadReal + sep3)) {
10225
10398
  return { root: "scratchpad", path: relative2(scratchpadReal, fileReal) };
10226
10399
  }
@@ -12293,8 +12466,8 @@ function routeTelegramUpdate(input) {
12293
12466
  // server/routes/telegram.ts
12294
12467
  var TAG23 = "[telegram-inbound]";
12295
12468
  function configDirName() {
12296
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12297
- const brandPath = join22(platformRoot4, "config", "brand.json");
12469
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12470
+ const brandPath = join22(platformRoot5, "config", "brand.json");
12298
12471
  if (existsSync15(brandPath)) {
12299
12472
  try {
12300
12473
  return JSON.parse(readFileSync23(brandPath, "utf-8")).configDir ?? ".maxy";
@@ -12493,7 +12666,7 @@ var quickbooks_default = app9;
12493
12666
  // server/routes/onboarding.ts
12494
12667
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync3 } from "child_process";
12495
12668
  import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync18, readFileSync as readFileSync26, unlinkSync as unlinkSync3, renameSync as renameSync7 } from "fs";
12496
- import { createHash as createHash4, randomUUID as randomUUID7 } from "crypto";
12669
+ import { createHash as createHash4, randomUUID as randomUUID8 } from "crypto";
12497
12670
 
12498
12671
  // app/lib/claude-spawn-env.ts
12499
12672
  function claudeBin() {
@@ -12989,7 +13162,7 @@ app10.post("/set-pin", async (c) => {
12989
13162
  const hash = hashPin2(body.pin);
12990
13163
  const account = resolveAccount();
12991
13164
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
12992
- const userId = existingOwnerUserId ?? randomUUID7();
13165
+ const userId = existingOwnerUserId ?? randomUUID8();
12993
13166
  if (existingOwnerUserId) {
12994
13167
  console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
12995
13168
  } else {
@@ -13381,7 +13554,7 @@ app11.post("/", async (c) => {
13381
13554
  var client_error_default = app11;
13382
13555
 
13383
13556
  // server/routes/admin/session.ts
13384
- import { randomUUID as randomUUID8 } from "crypto";
13557
+ import { randomUUID as randomUUID9 } from "crypto";
13385
13558
  async function resolveUserIdentity(accountId, userId) {
13386
13559
  const result = await loadAdminUserName(userId);
13387
13560
  if (result.source === "neo4j") {
@@ -13395,7 +13568,7 @@ async function resolveUserIdentity(accountId, userId) {
13395
13568
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
13396
13569
  const account = resolveAccount();
13397
13570
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
13398
- const signedSessionToken = randomUUID8();
13571
+ const signedSessionToken = randomUUID9();
13399
13572
  const cacheKey = fingerprintSessionKey(signedSessionToken);
13400
13573
  registerSession(cacheKey, "admin", accountId, void 0, userId, userName ?? void 0, role);
13401
13574
  if (userId) setWantsPriorConversation(cacheKey);
@@ -13437,15 +13610,15 @@ function operatorRoleFor(config, userId) {
13437
13610
  async function buildAccountOptionList(accounts, userId) {
13438
13611
  const houseAccount = accounts.find((a) => a.config.role === "house");
13439
13612
  console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
13440
- const resolve36 = async (a) => {
13613
+ const resolve37 = async (a) => {
13441
13614
  const businessName = await fetchAccountName(a.accountId) || void 0;
13442
13615
  return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
13443
13616
  };
13444
13617
  const houseOptions = await Promise.all(
13445
- accounts.filter((a) => a.config.role === "house").map(resolve36)
13618
+ accounts.filter((a) => a.config.role === "house").map(resolve37)
13446
13619
  );
13447
13620
  const nonHouseOptions = await Promise.all(
13448
- accounts.filter((a) => a.config.role !== "house").map(resolve36)
13621
+ accounts.filter((a) => a.config.role !== "house").map(resolve37)
13449
13622
  );
13450
13623
  const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
13451
13624
  nonHouseOptions.sort((x, y) => {
@@ -14929,10 +15102,10 @@ function readTunnelState(brandConfigDir) {
14929
15102
  }
14930
15103
  }
14931
15104
  function resolveTunnelUrl() {
14932
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
15105
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
14933
15106
  let brand;
14934
15107
  try {
14935
- brand = JSON.parse(readFileSync30(resolve21(platformRoot4, "config", "brand.json"), "utf-8"));
15108
+ brand = JSON.parse(readFileSync30(resolve21(platformRoot5, "config", "brand.json"), "utf-8"));
14936
15109
  } catch {
14937
15110
  return null;
14938
15111
  }
@@ -15141,18 +15314,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
15141
15314
  ]);
15142
15315
  var app21 = new Hono();
15143
15316
  app21.post("/", async (c) => {
15144
- const TAG45 = "[admin:events]";
15317
+ const TAG46 = "[admin:events]";
15145
15318
  let body;
15146
15319
  try {
15147
15320
  body = await c.req.json();
15148
15321
  } catch (err) {
15149
15322
  const detail = err instanceof Error ? err.message : String(err);
15150
- console.error(`${TAG45} reject reason=body-not-json detail=${detail}`);
15323
+ console.error(`${TAG46} reject reason=body-not-json detail=${detail}`);
15151
15324
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
15152
15325
  }
15153
15326
  const event = typeof body.event === "string" ? body.event : "";
15154
15327
  if (!ALLOWED_EVENTS.has(event)) {
15155
- console.error(`${TAG45} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15328
+ console.error(`${TAG46} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15156
15329
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
15157
15330
  }
15158
15331
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -15176,8 +15349,8 @@ var events_default = app21;
15176
15349
  // server/routes/admin/files.ts
15177
15350
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync27 } from "fs";
15178
15351
  import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, unlink as unlink2, rename, rmdir } from "fs/promises";
15179
- import { realpathSync as realpathSync5 } from "fs";
15180
- import { randomUUID as randomUUID9 } from "crypto";
15352
+ import { realpathSync as realpathSync6 } from "fs";
15353
+ import { randomUUID as randomUUID10 } from "crypto";
15181
15354
  import { basename as basename9, dirname as dirname11, join as join30, relative as relative4, resolve as resolve23, sep as sep6 } from "path";
15182
15355
  import { Readable as Readable2 } from "stream";
15183
15356
 
@@ -16542,8 +16715,8 @@ async function resolveUploadTarget(c, accountId, uid) {
16542
16715
  const info = await stat4(destDir).catch(() => null);
16543
16716
  if (!info || !info.isDirectory()) return { ok: false, status: 400, error: "Upload destination is not a directory" };
16544
16717
  }
16545
- const dataRootReal = realpathSync5(DATA_ROOT);
16546
- const destDirReal = realpathSync5(destDir);
16718
+ const dataRootReal = realpathSync6(DATA_ROOT);
16719
+ const destDirReal = realpathSync6(destDir);
16547
16720
  if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep6)) {
16548
16721
  console.error(`[data-upload] op=path-traversal-blocked uid=${uid} requested="${destRel}" resolved="${destDirReal}"`);
16549
16722
  return { ok: false, status: 403, error: "Upload destination escapes DATA_ROOT" };
@@ -16569,7 +16742,7 @@ app22.post("/upload", requireAdminSession, async (c) => {
16569
16742
  if (c.req.query("uploadId") !== void 0) {
16570
16743
  return handleChunkUpload(c, accountId);
16571
16744
  }
16572
- const uid = `${Date.now()}-${randomUUID9().slice(0, 8)}`;
16745
+ const uid = `${Date.now()}-${randomUUID10().slice(0, 8)}`;
16573
16746
  const declaredBytes = c.req.header("content-length") ?? "unknown";
16574
16747
  const target = await resolveUploadTarget(c, accountId, uid);
16575
16748
  if (!target.ok) return c.json({ error: target.error }, target.status);
@@ -19644,9 +19817,9 @@ app34.get("/", requireAdminSession, async (c) => {
19644
19817
  var session_usage_default = app34;
19645
19818
 
19646
19819
  // server/routes/admin/session-rc-spawn.ts
19647
- import { randomUUID as randomUUID10 } from "crypto";
19820
+ import { randomUUID as randomUUID11 } from "crypto";
19648
19821
  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}$/;
19649
- function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, adminUserId, authenticatedName, callerAccountId) {
19822
+ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, adminUserId, authenticatedName, callerAccountId) {
19650
19823
  const operator = isOperatorHost(host, operatorDomains);
19651
19824
  const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
19652
19825
  const name = typeof body.name === "string" && body.name.length > 0 ? body.name : void 0;
@@ -19701,8 +19874,8 @@ app35.post("/", requireAdminSession, async (c) => {
19701
19874
  const adminUserId = cacheKey ? getUserIdForSession(cacheKey) : void 0;
19702
19875
  const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
19703
19876
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
19704
- const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID10, adminUserId, authenticatedName, accountId);
19705
- const spawnReqId = randomUUID10();
19877
+ const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID11, adminUserId, authenticatedName, accountId);
19878
+ const spawnReqId = randomUUID11();
19706
19879
  console.log(
19707
19880
  `[chat-spawn] op=request spawnReqId=${spawnReqId} origin=${plan.origin} kind=${plan.kind} sessionId=${plan.kind === "new" ? "new" : plan.sessionId}`
19708
19881
  );
@@ -19774,8 +19947,8 @@ app35.post("/", requireAdminSession, async (c) => {
19774
19947
  var session_rc_spawn_default = app35;
19775
19948
 
19776
19949
  // server/routes/admin/session-reseat.ts
19777
- import { randomUUID as randomUUID11 } from "crypto";
19778
- function resolveReseatPlan(body, mintId = randomUUID11, adminUserId, authenticatedName) {
19950
+ import { randomUUID as randomUUID12 } from "crypto";
19951
+ function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName) {
19779
19952
  const sessionId = mintId();
19780
19953
  const key = `session:${sessionId}`;
19781
19954
  const payload = {
@@ -19829,11 +20002,11 @@ app36.post("/", requireAdminSession, async (c) => {
19829
20002
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
19830
20003
  const plan = resolveReseatPlan(
19831
20004
  { fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
19832
- randomUUID11,
20005
+ randomUUID12,
19833
20006
  adminUserId,
19834
20007
  authenticatedName
19835
20008
  );
19836
- const reseatReqId = randomUUID11();
20009
+ const reseatReqId = randomUUID12();
19837
20010
  console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
19838
20011
  let res;
19839
20012
  try {
@@ -20076,7 +20249,7 @@ app38.get("/", async (c) => {
20076
20249
  var health_default2 = app38;
20077
20250
 
20078
20251
  // server/routes/admin/linkedin-ingest.ts
20079
- import { randomUUID as randomUUID12 } from "crypto";
20252
+ import { randomUUID as randomUUID13 } from "crypto";
20080
20253
 
20081
20254
  // app/lib/channel-pty-bridge/manager-client.ts
20082
20255
  function managerBase2() {
@@ -20329,7 +20502,7 @@ app39.post("/", requireAdminSession, async (c) => {
20329
20502
  );
20330
20503
  const initialMessage = buildInitialMessage(envelope);
20331
20504
  const spawnStart = Date.now();
20332
- const sessionId = randomUUID12();
20505
+ const sessionId = randomUUID13();
20333
20506
  console.log(TAG27 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
20334
20507
  const spawned = await managerRcSpawn({
20335
20508
  sessionId,
@@ -21367,7 +21540,7 @@ app47.get("/", requireAdminSession, async (c) => {
21367
21540
  var tasks_list_default = app47;
21368
21541
 
21369
21542
  // server/routes/admin/task-timer-start.ts
21370
- import { randomUUID as randomUUID13 } from "crypto";
21543
+ import { randomUUID as randomUUID14 } from "crypto";
21371
21544
  var TAG34 = "[task-timer]";
21372
21545
  var app48 = new Hono();
21373
21546
  app48.post("/", requireAdminSession, async (c) => {
@@ -21405,7 +21578,7 @@ app48.post("/", requireAdminSession, async (c) => {
21405
21578
  console.log(`${TAG34} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
21406
21579
  return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
21407
21580
  }
21408
- const entryId = randomUUID13();
21581
+ const entryId = randomUUID14();
21409
21582
  await session.run(
21410
21583
  `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
21411
21584
  CREATE (te:TimeEntry {entryId: $entryId, taskId: $taskId, accountId: $accountId, startedAt: $now, endedAt: null})
@@ -21532,7 +21705,7 @@ app50.post("/", requireAdminSession, async (c) => {
21532
21705
  var task_complete_default = app50;
21533
21706
 
21534
21707
  // server/routes/admin/task-time-adjust.ts
21535
- import { randomUUID as randomUUID14 } from "crypto";
21708
+ import { randomUUID as randomUUID15 } from "crypto";
21536
21709
  import neo4j6 from "neo4j-driver";
21537
21710
  var TAG37 = "[task-time-adjust]";
21538
21711
  var app51 = new Hono();
@@ -21557,7 +21730,7 @@ app51.post("/", requireAdminSession, async (c) => {
21557
21730
  console.error(`${TAG37} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
21558
21731
  return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
21559
21732
  }
21560
- const adjustmentId = randomUUID14();
21733
+ const adjustmentId = randomUUID15();
21561
21734
  const now = (/* @__PURE__ */ new Date()).toISOString();
21562
21735
  console.log(`${TAG37} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
21563
21736
  const session = getSession();
@@ -21891,7 +22064,7 @@ app55.route("/request-magic-link", request_magic_link_default);
21891
22064
  var access_default = app55;
21892
22065
 
21893
22066
  // server/routes/sites.ts
21894
- import { existsSync as existsSync31, readFileSync as readFileSync34, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
22067
+ import { existsSync as existsSync31, readFileSync as readFileSync34, realpathSync as realpathSync7, statSync as statSync12 } from "fs";
21895
22068
  import { resolve as resolve28 } from "path";
21896
22069
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
21897
22070
  var MIME = {
@@ -21983,8 +22156,8 @@ app56.get("/:rel{.*}", (c) => {
21983
22156
  let realPath;
21984
22157
  let realRoot;
21985
22158
  try {
21986
- realPath = realpathSync6(filePath);
21987
- realRoot = realpathSync6(rootDir);
22159
+ realPath = realpathSync7(filePath);
22160
+ realRoot = realpathSync7(rootDir);
21988
22161
  } catch {
21989
22162
  console.error(`[sites] not-found path=${reqPath} status=404`);
21990
22163
  return c.text("Not found", 404);
@@ -22114,9 +22287,9 @@ var cachedAttempted = false;
22114
22287
  function readBrandConfig() {
22115
22288
  if (cachedAttempted) return cached2;
22116
22289
  cachedAttempted = true;
22117
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
22118
- if (!platformRoot4) return null;
22119
- const brandPath = join35(platformRoot4, "config", "brand.json");
22290
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
22291
+ if (!platformRoot5) return null;
22292
+ const brandPath = join35(platformRoot5, "config", "brand.json");
22120
22293
  if (!existsSync32(brandPath)) return null;
22121
22294
  try {
22122
22295
  cached2 = JSON.parse(readFileSync36(brandPath, "utf-8"));
@@ -24453,16 +24626,16 @@ var WebchatGateway = class _WebchatGateway {
24453
24626
  * The public /api/chat route awaits this to return the reply on the same SSE
24454
24627
  * response, preserving the pre-756 POST→single-blob browser contract. */
24455
24628
  awaitReply(key, timeoutMs) {
24456
- return new Promise((resolve36) => {
24629
+ return new Promise((resolve37) => {
24457
24630
  const timer2 = setTimeout(() => {
24458
24631
  this.replyAwaiters.delete(key);
24459
- resolve36({ timeout: true });
24632
+ resolve37({ timeout: true });
24460
24633
  }, timeoutMs);
24461
24634
  if (timer2.unref) timer2.unref();
24462
24635
  this.replyAwaiters.set(key, (r) => {
24463
24636
  clearTimeout(timer2);
24464
24637
  this.replyAwaiters.delete(key);
24465
- resolve36(r);
24638
+ resolve37(r);
24466
24639
  });
24467
24640
  });
24468
24641
  }
@@ -24473,11 +24646,11 @@ var WebchatGateway = class _WebchatGateway {
24473
24646
  * terminal dialog stays answerable). */
24474
24647
  awaitPermissionVerdict(p, timeoutMs) {
24475
24648
  const k = _WebchatGateway.promptKey(p.key, p.requestId);
24476
- return new Promise((resolve36) => {
24649
+ return new Promise((resolve37) => {
24477
24650
  this.pendingPrompts.get(k)?.resolve({ timeout: true });
24478
24651
  const timer2 = setTimeout(() => {
24479
24652
  this.pendingPrompts.delete(k);
24480
- resolve36({ timeout: true });
24653
+ resolve37({ timeout: true });
24481
24654
  }, timeoutMs);
24482
24655
  if (timer2.unref) timer2.unref();
24483
24656
  this.pendingPrompts.set(k, {
@@ -24486,7 +24659,7 @@ var WebchatGateway = class _WebchatGateway {
24486
24659
  resolve: (r) => {
24487
24660
  clearTimeout(timer2);
24488
24661
  this.pendingPrompts.delete(k);
24489
- resolve36(r);
24662
+ resolve37(r);
24490
24663
  }
24491
24664
  });
24492
24665
  console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
@@ -24817,27 +24990,6 @@ var WebchatGateway = class _WebchatGateway {
24817
24990
  }
24818
24991
  };
24819
24992
 
24820
- // app/lib/webchat/gateway/public-spawn-request.ts
24821
- function buildPublicWebchatSpawnRequest(input) {
24822
- const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
24823
- return {
24824
- senderId: input.key,
24825
- role: "public",
24826
- channel: "webchat",
24827
- accountId: input.accountId,
24828
- agentSlug,
24829
- model: SONNET_MODEL,
24830
- // sliceToken/personId are present only for a gated visitor; open-mode
24831
- // public agents carry neither (mirrors the bridge's open-mode key).
24832
- ...sliceToken ? { sliceToken } : {},
24833
- ...personId ? { personId } : {},
24834
- ...visitorId ? { visitorId } : {},
24835
- ...input.previousContext ? { previousContext: input.previousContext } : {},
24836
- name,
24837
- webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
24838
- };
24839
- }
24840
-
24841
24993
  // app/lib/channel-pty-bridge/follower.ts
24842
24994
  function followerPendingMaxMs() {
24843
24995
  return Number(process.env.CHANNEL_PTY_FOLLOWER_PENDING_MAX_MS ?? String(3e5));
@@ -24845,6 +24997,13 @@ function followerPendingMaxMs() {
24845
24997
  function followerRetryMs() {
24846
24998
  return Number(process.env.CHANNEL_PTY_FOLLOWER_RETRY_MS ?? String(1e3));
24847
24999
  }
25000
+ function toolResultText(content) {
25001
+ if (typeof content === "string") return content;
25002
+ if (!Array.isArray(content)) return "";
25003
+ return content.map(
25004
+ (b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : ""
25005
+ ).join("");
25006
+ }
24848
25007
  function startFollower(opts) {
24849
25008
  const abort = new AbortController();
24850
25009
  const { entry, tag } = opts;
@@ -24887,6 +25046,8 @@ function startFollower(opts) {
24887
25046
  const decoder = new TextDecoder("utf8");
24888
25047
  let buffered = "";
24889
25048
  const fileDelivery = opts.fileDelivery ?? null;
25049
+ const captureVerdicts = typeof fileDelivery?.onFileToolResult === "function";
25050
+ const awaitingVerdict = /* @__PURE__ */ new Set();
24890
25051
  let firedFileTools = [];
24891
25052
  let suppressing = opts.suppressResumeReplay === true;
24892
25053
  let discardedTurns = 0;
@@ -24920,6 +25081,28 @@ function startFollower(opts) {
24920
25081
  continue;
24921
25082
  }
24922
25083
  if (event.type === "user") {
25084
+ if (captureVerdicts && awaitingVerdict.size > 0 && !suppressing) {
25085
+ const umsg = event.message;
25086
+ if (umsg && Array.isArray(umsg.content)) {
25087
+ for (const block of umsg.content) {
25088
+ if (block?.type !== "tool_result") continue;
25089
+ const id = block.tool_use_id;
25090
+ if (typeof id !== "string" || !awaitingVerdict.has(id)) continue;
25091
+ awaitingVerdict.delete(id);
25092
+ try {
25093
+ await fileDelivery.onFileToolResult({
25094
+ toolUseId: id,
25095
+ isError: block.is_error === true,
25096
+ text: toolResultText(block.content)
25097
+ });
25098
+ } catch (err) {
25099
+ console.error(
25100
+ `${tag} file-delivery-error sessionId=${sid} phase=tool-result message=${err instanceof Error ? err.message : String(err)}`
25101
+ );
25102
+ }
25103
+ }
25104
+ }
25105
+ }
24923
25106
  entry.pendingTurnText = "";
24924
25107
  firedFileTools = [];
24925
25108
  continue;
@@ -24941,8 +25124,10 @@ function startFollower(opts) {
24941
25124
  continue;
24942
25125
  }
24943
25126
  firedFileTools.push(block.name);
25127
+ const toolUseId = typeof block.id === "string" ? block.id : "";
25128
+ if (captureVerdicts && toolUseId) awaitingVerdict.add(toolUseId);
24944
25129
  try {
24945
- await fileDelivery.onFileToolUse({ toolName: block.name, input: block.input });
25130
+ await fileDelivery.onFileToolUse({ toolName: block.name, toolUseId, input: block.input });
24946
25131
  } catch (err) {
24947
25132
  console.error(
24948
25133
  `${tag} file-delivery-error sessionId=${sid} tool=${block.name} message=${err instanceof Error ? err.message : String(err)}`
@@ -24964,9 +25149,10 @@ function startFollower(opts) {
24964
25149
  if (flush.trim()) {
24965
25150
  await fanOut(entry.subscribers, flush, opts.onError, tag);
24966
25151
  }
24967
- if (fileDelivery && firedFileTools.length > 0) {
25152
+ if (fileDelivery && (firedFileTools.length > 0 || captureVerdicts)) {
24968
25153
  const fired = firedFileTools;
24969
25154
  firedFileTools = [];
25155
+ awaitingVerdict.clear();
24970
25156
  try {
24971
25157
  fileDelivery.onTurnEnd(fired);
24972
25158
  } catch (err) {
@@ -25004,28 +25190,55 @@ async function fanOut(subscribers, text, onError, tag) {
25004
25190
  );
25005
25191
  }
25006
25192
 
25193
+ // app/lib/webchat/file-delivery.ts
25194
+ import { realpathSync as realpathSync8 } from "fs";
25195
+ import { resolve as resolve32 } from "path";
25196
+
25007
25197
  // app/lib/channel-pty-bridge/file-delivery.ts
25008
25198
  var SEND_USER_FILE = "SendUserFile";
25009
25199
  function makeFileDelivery(opts) {
25010
- const { entry, tag, channel, sendFile } = opts;
25200
+ const { entry, tag, channel, sendFile, deferUntilVerdict = false } = opts;
25011
25201
  let failedFiles = [];
25012
25202
  let attempts = 0;
25013
- return {
25203
+ const pending = /* @__PURE__ */ new Map();
25204
+ function parseInput(input) {
25205
+ const i = input ?? {};
25206
+ return {
25207
+ // A non-array `files` yields zero files. This is the observed refused-call
25208
+ // shape: with the tool absent from the enabled set its schema is absent
25209
+ // from the request, so the model emits a JSON-encoded string. Never parse
25210
+ // it back — the harness refused the call.
25211
+ files: Array.isArray(i.files) ? i.files.filter((f) => typeof f === "string") : [],
25212
+ caption: typeof i.caption === "string" ? i.caption : void 0
25213
+ };
25214
+ }
25215
+ async function forward(call) {
25216
+ for (let i = 0; i < call.files.length; i++) {
25217
+ attempts++;
25218
+ const result = await sendFile(call.files[i], i === 0 ? call.caption : void 0);
25219
+ const name = call.files[i].split("/").pop() ?? call.files[i];
25220
+ console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
25221
+ if (!result.ok) failedFiles.push(call.files[i]);
25222
+ }
25223
+ }
25224
+ function unreconciledCall(reason, files, detail) {
25225
+ const d = detail ? ` detail="${detail.replace(/\s+/g, " ").slice(0, 120)}"` : "";
25226
+ console.error(
25227
+ `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${entry.sessionId.slice(0, 8)} tool=${SEND_USER_FILE} reason=${reason} files=${files}${d}`
25228
+ );
25229
+ }
25230
+ const handler = {
25014
25231
  isFileDeliveryTool(toolName) {
25015
25232
  return toolName === SEND_USER_FILE;
25016
25233
  },
25017
25234
  async onFileToolUse(use) {
25018
25235
  if (use.toolName !== SEND_USER_FILE) return;
25019
- const input = use.input ?? {};
25020
- const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
25021
- const caption = typeof input.caption === "string" ? input.caption : void 0;
25022
- for (let i = 0; i < files.length; i++) {
25023
- attempts++;
25024
- const result = await sendFile(files[i], i === 0 ? caption : void 0);
25025
- const name = files[i].split("/").pop() ?? files[i];
25026
- console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
25027
- if (!result.ok) failedFiles.push(files[i]);
25236
+ const call = parseInput(use.input);
25237
+ if (deferUntilVerdict) {
25238
+ pending.set(use.toolUseId, call);
25239
+ return;
25028
25240
  }
25241
+ await forward(call);
25029
25242
  },
25030
25243
  onTurnEnd(firedTools) {
25031
25244
  const failed = failedFiles;
@@ -25038,6 +25251,11 @@ function makeFileDelivery(opts) {
25038
25251
  `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
25039
25252
  );
25040
25253
  }
25254
+ if (deferUntilVerdict) {
25255
+ for (const call of pending.values()) unreconciledCall("no-verdict", call.files.length);
25256
+ pending.clear();
25257
+ return;
25258
+ }
25041
25259
  if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
25042
25260
  console.error(
25043
25261
  `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
@@ -25045,13 +25263,123 @@ function makeFileDelivery(opts) {
25045
25263
  }
25046
25264
  }
25047
25265
  };
25266
+ if (deferUntilVerdict) {
25267
+ handler.onFileToolResult = async (result) => {
25268
+ const call = pending.get(result.toolUseId);
25269
+ if (!call) return;
25270
+ pending.delete(result.toolUseId);
25271
+ if (result.isError) {
25272
+ unreconciledCall("harness-refused", call.files.length, result.text);
25273
+ return;
25274
+ }
25275
+ if (call.files.length === 0) {
25276
+ unreconciledCall("no-files", 0);
25277
+ return;
25278
+ }
25279
+ await forward(call);
25280
+ };
25281
+ }
25282
+ return handler;
25283
+ }
25284
+
25285
+ // app/lib/webchat/file-delivery.ts
25286
+ var TAG41 = "[webchat-adaptor]";
25287
+ function platformRoot2() {
25288
+ return process.env.MAXY_PLATFORM_ROOT || "";
25289
+ }
25290
+ function makeWebchatSendFile(entry) {
25291
+ return async (filePath) => {
25292
+ if (!entry.accountId) {
25293
+ console.error(`${TAG41} file-delivery reject reason=no-account sender=${entry.senderId}`);
25294
+ return { ok: false, error: "no-account" };
25295
+ }
25296
+ const accountDir = resolve32(platformRoot2(), "..", "data/accounts", entry.accountId);
25297
+ try {
25298
+ const resolved = realpathSync8(filePath);
25299
+ const accountResolved = realpathSync8(accountDir);
25300
+ if (!resolved.startsWith(accountResolved + "/")) {
25301
+ console.error(`${TAG41} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
25302
+ return { ok: false, error: "outside-account" };
25303
+ }
25304
+ return { ok: true };
25305
+ } catch (err) {
25306
+ const code = err.code;
25307
+ console.error(
25308
+ `${TAG41} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
25309
+ );
25310
+ return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
25311
+ }
25312
+ };
25313
+ }
25314
+ function makeWebchatFileDelivery(entry) {
25315
+ return makeFileDelivery({
25316
+ entry,
25317
+ tag: TAG41,
25318
+ channel: "webchat",
25319
+ sendFile: makeWebchatSendFile(entry),
25320
+ deferUntilVerdict: true
25321
+ });
25322
+ }
25323
+
25324
+ // app/lib/webchat/gateway/native-file-follower.ts
25325
+ function startWebchatNativeFileFollower(input) {
25326
+ const entry = {
25327
+ sessionId: input.sessionId,
25328
+ role: "admin",
25329
+ channel: "webchat",
25330
+ accountId: input.accountId,
25331
+ senderId: input.key,
25332
+ agentSlug: "admin",
25333
+ lastInboundAt: Date.now(),
25334
+ writeChain: Promise.resolve(),
25335
+ subscribers: /* @__PURE__ */ new Set(),
25336
+ pendingTurnText: "",
25337
+ followerAbort: null,
25338
+ followerRunning: false,
25339
+ sliceToken: "",
25340
+ personId: null,
25341
+ name: null,
25342
+ // Webchat renders from the transcript; there is no separate reply target.
25343
+ replyTarget: null
25344
+ };
25345
+ return startFollower({
25346
+ entry,
25347
+ tag: "[webchat-adaptor]",
25348
+ fileDelivery: makeWebchatFileDelivery(entry),
25349
+ // A resumed session's JSONL already holds prior SendUserFile tool_uses;
25350
+ // suppress replay so historical calls are not re-reconciled on attach.
25351
+ suppressResumeReplay: true,
25352
+ onClose: input.onClose ?? (() => {
25353
+ })
25354
+ });
25355
+ }
25356
+
25357
+ // app/lib/webchat/gateway/public-spawn-request.ts
25358
+ function buildPublicWebchatSpawnRequest(input) {
25359
+ const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
25360
+ return {
25361
+ senderId: input.key,
25362
+ role: "public",
25363
+ channel: "webchat",
25364
+ accountId: input.accountId,
25365
+ agentSlug,
25366
+ model: SONNET_MODEL,
25367
+ // sliceToken/personId are present only for a gated visitor; open-mode
25368
+ // public agents carry neither (mirrors the bridge's open-mode key).
25369
+ ...sliceToken ? { sliceToken } : {},
25370
+ ...personId ? { personId } : {},
25371
+ ...visitorId ? { visitorId } : {},
25372
+ ...input.previousContext ? { previousContext: input.previousContext } : {},
25373
+ name,
25374
+ webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
25375
+ };
25048
25376
  }
25049
25377
 
25050
25378
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
25051
- var TAG41 = "[whatsapp-adaptor]";
25379
+ var TAG42 = "[whatsapp-adaptor]";
25052
25380
  var SEND_USER_FILE2 = "SendUserFile";
25053
25381
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
25054
- function platformRoot2() {
25382
+ function platformRoot3() {
25055
25383
  return process.env.MAXY_PLATFORM_ROOT || "";
25056
25384
  }
25057
25385
  function makeWhatsAppSendFile(entry, maxyAccountId) {
@@ -25062,11 +25390,11 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25062
25390
  caption,
25063
25391
  accountId: entry.accountId,
25064
25392
  maxyAccountId,
25065
- platformRoot: platformRoot2()
25393
+ platformRoot: platformRoot3()
25066
25394
  });
25067
25395
  if (result.ok) return { ok: true };
25068
25396
  console.error(
25069
- `${TAG41} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25397
+ `${TAG42} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25070
25398
  );
25071
25399
  return { ok: false, error: result.error };
25072
25400
  };
@@ -25074,7 +25402,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25074
25402
  function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25075
25403
  const shared = makeFileDelivery({
25076
25404
  entry,
25077
- tag: TAG41,
25405
+ tag: TAG42,
25078
25406
  channel: "whatsapp",
25079
25407
  sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
25080
25408
  });
@@ -25109,7 +25437,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25109
25437
  if (!delivered) {
25110
25438
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
25111
25439
  console.error(
25112
- `${TAG41} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25440
+ `${TAG42} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25113
25441
  );
25114
25442
  }
25115
25443
  }
@@ -25500,35 +25828,35 @@ function buildTelegramSpawnRequest(input) {
25500
25828
  }
25501
25829
 
25502
25830
  // app/lib/telegram/outbound/send-document.ts
25503
- import { realpathSync as realpathSync7 } from "fs";
25831
+ import { realpathSync as realpathSync9 } from "fs";
25504
25832
  import { readFile as readFile7, stat as stat7 } from "fs/promises";
25505
- import { resolve as resolve32, basename as basename11 } from "path";
25506
- var TAG42 = "[telegram:outbound]";
25833
+ import { resolve as resolve33, basename as basename11 } from "path";
25834
+ var TAG43 = "[telegram:outbound]";
25507
25835
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
25508
25836
  async function sendTelegramDocument(input) {
25509
- const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
25837
+ const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
25510
25838
  if (!botToken || !filePath) {
25511
25839
  return { ok: false, status: 400, error: "Missing required fields: botToken, filePath" };
25512
25840
  }
25513
- if (!maxyAccountId || !platformRoot4) {
25841
+ if (!maxyAccountId || !platformRoot5) {
25514
25842
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
25515
25843
  }
25516
- const accountDir = resolve32(platformRoot4, "..", "data/accounts", maxyAccountId);
25844
+ const accountDir = resolve33(platformRoot5, "..", "data/accounts", maxyAccountId);
25517
25845
  let resolvedPath;
25518
25846
  try {
25519
- resolvedPath = realpathSync7(filePath);
25520
- const accountResolved = realpathSync7(accountDir);
25847
+ resolvedPath = realpathSync9(filePath);
25848
+ const accountResolved = realpathSync9(accountDir);
25521
25849
  if (!resolvedPath.startsWith(accountResolved + "/")) {
25522
- console.error(`${TAG42} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25850
+ console.error(`${TAG43} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25523
25851
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
25524
25852
  }
25525
25853
  } catch (err) {
25526
25854
  const code = err.code;
25527
25855
  if (code === "ENOENT") {
25528
- console.error(`${TAG42} document ENOENT path=${filePath}`);
25856
+ console.error(`${TAG43} document ENOENT path=${filePath}`);
25529
25857
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
25530
25858
  }
25531
- console.error(`${TAG42} document path error: ${String(err)}`);
25859
+ console.error(`${TAG43} document path error: ${String(err)}`);
25532
25860
  return { ok: false, status: 500, error: String(err) };
25533
25861
  }
25534
25862
  const fileStat = await stat7(resolvedPath);
@@ -25561,14 +25889,14 @@ async function sendTelegramDocument(input) {
25561
25889
  error = e instanceof Error ? e.message : String(e);
25562
25890
  }
25563
25891
  console.error(
25564
- `${TAG42} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25892
+ `${TAG43} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25565
25893
  );
25566
25894
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
25567
25895
  }
25568
25896
 
25569
25897
  // app/lib/telegram/outbound/file-delivery.ts
25570
- var TAG43 = "[telegram:outbound]";
25571
- function platformRoot3() {
25898
+ var TAG44 = "[telegram:outbound]";
25899
+ function platformRoot4() {
25572
25900
  return process.env.MAXY_PLATFORM_ROOT || "";
25573
25901
  }
25574
25902
  function makeTelegramSendFile(entry) {
@@ -25579,11 +25907,11 @@ function makeTelegramSendFile(entry) {
25579
25907
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
25580
25908
  }
25581
25909
  if (!botToken) {
25582
- console.error(`${TAG43} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25910
+ console.error(`${TAG44} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25583
25911
  return { ok: false, error: "no-bot-token" };
25584
25912
  }
25585
25913
  if (entry.replyTarget == null) {
25586
- console.error(`${TAG43} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25914
+ console.error(`${TAG44} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25587
25915
  return { ok: false, error: "no-reply-target" };
25588
25916
  }
25589
25917
  const result = await sendTelegramDocument({
@@ -25592,7 +25920,7 @@ function makeTelegramSendFile(entry) {
25592
25920
  filePath,
25593
25921
  caption,
25594
25922
  maxyAccountId: entry.accountId,
25595
- platformRoot: platformRoot3()
25923
+ platformRoot: platformRoot4()
25596
25924
  });
25597
25925
  return result.ok ? { ok: true } : { ok: false, error: result.error };
25598
25926
  };
@@ -25600,7 +25928,7 @@ function makeTelegramSendFile(entry) {
25600
25928
  function makeTelegramFileDelivery(entry) {
25601
25929
  return makeFileDelivery({
25602
25930
  entry,
25603
- tag: TAG43,
25931
+ tag: TAG44,
25604
25932
  channel: "telegram",
25605
25933
  sendFile: makeTelegramSendFile(entry)
25606
25934
  });
@@ -25640,17 +25968,17 @@ function startTelegramNativeFileFollower(input) {
25640
25968
  }
25641
25969
 
25642
25970
  // server/telegram-descriptor.ts
25643
- import { resolve as resolve33, join as join37 } from "path";
25971
+ import { resolve as resolve34, join as join37 } from "path";
25644
25972
  function telegramGatewayUrl() {
25645
25973
  return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
25646
25974
  }
25647
25975
  function telegramServerPath() {
25648
- return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/telegram-channel/dist/server.js");
25976
+ return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/telegram-channel/dist/server.js");
25649
25977
  }
25650
25978
 
25651
25979
  // app/lib/channel-pty-bridge/public-session-end-review.ts
25652
- import { randomUUID as randomUUID15 } from "crypto";
25653
- var TAG44 = "[public-session-review]";
25980
+ import { randomUUID as randomUUID16 } from "crypto";
25981
+ var TAG45 = "[public-session-review]";
25654
25982
  var CONSUMED_CAP = 500;
25655
25983
  var consumed = /* @__PURE__ */ new Set();
25656
25984
  function consumeOnce(sessionId) {
@@ -25677,7 +26005,7 @@ async function fetchJsonl(sessionId) {
25677
26005
  return await res.text();
25678
26006
  } catch (err) {
25679
26007
  console.error(
25680
- `${TAG44} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
26008
+ `${TAG45} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
25681
26009
  );
25682
26010
  return null;
25683
26011
  }
@@ -25701,7 +26029,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
25701
26029
  const res = await fetch(url);
25702
26030
  if (!res.ok) {
25703
26031
  console.error(
25704
- `${TAG44} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
26032
+ `${TAG45} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
25705
26033
  );
25706
26034
  return [];
25707
26035
  }
@@ -25709,7 +26037,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
25709
26037
  return Array.isArray(body.writes) ? body.writes : [];
25710
26038
  } catch (err) {
25711
26039
  console.error(
25712
- `${TAG44} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
26040
+ `${TAG45} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
25713
26041
  );
25714
26042
  return [];
25715
26043
  }
@@ -25737,8 +26065,8 @@ function composeInitialMessage(input) {
25737
26065
  ].join("\n");
25738
26066
  }
25739
26067
  async function dispatchReviewer(input, initialMessage) {
25740
- const sessionId = randomUUID15();
25741
- console.log(`${TAG44} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
26068
+ const sessionId = randomUUID16();
26069
+ console.log(`${TAG45} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
25742
26070
  const spawned = await managerRcSpawn({
25743
26071
  sessionId,
25744
26072
  initialMessage,
@@ -25758,21 +26086,21 @@ async function firePublicSessionEndReview(input) {
25758
26086
  const dispatchedAt = Date.now();
25759
26087
  if (consumed.has(input.sessionId)) {
25760
26088
  console.log(
25761
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26089
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
25762
26090
  );
25763
26091
  return;
25764
26092
  }
25765
26093
  const jsonl = await fetchJsonl(input.sessionId);
25766
26094
  if (!jsonl) {
25767
26095
  console.log(
25768
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
26096
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
25769
26097
  );
25770
26098
  return;
25771
26099
  }
25772
26100
  const operatorTurns = countOperatorTurns(jsonl);
25773
26101
  if (operatorTurns === 0) {
25774
26102
  console.log(
25775
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
26103
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
25776
26104
  );
25777
26105
  return;
25778
26106
  }
@@ -25786,19 +26114,19 @@ async function firePublicSessionEndReview(input) {
25786
26114
  });
25787
26115
  if (!consumeOnce(input.sessionId)) {
25788
26116
  console.log(
25789
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26117
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
25790
26118
  );
25791
26119
  return;
25792
26120
  }
25793
26121
  const dispatched = await dispatchReviewer(input, initialMessage);
25794
26122
  if (!dispatched.ok) {
25795
26123
  console.error(
25796
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
26124
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
25797
26125
  );
25798
26126
  return;
25799
26127
  }
25800
26128
  console.log(
25801
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
26129
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
25802
26130
  );
25803
26131
  }
25804
26132
 
@@ -25908,7 +26236,7 @@ function broadcastAdminShutdown(reason) {
25908
26236
  // ../lib/entitlement/src/index.ts
25909
26237
  import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
25910
26238
  import { existsSync as existsSync35, readFileSync as readFileSync37, statSync as statSync13 } from "fs";
25911
- import { resolve as resolve34 } from "path";
26239
+ import { resolve as resolve35 } from "path";
25912
26240
 
25913
26241
  // ../lib/entitlement/src/canonicalize.ts
25914
26242
  function canonicalize(value) {
@@ -25943,7 +26271,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
25943
26271
  var GRACE_DAYS = 7;
25944
26272
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
25945
26273
  function pubkeyPath(brand) {
25946
- return resolve34(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
26274
+ return resolve35(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
25947
26275
  }
25948
26276
  var memo = null;
25949
26277
  function memoKey(mtimeMs, account) {
@@ -25955,7 +26283,7 @@ function resolveEntitlement(brand, account) {
25955
26283
  if (brand.commercialMode !== true) {
25956
26284
  return logResolved(implicitTrust(account), null);
25957
26285
  }
25958
- const entitlementPath = resolve34(brand.configDir, "entitlement.json");
26286
+ const entitlementPath = resolve35(brand.configDir, "entitlement.json");
25959
26287
  if (!existsSync35(entitlementPath)) {
25960
26288
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
25961
26289
  }
@@ -26215,6 +26543,7 @@ function isPublicHost(host) {
26215
26543
  }
26216
26544
  var app62 = new Hono();
26217
26545
  var nativeFileFollowers = /* @__PURE__ */ new Map();
26546
+ var webchatFileFollowers = /* @__PURE__ */ new Map();
26218
26547
  async function fetchAccountStandingRules(accountId) {
26219
26548
  const session = getSession();
26220
26549
  try {
@@ -26252,7 +26581,7 @@ async function recordPassiveDispatch(input) {
26252
26581
  var waGateway = new WaGateway({
26253
26582
  fetchStandingRules: fetchAccountStandingRules,
26254
26583
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
26255
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
26584
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
26256
26585
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
26257
26586
  // path-validation scope) is the sender's effective SESSION account, resolved
26258
26587
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -26267,7 +26596,7 @@ var waGateway = new WaGateway({
26267
26596
  caption,
26268
26597
  accountId,
26269
26598
  maxyAccountId,
26270
- platformRoot: resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."))
26599
+ platformRoot: resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."))
26271
26600
  });
26272
26601
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
26273
26602
  },
@@ -26425,6 +26754,20 @@ var webchatGateway = new WebchatGateway({
26425
26754
  console.error(`[webchat:inbound] op=spawn-failed key=${key} error=${result.error} status=${result.status}`);
26426
26755
  return;
26427
26756
  }
26757
+ const priorWebchatFollower = webchatFileFollowers.get(key);
26758
+ if (priorWebchatFollower) priorWebchatFollower.abort();
26759
+ let wac;
26760
+ wac = startWebchatNativeFileFollower({
26761
+ sessionId: result.sessionId,
26762
+ key,
26763
+ // The session's own resolved account (house or sub-account) — the
26764
+ // file-path validation scope, exactly as passed to the spawn above.
26765
+ accountId,
26766
+ onClose: () => {
26767
+ if (webchatFileFollowers.get(key) === wac) webchatFileFollowers.delete(key);
26768
+ }
26769
+ });
26770
+ webchatFileFollowers.set(key, wac);
26428
26771
  return { sessionId: result.sessionId };
26429
26772
  },
26430
26773
  // Task-962 — the recovery pass asks the manager to respawn a wedged already-
@@ -26886,8 +27229,8 @@ app62.get("/agent-assets/:slug/:filename", (c) => {
26886
27229
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
26887
27230
  return c.text("Not found", 404);
26888
27231
  }
26889
- const filePath = resolve35(account.accountDir, "agents", slug, "assets", filename);
26890
- const expectedDir = resolve35(account.accountDir, "agents", slug, "assets");
27232
+ const filePath = resolve36(account.accountDir, "agents", slug, "assets", filename);
27233
+ const expectedDir = resolve36(account.accountDir, "agents", slug, "assets");
26891
27234
  if (!filePath.startsWith(expectedDir + "/")) {
26892
27235
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
26893
27236
  return c.text("Forbidden", 403);
@@ -26916,8 +27259,8 @@ app62.get("/generated/:filename", (c) => {
26916
27259
  console.error(`[generated] serve file=${filename} status=404`);
26917
27260
  return c.text("Not found", 404);
26918
27261
  }
26919
- const filePath = resolve35(account.accountDir, "generated", filename);
26920
- const expectedDir = resolve35(account.accountDir, "generated");
27262
+ const filePath = resolve36(account.accountDir, "generated", filename);
27263
+ const expectedDir = resolve36(account.accountDir, "generated");
26921
27264
  if (!filePath.startsWith(expectedDir + "/")) {
26922
27265
  console.error(`[generated] serve file=${filename} status=403`);
26923
27266
  return c.text("Forbidden", 403);
@@ -26971,11 +27314,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
26971
27314
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
26972
27315
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
26973
27316
  var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
26974
- (p) => existsSync36(resolve35(process.cwd(), "public", p.replace(/^\//, "")))
27317
+ (p) => existsSync36(resolve36(process.cwd(), "public", p.replace(/^\//, "")))
26975
27318
  );
26976
27319
  var SW_SOURCE = (() => {
26977
27320
  try {
26978
- return readFileSync38(resolve35(process.cwd(), "public", "sw.js"), "utf-8");
27321
+ return readFileSync38(resolve36(process.cwd(), "public", "sw.js"), "utf-8");
26979
27322
  } catch {
26980
27323
  return null;
26981
27324
  }
@@ -27026,7 +27369,7 @@ var clientErrorReporterScript = `<script>
27026
27369
  function cachedHtml(file) {
27027
27370
  let html = htmlCache.get(file);
27028
27371
  if (!html) {
27029
- html = readFileSync38(resolve35(process.cwd(), "public", file), "utf-8");
27372
+ html = readFileSync38(resolve36(process.cwd(), "public", file), "utf-8");
27030
27373
  const productNameEsc = escapeHtml(BRAND.productName);
27031
27374
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
27032
27375
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -27144,7 +27487,7 @@ app62.use("/vnc-popout.html", logViewerFetch);
27144
27487
  app62.get("/vnc-popout.html", (c) => {
27145
27488
  let html = htmlCache.get("vnc-popout.html");
27146
27489
  if (!html) {
27147
- html = readFileSync38(resolve35(process.cwd(), "public", "vnc-popout.html"), "utf-8");
27490
+ html = readFileSync38(resolve36(process.cwd(), "public", "vnc-popout.html"), "utf-8");
27148
27491
  const name = escapeHtml(BRAND.productName);
27149
27492
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
27150
27493
  html = html.replace("</head>", ` ${brandScript}
@@ -27277,7 +27620,7 @@ var httpServer = serve({ fetch: app62.fetch, port, hostname });
27277
27620
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27278
27621
  {
27279
27622
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27280
- const reconcileScript = resolve35(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
27623
+ const reconcileScript = resolve36(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
27281
27624
  const RECONCILE_INTERVAL_MS = 12e4;
27282
27625
  const runReconcile = () => {
27283
27626
  if (!existsSync36(reconcileScript)) return;
@@ -27299,7 +27642,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27299
27642
  }
27300
27643
  {
27301
27644
  const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27302
- const outlookScript = resolve35(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
27645
+ const outlookScript = resolve36(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
27303
27646
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
27304
27647
  const runOutlookComplete = () => {
27305
27648
  if (!existsSync36(outlookScript)) return;
@@ -27321,7 +27664,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27321
27664
  }
27322
27665
  {
27323
27666
  const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27324
- const strandedAccountsDir = resolve35(auditRoot, "..", "data/accounts");
27667
+ const strandedAccountsDir = resolve36(auditRoot, "..", "data/accounts");
27325
27668
  const STRANDED_AUDIT_INTERVAL_MS = 3e5;
27326
27669
  const STRANDED_AGE_MS = 16 * 6e4;
27327
27670
  const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -27331,7 +27674,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27331
27674
  const now = Date.now();
27332
27675
  for (const name of readdirSync21(strandedAccountsDir)) {
27333
27676
  if (!STRANDED_UUID_RE.test(name)) continue;
27334
- const pendingPath2 = resolve35(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
27677
+ const pendingPath2 = resolve36(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
27335
27678
  if (!existsSync36(pendingPath2)) continue;
27336
27679
  const ageMs = now - statSync14(pendingPath2).mtimeMs;
27337
27680
  if (ageMs > STRANDED_AGE_MS) {
@@ -27359,10 +27702,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27359
27702
  auditFirst.unref();
27360
27703
  const auditLoop = setInterval(runStorageAuditSafe, STORAGE_AUDIT_INTERVAL_MS);
27361
27704
  auditLoop.unref();
27705
+ const runPagesAuditSafe = () => {
27706
+ runPagesAudit(auditPlatformRoot).catch(
27707
+ (err) => console.error(`[pages-audit] error="${err.message}"`)
27708
+ );
27709
+ };
27710
+ const pagesAuditFirst = setTimeout(runPagesAuditSafe, 35e3);
27711
+ pagesAuditFirst.unref();
27712
+ const pagesAuditLoop = setInterval(runPagesAuditSafe, STORAGE_AUDIT_INTERVAL_MS);
27713
+ pagesAuditLoop.unref();
27362
27714
  }
27363
27715
  {
27364
27716
  const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27365
- const publishScript = resolve35(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
27717
+ const publishScript = resolve36(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
27366
27718
  const PUBLISH_INTERVAL_MS = 3e5;
27367
27719
  const currentAccount = () => {
27368
27720
  try {
@@ -27391,7 +27743,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27391
27743
  }
27392
27744
  };
27393
27745
  const auditSnapshotAge = (account) => {
27394
- const availPath = resolve35(account.accountDir, "calendar-availability.json");
27746
+ const availPath = resolve36(account.accountDir, "calendar-availability.json");
27395
27747
  let hasBookingSite = false;
27396
27748
  try {
27397
27749
  if (existsSync36(availPath)) {
@@ -27401,7 +27753,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27401
27753
  } catch {
27402
27754
  }
27403
27755
  if (!hasBookingSite) return;
27404
- const statePath = resolve35(account.accountDir, "state", "booking-availability", "last-publish.json");
27756
+ const statePath = resolve36(account.accountDir, "state", "booking-availability", "last-publish.json");
27405
27757
  let lastSuccessAt = null;
27406
27758
  if (existsSync36(statePath)) {
27407
27759
  try {
@@ -27430,7 +27782,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27430
27782
  startTimeEntryCensus(() => getSession());
27431
27783
  {
27432
27784
  const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27433
- const auditScript = resolve35(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
27785
+ const auditScript = resolve36(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
27434
27786
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
27435
27787
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
27436
27788
  const runConnectorAudit = () => {
@@ -27699,7 +28051,7 @@ if (bootAccountConfig?.whatsapp) {
27699
28051
  }
27700
28052
  init({
27701
28053
  configDir: configDirForWhatsApp,
27702
- platformRoot: resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..")),
28054
+ platformRoot: resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..")),
27703
28055
  accountConfig: bootAccountConfig,
27704
28056
  onMessage: async (msg) => {
27705
28057
  if (msg.isOwnerMirror) {