@rubytech/create-maxy-code 0.1.461 → 0.1.463

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 (186) 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 +23 -12
  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/internals.md +1 -1
  56. package/payload/platform/plugins/docs/references/outlook-guide.md +11 -4
  57. package/payload/platform/plugins/docs/references/plugins-guide.md +8 -6
  58. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +19 -19
  59. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +19 -19
  60. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +19 -19
  61. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  62. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +19 -19
  63. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +2 -2
  64. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -1
  65. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  66. package/payload/platform/plugins/outlook/PLUGIN.md +2 -2
  67. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +19 -19
  68. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.d.ts +2 -0
  69. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.d.ts.map +1 -0
  70. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.js +238 -0
  71. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.js.map +1 -0
  72. package/payload/platform/plugins/outlook/mcp/dist/index.js +9 -5
  73. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  74. package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts +1 -6
  75. package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts.map +1 -1
  76. package/payload/platform/plugins/outlook/mcp/dist/lib/message.js +9 -17
  77. package/payload/platform/plugins/outlook/mcp/dist/lib/message.js.map +1 -1
  78. package/payload/platform/plugins/outlook/mcp/dist/lib/recipients.d.ts +56 -0
  79. package/payload/platform/plugins/outlook/mcp/dist/lib/recipients.d.ts.map +1 -0
  80. package/payload/platform/plugins/outlook/mcp/dist/lib/recipients.js +40 -0
  81. package/payload/platform/plugins/outlook/mcp/dist/lib/recipients.js.map +1 -0
  82. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts.map +1 -1
  83. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js +1 -3
  84. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js.map +1 -1
  85. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +58 -9
  86. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
  87. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +97 -18
  88. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
  89. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts.map +1 -1
  90. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js +1 -3
  91. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js.map +1 -1
  92. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts.map +1 -1
  93. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js +3 -18
  94. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js.map +1 -1
  95. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +5 -2
  96. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +19 -19
  97. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +19 -19
  98. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +19 -19
  99. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +6 -32
  100. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  101. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  102. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +10 -30
  103. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  104. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.js +24 -21
  105. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-cancel.test.js.map +1 -1
  106. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts +27 -18
  107. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.d.ts.map +1 -1
  108. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js +31 -27
  109. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-cancel.js.map +1 -1
  110. package/payload/platform/plugins/storage-broker/PLUGIN.md +9 -1
  111. package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +19 -19
  112. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts.map +1 -1
  113. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js +16 -5
  114. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js.map +1 -1
  115. package/payload/platform/plugins/storage-broker/mcp/dist/index.js +49 -0
  116. package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
  117. package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +19 -19
  118. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +19 -19
  119. package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +19 -19
  120. package/payload/platform/plugins/web-designer/PLUGIN.md +1 -1
  121. package/payload/platform/plugins/web-designer/commands/website.md +6 -0
  122. package/payload/platform/plugins/web-designer/skills/web-design/references/deploy.md +12 -0
  123. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +19 -19
  124. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +19 -19
  125. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +19 -19
  126. package/payload/platform/scripts/__tests__/logs-rotate.test.sh +24 -20
  127. package/payload/platform/scripts/logs-read.sh +31 -0
  128. package/payload/platform/scripts/logs-rotate.sh +21 -18
  129. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  130. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  131. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  132. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  133. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +37 -0
  134. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  135. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +5 -3
  136. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  137. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  138. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
  139. package/payload/platform/services/telegram-channel/dist/server.js +0 -4
  140. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
  141. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  142. package/payload/platform/services/webchat-channel/dist/server.js +0 -4
  143. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  144. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  145. package/payload/platform/services/whatsapp-channel/dist/server.js +0 -4
  146. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  147. package/payload/server/{chunk-JXWFVE5X.js → chunk-BBSPFN45.js} +65 -0
  148. package/payload/server/public/assets/{AdminLoginScreens-BFbpkEUS.js → AdminLoginScreens-Bed8SDxY.js} +1 -1
  149. package/payload/server/public/assets/{AdminShell-CdOGObaZ.js → AdminShell-BLh9O6dn.js} +1 -1
  150. package/payload/server/public/assets/{Checkbox-RFD_F0c0.js → Checkbox-BC_KSbpS.js} +1 -1
  151. package/payload/server/public/assets/{admin-CYGdL-1O.js → admin-DfWgJVI3.js} +1 -1
  152. package/payload/server/public/assets/{browser-DVfa_plj.js → browser-C0vkv-hH.js} +1 -1
  153. package/payload/server/public/assets/{calendar-JC_Dxa4n.js → calendar-_pfHw0Hp.js} +1 -1
  154. package/payload/server/public/assets/chat-DYT5H81o.js +1 -0
  155. package/payload/server/public/assets/chevron-left-BnpVBu51.js +1 -0
  156. package/payload/server/public/assets/data-CR-fbPWr.js +1 -0
  157. package/payload/server/public/assets/{graph-hBUrRSkr.js → graph-BZiudUGr.js} +1 -1
  158. package/payload/server/public/assets/{graph-labels-CHh6zs8t.js → graph-labels-BzmmJwCR.js} +1 -1
  159. package/payload/server/public/assets/{maximize-2-DjxE4rm9.js → maximize-2-Bhdsv0U-.js} +1 -1
  160. package/payload/server/public/assets/{operator-BqU9SQuu.js → operator-gm2eP4r6.js} +1 -1
  161. package/payload/server/public/assets/{page-DiQhU6lD.js → page-8AB3jUa_.js} +1 -1
  162. package/payload/server/public/assets/page-BoPorLg-.js +32 -0
  163. package/payload/server/public/assets/{public-Cu_eALgY.js → public-DqJk6qgW.js} +1 -1
  164. package/payload/server/public/assets/{rotate-ccw-F9DsSTSJ.js → rotate-ccw-D_5cETGt.js} +1 -1
  165. package/payload/server/public/assets/{tasks-CE-D0f50.js → tasks-EyKLXINi.js} +1 -1
  166. package/payload/server/public/assets/{time-entry-format-D2rsWhov.js → time-entry-format-DAwmmR3i.js} +1 -1
  167. package/payload/server/public/assets/{useCopyFeedback-QxOmoobV.js → useCopyFeedback-rOWd_lzl.js} +1 -1
  168. package/payload/server/public/assets/{useSelectionMode-RIzNqFjV.js → useSelectionMode-DDmLmPph.js} +1 -1
  169. package/payload/server/public/assets/{useSubAccountSwitcher-BW_fK1Nt.css → useSubAccountSwitcher-pu0jjLCz.css} +1 -1
  170. package/payload/server/public/assets/{useVoiceRecorder-WrWurSYI.js → useVoiceRecorder-azG8zSUI.js} +1 -1
  171. package/payload/server/public/browser.html +5 -5
  172. package/payload/server/public/calendar.html +6 -6
  173. package/payload/server/public/chat.html +12 -12
  174. package/payload/server/public/data.html +10 -10
  175. package/payload/server/public/graph.html +11 -11
  176. package/payload/server/public/index.html +13 -13
  177. package/payload/server/public/operator.html +14 -14
  178. package/payload/server/public/public.html +12 -12
  179. package/payload/server/public/tasks.html +5 -5
  180. package/payload/server/server.js +596 -235
  181. package/payload/server/{src-4F37OHLK.js → src-YLGAZY36.js} +5 -1
  182. package/payload/server/public/assets/chat-Yf8Nkop8.js +0 -1
  183. package/payload/server/public/assets/chevron-left-CAiAUBxO.js +0 -1
  184. package/payload/server/public/assets/data-hqdYXaEn.js +0 -1
  185. package/payload/server/public/assets/page-B4c4_4HP.js +0 -32
  186. /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
 
@@ -2187,6 +2188,7 @@ import {
2187
2188
  } from "@whiskeysockets/baileys";
2188
2189
  import { createRequire } from "module";
2189
2190
  var TAG2 = "[whatsapp:session]";
2191
+ var WIRE_TAG = "[whatsapp:wire]";
2190
2192
  var baileysPkgVersion = (() => {
2191
2193
  try {
2192
2194
  const req = createRequire(import.meta.url);
@@ -2248,7 +2250,7 @@ var credsSaveQueue = Promise.resolve();
2248
2250
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2249
2251
  console.error(`${TAG2} draining credential save queue\u2026`);
2250
2252
  const timer2 = new Promise(
2251
- (resolve36) => setTimeout(() => resolve36("timeout"), timeoutMs)
2253
+ (resolve37) => setTimeout(() => resolve37("timeout"), timeoutMs)
2252
2254
  );
2253
2255
  const result = await Promise.race([
2254
2256
  credsSaveQueue.then(() => "drained"),
@@ -2407,15 +2409,21 @@ async function createWaSocket(opts) {
2407
2409
  sock.ws.on("error", (err) => {
2408
2410
  console.error(`${TAG2} WebSocket error: ${String(err)}`);
2409
2411
  });
2412
+ sock.ws.on("CB:message", (node) => {
2413
+ const attrs = node?.attrs ?? {};
2414
+ console.error(
2415
+ `${WIRE_TAG} op=stanza msgId=${attrs.id ?? "none"} from=${attrs.from ?? "none"} type=${attrs.type ?? "none"} offline=${attrs.offline ?? "none"} account=${acct}`
2416
+ );
2417
+ });
2410
2418
  }
2411
2419
  return sock;
2412
2420
  }
2413
2421
  async function waitForConnection(sock) {
2414
- return new Promise((resolve36, reject) => {
2422
+ return new Promise((resolve37, reject) => {
2415
2423
  const handler = (update) => {
2416
2424
  if (update.connection === "open") {
2417
2425
  sock.ev.off("connection.update", handler);
2418
- resolve36();
2426
+ resolve37();
2419
2427
  }
2420
2428
  if (update.connection === "close") {
2421
2429
  sock.ev.off("connection.update", handler);
@@ -2520,7 +2528,7 @@ import { inspect as inspect2 } from "util";
2520
2528
 
2521
2529
  // app/lib/whatsapp/with-timeout.ts
2522
2530
  function withTimeout(label, promise, timeoutMs) {
2523
- return new Promise((resolve36, reject) => {
2531
+ return new Promise((resolve37, reject) => {
2524
2532
  const timer2 = setTimeout(() => {
2525
2533
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
2526
2534
  }, timeoutMs);
@@ -2528,7 +2536,7 @@ function withTimeout(label, promise, timeoutMs) {
2528
2536
  promise.then(
2529
2537
  (value) => {
2530
2538
  clearTimeout(timer2);
2531
- resolve36(value);
2539
+ resolve37(value);
2532
2540
  },
2533
2541
  (err) => {
2534
2542
  clearTimeout(timer2);
@@ -3779,8 +3787,8 @@ async function persistWhatsAppMessage(input) {
3779
3787
  const { givenName, familyName } = splitName(input.pushName);
3780
3788
  const prev = sessionWriteLocks.get(input.cacheKey);
3781
3789
  let release;
3782
- const mine = new Promise((resolve36) => {
3783
- release = resolve36;
3790
+ const mine = new Promise((resolve37) => {
3791
+ release = resolve37;
3784
3792
  });
3785
3793
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3786
3794
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -5270,14 +5278,6 @@ async function registerLoginSocket(accountId, sock, authDir) {
5270
5278
  throw err;
5271
5279
  }
5272
5280
  await stopConnection(accountId);
5273
- const selfId = readSelfId(authDir);
5274
- const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
5275
- const lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
5276
- if (selfId.lid) {
5277
- console.error(`[whatsapp:lid] resolved self-lid accountId=${accountId} selfLid=${selfId.lid} source=creds`);
5278
- } else {
5279
- console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${accountId} reason=creds-missing-lid`);
5280
- }
5281
5281
  const conn = {
5282
5282
  accountId,
5283
5283
  platformAccountId,
@@ -5285,16 +5285,18 @@ async function registerLoginSocket(accountId, sock, authDir) {
5285
5285
  authDir,
5286
5286
  sock,
5287
5287
  connected: true,
5288
- selfPhone: selfId.e164,
5289
- selfJid: selfId.jid,
5290
- selfLid: selfId.lid,
5288
+ selfPhone: null,
5289
+ selfJid: null,
5290
+ selfLid: null,
5291
5291
  reconnectAttempts: 0,
5292
5292
  lastConnectedAt: Date.now(),
5293
5293
  abortController: new AbortController(),
5294
5294
  debouncer: null,
5295
- lidMapping
5295
+ lidMapping: null
5296
5296
  };
5297
+ applySelfIdentity(conn, sock, "register");
5297
5298
  connections.set(accountId, conn);
5299
+ monitorInbound(conn);
5298
5300
  try {
5299
5301
  await sock.sendPresenceUpdate("available");
5300
5302
  console.error(`${TAG13} presence set to available account=${accountId}`);
@@ -5308,10 +5310,9 @@ async function registerLoginSocket(accountId, sock, authDir) {
5308
5310
  attempt: 0,
5309
5311
  signal: conn.abortController.signal
5310
5312
  });
5311
- monitorInbound(conn);
5312
5313
  watchForDisconnect(conn);
5313
5314
  attachSelfIdRefreshOnCredsUpdate(conn);
5314
- console.error(`${TAG13} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
5315
+ console.error(`${TAG13} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
5315
5316
  }
5316
5317
  function attachSelfIdRefreshOnCredsUpdate(conn) {
5317
5318
  if (!conn.sock) return;
@@ -5391,30 +5392,27 @@ async function connectWithReconnect(conn) {
5391
5392
  let connectedAt;
5392
5393
  try {
5393
5394
  console.error(`${TAG13} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
5395
+ if (conn.debouncer) {
5396
+ await conn.debouncer.destroy();
5397
+ conn.debouncer = null;
5398
+ }
5394
5399
  const sock = await createWaSocket({
5395
5400
  authDir: conn.authDir,
5396
- silent: true
5401
+ silent: true,
5402
+ account: conn.accountId
5397
5403
  });
5398
5404
  conn.sock = sock;
5405
+ applySelfIdentity(conn, sock, "pre-listener");
5406
+ monitorInbound(conn);
5399
5407
  console.error(`${TAG13} socket created account=${conn.accountId} \u2014 waiting for connection`);
5400
5408
  await waitForConnection(sock);
5401
- const selfId = readSelfId(conn.authDir);
5402
5409
  connectedAt = Date.now();
5403
5410
  conn.connected = true;
5404
- conn.selfPhone = selfId.e164;
5405
- conn.selfJid = selfId.jid;
5406
- conn.selfLid = selfId.lid;
5407
5411
  conn.lastConnectedAt = connectedAt;
5408
5412
  conn.lastError = void 0;
5409
5413
  conn.terminalReason = void 0;
5410
- const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
5411
- conn.lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
5412
- if (selfId.lid) {
5413
- console.error(`[whatsapp:lid] resolved self-lid accountId=${conn.accountId} selfLid=${selfId.lid} source=creds`);
5414
- } else {
5415
- console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${conn.accountId} reason=creds-missing-lid`);
5416
- }
5417
- console.error(`${TAG13} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
5414
+ applySelfIdentity(conn, sock, "post-connect");
5415
+ console.error(`${TAG13} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
5418
5416
  try {
5419
5417
  await sock.sendPresenceUpdate("available");
5420
5418
  console.error(`${TAG13} presence set to available account=${conn.accountId}`);
@@ -5428,11 +5426,6 @@ async function connectWithReconnect(conn) {
5428
5426
  attempt: conn.reconnectAttempts,
5429
5427
  signal: conn.abortController.signal
5430
5428
  });
5431
- if (conn.debouncer) {
5432
- await conn.debouncer.destroy();
5433
- conn.debouncer = null;
5434
- }
5435
- monitorInbound(conn);
5436
5429
  attachSelfIdRefreshOnCredsUpdate(conn);
5437
5430
  await waitForDisconnectEvent(conn);
5438
5431
  uptimeMs = Date.now() - connectedAt;
@@ -5484,11 +5477,11 @@ async function connectWithReconnect(conn) {
5484
5477
  console.error(
5485
5478
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5486
5479
  );
5487
- await new Promise((resolve36) => {
5488
- const timer2 = setTimeout(resolve36, delay);
5480
+ await new Promise((resolve37) => {
5481
+ const timer2 = setTimeout(resolve37, delay);
5489
5482
  conn.abortController.signal.addEventListener("abort", () => {
5490
5483
  clearTimeout(timer2);
5491
- resolve36();
5484
+ resolve37();
5492
5485
  }, { once: true });
5493
5486
  });
5494
5487
  }
@@ -5496,16 +5489,16 @@ async function connectWithReconnect(conn) {
5496
5489
  }
5497
5490
  }
5498
5491
  function waitForDisconnectEvent(conn) {
5499
- return new Promise((resolve36) => {
5492
+ return new Promise((resolve37) => {
5500
5493
  if (!conn.sock) {
5501
- resolve36();
5494
+ resolve37();
5502
5495
  return;
5503
5496
  }
5504
5497
  const sock = conn.sock;
5505
5498
  const handler = (update) => {
5506
5499
  if (update.connection === "close") {
5507
5500
  sock.ev.off("connection.update", handler);
5508
- resolve36();
5501
+ resolve37();
5509
5502
  }
5510
5503
  };
5511
5504
  sock.ev.on("connection.update", handler);
@@ -5525,6 +5518,23 @@ function watchForDisconnect(conn) {
5525
5518
  }
5526
5519
  });
5527
5520
  }
5521
+ function applySelfIdentity(conn, sock, phase) {
5522
+ const selfId = readSelfId(conn.authDir);
5523
+ conn.selfPhone = selfId.e164;
5524
+ conn.selfJid = selfId.jid;
5525
+ conn.selfLid = selfId.lid;
5526
+ const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
5527
+ conn.lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
5528
+ if (selfId.lid) {
5529
+ console.error(
5530
+ `[whatsapp:lid] resolved self-lid accountId=${conn.accountId} selfLid=${selfId.lid} source=creds phase=${phase}`
5531
+ );
5532
+ } else {
5533
+ console.error(
5534
+ `[whatsapp:lid] FAIL self-lid-resolution accountId=${conn.accountId} reason=creds-missing-lid phase=${phase}`
5535
+ );
5536
+ }
5537
+ }
5528
5538
  function monitorInbound(conn) {
5529
5539
  if (!conn.sock || !onInboundMessage) return;
5530
5540
  const sock = conn.sock;
@@ -5873,8 +5883,8 @@ async function handleInboundMessage(conn, msg) {
5873
5883
  const conversationKey = isGroup ? remoteJid : senderPhone;
5874
5884
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
5875
5885
  let resolvePending;
5876
- const sttPending = new Promise((resolve36) => {
5877
- resolvePending = resolve36;
5886
+ const sttPending = new Promise((resolve37) => {
5887
+ resolvePending = resolve37;
5878
5888
  });
5879
5889
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
5880
5890
  try {
@@ -6302,20 +6312,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
6302
6312
 
6303
6313
  // server/routes/health.ts
6304
6314
  function checkPort(port2, timeoutMs = 500) {
6305
- return new Promise((resolve36) => {
6315
+ return new Promise((resolve37) => {
6306
6316
  const socket = createConnection2(port2, "127.0.0.1");
6307
6317
  socket.setTimeout(timeoutMs);
6308
6318
  socket.once("connect", () => {
6309
6319
  socket.destroy();
6310
- resolve36(true);
6320
+ resolve37(true);
6311
6321
  });
6312
6322
  socket.once("error", () => {
6313
6323
  socket.destroy();
6314
- resolve36(false);
6324
+ resolve37(false);
6315
6325
  });
6316
6326
  socket.once("timeout", () => {
6317
6327
  socket.destroy();
6318
- resolve36(false);
6328
+ resolve37(false);
6319
6329
  });
6320
6330
  });
6321
6331
  }
@@ -6865,7 +6875,6 @@ function parseScheduleProvenance(v) {
6865
6875
 
6866
6876
  // server/routes/channel/schedule-inject.ts
6867
6877
  var TAG16 = "[schedule-inject]";
6868
- var UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
6869
6878
  function isLoopbackAddr(addr) {
6870
6879
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
6871
6880
  }
@@ -6898,7 +6907,6 @@ function createScheduleInjectRoutes(deps) {
6898
6907
  const prompt = typeof body.prompt === "string" ? body.prompt : "";
6899
6908
  const eventId = typeof body.eventId === "string" ? body.eventId : "";
6900
6909
  const scheduleProvenance = parseScheduleProvenance(body.scheduleProvenance);
6901
- const overrideSessionId = typeof body.sessionId === "string" && UUID_V4.test(body.sessionId) ? body.sessionId : void 0;
6902
6910
  if (channel !== "whatsapp" && channel !== "telegram" || !destination || !prompt) {
6903
6911
  console.error(`${TAG16} reject reason=bad-request channel=${String(channel)} hasDestination=${Boolean(destination)} hasPrompt=${Boolean(prompt)}`);
6904
6912
  return c.json({ ok: false, error: "schedule-inject-bad-request" }, 400);
@@ -6917,7 +6925,7 @@ function createScheduleInjectRoutes(deps) {
6917
6925
  return c.json({ ok: false, error: "account-manager-unresolved" }, 403);
6918
6926
  }
6919
6927
  console.error(`${TAG16} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount2}`);
6920
- const sessionId2 = overrideSessionId ?? adminSessionIdFor(effectiveAccount2, destination);
6928
+ const sessionId2 = adminSessionIdFor(effectiveAccount2, normalizeTarget(destination) ?? destination);
6921
6929
  const reply2 = (text) => deps.sendWhatsAppText(houseAccountId, destination, text);
6922
6930
  try {
6923
6931
  await deps.waHandleInbound({
@@ -6935,7 +6943,6 @@ function createScheduleInjectRoutes(deps) {
6935
6943
  media: [],
6936
6944
  source: "schedule",
6937
6945
  scheduleProvenance,
6938
- scheduledSessionId: overrideSessionId,
6939
6946
  reply: reply2
6940
6947
  });
6941
6948
  } catch (err) {
@@ -6953,7 +6960,7 @@ function createScheduleInjectRoutes(deps) {
6953
6960
  return c.json({ ok: false, error: "no-admin-bot-token" }, 400);
6954
6961
  }
6955
6962
  const chatId = Number(destination);
6956
- const sessionId = overrideSessionId ?? adminSessionIdFor(effectiveAccount, destination);
6963
+ const sessionId = adminSessionIdFor(effectiveAccount, destination);
6957
6964
  const reply = (text) => deps.sendTelegramText(botToken, chatId, text);
6958
6965
  try {
6959
6966
  await deps.tgHandleInbound({
@@ -6965,7 +6972,6 @@ function createScheduleInjectRoutes(deps) {
6965
6972
  text: prompt,
6966
6973
  source: "schedule",
6967
6974
  scheduleProvenance,
6968
- scheduledSessionId: overrideSessionId,
6969
6975
  reply
6970
6976
  });
6971
6977
  } catch (err) {
@@ -7593,8 +7599,8 @@ async function startLogin(opts) {
7593
7599
  await clearAuth(authDir);
7594
7600
  let resolveCode = null;
7595
7601
  let rejectCode = null;
7596
- const codePromise = new Promise((resolve36, reject) => {
7597
- resolveCode = resolve36;
7602
+ const codePromise = new Promise((resolve37, reject) => {
7603
+ resolveCode = resolve37;
7598
7604
  rejectCode = reject;
7599
7605
  });
7600
7606
  const codeTimer = setTimeout(
@@ -7766,14 +7772,14 @@ function routeDocumentOutboundAt(to, filePath) {
7766
7772
  return lastRouteDocumentOutboundAt.get(routeKey(to, filePath));
7767
7773
  }
7768
7774
  async function sendWhatsAppDocument(input) {
7769
- const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot4 } = input;
7775
+ const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot5 } = input;
7770
7776
  if (!to || !filePath) {
7771
7777
  return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
7772
7778
  }
7773
- if (!maxyAccountId || !platformRoot4) {
7779
+ if (!maxyAccountId || !platformRoot5) {
7774
7780
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
7775
7781
  }
7776
- const accountDir = resolve9(platformRoot4, "..", "data/accounts", maxyAccountId);
7782
+ const accountDir = resolve9(platformRoot5, "..", "data/accounts", maxyAccountId);
7777
7783
  let resolvedPath;
7778
7784
  try {
7779
7785
  resolvedPath = realpathSync3(filePath);
@@ -8734,7 +8740,8 @@ var bodyLimit = (options) => {
8734
8740
 
8735
8741
  // server/routes/storage-broker.ts
8736
8742
  import { join as join11, resolve as resolve11 } from "path";
8737
- import { readdirSync as readdirSync6, readFileSync as readFileSync13, existsSync as existsSync8 } from "fs";
8743
+ import { readdirSync as readdirSync6, readFileSync as readFileSync13, existsSync as existsSync8, realpathSync as realpathSync4 } from "fs";
8744
+ import { randomUUID as randomUUID7 } from "crypto";
8738
8745
  var app3 = new Hono();
8739
8746
  function platformRoot() {
8740
8747
  return process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? join11(__dirname, "..", "..");
@@ -8793,6 +8800,125 @@ var d1QueryBodyLimit = bodyLimit({
8793
8800
  );
8794
8801
  }
8795
8802
  });
8803
+ var pagesDeployBodyLimit = bodyLimit({
8804
+ maxSize: STORAGE_RESOURCE_NAME_MAX_BODY_BYTES,
8805
+ onError: (c) => {
8806
+ log("write", "pages", caller(c) ?? "none", "*", null, "deny", "publish-body-too-large");
8807
+ return c.json(
8808
+ {
8809
+ error: `request body exceeds the ${(STORAGE_RESOURCE_NAME_MAX_BODY_BYTES / 1024).toFixed(
8810
+ 1
8811
+ )} 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.`
8812
+ },
8813
+ 413
8814
+ );
8815
+ }
8816
+ });
8817
+ function resolveSiteDir(account, site) {
8818
+ if (!site || site === "." || site === "..") return null;
8819
+ if (site.includes("/") || site.includes("\\") || site.includes("\0")) return null;
8820
+ const accountsDir = resolve11(platformRoot(), "..", "data", "accounts");
8821
+ const base = resolve11(accountsDir, account, "sites");
8822
+ const dir = resolve11(base, site);
8823
+ if (dir !== base && !dir.startsWith(base + "/")) return null;
8824
+ try {
8825
+ const realBase = realpathSync4(base);
8826
+ const realDir = realpathSync4(dir);
8827
+ if (!realDir.startsWith(realBase + "/")) return null;
8828
+ return realDir;
8829
+ } catch {
8830
+ return null;
8831
+ }
8832
+ }
8833
+ async function verifyDeployed(url) {
8834
+ try {
8835
+ const res = await fetch(`${url}?cb=${Date.now()}`, { method: "GET", redirect: "follow" });
8836
+ return res.status;
8837
+ } catch {
8838
+ return null;
8839
+ }
8840
+ }
8841
+ app3.post("/pages/deploy", pagesDeployBodyLimit, async (c) => {
8842
+ const pub = randomUUID7().slice(0, 8);
8843
+ const account = caller(c);
8844
+ if (!account) {
8845
+ log("write", "pages", "none", "*", null, "deny", "no-caller");
8846
+ return c.json({ error: "no-caller" }, 403);
8847
+ }
8848
+ const parsed = await readJsonBody(c);
8849
+ if (!parsed.ok) return parsed.res;
8850
+ const { project, site, branch } = parsed.body;
8851
+ if (!project || typeof project !== "string") return c.json({ error: "project-required" }, 400);
8852
+ if (!site || typeof site !== "string") return c.json({ error: "site-required" }, 400);
8853
+ if (branch !== void 0 && typeof branch !== "string") return c.json({ error: "branch-invalid" }, 400);
8854
+ console.error(
8855
+ `[pages-broker] op=request pub=${pub} caller=${account} project=${project} site=${site} branch=${branch ?? "default"}`
8856
+ );
8857
+ const dir = resolveSiteDir(account, site);
8858
+ if (dir === null) {
8859
+ console.error(`[pages-broker] op=path pub=${pub} site=${site} inside=false`);
8860
+ log("write", "pages", account, String(site), null, "deny", "invalid-site");
8861
+ return c.json({ error: "invalid-site" }, 400);
8862
+ }
8863
+ console.error(`[pages-broker] op=path pub=${pub} resolved=${dir} inside=true`);
8864
+ const session = getSession();
8865
+ try {
8866
+ const owner = await resolveOwner(session, "pages", project);
8867
+ const decision = authorizeAccess({ caller: account, owner, action: "write" });
8868
+ console.error(
8869
+ `[pages-broker] op=authorize pub=${pub} caller=${account} owner=${owner ?? "none"} decision=${decision.allow ? "allow" : "deny"} reason=${decision.reason}`
8870
+ );
8871
+ log("write", "pages", account, project, owner, decision.allow ? "allow" : "deny", decision.allow ? void 0 : decision.reason);
8872
+ if (!decision.allow) return c.json({ error: decision.reason }, 403);
8873
+ const started = Date.now();
8874
+ const { url } = await (await makeHousePagesExec(platformRoot())).pagesDeploy(dir, project, branch);
8875
+ console.error(`[pages-broker] op=deploy pub=${pub} ranMs=${Date.now() - started} url=${url}`);
8876
+ const httpStatus = await verifyDeployed(url);
8877
+ const verified = httpStatus === 200;
8878
+ console.error(
8879
+ `[pages-broker] op=complete pub=${pub} httpStatus=${httpStatus ?? "unreachable"} verified=${verified}`
8880
+ );
8881
+ if (!verified) {
8882
+ return c.json({ error: "deploy-unverified", url, httpStatus }, 502);
8883
+ }
8884
+ return c.json({ url });
8885
+ } catch (err) {
8886
+ console.error(`[pages-broker] op=exit pub=${pub} result=error detail="${String(err)}"`);
8887
+ return c.json({ error: String(err) }, 500);
8888
+ } finally {
8889
+ await session.close();
8890
+ }
8891
+ });
8892
+ app3.post("/pages/adopt", nameBodyLimit("pages"), async (c) => {
8893
+ const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
8894
+ const account = caller(c);
8895
+ if (!houseAdminScope) {
8896
+ log("write", "pages", account ?? "none", "*", null, "deny", "not-house-admin");
8897
+ return c.json({ error: "not-house-admin" }, 403);
8898
+ }
8899
+ const parsed = await readJsonBody(c);
8900
+ if (!parsed.ok) return parsed.res;
8901
+ const { project, account: target } = parsed.body;
8902
+ if (!project || typeof project !== "string") return c.json({ error: "project-required" }, 400);
8903
+ if (!target || typeof target !== "string") return c.json({ error: "account-required" }, 400);
8904
+ const session = getSession();
8905
+ try {
8906
+ const owner = await resolveOwner(session, "pages", project);
8907
+ if (owner !== null && owner !== target) {
8908
+ log("write", "pages", target, project, owner, "deny", "already-owned");
8909
+ return c.json({ error: "already-owned", owner }, 409);
8910
+ }
8911
+ if (owner === null) {
8912
+ await registerResource(session, { accountId: target, kind: "pages", name: project, cfResourceId: project });
8913
+ }
8914
+ log("write", "pages", target, project, target, "allow");
8915
+ return c.json({ project, account: target });
8916
+ } catch (err) {
8917
+ return c.json({ error: String(err) }, 500);
8918
+ } finally {
8919
+ await session.close();
8920
+ }
8921
+ });
8796
8922
  app3.get("/d1/list", async (c) => {
8797
8923
  const account = caller(c);
8798
8924
  if (!account) return c.json({ error: "no-caller" }, 403);
@@ -9062,7 +9188,7 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
9062
9188
  });
9063
9189
  var storage_broker_default = app3;
9064
9190
  async function runStorageAudit(root) {
9065
- const { reconcileStorage } = await import("./src-4F37OHLK.js");
9191
+ const { reconcileStorage } = await import("./src-YLGAZY36.js");
9066
9192
  const session = getSession();
9067
9193
  try {
9068
9194
  const cf = await makeHouseCfExec(root);
@@ -9094,9 +9220,31 @@ async function runStorageAudit(root) {
9094
9220
  await session.close();
9095
9221
  }
9096
9222
  }
9223
+ async function runPagesAudit(root) {
9224
+ const { reconcilePages } = await import("./src-YLGAZY36.js");
9225
+ const session = getSession();
9226
+ try {
9227
+ const cf = await makeHousePagesExec(root);
9228
+ const r = await reconcilePages({
9229
+ cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
9230
+ registeredProjects: async () => {
9231
+ const res = await session.run(
9232
+ "MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
9233
+ { kind: "pages" }
9234
+ );
9235
+ return res.records.map((rec) => rec.get("name"));
9236
+ }
9237
+ });
9238
+ console.error(
9239
+ `[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("")
9240
+ );
9241
+ } finally {
9242
+ await session.close();
9243
+ }
9244
+ }
9097
9245
 
9098
9246
  // 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";
9247
+ import { readFileSync as readFileSync17, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync11, readdirSync as readdirSync11, realpathSync as realpathSync5 } from "fs";
9100
9248
  import { basename as basename4, dirname as dirname5, isAbsolute, join as join16, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
9101
9249
 
9102
9250
  // server/routes/admin/sidebar-sessions.ts
@@ -9728,7 +9876,31 @@ function joinTextBlocks(content) {
9728
9876
  }
9729
9877
  return parts.join("");
9730
9878
  }
9731
- function assistantTurns(content, ts, promoteFinal = false) {
9879
+ function erroredToolUseIds(lines) {
9880
+ const ids = /* @__PURE__ */ new Set();
9881
+ for (const line of lines) {
9882
+ if (!line || line[0] !== "{" || !line.includes("is_error")) continue;
9883
+ let row;
9884
+ try {
9885
+ const parsed = JSON.parse(line);
9886
+ if (!parsed || typeof parsed !== "object") continue;
9887
+ row = parsed;
9888
+ } catch {
9889
+ continue;
9890
+ }
9891
+ const content = row.message?.content;
9892
+ if (!Array.isArray(content)) continue;
9893
+ for (const block of content) {
9894
+ if (!block || typeof block !== "object") continue;
9895
+ const b = block;
9896
+ if (b.type === "tool_result" && b.is_error === true && typeof b.tool_use_id === "string") {
9897
+ ids.add(b.tool_use_id);
9898
+ }
9899
+ }
9900
+ }
9901
+ return ids;
9902
+ }
9903
+ function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set()) {
9732
9904
  if (!Array.isArray(content)) return [];
9733
9905
  const out = [];
9734
9906
  for (const block of content) {
@@ -9748,7 +9920,12 @@ function assistantTurns(content, ts, promoteFinal = false) {
9748
9920
  const input = b.input ?? {};
9749
9921
  const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
9750
9922
  const caption = typeof input.caption === "string" ? input.caption : null;
9751
- out.push({ kind: "agent-file", files, caption, ts });
9923
+ const refused = typeof b.id === "string" && erroredIds.has(b.id);
9924
+ if (files.length === 0 || refused) {
9925
+ out.push({ kind: "tool-call", name: SEND_USER_FILE_TOOL, input: b.input ?? null, ts });
9926
+ } else {
9927
+ out.push({ kind: "agent-file", files, caption, ts });
9928
+ }
9752
9929
  } else if (b.type === "tool_use" && typeof b.name === "string") {
9753
9930
  out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
9754
9931
  }
@@ -9788,6 +9965,7 @@ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress) {
9788
9965
  }
9789
9966
  function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}) {
9790
9967
  const out = [];
9968
+ const erroredIds = erroredToolUseIds(lines);
9791
9969
  for (const line of lines) {
9792
9970
  if (!line || line[0] !== "{") continue;
9793
9971
  let row;
@@ -9812,7 +9990,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
9812
9990
  if (hasWebchatReply(msg?.content)) turnCtx.repliedThisTurn = true;
9813
9991
  const isEnd = msg?.stop_reason === "end_turn";
9814
9992
  const promoteFinal = isEnd && turnCtx.lastChannelSource === "webchat" && !turnCtx.repliedThisTurn;
9815
- out.push(...assistantTurns(msg?.content, ts, promoteFinal));
9993
+ out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds));
9816
9994
  continue;
9817
9995
  }
9818
9996
  if (row.type === "queue-operation" && row.operation === "enqueue") {
@@ -10209,7 +10387,7 @@ function enrichDownloads(turn, scratchpadRoot) {
10209
10387
  if (turn.kind !== "agent-file") return turn;
10210
10388
  let scratchpadReal = null;
10211
10389
  try {
10212
- scratchpadReal = realpathSync4(scratchpadRoot);
10390
+ scratchpadReal = realpathSync5(scratchpadRoot);
10213
10391
  } catch {
10214
10392
  scratchpadReal = null;
10215
10393
  }
@@ -10220,7 +10398,7 @@ function enrichDownloads(turn, scratchpadRoot) {
10220
10398
  }
10221
10399
  if (scratchpadReal) {
10222
10400
  try {
10223
- const fileReal = realpathSync4(f);
10401
+ const fileReal = realpathSync5(f);
10224
10402
  if (fileReal === scratchpadReal || fileReal.startsWith(scratchpadReal + sep3)) {
10225
10403
  return { root: "scratchpad", path: relative2(scratchpadReal, fileReal) };
10226
10404
  }
@@ -12293,8 +12471,8 @@ function routeTelegramUpdate(input) {
12293
12471
  // server/routes/telegram.ts
12294
12472
  var TAG23 = "[telegram-inbound]";
12295
12473
  function configDirName() {
12296
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12297
- const brandPath = join22(platformRoot4, "config", "brand.json");
12474
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12475
+ const brandPath = join22(platformRoot5, "config", "brand.json");
12298
12476
  if (existsSync15(brandPath)) {
12299
12477
  try {
12300
12478
  return JSON.parse(readFileSync23(brandPath, "utf-8")).configDir ?? ".maxy";
@@ -12493,7 +12671,7 @@ var quickbooks_default = app9;
12493
12671
  // server/routes/onboarding.ts
12494
12672
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync3 } from "child_process";
12495
12673
  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";
12674
+ import { createHash as createHash4, randomUUID as randomUUID8 } from "crypto";
12497
12675
 
12498
12676
  // app/lib/claude-spawn-env.ts
12499
12677
  function claudeBin() {
@@ -12989,7 +13167,7 @@ app10.post("/set-pin", async (c) => {
12989
13167
  const hash = hashPin2(body.pin);
12990
13168
  const account = resolveAccount();
12991
13169
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
12992
- const userId = existingOwnerUserId ?? randomUUID7();
13170
+ const userId = existingOwnerUserId ?? randomUUID8();
12993
13171
  if (existingOwnerUserId) {
12994
13172
  console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
12995
13173
  } else {
@@ -13381,7 +13559,7 @@ app11.post("/", async (c) => {
13381
13559
  var client_error_default = app11;
13382
13560
 
13383
13561
  // server/routes/admin/session.ts
13384
- import { randomUUID as randomUUID8 } from "crypto";
13562
+ import { randomUUID as randomUUID9 } from "crypto";
13385
13563
  async function resolveUserIdentity(accountId, userId) {
13386
13564
  const result = await loadAdminUserName(userId);
13387
13565
  if (result.source === "neo4j") {
@@ -13395,7 +13573,7 @@ async function resolveUserIdentity(accountId, userId) {
13395
13573
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
13396
13574
  const account = resolveAccount();
13397
13575
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
13398
- const signedSessionToken = randomUUID8();
13576
+ const signedSessionToken = randomUUID9();
13399
13577
  const cacheKey = fingerprintSessionKey(signedSessionToken);
13400
13578
  registerSession(cacheKey, "admin", accountId, void 0, userId, userName ?? void 0, role);
13401
13579
  if (userId) setWantsPriorConversation(cacheKey);
@@ -13437,15 +13615,15 @@ function operatorRoleFor(config, userId) {
13437
13615
  async function buildAccountOptionList(accounts, userId) {
13438
13616
  const houseAccount = accounts.find((a) => a.config.role === "house");
13439
13617
  console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
13440
- const resolve36 = async (a) => {
13618
+ const resolve37 = async (a) => {
13441
13619
  const businessName = await fetchAccountName(a.accountId) || void 0;
13442
13620
  return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
13443
13621
  };
13444
13622
  const houseOptions = await Promise.all(
13445
- accounts.filter((a) => a.config.role === "house").map(resolve36)
13623
+ accounts.filter((a) => a.config.role === "house").map(resolve37)
13446
13624
  );
13447
13625
  const nonHouseOptions = await Promise.all(
13448
- accounts.filter((a) => a.config.role !== "house").map(resolve36)
13626
+ accounts.filter((a) => a.config.role !== "house").map(resolve37)
13449
13627
  );
13450
13628
  const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
13451
13629
  nonHouseOptions.sort((x, y) => {
@@ -14929,10 +15107,10 @@ function readTunnelState(brandConfigDir) {
14929
15107
  }
14930
15108
  }
14931
15109
  function resolveTunnelUrl() {
14932
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
15110
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
14933
15111
  let brand;
14934
15112
  try {
14935
- brand = JSON.parse(readFileSync30(resolve21(platformRoot4, "config", "brand.json"), "utf-8"));
15113
+ brand = JSON.parse(readFileSync30(resolve21(platformRoot5, "config", "brand.json"), "utf-8"));
14936
15114
  } catch {
14937
15115
  return null;
14938
15116
  }
@@ -15141,18 +15319,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
15141
15319
  ]);
15142
15320
  var app21 = new Hono();
15143
15321
  app21.post("/", async (c) => {
15144
- const TAG45 = "[admin:events]";
15322
+ const TAG46 = "[admin:events]";
15145
15323
  let body;
15146
15324
  try {
15147
15325
  body = await c.req.json();
15148
15326
  } catch (err) {
15149
15327
  const detail = err instanceof Error ? err.message : String(err);
15150
- console.error(`${TAG45} reject reason=body-not-json detail=${detail}`);
15328
+ console.error(`${TAG46} reject reason=body-not-json detail=${detail}`);
15151
15329
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
15152
15330
  }
15153
15331
  const event = typeof body.event === "string" ? body.event : "";
15154
15332
  if (!ALLOWED_EVENTS.has(event)) {
15155
- console.error(`${TAG45} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15333
+ console.error(`${TAG46} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15156
15334
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
15157
15335
  }
15158
15336
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -15176,8 +15354,8 @@ var events_default = app21;
15176
15354
  // server/routes/admin/files.ts
15177
15355
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync27 } from "fs";
15178
15356
  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";
15357
+ import { realpathSync as realpathSync6 } from "fs";
15358
+ import { randomUUID as randomUUID10 } from "crypto";
15181
15359
  import { basename as basename9, dirname as dirname11, join as join30, relative as relative4, resolve as resolve23, sep as sep6 } from "path";
15182
15360
  import { Readable as Readable2 } from "stream";
15183
15361
 
@@ -16542,8 +16720,8 @@ async function resolveUploadTarget(c, accountId, uid) {
16542
16720
  const info = await stat4(destDir).catch(() => null);
16543
16721
  if (!info || !info.isDirectory()) return { ok: false, status: 400, error: "Upload destination is not a directory" };
16544
16722
  }
16545
- const dataRootReal = realpathSync5(DATA_ROOT);
16546
- const destDirReal = realpathSync5(destDir);
16723
+ const dataRootReal = realpathSync6(DATA_ROOT);
16724
+ const destDirReal = realpathSync6(destDir);
16547
16725
  if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep6)) {
16548
16726
  console.error(`[data-upload] op=path-traversal-blocked uid=${uid} requested="${destRel}" resolved="${destDirReal}"`);
16549
16727
  return { ok: false, status: 403, error: "Upload destination escapes DATA_ROOT" };
@@ -16569,7 +16747,7 @@ app22.post("/upload", requireAdminSession, async (c) => {
16569
16747
  if (c.req.query("uploadId") !== void 0) {
16570
16748
  return handleChunkUpload(c, accountId);
16571
16749
  }
16572
- const uid = `${Date.now()}-${randomUUID9().slice(0, 8)}`;
16750
+ const uid = `${Date.now()}-${randomUUID10().slice(0, 8)}`;
16573
16751
  const declaredBytes = c.req.header("content-length") ?? "unknown";
16574
16752
  const target = await resolveUploadTarget(c, accountId, uid);
16575
16753
  if (!target.ok) return c.json({ error: target.error }, target.status);
@@ -19644,9 +19822,9 @@ app34.get("/", requireAdminSession, async (c) => {
19644
19822
  var session_usage_default = app34;
19645
19823
 
19646
19824
  // server/routes/admin/session-rc-spawn.ts
19647
- import { randomUUID as randomUUID10 } from "crypto";
19825
+ import { randomUUID as randomUUID11 } from "crypto";
19648
19826
  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) {
19827
+ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, adminUserId, authenticatedName, callerAccountId) {
19650
19828
  const operator = isOperatorHost(host, operatorDomains);
19651
19829
  const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
19652
19830
  const name = typeof body.name === "string" && body.name.length > 0 ? body.name : void 0;
@@ -19701,8 +19879,8 @@ app35.post("/", requireAdminSession, async (c) => {
19701
19879
  const adminUserId = cacheKey ? getUserIdForSession(cacheKey) : void 0;
19702
19880
  const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
19703
19881
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
19704
- const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID10, adminUserId, authenticatedName, accountId);
19705
- const spawnReqId = randomUUID10();
19882
+ const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID11, adminUserId, authenticatedName, accountId);
19883
+ const spawnReqId = randomUUID11();
19706
19884
  console.log(
19707
19885
  `[chat-spawn] op=request spawnReqId=${spawnReqId} origin=${plan.origin} kind=${plan.kind} sessionId=${plan.kind === "new" ? "new" : plan.sessionId}`
19708
19886
  );
@@ -19774,8 +19952,8 @@ app35.post("/", requireAdminSession, async (c) => {
19774
19952
  var session_rc_spawn_default = app35;
19775
19953
 
19776
19954
  // server/routes/admin/session-reseat.ts
19777
- import { randomUUID as randomUUID11 } from "crypto";
19778
- function resolveReseatPlan(body, mintId = randomUUID11, adminUserId, authenticatedName) {
19955
+ import { randomUUID as randomUUID12 } from "crypto";
19956
+ function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName) {
19779
19957
  const sessionId = mintId();
19780
19958
  const key = `session:${sessionId}`;
19781
19959
  const payload = {
@@ -19829,11 +20007,11 @@ app36.post("/", requireAdminSession, async (c) => {
19829
20007
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
19830
20008
  const plan = resolveReseatPlan(
19831
20009
  { fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
19832
- randomUUID11,
20010
+ randomUUID12,
19833
20011
  adminUserId,
19834
20012
  authenticatedName
19835
20013
  );
19836
- const reseatReqId = randomUUID11();
20014
+ const reseatReqId = randomUUID12();
19837
20015
  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
20016
  let res;
19839
20017
  try {
@@ -20076,7 +20254,7 @@ app38.get("/", async (c) => {
20076
20254
  var health_default2 = app38;
20077
20255
 
20078
20256
  // server/routes/admin/linkedin-ingest.ts
20079
- import { randomUUID as randomUUID12 } from "crypto";
20257
+ import { randomUUID as randomUUID13 } from "crypto";
20080
20258
 
20081
20259
  // app/lib/channel-pty-bridge/manager-client.ts
20082
20260
  function managerBase2() {
@@ -20329,7 +20507,7 @@ app39.post("/", requireAdminSession, async (c) => {
20329
20507
  );
20330
20508
  const initialMessage = buildInitialMessage(envelope);
20331
20509
  const spawnStart = Date.now();
20332
- const sessionId = randomUUID12();
20510
+ const sessionId = randomUUID13();
20333
20511
  console.log(TAG27 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
20334
20512
  const spawned = await managerRcSpawn({
20335
20513
  sessionId,
@@ -21367,7 +21545,7 @@ app47.get("/", requireAdminSession, async (c) => {
21367
21545
  var tasks_list_default = app47;
21368
21546
 
21369
21547
  // server/routes/admin/task-timer-start.ts
21370
- import { randomUUID as randomUUID13 } from "crypto";
21548
+ import { randomUUID as randomUUID14 } from "crypto";
21371
21549
  var TAG34 = "[task-timer]";
21372
21550
  var app48 = new Hono();
21373
21551
  app48.post("/", requireAdminSession, async (c) => {
@@ -21405,7 +21583,7 @@ app48.post("/", requireAdminSession, async (c) => {
21405
21583
  console.log(`${TAG34} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
21406
21584
  return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
21407
21585
  }
21408
- const entryId = randomUUID13();
21586
+ const entryId = randomUUID14();
21409
21587
  await session.run(
21410
21588
  `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
21411
21589
  CREATE (te:TimeEntry {entryId: $entryId, taskId: $taskId, accountId: $accountId, startedAt: $now, endedAt: null})
@@ -21532,7 +21710,7 @@ app50.post("/", requireAdminSession, async (c) => {
21532
21710
  var task_complete_default = app50;
21533
21711
 
21534
21712
  // server/routes/admin/task-time-adjust.ts
21535
- import { randomUUID as randomUUID14 } from "crypto";
21713
+ import { randomUUID as randomUUID15 } from "crypto";
21536
21714
  import neo4j6 from "neo4j-driver";
21537
21715
  var TAG37 = "[task-time-adjust]";
21538
21716
  var app51 = new Hono();
@@ -21557,7 +21735,7 @@ app51.post("/", requireAdminSession, async (c) => {
21557
21735
  console.error(`${TAG37} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
21558
21736
  return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
21559
21737
  }
21560
- const adjustmentId = randomUUID14();
21738
+ const adjustmentId = randomUUID15();
21561
21739
  const now = (/* @__PURE__ */ new Date()).toISOString();
21562
21740
  console.log(`${TAG37} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
21563
21741
  const session = getSession();
@@ -21891,7 +22069,7 @@ app55.route("/request-magic-link", request_magic_link_default);
21891
22069
  var access_default = app55;
21892
22070
 
21893
22071
  // server/routes/sites.ts
21894
- import { existsSync as existsSync31, readFileSync as readFileSync34, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
22072
+ import { existsSync as existsSync31, readFileSync as readFileSync34, realpathSync as realpathSync7, statSync as statSync12 } from "fs";
21895
22073
  import { resolve as resolve28 } from "path";
21896
22074
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
21897
22075
  var MIME = {
@@ -21983,8 +22161,8 @@ app56.get("/:rel{.*}", (c) => {
21983
22161
  let realPath;
21984
22162
  let realRoot;
21985
22163
  try {
21986
- realPath = realpathSync6(filePath);
21987
- realRoot = realpathSync6(rootDir);
22164
+ realPath = realpathSync7(filePath);
22165
+ realRoot = realpathSync7(rootDir);
21988
22166
  } catch {
21989
22167
  console.error(`[sites] not-found path=${reqPath} status=404`);
21990
22168
  return c.text("Not found", 404);
@@ -22114,9 +22292,9 @@ var cachedAttempted = false;
22114
22292
  function readBrandConfig() {
22115
22293
  if (cachedAttempted) return cached2;
22116
22294
  cachedAttempted = true;
22117
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
22118
- if (!platformRoot4) return null;
22119
- const brandPath = join35(platformRoot4, "config", "brand.json");
22295
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
22296
+ if (!platformRoot5) return null;
22297
+ const brandPath = join35(platformRoot5, "config", "brand.json");
22120
22298
  if (!existsSync32(brandPath)) return null;
22121
22299
  try {
22122
22300
  cached2 = JSON.parse(readFileSync36(brandPath, "utf-8"));
@@ -24038,14 +24216,14 @@ var WaGateway = class {
24038
24216
  },
24039
24217
  Date.now()
24040
24218
  );
24219
+ const resolvedSessionId = adminSessionIdFor(
24220
+ input.effectiveAccountId,
24221
+ senderId,
24222
+ input.role === "public" && input.personId ? input.personId : void 0
24223
+ );
24041
24224
  console.error(
24042
- `[whatsapp-native] op=inbound senderId=${senderId} accountId=${input.accountId} bytes=${bytes} ${mediaField} source=${source} delivery=${willDeliverNow ? "immediate" : "queued"}`
24225
+ `[whatsapp-native] op=inbound senderId=${senderId} accountId=${input.accountId} bytes=${bytes} ${mediaField} source=${source} delivery=${willDeliverNow ? "immediate" : "queued"} sessionId=${resolvedSessionId}`
24043
24226
  );
24044
- if (source === "schedule" && (hadSubscriber || this.spawning.has(senderId))) {
24045
- console.error(
24046
- `[whatsapp-native] op=coalesced-onto-live-session eventId=${input.scheduleProvenance?.eventId ?? "-"} senderId=${senderId} droppedSeat=${input.scheduledSessionId ?? "-"} reason=${hadSubscriber ? "live-subscriber" : "spawn-in-flight"}`
24047
- );
24048
- }
24049
24227
  if (!hadSubscriber && !this.spawning.has(senderId)) {
24050
24228
  this.spawning.add(senderId);
24051
24229
  console.error(`[whatsapp-native] op=spawn-trigger senderId=${senderId}`);
@@ -24058,8 +24236,6 @@ var WaGateway = class {
24058
24236
  personId: input.personId ?? null,
24059
24237
  gatewayUrl: this.deps.gatewayUrl,
24060
24238
  serverPath: this.deps.serverPath,
24061
- // Task 1526 — a scheduled firing's per-firing seat id; absent on real inbound.
24062
- sessionIdOverride: input.scheduledSessionId,
24063
24239
  // Task 1575 — the gate's owner-self verdict; drives owner attribution
24064
24240
  // for an unbound owner self-phone at the spawn site.
24065
24241
  ownerSelfPhone: input.ownerSelfPhone
@@ -24113,7 +24289,12 @@ function logDuplicateSenderGroups(groups) {
24113
24289
  function buildWaSpawnRequest(input) {
24114
24290
  const personId = input.role === "public" && input.personId ? input.personId : void 0;
24115
24291
  return {
24116
- sessionId: input.sessionIdOverride && input.sessionIdOverride.length > 0 ? input.sessionIdOverride : adminSessionIdFor(input.accountId, input.senderId, personId),
24292
+ // Task 1746 the deterministic per-sender id is the ONLY id a sender's
24293
+ // session ever has. Task 1526's per-firing seat override used to replace it
24294
+ // on a scheduled cold start, which spawned a second session for the sender;
24295
+ // that session then attached under the sender's own hub key and swallowed
24296
+ // their real messages until it detached, splitting the conversation in two.
24297
+ sessionId: adminSessionIdFor(input.accountId, input.senderId, personId),
24117
24298
  role: input.role,
24118
24299
  channel: "whatsapp",
24119
24300
  personId,
@@ -24453,16 +24634,16 @@ var WebchatGateway = class _WebchatGateway {
24453
24634
  * The public /api/chat route awaits this to return the reply on the same SSE
24454
24635
  * response, preserving the pre-756 POST→single-blob browser contract. */
24455
24636
  awaitReply(key, timeoutMs) {
24456
- return new Promise((resolve36) => {
24637
+ return new Promise((resolve37) => {
24457
24638
  const timer2 = setTimeout(() => {
24458
24639
  this.replyAwaiters.delete(key);
24459
- resolve36({ timeout: true });
24640
+ resolve37({ timeout: true });
24460
24641
  }, timeoutMs);
24461
24642
  if (timer2.unref) timer2.unref();
24462
24643
  this.replyAwaiters.set(key, (r) => {
24463
24644
  clearTimeout(timer2);
24464
24645
  this.replyAwaiters.delete(key);
24465
- resolve36(r);
24646
+ resolve37(r);
24466
24647
  });
24467
24648
  });
24468
24649
  }
@@ -24473,11 +24654,11 @@ var WebchatGateway = class _WebchatGateway {
24473
24654
  * terminal dialog stays answerable). */
24474
24655
  awaitPermissionVerdict(p, timeoutMs) {
24475
24656
  const k = _WebchatGateway.promptKey(p.key, p.requestId);
24476
- return new Promise((resolve36) => {
24657
+ return new Promise((resolve37) => {
24477
24658
  this.pendingPrompts.get(k)?.resolve({ timeout: true });
24478
24659
  const timer2 = setTimeout(() => {
24479
24660
  this.pendingPrompts.delete(k);
24480
- resolve36({ timeout: true });
24661
+ resolve37({ timeout: true });
24481
24662
  }, timeoutMs);
24482
24663
  if (timer2.unref) timer2.unref();
24483
24664
  this.pendingPrompts.set(k, {
@@ -24486,7 +24667,7 @@ var WebchatGateway = class _WebchatGateway {
24486
24667
  resolve: (r) => {
24487
24668
  clearTimeout(timer2);
24488
24669
  this.pendingPrompts.delete(k);
24489
- resolve36(r);
24670
+ resolve37(r);
24490
24671
  }
24491
24672
  });
24492
24673
  console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
@@ -24817,27 +24998,6 @@ var WebchatGateway = class _WebchatGateway {
24817
24998
  }
24818
24999
  };
24819
25000
 
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
25001
  // app/lib/channel-pty-bridge/follower.ts
24842
25002
  function followerPendingMaxMs() {
24843
25003
  return Number(process.env.CHANNEL_PTY_FOLLOWER_PENDING_MAX_MS ?? String(3e5));
@@ -24845,6 +25005,13 @@ function followerPendingMaxMs() {
24845
25005
  function followerRetryMs() {
24846
25006
  return Number(process.env.CHANNEL_PTY_FOLLOWER_RETRY_MS ?? String(1e3));
24847
25007
  }
25008
+ function toolResultText(content) {
25009
+ if (typeof content === "string") return content;
25010
+ if (!Array.isArray(content)) return "";
25011
+ return content.map(
25012
+ (b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : ""
25013
+ ).join("");
25014
+ }
24848
25015
  function startFollower(opts) {
24849
25016
  const abort = new AbortController();
24850
25017
  const { entry, tag } = opts;
@@ -24887,6 +25054,8 @@ function startFollower(opts) {
24887
25054
  const decoder = new TextDecoder("utf8");
24888
25055
  let buffered = "";
24889
25056
  const fileDelivery = opts.fileDelivery ?? null;
25057
+ const captureVerdicts = typeof fileDelivery?.onFileToolResult === "function";
25058
+ const awaitingVerdict = /* @__PURE__ */ new Set();
24890
25059
  let firedFileTools = [];
24891
25060
  let suppressing = opts.suppressResumeReplay === true;
24892
25061
  let discardedTurns = 0;
@@ -24920,6 +25089,28 @@ function startFollower(opts) {
24920
25089
  continue;
24921
25090
  }
24922
25091
  if (event.type === "user") {
25092
+ if (captureVerdicts && awaitingVerdict.size > 0 && !suppressing) {
25093
+ const umsg = event.message;
25094
+ if (umsg && Array.isArray(umsg.content)) {
25095
+ for (const block of umsg.content) {
25096
+ if (block?.type !== "tool_result") continue;
25097
+ const id = block.tool_use_id;
25098
+ if (typeof id !== "string" || !awaitingVerdict.has(id)) continue;
25099
+ awaitingVerdict.delete(id);
25100
+ try {
25101
+ await fileDelivery.onFileToolResult({
25102
+ toolUseId: id,
25103
+ isError: block.is_error === true,
25104
+ text: toolResultText(block.content)
25105
+ });
25106
+ } catch (err) {
25107
+ console.error(
25108
+ `${tag} file-delivery-error sessionId=${sid} phase=tool-result message=${err instanceof Error ? err.message : String(err)}`
25109
+ );
25110
+ }
25111
+ }
25112
+ }
25113
+ }
24923
25114
  entry.pendingTurnText = "";
24924
25115
  firedFileTools = [];
24925
25116
  continue;
@@ -24941,8 +25132,10 @@ function startFollower(opts) {
24941
25132
  continue;
24942
25133
  }
24943
25134
  firedFileTools.push(block.name);
25135
+ const toolUseId = typeof block.id === "string" ? block.id : "";
25136
+ if (captureVerdicts && toolUseId) awaitingVerdict.add(toolUseId);
24944
25137
  try {
24945
- await fileDelivery.onFileToolUse({ toolName: block.name, input: block.input });
25138
+ await fileDelivery.onFileToolUse({ toolName: block.name, toolUseId, input: block.input });
24946
25139
  } catch (err) {
24947
25140
  console.error(
24948
25141
  `${tag} file-delivery-error sessionId=${sid} tool=${block.name} message=${err instanceof Error ? err.message : String(err)}`
@@ -24964,9 +25157,10 @@ function startFollower(opts) {
24964
25157
  if (flush.trim()) {
24965
25158
  await fanOut(entry.subscribers, flush, opts.onError, tag);
24966
25159
  }
24967
- if (fileDelivery && firedFileTools.length > 0) {
25160
+ if (fileDelivery && (firedFileTools.length > 0 || captureVerdicts)) {
24968
25161
  const fired = firedFileTools;
24969
25162
  firedFileTools = [];
25163
+ awaitingVerdict.clear();
24970
25164
  try {
24971
25165
  fileDelivery.onTurnEnd(fired);
24972
25166
  } catch (err) {
@@ -25004,28 +25198,55 @@ async function fanOut(subscribers, text, onError, tag) {
25004
25198
  );
25005
25199
  }
25006
25200
 
25201
+ // app/lib/webchat/file-delivery.ts
25202
+ import { realpathSync as realpathSync8 } from "fs";
25203
+ import { resolve as resolve32 } from "path";
25204
+
25007
25205
  // app/lib/channel-pty-bridge/file-delivery.ts
25008
25206
  var SEND_USER_FILE = "SendUserFile";
25009
25207
  function makeFileDelivery(opts) {
25010
- const { entry, tag, channel, sendFile } = opts;
25208
+ const { entry, tag, channel, sendFile, deferUntilVerdict = false } = opts;
25011
25209
  let failedFiles = [];
25012
25210
  let attempts = 0;
25013
- return {
25211
+ const pending = /* @__PURE__ */ new Map();
25212
+ function parseInput(input) {
25213
+ const i = input ?? {};
25214
+ return {
25215
+ // A non-array `files` yields zero files. This is the observed refused-call
25216
+ // shape: with the tool absent from the enabled set its schema is absent
25217
+ // from the request, so the model emits a JSON-encoded string. Never parse
25218
+ // it back — the harness refused the call.
25219
+ files: Array.isArray(i.files) ? i.files.filter((f) => typeof f === "string") : [],
25220
+ caption: typeof i.caption === "string" ? i.caption : void 0
25221
+ };
25222
+ }
25223
+ async function forward(call) {
25224
+ for (let i = 0; i < call.files.length; i++) {
25225
+ attempts++;
25226
+ const result = await sendFile(call.files[i], i === 0 ? call.caption : void 0);
25227
+ const name = call.files[i].split("/").pop() ?? call.files[i];
25228
+ console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
25229
+ if (!result.ok) failedFiles.push(call.files[i]);
25230
+ }
25231
+ }
25232
+ function unreconciledCall(reason, files, detail) {
25233
+ const d = detail ? ` detail="${detail.replace(/\s+/g, " ").slice(0, 120)}"` : "";
25234
+ console.error(
25235
+ `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${entry.sessionId.slice(0, 8)} tool=${SEND_USER_FILE} reason=${reason} files=${files}${d}`
25236
+ );
25237
+ }
25238
+ const handler = {
25014
25239
  isFileDeliveryTool(toolName) {
25015
25240
  return toolName === SEND_USER_FILE;
25016
25241
  },
25017
25242
  async onFileToolUse(use) {
25018
25243
  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]);
25244
+ const call = parseInput(use.input);
25245
+ if (deferUntilVerdict) {
25246
+ pending.set(use.toolUseId, call);
25247
+ return;
25028
25248
  }
25249
+ await forward(call);
25029
25250
  },
25030
25251
  onTurnEnd(firedTools) {
25031
25252
  const failed = failedFiles;
@@ -25038,6 +25259,11 @@ function makeFileDelivery(opts) {
25038
25259
  `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
25039
25260
  );
25040
25261
  }
25262
+ if (deferUntilVerdict) {
25263
+ for (const call of pending.values()) unreconciledCall("no-verdict", call.files.length);
25264
+ pending.clear();
25265
+ return;
25266
+ }
25041
25267
  if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
25042
25268
  console.error(
25043
25269
  `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
@@ -25045,13 +25271,123 @@ function makeFileDelivery(opts) {
25045
25271
  }
25046
25272
  }
25047
25273
  };
25274
+ if (deferUntilVerdict) {
25275
+ handler.onFileToolResult = async (result) => {
25276
+ const call = pending.get(result.toolUseId);
25277
+ if (!call) return;
25278
+ pending.delete(result.toolUseId);
25279
+ if (result.isError) {
25280
+ unreconciledCall("harness-refused", call.files.length, result.text);
25281
+ return;
25282
+ }
25283
+ if (call.files.length === 0) {
25284
+ unreconciledCall("no-files", 0);
25285
+ return;
25286
+ }
25287
+ await forward(call);
25288
+ };
25289
+ }
25290
+ return handler;
25291
+ }
25292
+
25293
+ // app/lib/webchat/file-delivery.ts
25294
+ var TAG41 = "[webchat-adaptor]";
25295
+ function platformRoot2() {
25296
+ return process.env.MAXY_PLATFORM_ROOT || "";
25297
+ }
25298
+ function makeWebchatSendFile(entry) {
25299
+ return async (filePath) => {
25300
+ if (!entry.accountId) {
25301
+ console.error(`${TAG41} file-delivery reject reason=no-account sender=${entry.senderId}`);
25302
+ return { ok: false, error: "no-account" };
25303
+ }
25304
+ const accountDir = resolve32(platformRoot2(), "..", "data/accounts", entry.accountId);
25305
+ try {
25306
+ const resolved = realpathSync8(filePath);
25307
+ const accountResolved = realpathSync8(accountDir);
25308
+ if (!resolved.startsWith(accountResolved + "/")) {
25309
+ console.error(`${TAG41} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
25310
+ return { ok: false, error: "outside-account" };
25311
+ }
25312
+ return { ok: true };
25313
+ } catch (err) {
25314
+ const code = err.code;
25315
+ console.error(
25316
+ `${TAG41} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
25317
+ );
25318
+ return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
25319
+ }
25320
+ };
25321
+ }
25322
+ function makeWebchatFileDelivery(entry) {
25323
+ return makeFileDelivery({
25324
+ entry,
25325
+ tag: TAG41,
25326
+ channel: "webchat",
25327
+ sendFile: makeWebchatSendFile(entry),
25328
+ deferUntilVerdict: true
25329
+ });
25330
+ }
25331
+
25332
+ // app/lib/webchat/gateway/native-file-follower.ts
25333
+ function startWebchatNativeFileFollower(input) {
25334
+ const entry = {
25335
+ sessionId: input.sessionId,
25336
+ role: "admin",
25337
+ channel: "webchat",
25338
+ accountId: input.accountId,
25339
+ senderId: input.key,
25340
+ agentSlug: "admin",
25341
+ lastInboundAt: Date.now(),
25342
+ writeChain: Promise.resolve(),
25343
+ subscribers: /* @__PURE__ */ new Set(),
25344
+ pendingTurnText: "",
25345
+ followerAbort: null,
25346
+ followerRunning: false,
25347
+ sliceToken: "",
25348
+ personId: null,
25349
+ name: null,
25350
+ // Webchat renders from the transcript; there is no separate reply target.
25351
+ replyTarget: null
25352
+ };
25353
+ return startFollower({
25354
+ entry,
25355
+ tag: "[webchat-adaptor]",
25356
+ fileDelivery: makeWebchatFileDelivery(entry),
25357
+ // A resumed session's JSONL already holds prior SendUserFile tool_uses;
25358
+ // suppress replay so historical calls are not re-reconciled on attach.
25359
+ suppressResumeReplay: true,
25360
+ onClose: input.onClose ?? (() => {
25361
+ })
25362
+ });
25363
+ }
25364
+
25365
+ // app/lib/webchat/gateway/public-spawn-request.ts
25366
+ function buildPublicWebchatSpawnRequest(input) {
25367
+ const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
25368
+ return {
25369
+ senderId: input.key,
25370
+ role: "public",
25371
+ channel: "webchat",
25372
+ accountId: input.accountId,
25373
+ agentSlug,
25374
+ model: SONNET_MODEL,
25375
+ // sliceToken/personId are present only for a gated visitor; open-mode
25376
+ // public agents carry neither (mirrors the bridge's open-mode key).
25377
+ ...sliceToken ? { sliceToken } : {},
25378
+ ...personId ? { personId } : {},
25379
+ ...visitorId ? { visitorId } : {},
25380
+ ...input.previousContext ? { previousContext: input.previousContext } : {},
25381
+ name,
25382
+ webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
25383
+ };
25048
25384
  }
25049
25385
 
25050
25386
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
25051
- var TAG41 = "[whatsapp-adaptor]";
25387
+ var TAG42 = "[whatsapp-adaptor]";
25052
25388
  var SEND_USER_FILE2 = "SendUserFile";
25053
25389
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
25054
- function platformRoot2() {
25390
+ function platformRoot3() {
25055
25391
  return process.env.MAXY_PLATFORM_ROOT || "";
25056
25392
  }
25057
25393
  function makeWhatsAppSendFile(entry, maxyAccountId) {
@@ -25062,11 +25398,11 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25062
25398
  caption,
25063
25399
  accountId: entry.accountId,
25064
25400
  maxyAccountId,
25065
- platformRoot: platformRoot2()
25401
+ platformRoot: platformRoot3()
25066
25402
  });
25067
25403
  if (result.ok) return { ok: true };
25068
25404
  console.error(
25069
- `${TAG41} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25405
+ `${TAG42} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25070
25406
  );
25071
25407
  return { ok: false, error: result.error };
25072
25408
  };
@@ -25074,7 +25410,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25074
25410
  function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25075
25411
  const shared = makeFileDelivery({
25076
25412
  entry,
25077
- tag: TAG41,
25413
+ tag: TAG42,
25078
25414
  channel: "whatsapp",
25079
25415
  sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
25080
25416
  });
@@ -25109,7 +25445,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25109
25445
  if (!delivered) {
25110
25446
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
25111
25447
  console.error(
25112
- `${TAG41} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25448
+ `${TAG42} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25113
25449
  );
25114
25450
  }
25115
25451
  }
@@ -25445,14 +25781,14 @@ var TelegramGateway = class {
25445
25781
  { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules, scheduleProvenance: input.scheduleProvenance },
25446
25782
  Date.now()
25447
25783
  );
25784
+ const resolvedSessionId = adminSessionIdFor(
25785
+ input.accountId,
25786
+ input.senderId,
25787
+ input.role === "public" && input.personId ? input.personId : void 0
25788
+ );
25448
25789
  console.error(
25449
- `[telegram-native] op=inbound key=${input.senderId} accountId=${input.accountId} botType=${input.role} bytes=${bytes} source=${source} delivery=${willDeliverNow ? "immediate" : "queued"}`
25790
+ `[telegram-native] op=inbound key=${input.senderId} accountId=${input.accountId} botType=${input.role} bytes=${bytes} source=${source} delivery=${willDeliverNow ? "immediate" : "queued"} sessionId=${resolvedSessionId}`
25450
25791
  );
25451
- if (source === "schedule" && (hadSubscriber || this.spawning.has(input.senderId))) {
25452
- console.error(
25453
- `[telegram-native] op=coalesced-onto-live-session eventId=${input.scheduleProvenance?.eventId ?? "-"} senderId=${input.senderId} droppedSeat=${input.scheduledSessionId ?? "-"} reason=${hadSubscriber ? "live-subscriber" : "spawn-in-flight"}`
25454
- );
25455
- }
25456
25792
  if (!hadSubscriber && !this.spawning.has(input.senderId)) {
25457
25793
  this.spawning.add(input.senderId);
25458
25794
  console.error(`[telegram-native] op=spawn-trigger key=${input.senderId} role=${input.role}`);
@@ -25464,9 +25800,7 @@ var TelegramGateway = class {
25464
25800
  personId: input.personId ?? null,
25465
25801
  chatId: input.chatId,
25466
25802
  gatewayUrl: this.deps.gatewayUrl,
25467
- serverPath: this.deps.serverPath,
25468
- // Task 1526 — a scheduled firing's per-firing seat id; absent on real inbound.
25469
- sessionIdOverride: input.scheduledSessionId
25803
+ serverPath: this.deps.serverPath
25470
25804
  });
25471
25805
  } finally {
25472
25806
  this.spawning.delete(input.senderId);
@@ -25484,7 +25818,10 @@ var TelegramGateway = class {
25484
25818
  function buildTelegramSpawnRequest(input) {
25485
25819
  const personId = input.role === "public" && input.personId ? input.personId : void 0;
25486
25820
  return {
25487
- sessionId: input.sessionIdOverride && input.sessionIdOverride.length > 0 ? input.sessionIdOverride : adminSessionIdFor(input.accountId, input.senderId, personId),
25821
+ // Task 1746 the deterministic per-sender id is the ONLY id a sender's
25822
+ // session ever has. Mirror of the WhatsApp builder; see its comment for why
25823
+ // Task 1526's per-firing override was removed.
25824
+ sessionId: adminSessionIdFor(input.accountId, input.senderId, personId),
25488
25825
  role: input.role,
25489
25826
  channel: "telegram",
25490
25827
  personId,
@@ -25500,35 +25837,35 @@ function buildTelegramSpawnRequest(input) {
25500
25837
  }
25501
25838
 
25502
25839
  // app/lib/telegram/outbound/send-document.ts
25503
- import { realpathSync as realpathSync7 } from "fs";
25840
+ import { realpathSync as realpathSync9 } from "fs";
25504
25841
  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]";
25842
+ import { resolve as resolve33, basename as basename11 } from "path";
25843
+ var TAG43 = "[telegram:outbound]";
25507
25844
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
25508
25845
  async function sendTelegramDocument(input) {
25509
- const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
25846
+ const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
25510
25847
  if (!botToken || !filePath) {
25511
25848
  return { ok: false, status: 400, error: "Missing required fields: botToken, filePath" };
25512
25849
  }
25513
- if (!maxyAccountId || !platformRoot4) {
25850
+ if (!maxyAccountId || !platformRoot5) {
25514
25851
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
25515
25852
  }
25516
- const accountDir = resolve32(platformRoot4, "..", "data/accounts", maxyAccountId);
25853
+ const accountDir = resolve33(platformRoot5, "..", "data/accounts", maxyAccountId);
25517
25854
  let resolvedPath;
25518
25855
  try {
25519
- resolvedPath = realpathSync7(filePath);
25520
- const accountResolved = realpathSync7(accountDir);
25856
+ resolvedPath = realpathSync9(filePath);
25857
+ const accountResolved = realpathSync9(accountDir);
25521
25858
  if (!resolvedPath.startsWith(accountResolved + "/")) {
25522
- console.error(`${TAG42} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25859
+ console.error(`${TAG43} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25523
25860
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
25524
25861
  }
25525
25862
  } catch (err) {
25526
25863
  const code = err.code;
25527
25864
  if (code === "ENOENT") {
25528
- console.error(`${TAG42} document ENOENT path=${filePath}`);
25865
+ console.error(`${TAG43} document ENOENT path=${filePath}`);
25529
25866
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
25530
25867
  }
25531
- console.error(`${TAG42} document path error: ${String(err)}`);
25868
+ console.error(`${TAG43} document path error: ${String(err)}`);
25532
25869
  return { ok: false, status: 500, error: String(err) };
25533
25870
  }
25534
25871
  const fileStat = await stat7(resolvedPath);
@@ -25561,14 +25898,14 @@ async function sendTelegramDocument(input) {
25561
25898
  error = e instanceof Error ? e.message : String(e);
25562
25899
  }
25563
25900
  console.error(
25564
- `${TAG42} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25901
+ `${TAG43} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25565
25902
  );
25566
25903
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
25567
25904
  }
25568
25905
 
25569
25906
  // app/lib/telegram/outbound/file-delivery.ts
25570
- var TAG43 = "[telegram:outbound]";
25571
- function platformRoot3() {
25907
+ var TAG44 = "[telegram:outbound]";
25908
+ function platformRoot4() {
25572
25909
  return process.env.MAXY_PLATFORM_ROOT || "";
25573
25910
  }
25574
25911
  function makeTelegramSendFile(entry) {
@@ -25579,11 +25916,11 @@ function makeTelegramSendFile(entry) {
25579
25916
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
25580
25917
  }
25581
25918
  if (!botToken) {
25582
- console.error(`${TAG43} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25919
+ console.error(`${TAG44} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25583
25920
  return { ok: false, error: "no-bot-token" };
25584
25921
  }
25585
25922
  if (entry.replyTarget == null) {
25586
- console.error(`${TAG43} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25923
+ console.error(`${TAG44} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25587
25924
  return { ok: false, error: "no-reply-target" };
25588
25925
  }
25589
25926
  const result = await sendTelegramDocument({
@@ -25592,7 +25929,7 @@ function makeTelegramSendFile(entry) {
25592
25929
  filePath,
25593
25930
  caption,
25594
25931
  maxyAccountId: entry.accountId,
25595
- platformRoot: platformRoot3()
25932
+ platformRoot: platformRoot4()
25596
25933
  });
25597
25934
  return result.ok ? { ok: true } : { ok: false, error: result.error };
25598
25935
  };
@@ -25600,7 +25937,7 @@ function makeTelegramSendFile(entry) {
25600
25937
  function makeTelegramFileDelivery(entry) {
25601
25938
  return makeFileDelivery({
25602
25939
  entry,
25603
- tag: TAG43,
25940
+ tag: TAG44,
25604
25941
  channel: "telegram",
25605
25942
  sendFile: makeTelegramSendFile(entry)
25606
25943
  });
@@ -25640,17 +25977,17 @@ function startTelegramNativeFileFollower(input) {
25640
25977
  }
25641
25978
 
25642
25979
  // server/telegram-descriptor.ts
25643
- import { resolve as resolve33, join as join37 } from "path";
25980
+ import { resolve as resolve34, join as join37 } from "path";
25644
25981
  function telegramGatewayUrl() {
25645
25982
  return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
25646
25983
  }
25647
25984
  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");
25985
+ return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/telegram-channel/dist/server.js");
25649
25986
  }
25650
25987
 
25651
25988
  // app/lib/channel-pty-bridge/public-session-end-review.ts
25652
- import { randomUUID as randomUUID15 } from "crypto";
25653
- var TAG44 = "[public-session-review]";
25989
+ import { randomUUID as randomUUID16 } from "crypto";
25990
+ var TAG45 = "[public-session-review]";
25654
25991
  var CONSUMED_CAP = 500;
25655
25992
  var consumed = /* @__PURE__ */ new Set();
25656
25993
  function consumeOnce(sessionId) {
@@ -25677,7 +26014,7 @@ async function fetchJsonl(sessionId) {
25677
26014
  return await res.text();
25678
26015
  } catch (err) {
25679
26016
  console.error(
25680
- `${TAG44} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
26017
+ `${TAG45} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
25681
26018
  );
25682
26019
  return null;
25683
26020
  }
@@ -25701,7 +26038,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
25701
26038
  const res = await fetch(url);
25702
26039
  if (!res.ok) {
25703
26040
  console.error(
25704
- `${TAG44} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
26041
+ `${TAG45} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
25705
26042
  );
25706
26043
  return [];
25707
26044
  }
@@ -25709,7 +26046,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
25709
26046
  return Array.isArray(body.writes) ? body.writes : [];
25710
26047
  } catch (err) {
25711
26048
  console.error(
25712
- `${TAG44} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
26049
+ `${TAG45} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
25713
26050
  );
25714
26051
  return [];
25715
26052
  }
@@ -25737,8 +26074,8 @@ function composeInitialMessage(input) {
25737
26074
  ].join("\n");
25738
26075
  }
25739
26076
  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)}`);
26077
+ const sessionId = randomUUID16();
26078
+ console.log(`${TAG45} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
25742
26079
  const spawned = await managerRcSpawn({
25743
26080
  sessionId,
25744
26081
  initialMessage,
@@ -25758,21 +26095,21 @@ async function firePublicSessionEndReview(input) {
25758
26095
  const dispatchedAt = Date.now();
25759
26096
  if (consumed.has(input.sessionId)) {
25760
26097
  console.log(
25761
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26098
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
25762
26099
  );
25763
26100
  return;
25764
26101
  }
25765
26102
  const jsonl = await fetchJsonl(input.sessionId);
25766
26103
  if (!jsonl) {
25767
26104
  console.log(
25768
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
26105
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
25769
26106
  );
25770
26107
  return;
25771
26108
  }
25772
26109
  const operatorTurns = countOperatorTurns(jsonl);
25773
26110
  if (operatorTurns === 0) {
25774
26111
  console.log(
25775
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
26112
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
25776
26113
  );
25777
26114
  return;
25778
26115
  }
@@ -25786,19 +26123,19 @@ async function firePublicSessionEndReview(input) {
25786
26123
  });
25787
26124
  if (!consumeOnce(input.sessionId)) {
25788
26125
  console.log(
25789
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26126
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
25790
26127
  );
25791
26128
  return;
25792
26129
  }
25793
26130
  const dispatched = await dispatchReviewer(input, initialMessage);
25794
26131
  if (!dispatched.ok) {
25795
26132
  console.error(
25796
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
26133
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
25797
26134
  );
25798
26135
  return;
25799
26136
  }
25800
26137
  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}`
26138
+ `${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
26139
  );
25803
26140
  }
25804
26141
 
@@ -25908,7 +26245,7 @@ function broadcastAdminShutdown(reason) {
25908
26245
  // ../lib/entitlement/src/index.ts
25909
26246
  import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
25910
26247
  import { existsSync as existsSync35, readFileSync as readFileSync37, statSync as statSync13 } from "fs";
25911
- import { resolve as resolve34 } from "path";
26248
+ import { resolve as resolve35 } from "path";
25912
26249
 
25913
26250
  // ../lib/entitlement/src/canonicalize.ts
25914
26251
  function canonicalize(value) {
@@ -25943,7 +26280,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
25943
26280
  var GRACE_DAYS = 7;
25944
26281
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
25945
26282
  function pubkeyPath(brand) {
25946
- return resolve34(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
26283
+ return resolve35(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
25947
26284
  }
25948
26285
  var memo = null;
25949
26286
  function memoKey(mtimeMs, account) {
@@ -25955,7 +26292,7 @@ function resolveEntitlement(brand, account) {
25955
26292
  if (brand.commercialMode !== true) {
25956
26293
  return logResolved(implicitTrust(account), null);
25957
26294
  }
25958
- const entitlementPath = resolve34(brand.configDir, "entitlement.json");
26295
+ const entitlementPath = resolve35(brand.configDir, "entitlement.json");
25959
26296
  if (!existsSync35(entitlementPath)) {
25960
26297
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
25961
26298
  }
@@ -26215,6 +26552,7 @@ function isPublicHost(host) {
26215
26552
  }
26216
26553
  var app62 = new Hono();
26217
26554
  var nativeFileFollowers = /* @__PURE__ */ new Map();
26555
+ var webchatFileFollowers = /* @__PURE__ */ new Map();
26218
26556
  async function fetchAccountStandingRules(accountId) {
26219
26557
  const session = getSession();
26220
26558
  try {
@@ -26252,7 +26590,7 @@ async function recordPassiveDispatch(input) {
26252
26590
  var waGateway = new WaGateway({
26253
26591
  fetchStandingRules: fetchAccountStandingRules,
26254
26592
  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"),
26593
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
26256
26594
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
26257
26595
  // path-validation scope) is the sender's effective SESSION account, resolved
26258
26596
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -26267,11 +26605,11 @@ var waGateway = new WaGateway({
26267
26605
  caption,
26268
26606
  accountId,
26269
26607
  maxyAccountId,
26270
- platformRoot: resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."))
26608
+ platformRoot: resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."))
26271
26609
  });
26272
26610
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
26273
26611
  },
26274
- ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, sessionIdOverride, ownerSelfPhone }) => {
26612
+ ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, ownerSelfPhone }) => {
26275
26613
  if (passive) {
26276
26614
  const phoneHash = createHash7("sha256").update(senderId).digest("hex").slice(0, 12);
26277
26615
  const prompt = (text ?? "").trim();
@@ -26319,7 +26657,7 @@ var waGateway = new WaGateway({
26319
26657
  const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
26320
26658
  console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
26321
26659
  }
26322
- const req = buildWaSpawnRequest({ accountId: effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, sessionIdOverride });
26660
+ const req = buildWaSpawnRequest({ accountId: effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath });
26323
26661
  const userId = role === "admin" ? resolveAdminUserId({ accountId: effectiveAccountId, senderPhone: senderId }) ?? (ownerSelfPhone ? resolveOwnerUserId(effectiveAccountId) ?? void 0 : void 0) : void 0;
26324
26662
  if (role === "admin") {
26325
26663
  const attribution = userId ?? (ownerSelfPhone ? "owner-self-unresolved" : "unbound-empty");
@@ -26425,6 +26763,20 @@ var webchatGateway = new WebchatGateway({
26425
26763
  console.error(`[webchat:inbound] op=spawn-failed key=${key} error=${result.error} status=${result.status}`);
26426
26764
  return;
26427
26765
  }
26766
+ const priorWebchatFollower = webchatFileFollowers.get(key);
26767
+ if (priorWebchatFollower) priorWebchatFollower.abort();
26768
+ let wac;
26769
+ wac = startWebchatNativeFileFollower({
26770
+ sessionId: result.sessionId,
26771
+ key,
26772
+ // The session's own resolved account (house or sub-account) — the
26773
+ // file-path validation scope, exactly as passed to the spawn above.
26774
+ accountId,
26775
+ onClose: () => {
26776
+ if (webchatFileFollowers.get(key) === wac) webchatFileFollowers.delete(key);
26777
+ }
26778
+ });
26779
+ webchatFileFollowers.set(key, wac);
26428
26780
  return { sessionId: result.sessionId };
26429
26781
  },
26430
26782
  // Task-962 — the recovery pass asks the manager to respawn a wedged already-
@@ -26461,8 +26813,8 @@ var telegramGateway = new TelegramGateway({
26461
26813
  fetchStandingRules: fetchAccountStandingRules,
26462
26814
  gatewayUrl: telegramGatewayUrl(),
26463
26815
  serverPath: telegramServerPath(),
26464
- ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath, sessionIdOverride }) => {
26465
- const req = buildTelegramSpawnRequest({ accountId, senderId, role, personId, gatewayUrl, serverPath, sessionIdOverride });
26816
+ ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
26817
+ const req = buildTelegramSpawnRequest({ accountId, senderId, role, personId, gatewayUrl, serverPath });
26466
26818
  const result = await managerRcSpawn(req);
26467
26819
  if ("error" in result) {
26468
26820
  console.error(`[telegram-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
@@ -26886,8 +27238,8 @@ app62.get("/agent-assets/:slug/:filename", (c) => {
26886
27238
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
26887
27239
  return c.text("Not found", 404);
26888
27240
  }
26889
- const filePath = resolve35(account.accountDir, "agents", slug, "assets", filename);
26890
- const expectedDir = resolve35(account.accountDir, "agents", slug, "assets");
27241
+ const filePath = resolve36(account.accountDir, "agents", slug, "assets", filename);
27242
+ const expectedDir = resolve36(account.accountDir, "agents", slug, "assets");
26891
27243
  if (!filePath.startsWith(expectedDir + "/")) {
26892
27244
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
26893
27245
  return c.text("Forbidden", 403);
@@ -26916,8 +27268,8 @@ app62.get("/generated/:filename", (c) => {
26916
27268
  console.error(`[generated] serve file=${filename} status=404`);
26917
27269
  return c.text("Not found", 404);
26918
27270
  }
26919
- const filePath = resolve35(account.accountDir, "generated", filename);
26920
- const expectedDir = resolve35(account.accountDir, "generated");
27271
+ const filePath = resolve36(account.accountDir, "generated", filename);
27272
+ const expectedDir = resolve36(account.accountDir, "generated");
26921
27273
  if (!filePath.startsWith(expectedDir + "/")) {
26922
27274
  console.error(`[generated] serve file=${filename} status=403`);
26923
27275
  return c.text("Forbidden", 403);
@@ -26971,11 +27323,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
26971
27323
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
26972
27324
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
26973
27325
  var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
26974
- (p) => existsSync36(resolve35(process.cwd(), "public", p.replace(/^\//, "")))
27326
+ (p) => existsSync36(resolve36(process.cwd(), "public", p.replace(/^\//, "")))
26975
27327
  );
26976
27328
  var SW_SOURCE = (() => {
26977
27329
  try {
26978
- return readFileSync38(resolve35(process.cwd(), "public", "sw.js"), "utf-8");
27330
+ return readFileSync38(resolve36(process.cwd(), "public", "sw.js"), "utf-8");
26979
27331
  } catch {
26980
27332
  return null;
26981
27333
  }
@@ -27026,7 +27378,7 @@ var clientErrorReporterScript = `<script>
27026
27378
  function cachedHtml(file) {
27027
27379
  let html = htmlCache.get(file);
27028
27380
  if (!html) {
27029
- html = readFileSync38(resolve35(process.cwd(), "public", file), "utf-8");
27381
+ html = readFileSync38(resolve36(process.cwd(), "public", file), "utf-8");
27030
27382
  const productNameEsc = escapeHtml(BRAND.productName);
27031
27383
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
27032
27384
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -27144,7 +27496,7 @@ app62.use("/vnc-popout.html", logViewerFetch);
27144
27496
  app62.get("/vnc-popout.html", (c) => {
27145
27497
  let html = htmlCache.get("vnc-popout.html");
27146
27498
  if (!html) {
27147
- html = readFileSync38(resolve35(process.cwd(), "public", "vnc-popout.html"), "utf-8");
27499
+ html = readFileSync38(resolve36(process.cwd(), "public", "vnc-popout.html"), "utf-8");
27148
27500
  const name = escapeHtml(BRAND.productName);
27149
27501
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
27150
27502
  html = html.replace("</head>", ` ${brandScript}
@@ -27277,7 +27629,7 @@ var httpServer = serve({ fetch: app62.fetch, port, hostname });
27277
27629
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27278
27630
  {
27279
27631
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27280
- const reconcileScript = resolve35(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
27632
+ const reconcileScript = resolve36(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
27281
27633
  const RECONCILE_INTERVAL_MS = 12e4;
27282
27634
  const runReconcile = () => {
27283
27635
  if (!existsSync36(reconcileScript)) return;
@@ -27299,7 +27651,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27299
27651
  }
27300
27652
  {
27301
27653
  const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27302
- const outlookScript = resolve35(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
27654
+ const outlookScript = resolve36(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
27303
27655
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
27304
27656
  const runOutlookComplete = () => {
27305
27657
  if (!existsSync36(outlookScript)) return;
@@ -27321,7 +27673,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27321
27673
  }
27322
27674
  {
27323
27675
  const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27324
- const strandedAccountsDir = resolve35(auditRoot, "..", "data/accounts");
27676
+ const strandedAccountsDir = resolve36(auditRoot, "..", "data/accounts");
27325
27677
  const STRANDED_AUDIT_INTERVAL_MS = 3e5;
27326
27678
  const STRANDED_AGE_MS = 16 * 6e4;
27327
27679
  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 +27683,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27331
27683
  const now = Date.now();
27332
27684
  for (const name of readdirSync21(strandedAccountsDir)) {
27333
27685
  if (!STRANDED_UUID_RE.test(name)) continue;
27334
- const pendingPath2 = resolve35(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
27686
+ const pendingPath2 = resolve36(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
27335
27687
  if (!existsSync36(pendingPath2)) continue;
27336
27688
  const ageMs = now - statSync14(pendingPath2).mtimeMs;
27337
27689
  if (ageMs > STRANDED_AGE_MS) {
@@ -27359,10 +27711,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27359
27711
  auditFirst.unref();
27360
27712
  const auditLoop = setInterval(runStorageAuditSafe, STORAGE_AUDIT_INTERVAL_MS);
27361
27713
  auditLoop.unref();
27714
+ const runPagesAuditSafe = () => {
27715
+ runPagesAudit(auditPlatformRoot).catch(
27716
+ (err) => console.error(`[pages-audit] error="${err.message}"`)
27717
+ );
27718
+ };
27719
+ const pagesAuditFirst = setTimeout(runPagesAuditSafe, 35e3);
27720
+ pagesAuditFirst.unref();
27721
+ const pagesAuditLoop = setInterval(runPagesAuditSafe, STORAGE_AUDIT_INTERVAL_MS);
27722
+ pagesAuditLoop.unref();
27362
27723
  }
27363
27724
  {
27364
27725
  const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27365
- const publishScript = resolve35(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
27726
+ const publishScript = resolve36(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
27366
27727
  const PUBLISH_INTERVAL_MS = 3e5;
27367
27728
  const currentAccount = () => {
27368
27729
  try {
@@ -27391,7 +27752,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27391
27752
  }
27392
27753
  };
27393
27754
  const auditSnapshotAge = (account) => {
27394
- const availPath = resolve35(account.accountDir, "calendar-availability.json");
27755
+ const availPath = resolve36(account.accountDir, "calendar-availability.json");
27395
27756
  let hasBookingSite = false;
27396
27757
  try {
27397
27758
  if (existsSync36(availPath)) {
@@ -27401,7 +27762,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27401
27762
  } catch {
27402
27763
  }
27403
27764
  if (!hasBookingSite) return;
27404
- const statePath = resolve35(account.accountDir, "state", "booking-availability", "last-publish.json");
27765
+ const statePath = resolve36(account.accountDir, "state", "booking-availability", "last-publish.json");
27405
27766
  let lastSuccessAt = null;
27406
27767
  if (existsSync36(statePath)) {
27407
27768
  try {
@@ -27430,7 +27791,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27430
27791
  startTimeEntryCensus(() => getSession());
27431
27792
  {
27432
27793
  const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27433
- const auditScript = resolve35(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
27794
+ const auditScript = resolve36(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
27434
27795
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
27435
27796
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
27436
27797
  const runConnectorAudit = () => {
@@ -27699,7 +28060,7 @@ if (bootAccountConfig?.whatsapp) {
27699
28060
  }
27700
28061
  init({
27701
28062
  configDir: configDirForWhatsApp,
27702
- platformRoot: resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..")),
28063
+ platformRoot: resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..")),
27703
28064
  accountConfig: bootAccountConfig,
27704
28065
  onMessage: async (msg) => {
27705
28066
  if (msg.isOwnerMirror) {