@rubytech/create-maxy-code 0.1.460 → 0.1.462

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. package/dist/__tests__/cron-heartbeat-registration.test.js +57 -28
  2. package/dist/__tests__/launchd-plist.test.js +26 -0
  3. package/dist/cron-registration.js +30 -8
  4. package/dist/index.js +50 -9
  5. package/dist/launchd-plist.js +1 -1
  6. package/dist/uninstall.js +9 -3
  7. package/package.json +1 -1
  8. package/payload/platform/lib/graph-mcp/dist/index.d.ts +2 -1
  9. package/payload/platform/lib/graph-mcp/dist/index.d.ts.map +1 -1
  10. package/payload/platform/lib/graph-mcp/dist/index.js +14 -22
  11. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  12. package/payload/platform/lib/graph-mcp/src/index.ts +15 -22
  13. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +8 -7
  14. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts.map +1 -1
  15. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +19 -19
  16. package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
  17. package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +34 -5
  18. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +22 -19
  19. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +35 -17
  20. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -1
  21. package/payload/platform/lib/mcp-stderr-tee/dist/index.js +41 -48
  22. package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -1
  23. package/payload/platform/lib/mcp-stderr-tee/src/index.ts +42 -51
  24. package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js +27 -0
  25. package/payload/platform/lib/storage-broker/dist/__tests__/audit.test.js.map +1 -1
  26. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +49 -0
  27. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
  28. package/payload/platform/lib/storage-broker/dist/audit.d.ts +11 -0
  29. package/payload/platform/lib/storage-broker/dist/audit.d.ts.map +1 -1
  30. package/payload/platform/lib/storage-broker/dist/audit.js +16 -0
  31. package/payload/platform/lib/storage-broker/dist/audit.js.map +1 -1
  32. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +18 -0
  33. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
  34. package/payload/platform/lib/storage-broker/dist/cf-exec.js +77 -0
  35. package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
  36. package/payload/platform/lib/storage-broker/dist/house-scoped-token.js +3 -0
  37. package/payload/platform/lib/storage-broker/dist/house-scoped-token.js.map +1 -1
  38. package/payload/platform/lib/storage-broker/dist/registry.d.ts +1 -1
  39. package/payload/platform/lib/storage-broker/dist/registry.d.ts.map +1 -1
  40. package/payload/platform/lib/storage-broker/dist/registry.js.map +1 -1
  41. package/payload/platform/lib/storage-broker/src/__tests__/audit.test.ts +32 -1
  42. package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +61 -0
  43. package/payload/platform/lib/storage-broker/src/audit.ts +45 -0
  44. package/payload/platform/lib/storage-broker/src/cf-exec.ts +91 -0
  45. package/payload/platform/lib/storage-broker/src/house-scoped-token.ts +3 -0
  46. package/payload/platform/lib/storage-broker/src/registry.ts +5 -1
  47. package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +19 -19
  48. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +22 -11
  49. package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +19 -19
  50. package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +19 -19
  51. package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
  52. package/payload/platform/plugins/cloudflare/bin/portal-enrol.mjs +96 -0
  53. package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-route.test.ts +54 -40
  54. package/payload/platform/plugins/cloudflare/mcp/__tests__/passcode.test.ts +169 -39
  55. package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-enrol.test.ts +207 -0
  56. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +5 -5
  57. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
  58. package/payload/platform/plugins/cloudflare/mcp/package.json +1 -1
  59. package/payload/platform/plugins/cloudflare/references/hosting-sites.md +2 -0
  60. package/payload/platform/plugins/cloudflare/skills/calendar-site/SKILL.md +1 -1
  61. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +21 -19
  62. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/passcode.mjs +193 -0
  63. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/auth.ts +18 -12
  64. package/payload/platform/plugins/cloudflare/skills/data-portal/template/schema.sql +14 -5
  65. package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +2 -0
  66. package/payload/platform/plugins/connector/lib/mcp-spawn-tee/index.js +19 -19
  67. package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +19 -19
  68. package/payload/platform/plugins/docs/references/admin-session.md +3 -3
  69. package/payload/platform/plugins/docs/references/cloudflare.md +2 -0
  70. package/payload/platform/plugins/docs/references/outlook-guide.md +11 -4
  71. package/payload/platform/plugins/docs/references/plugins-guide.md +8 -6
  72. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +19 -19
  73. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +19 -19
  74. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +19 -19
  75. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  76. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +19 -19
  77. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js +2 -2
  78. package/payload/platform/plugins/memory/mcp/dist/lib/log-ingest.js.map +1 -1
  79. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  80. package/payload/platform/plugins/outlook/PLUGIN.md +2 -2
  81. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +19 -19
  82. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.d.ts +2 -0
  83. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.d.ts.map +1 -0
  84. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.js +238 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-threaded.test.js.map +1 -0
  86. package/payload/platform/plugins/outlook/mcp/dist/index.js +9 -5
  87. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  88. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +58 -9
  89. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
  90. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +113 -19
  91. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
  92. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +5 -2
  93. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +19 -19
  94. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +19 -19
  95. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +19 -19
  96. package/payload/platform/plugins/storage-broker/PLUGIN.md +9 -1
  97. package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +19 -19
  98. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts.map +1 -1
  99. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js +16 -5
  100. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js.map +1 -1
  101. package/payload/platform/plugins/storage-broker/mcp/dist/index.js +49 -0
  102. package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
  103. package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +19 -19
  104. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +19 -19
  105. package/payload/platform/plugins/voice-mirror/PLUGIN.md +1 -1
  106. package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +19 -19
  107. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
  108. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js +98 -9
  109. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js.map +1 -1
  110. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-retrieve-conditioning.d.ts.map +1 -1
  111. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-retrieve-conditioning.js +54 -21
  112. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-retrieve-conditioning.js.map +1 -1
  113. package/payload/platform/plugins/voice-mirror/mcp/scripts/smoke.mjs +110 -0
  114. package/payload/platform/plugins/voice-mirror/skills/voice-mirror/SKILL.md +6 -3
  115. package/payload/platform/plugins/web-designer/PLUGIN.md +1 -1
  116. package/payload/platform/plugins/web-designer/commands/website.md +6 -0
  117. package/payload/platform/plugins/web-designer/skills/web-design/references/deploy.md +12 -0
  118. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +19 -19
  119. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +19 -19
  120. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +19 -19
  121. package/payload/platform/scripts/__tests__/logs-rotate.test.sh +260 -0
  122. package/payload/platform/scripts/__tests__/resume-tunnel.test.sh +79 -0
  123. package/payload/platform/scripts/logs-read.sh +31 -0
  124. package/payload/platform/scripts/logs-rotate.sh +207 -0
  125. package/payload/platform/scripts/resume-tunnel.sh +27 -0
  126. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  127. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  128. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  129. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  130. package/payload/platform/services/claude-session-manager/dist/http-server.js +41 -5
  131. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  132. package/payload/platform/services/claude-session-manager/dist/index.d.ts +1 -0
  133. package/payload/platform/services/claude-session-manager/dist/index.d.ts.map +1 -1
  134. package/payload/platform/services/claude-session-manager/dist/index.js +22 -0
  135. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  136. package/payload/platform/services/claude-session-manager/dist/install-start-counter.d.ts +2 -0
  137. package/payload/platform/services/claude-session-manager/dist/install-start-counter.d.ts.map +1 -0
  138. package/payload/platform/services/claude-session-manager/dist/install-start-counter.js +12 -0
  139. package/payload/platform/services/claude-session-manager/dist/install-start-counter.js.map +1 -0
  140. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +122 -9
  141. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
  142. package/payload/platform/services/claude-session-manager/dist/pty-census.js +214 -29
  143. package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
  144. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  145. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +39 -1
  146. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  147. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +5 -3
  148. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  149. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +13 -1
  150. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  151. package/payload/platform/services/claude-session-manager/dist/session-memory-cap.d.ts.map +1 -1
  152. package/payload/platform/services/claude-session-manager/dist/session-memory-cap.js +4 -2
  153. package/payload/platform/services/claude-session-manager/dist/session-memory-cap.js.map +1 -1
  154. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +150 -0
  155. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -0
  156. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +333 -0
  157. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -0
  158. package/payload/platform/services/claude-session-manager/dist/start-counter.d.ts +10 -0
  159. package/payload/platform/services/claude-session-manager/dist/start-counter.d.ts.map +1 -0
  160. package/payload/platform/services/claude-session-manager/dist/start-counter.js +55 -0
  161. package/payload/platform/services/claude-session-manager/dist/start-counter.js.map +1 -0
  162. package/payload/server/{chunk-JXWFVE5X.js → chunk-BBSPFN45.js} +65 -0
  163. package/payload/server/public/assets/{AdminLoginScreens-BFbpkEUS.js → AdminLoginScreens-Bed8SDxY.js} +1 -1
  164. package/payload/server/public/assets/{AdminShell-CdOGObaZ.js → AdminShell-BLh9O6dn.js} +1 -1
  165. package/payload/server/public/assets/{Checkbox-RFD_F0c0.js → Checkbox-BC_KSbpS.js} +1 -1
  166. package/payload/server/public/assets/{admin-CYGdL-1O.js → admin-DfWgJVI3.js} +1 -1
  167. package/payload/server/public/assets/{browser-DVfa_plj.js → browser-C0vkv-hH.js} +1 -1
  168. package/payload/server/public/assets/{calendar-JC_Dxa4n.js → calendar-_pfHw0Hp.js} +1 -1
  169. package/payload/server/public/assets/chat-CufidiBQ.js +1 -0
  170. package/payload/server/public/assets/chevron-left-BnpVBu51.js +1 -0
  171. package/payload/server/public/assets/data-CR-fbPWr.js +1 -0
  172. package/payload/server/public/assets/{graph-hBUrRSkr.js → graph-BZiudUGr.js} +1 -1
  173. package/payload/server/public/assets/{graph-labels-CHh6zs8t.js → graph-labels-BzmmJwCR.js} +1 -1
  174. package/payload/server/public/assets/{maximize-2-DjxE4rm9.js → maximize-2-Bhdsv0U-.js} +1 -1
  175. package/payload/server/public/assets/{operator-BqU9SQuu.js → operator-Cj0B_usT.js} +1 -1
  176. package/payload/server/public/assets/page-84Fdwiu_.js +32 -0
  177. package/payload/server/public/assets/{page-DiQhU6lD.js → page-8AB3jUa_.js} +1 -1
  178. package/payload/server/public/assets/{public-Cu_eALgY.js → public-BkNW9e9T.js} +1 -1
  179. package/payload/server/public/assets/{rotate-ccw-F9DsSTSJ.js → rotate-ccw-D_5cETGt.js} +1 -1
  180. package/payload/server/public/assets/{tasks-CE-D0f50.js → tasks-EyKLXINi.js} +1 -1
  181. package/payload/server/public/assets/{time-entry-format-D2rsWhov.js → time-entry-format-DAwmmR3i.js} +1 -1
  182. package/payload/server/public/assets/{useCopyFeedback-QxOmoobV.js → useCopyFeedback-rOWd_lzl.js} +1 -1
  183. package/payload/server/public/assets/{useSelectionMode-RIzNqFjV.js → useSelectionMode-DDmLmPph.js} +1 -1
  184. package/payload/server/public/assets/{useSubAccountSwitcher-BW_fK1Nt.css → useSubAccountSwitcher-pu0jjLCz.css} +1 -1
  185. package/payload/server/public/assets/{useVoiceRecorder-WrWurSYI.js → useVoiceRecorder-azG8zSUI.js} +1 -1
  186. package/payload/server/public/browser.html +5 -5
  187. package/payload/server/public/calendar.html +6 -6
  188. package/payload/server/public/chat.html +12 -12
  189. package/payload/server/public/data.html +10 -10
  190. package/payload/server/public/graph.html +11 -11
  191. package/payload/server/public/index.html +13 -13
  192. package/payload/server/public/operator.html +14 -14
  193. package/payload/server/public/public.html +12 -12
  194. package/payload/server/public/tasks.html +5 -5
  195. package/payload/server/server.js +581 -209
  196. package/payload/server/{src-4F37OHLK.js → src-YLGAZY36.js} +5 -1
  197. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/passcode.ts +0 -73
  198. package/payload/server/public/assets/chat-Yf8Nkop8.js +0 -1
  199. package/payload/server/public/assets/chevron-left-CAiAUBxO.js +0 -1
  200. package/payload/server/public/assets/data-hqdYXaEn.js +0 -1
  201. package/payload/server/public/assets/page-B4c4_4HP.js +0 -32
  202. /package/payload/server/public/assets/{useSubAccountSwitcher-wo_qGW-I.js → useSubAccountSwitcher-CU-lOxTd.js} +0 -0
@@ -122,10 +122,11 @@ import {
122
122
  authorizeAccess,
123
123
  listResources,
124
124
  makeHouseCfExec,
125
+ makeHousePagesExec,
125
126
  registerResource,
126
127
  resolveOwner,
127
128
  tooLargeMessage
128
- } from "./chunk-JXWFVE5X.js";
129
+ } from "./chunk-BBSPFN45.js";
129
130
  import {
130
131
  __commonJS,
131
132
  __toESM
@@ -1535,7 +1536,7 @@ var serveStatic = (options = { root: "" }) => {
1535
1536
  import { readFileSync as readFileSync38, existsSync as existsSync36, watchFile, readdirSync as readdirSync21, statSync as statSync14 } from "fs";
1536
1537
  import { spawn as spawn3 } from "child_process";
1537
1538
  import { createHash as createHash7 } from "crypto";
1538
- import { resolve as resolve35, join as join38, basename as basename12 } from "path";
1539
+ import { resolve as resolve36, join as join38, basename as basename12 } from "path";
1539
1540
  import { homedir as homedir4 } from "os";
1540
1541
  import { monitorEventLoopDelay } from "perf_hooks";
1541
1542
 
@@ -2248,7 +2249,7 @@ var credsSaveQueue = Promise.resolve();
2248
2249
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2249
2250
  console.error(`${TAG2} draining credential save queue\u2026`);
2250
2251
  const timer2 = new Promise(
2251
- (resolve36) => setTimeout(() => resolve36("timeout"), timeoutMs)
2252
+ (resolve37) => setTimeout(() => resolve37("timeout"), timeoutMs)
2252
2253
  );
2253
2254
  const result = await Promise.race([
2254
2255
  credsSaveQueue.then(() => "drained"),
@@ -2411,11 +2412,11 @@ async function createWaSocket(opts) {
2411
2412
  return sock;
2412
2413
  }
2413
2414
  async function waitForConnection(sock) {
2414
- return new Promise((resolve36, reject) => {
2415
+ return new Promise((resolve37, reject) => {
2415
2416
  const handler = (update) => {
2416
2417
  if (update.connection === "open") {
2417
2418
  sock.ev.off("connection.update", handler);
2418
- resolve36();
2419
+ resolve37();
2419
2420
  }
2420
2421
  if (update.connection === "close") {
2421
2422
  sock.ev.off("connection.update", handler);
@@ -2520,7 +2521,7 @@ import { inspect as inspect2 } from "util";
2520
2521
 
2521
2522
  // app/lib/whatsapp/with-timeout.ts
2522
2523
  function withTimeout(label, promise, timeoutMs) {
2523
- return new Promise((resolve36, reject) => {
2524
+ return new Promise((resolve37, reject) => {
2524
2525
  const timer2 = setTimeout(() => {
2525
2526
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
2526
2527
  }, timeoutMs);
@@ -2528,7 +2529,7 @@ function withTimeout(label, promise, timeoutMs) {
2528
2529
  promise.then(
2529
2530
  (value) => {
2530
2531
  clearTimeout(timer2);
2531
- resolve36(value);
2532
+ resolve37(value);
2532
2533
  },
2533
2534
  (err) => {
2534
2535
  clearTimeout(timer2);
@@ -3779,8 +3780,8 @@ async function persistWhatsAppMessage(input) {
3779
3780
  const { givenName, familyName } = splitName(input.pushName);
3780
3781
  const prev = sessionWriteLocks.get(input.cacheKey);
3781
3782
  let release;
3782
- const mine = new Promise((resolve36) => {
3783
- release = resolve36;
3783
+ const mine = new Promise((resolve37) => {
3784
+ release = resolve37;
3784
3785
  });
3785
3786
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3786
3787
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -5484,11 +5485,11 @@ async function connectWithReconnect(conn) {
5484
5485
  console.error(
5485
5486
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5486
5487
  );
5487
- await new Promise((resolve36) => {
5488
- const timer2 = setTimeout(resolve36, delay);
5488
+ await new Promise((resolve37) => {
5489
+ const timer2 = setTimeout(resolve37, delay);
5489
5490
  conn.abortController.signal.addEventListener("abort", () => {
5490
5491
  clearTimeout(timer2);
5491
- resolve36();
5492
+ resolve37();
5492
5493
  }, { once: true });
5493
5494
  });
5494
5495
  }
@@ -5496,16 +5497,16 @@ async function connectWithReconnect(conn) {
5496
5497
  }
5497
5498
  }
5498
5499
  function waitForDisconnectEvent(conn) {
5499
- return new Promise((resolve36) => {
5500
+ return new Promise((resolve37) => {
5500
5501
  if (!conn.sock) {
5501
- resolve36();
5502
+ resolve37();
5502
5503
  return;
5503
5504
  }
5504
5505
  const sock = conn.sock;
5505
5506
  const handler = (update) => {
5506
5507
  if (update.connection === "close") {
5507
5508
  sock.ev.off("connection.update", handler);
5508
- resolve36();
5509
+ resolve37();
5509
5510
  }
5510
5511
  };
5511
5512
  sock.ev.on("connection.update", handler);
@@ -5873,8 +5874,8 @@ async function handleInboundMessage(conn, msg) {
5873
5874
  const conversationKey = isGroup ? remoteJid : senderPhone;
5874
5875
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
5875
5876
  let resolvePending;
5876
- const sttPending = new Promise((resolve36) => {
5877
- resolvePending = resolve36;
5877
+ const sttPending = new Promise((resolve37) => {
5878
+ resolvePending = resolve37;
5878
5879
  });
5879
5880
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
5880
5881
  try {
@@ -6302,20 +6303,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
6302
6303
 
6303
6304
  // server/routes/health.ts
6304
6305
  function checkPort(port2, timeoutMs = 500) {
6305
- return new Promise((resolve36) => {
6306
+ return new Promise((resolve37) => {
6306
6307
  const socket = createConnection2(port2, "127.0.0.1");
6307
6308
  socket.setTimeout(timeoutMs);
6308
6309
  socket.once("connect", () => {
6309
6310
  socket.destroy();
6310
- resolve36(true);
6311
+ resolve37(true);
6311
6312
  });
6312
6313
  socket.once("error", () => {
6313
6314
  socket.destroy();
6314
- resolve36(false);
6315
+ resolve37(false);
6315
6316
  });
6316
6317
  socket.once("timeout", () => {
6317
6318
  socket.destroy();
6318
- resolve36(false);
6319
+ resolve37(false);
6319
6320
  });
6320
6321
  });
6321
6322
  }
@@ -6780,7 +6781,7 @@ ${result.result.text}` : result.result.text;
6780
6781
  spawnedAt: /* @__PURE__ */ new Date()
6781
6782
  });
6782
6783
  const replyP = deps.awaitReply(session_key, webchatTurnTimeoutMs());
6783
- await deps.handleInbound({
6784
+ const { spawn: spawn4 } = await deps.handleInbound({
6784
6785
  role: "public",
6785
6786
  accountId: account.accountId,
6786
6787
  key: session_key,
@@ -6793,6 +6794,7 @@ ${result.result.text}` : result.result.text;
6793
6794
  name: publicTitle
6794
6795
  }
6795
6796
  });
6797
+ if (spawn4) await spawn4;
6796
6798
  const reply = await replyP;
6797
6799
  if ("timeout" in reply) {
6798
6800
  controller.enqueue(
@@ -7592,8 +7594,8 @@ async function startLogin(opts) {
7592
7594
  await clearAuth(authDir);
7593
7595
  let resolveCode = null;
7594
7596
  let rejectCode = null;
7595
- const codePromise = new Promise((resolve36, reject) => {
7596
- resolveCode = resolve36;
7597
+ const codePromise = new Promise((resolve37, reject) => {
7598
+ resolveCode = resolve37;
7597
7599
  rejectCode = reject;
7598
7600
  });
7599
7601
  const codeTimer = setTimeout(
@@ -7765,14 +7767,14 @@ function routeDocumentOutboundAt(to, filePath) {
7765
7767
  return lastRouteDocumentOutboundAt.get(routeKey(to, filePath));
7766
7768
  }
7767
7769
  async function sendWhatsAppDocument(input) {
7768
- const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot4 } = input;
7770
+ const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot5 } = input;
7769
7771
  if (!to || !filePath) {
7770
7772
  return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
7771
7773
  }
7772
- if (!maxyAccountId || !platformRoot4) {
7774
+ if (!maxyAccountId || !platformRoot5) {
7773
7775
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
7774
7776
  }
7775
- const accountDir = resolve9(platformRoot4, "..", "data/accounts", maxyAccountId);
7777
+ const accountDir = resolve9(platformRoot5, "..", "data/accounts", maxyAccountId);
7776
7778
  let resolvedPath;
7777
7779
  try {
7778
7780
  resolvedPath = realpathSync3(filePath);
@@ -8733,7 +8735,8 @@ var bodyLimit = (options) => {
8733
8735
 
8734
8736
  // server/routes/storage-broker.ts
8735
8737
  import { join as join11, resolve as resolve11 } from "path";
8736
- import { readdirSync as readdirSync6, readFileSync as readFileSync13, existsSync as existsSync8 } from "fs";
8738
+ import { readdirSync as readdirSync6, readFileSync as readFileSync13, existsSync as existsSync8, realpathSync as realpathSync4 } from "fs";
8739
+ import { randomUUID as randomUUID7 } from "crypto";
8737
8740
  var app3 = new Hono();
8738
8741
  function platformRoot() {
8739
8742
  return process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? join11(__dirname, "..", "..");
@@ -8792,6 +8795,125 @@ var d1QueryBodyLimit = bodyLimit({
8792
8795
  );
8793
8796
  }
8794
8797
  });
8798
+ var pagesDeployBodyLimit = bodyLimit({
8799
+ maxSize: STORAGE_RESOURCE_NAME_MAX_BODY_BYTES,
8800
+ onError: (c) => {
8801
+ log("write", "pages", caller(c) ?? "none", "*", null, "deny", "publish-body-too-large");
8802
+ return c.json(
8803
+ {
8804
+ error: `request body exceeds the ${(STORAGE_RESOURCE_NAME_MAX_BODY_BYTES / 1024).toFixed(
8805
+ 1
8806
+ )} KiB (${STORAGE_RESOURCE_NAME_MAX_BODY_BYTES} bytes) limit for a Pages publish, which carries only a project name, a site folder name and an optional branch.`
8807
+ },
8808
+ 413
8809
+ );
8810
+ }
8811
+ });
8812
+ function resolveSiteDir(account, site) {
8813
+ if (!site || site === "." || site === "..") return null;
8814
+ if (site.includes("/") || site.includes("\\") || site.includes("\0")) return null;
8815
+ const accountsDir = resolve11(platformRoot(), "..", "data", "accounts");
8816
+ const base = resolve11(accountsDir, account, "sites");
8817
+ const dir = resolve11(base, site);
8818
+ if (dir !== base && !dir.startsWith(base + "/")) return null;
8819
+ try {
8820
+ const realBase = realpathSync4(base);
8821
+ const realDir = realpathSync4(dir);
8822
+ if (!realDir.startsWith(realBase + "/")) return null;
8823
+ return realDir;
8824
+ } catch {
8825
+ return null;
8826
+ }
8827
+ }
8828
+ async function verifyDeployed(url) {
8829
+ try {
8830
+ const res = await fetch(`${url}?cb=${Date.now()}`, { method: "GET", redirect: "follow" });
8831
+ return res.status;
8832
+ } catch {
8833
+ return null;
8834
+ }
8835
+ }
8836
+ app3.post("/pages/deploy", pagesDeployBodyLimit, async (c) => {
8837
+ const pub = randomUUID7().slice(0, 8);
8838
+ const account = caller(c);
8839
+ if (!account) {
8840
+ log("write", "pages", "none", "*", null, "deny", "no-caller");
8841
+ return c.json({ error: "no-caller" }, 403);
8842
+ }
8843
+ const parsed = await readJsonBody(c);
8844
+ if (!parsed.ok) return parsed.res;
8845
+ const { project, site, branch } = parsed.body;
8846
+ if (!project || typeof project !== "string") return c.json({ error: "project-required" }, 400);
8847
+ if (!site || typeof site !== "string") return c.json({ error: "site-required" }, 400);
8848
+ if (branch !== void 0 && typeof branch !== "string") return c.json({ error: "branch-invalid" }, 400);
8849
+ console.error(
8850
+ `[pages-broker] op=request pub=${pub} caller=${account} project=${project} site=${site} branch=${branch ?? "default"}`
8851
+ );
8852
+ const dir = resolveSiteDir(account, site);
8853
+ if (dir === null) {
8854
+ console.error(`[pages-broker] op=path pub=${pub} site=${site} inside=false`);
8855
+ log("write", "pages", account, String(site), null, "deny", "invalid-site");
8856
+ return c.json({ error: "invalid-site" }, 400);
8857
+ }
8858
+ console.error(`[pages-broker] op=path pub=${pub} resolved=${dir} inside=true`);
8859
+ const session = getSession();
8860
+ try {
8861
+ const owner = await resolveOwner(session, "pages", project);
8862
+ const decision = authorizeAccess({ caller: account, owner, action: "write" });
8863
+ console.error(
8864
+ `[pages-broker] op=authorize pub=${pub} caller=${account} owner=${owner ?? "none"} decision=${decision.allow ? "allow" : "deny"} reason=${decision.reason}`
8865
+ );
8866
+ log("write", "pages", account, project, owner, decision.allow ? "allow" : "deny", decision.allow ? void 0 : decision.reason);
8867
+ if (!decision.allow) return c.json({ error: decision.reason }, 403);
8868
+ const started = Date.now();
8869
+ const { url } = await (await makeHousePagesExec(platformRoot())).pagesDeploy(dir, project, branch);
8870
+ console.error(`[pages-broker] op=deploy pub=${pub} ranMs=${Date.now() - started} url=${url}`);
8871
+ const httpStatus = await verifyDeployed(url);
8872
+ const verified = httpStatus === 200;
8873
+ console.error(
8874
+ `[pages-broker] op=complete pub=${pub} httpStatus=${httpStatus ?? "unreachable"} verified=${verified}`
8875
+ );
8876
+ if (!verified) {
8877
+ return c.json({ error: "deploy-unverified", url, httpStatus }, 502);
8878
+ }
8879
+ return c.json({ url });
8880
+ } catch (err) {
8881
+ console.error(`[pages-broker] op=exit pub=${pub} result=error detail="${String(err)}"`);
8882
+ return c.json({ error: String(err) }, 500);
8883
+ } finally {
8884
+ await session.close();
8885
+ }
8886
+ });
8887
+ app3.post("/pages/adopt", nameBodyLimit("pages"), async (c) => {
8888
+ const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
8889
+ const account = caller(c);
8890
+ if (!houseAdminScope) {
8891
+ log("write", "pages", account ?? "none", "*", null, "deny", "not-house-admin");
8892
+ return c.json({ error: "not-house-admin" }, 403);
8893
+ }
8894
+ const parsed = await readJsonBody(c);
8895
+ if (!parsed.ok) return parsed.res;
8896
+ const { project, account: target } = parsed.body;
8897
+ if (!project || typeof project !== "string") return c.json({ error: "project-required" }, 400);
8898
+ if (!target || typeof target !== "string") return c.json({ error: "account-required" }, 400);
8899
+ const session = getSession();
8900
+ try {
8901
+ const owner = await resolveOwner(session, "pages", project);
8902
+ if (owner !== null && owner !== target) {
8903
+ log("write", "pages", target, project, owner, "deny", "already-owned");
8904
+ return c.json({ error: "already-owned", owner }, 409);
8905
+ }
8906
+ if (owner === null) {
8907
+ await registerResource(session, { accountId: target, kind: "pages", name: project, cfResourceId: project });
8908
+ }
8909
+ log("write", "pages", target, project, target, "allow");
8910
+ return c.json({ project, account: target });
8911
+ } catch (err) {
8912
+ return c.json({ error: String(err) }, 500);
8913
+ } finally {
8914
+ await session.close();
8915
+ }
8916
+ });
8795
8917
  app3.get("/d1/list", async (c) => {
8796
8918
  const account = caller(c);
8797
8919
  if (!account) return c.json({ error: "no-caller" }, 403);
@@ -9061,7 +9183,7 @@ app3.post("/r2/object/delete", envelopeBodyLimit("write"), async (c) => {
9061
9183
  });
9062
9184
  var storage_broker_default = app3;
9063
9185
  async function runStorageAudit(root) {
9064
- const { reconcileStorage } = await import("./src-4F37OHLK.js");
9186
+ const { reconcileStorage } = await import("./src-YLGAZY36.js");
9065
9187
  const session = getSession();
9066
9188
  try {
9067
9189
  const cf = await makeHouseCfExec(root);
@@ -9093,9 +9215,31 @@ async function runStorageAudit(root) {
9093
9215
  await session.close();
9094
9216
  }
9095
9217
  }
9218
+ async function runPagesAudit(root) {
9219
+ const { reconcilePages } = await import("./src-YLGAZY36.js");
9220
+ const session = getSession();
9221
+ try {
9222
+ const cf = await makeHousePagesExec(root);
9223
+ const r = await reconcilePages({
9224
+ cfProjects: async () => (await cf.pagesProjectList()).map((p) => p.name),
9225
+ registeredProjects: async () => {
9226
+ const res = await session.run(
9227
+ "MATCH (r:StorageResource {kind: $kind}) RETURN r.name AS name",
9228
+ { kind: "pages" }
9229
+ );
9230
+ return res.records.map((rec) => rec.get("name"));
9231
+ }
9232
+ });
9233
+ console.error(
9234
+ `[pages-audit] projects=${r.projects} registered=${r.registered} orphan=${r.orphans.length} phantom=${r.phantoms.length}` + r.orphans.map((n) => ` orphan:${n}`).join("") + r.phantoms.map((n) => ` phantom:${n}`).join("")
9235
+ );
9236
+ } finally {
9237
+ await session.close();
9238
+ }
9239
+ }
9096
9240
 
9097
9241
  // server/routes/whatsapp-reader.ts
9098
- import { readFileSync as readFileSync17, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync11, readdirSync as readdirSync11, realpathSync as realpathSync4 } from "fs";
9242
+ import { readFileSync as readFileSync17, watch, statSync as statSync5, openSync, readSync, closeSync, existsSync as existsSync11, readdirSync as readdirSync11, realpathSync as realpathSync5 } from "fs";
9099
9243
  import { basename as basename4, dirname as dirname5, isAbsolute, join as join16, relative as relative2, resolve as resolve13, sep as sep3 } from "path";
9100
9244
 
9101
9245
  // server/routes/admin/sidebar-sessions.ts
@@ -9727,7 +9871,31 @@ function joinTextBlocks(content) {
9727
9871
  }
9728
9872
  return parts.join("");
9729
9873
  }
9730
- function assistantTurns(content, ts, promoteFinal = false) {
9874
+ function erroredToolUseIds(lines) {
9875
+ const ids = /* @__PURE__ */ new Set();
9876
+ for (const line of lines) {
9877
+ if (!line || line[0] !== "{" || !line.includes("is_error")) continue;
9878
+ let row;
9879
+ try {
9880
+ const parsed = JSON.parse(line);
9881
+ if (!parsed || typeof parsed !== "object") continue;
9882
+ row = parsed;
9883
+ } catch {
9884
+ continue;
9885
+ }
9886
+ const content = row.message?.content;
9887
+ if (!Array.isArray(content)) continue;
9888
+ for (const block of content) {
9889
+ if (!block || typeof block !== "object") continue;
9890
+ const b = block;
9891
+ if (b.type === "tool_result" && b.is_error === true && typeof b.tool_use_id === "string") {
9892
+ ids.add(b.tool_use_id);
9893
+ }
9894
+ }
9895
+ }
9896
+ return ids;
9897
+ }
9898
+ function assistantTurns(content, ts, promoteFinal = false, erroredIds = /* @__PURE__ */ new Set()) {
9731
9899
  if (!Array.isArray(content)) return [];
9732
9900
  const out = [];
9733
9901
  for (const block of content) {
@@ -9747,7 +9915,12 @@ function assistantTurns(content, ts, promoteFinal = false) {
9747
9915
  const input = b.input ?? {};
9748
9916
  const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
9749
9917
  const caption = typeof input.caption === "string" ? input.caption : null;
9750
- out.push({ kind: "agent-file", files, caption, ts });
9918
+ const refused = typeof b.id === "string" && erroredIds.has(b.id);
9919
+ if (files.length === 0 || refused) {
9920
+ out.push({ kind: "tool-call", name: SEND_USER_FILE_TOOL, input: b.input ?? null, ts });
9921
+ } else {
9922
+ out.push({ kind: "agent-file", files, caption, ts });
9923
+ }
9751
9924
  } else if (b.type === "tool_use" && typeof b.name === "string") {
9752
9925
  out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
9753
9926
  }
@@ -9787,6 +9960,7 @@ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress) {
9787
9960
  }
9788
9961
  function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}) {
9789
9962
  const out = [];
9963
+ const erroredIds = erroredToolUseIds(lines);
9790
9964
  for (const line of lines) {
9791
9965
  if (!line || line[0] !== "{") continue;
9792
9966
  let row;
@@ -9811,7 +9985,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
9811
9985
  if (hasWebchatReply(msg?.content)) turnCtx.repliedThisTurn = true;
9812
9986
  const isEnd = msg?.stop_reason === "end_turn";
9813
9987
  const promoteFinal = isEnd && turnCtx.lastChannelSource === "webchat" && !turnCtx.repliedThisTurn;
9814
- out.push(...assistantTurns(msg?.content, ts, promoteFinal));
9988
+ out.push(...assistantTurns(msg?.content, ts, promoteFinal, erroredIds));
9815
9989
  continue;
9816
9990
  }
9817
9991
  if (row.type === "queue-operation" && row.operation === "enqueue") {
@@ -10208,7 +10382,7 @@ function enrichDownloads(turn, scratchpadRoot) {
10208
10382
  if (turn.kind !== "agent-file") return turn;
10209
10383
  let scratchpadReal = null;
10210
10384
  try {
10211
- scratchpadReal = realpathSync4(scratchpadRoot);
10385
+ scratchpadReal = realpathSync5(scratchpadRoot);
10212
10386
  } catch {
10213
10387
  scratchpadReal = null;
10214
10388
  }
@@ -10219,7 +10393,7 @@ function enrichDownloads(turn, scratchpadRoot) {
10219
10393
  }
10220
10394
  if (scratchpadReal) {
10221
10395
  try {
10222
- const fileReal = realpathSync4(f);
10396
+ const fileReal = realpathSync5(f);
10223
10397
  if (fileReal === scratchpadReal || fileReal.startsWith(scratchpadReal + sep3)) {
10224
10398
  return { root: "scratchpad", path: relative2(scratchpadReal, fileReal) };
10225
10399
  }
@@ -11881,7 +12055,7 @@ ${note}` : note;
11881
12055
  console.error(`[webchat:inbound] op=send-refused kind=${kind} reason=empty-text`);
11882
12056
  return c.json({ error: "text required" }, 400);
11883
12057
  }
11884
- console.log(`[webchat:inbound] op=send-accepted kind=${kind} files=${stored.length} bytes=${Buffer.byteLength(text, "utf8")}`);
12058
+ console.log(`[webchat:inbound] op=send-parsed kind=${kind} files=${stored.length} bytes=${Buffer.byteLength(text, "utf8")}`);
11885
12059
  }
11886
12060
  let deliveredIntId = null;
11887
12061
  if (deliverySessionId !== void 0 && await managerActivity(deliverySessionId) === "busy") {
@@ -11893,11 +12067,8 @@ ${note}` : note;
11893
12067
  deliveredIntId = outcome.intId;
11894
12068
  }
11895
12069
  const adminUserId = requesterUserId ?? void 0;
11896
- if (deliverySessionId !== void 0) {
11897
- await deps.handleInbound({ role: "admin", accountId: deliveryAccountId, key: deliveryKey, text, sessionId: deliverySessionId, adminUserId });
11898
- } else {
11899
- await deps.handleInbound({ role: "admin", accountId: bootstrapAccountId, key: deliveryKey, text, adminUserId });
11900
- }
12070
+ const inboundStartedAt = Date.now();
12071
+ const inbound = deliverySessionId !== void 0 ? await deps.handleInbound({ role: "admin", accountId: deliveryAccountId, key: deliveryKey, text, sessionId: deliverySessionId, adminUserId }) : await deps.handleInbound({ role: "admin", accountId: bootstrapAccountId, key: deliveryKey, text, adminUserId });
11901
12072
  if (deliveredIntId !== null) {
11902
12073
  console.log(`[webchat-interrupt] op=delivered intId=${deliveredIntId} sessionId=${deliverySessionId.slice(0, 8)}`);
11903
12074
  }
@@ -11914,7 +12085,9 @@ ${note}` : note;
11914
12085
  }
11915
12086
  const keyDisplay2 = deliveryKey.startsWith("session:") ? deliveryKey.slice(0, "session:".length + 8) : WEBCHAT_ADMIN_KEY;
11916
12087
  const baseline = jsonlSizeBytes(locateSession(turnSessionId).projectDir, turnSessionId);
11917
- console.error(`[webchat:inbound] op=send-accepted key=${keyDisplay2} sendId=${sendId} baselineBytes=${baseline ?? "absent"}`);
12088
+ console.error(
12089
+ `[webchat:inbound] op=send-accepted key=${keyDisplay2} sendId=${sendId} baselineBytes=${baseline ?? "absent"} acceptMs=${Date.now() - inboundStartedAt} spawn=${inbound.spawn !== null ? "triggered" : "none"}`
12090
+ );
11918
12091
  const turnTimer = setTimeout(() => {
11919
12092
  const now = jsonlSizeBytes(locateSession(turnSessionId).projectDir, turnSessionId);
11920
12093
  const grew = now !== null && (baseline === null || now > baseline);
@@ -12293,8 +12466,8 @@ function routeTelegramUpdate(input) {
12293
12466
  // server/routes/telegram.ts
12294
12467
  var TAG23 = "[telegram-inbound]";
12295
12468
  function configDirName() {
12296
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12297
- const brandPath = join22(platformRoot4, "config", "brand.json");
12469
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12470
+ const brandPath = join22(platformRoot5, "config", "brand.json");
12298
12471
  if (existsSync15(brandPath)) {
12299
12472
  try {
12300
12473
  return JSON.parse(readFileSync23(brandPath, "utf-8")).configDir ?? ".maxy";
@@ -12493,7 +12666,7 @@ var quickbooks_default = app9;
12493
12666
  // server/routes/onboarding.ts
12494
12667
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync3 } from "child_process";
12495
12668
  import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync18, readFileSync as readFileSync26, unlinkSync as unlinkSync3, renameSync as renameSync7 } from "fs";
12496
- import { createHash as createHash4, randomUUID as randomUUID7 } from "crypto";
12669
+ import { createHash as createHash4, randomUUID as randomUUID8 } from "crypto";
12497
12670
 
12498
12671
  // app/lib/claude-spawn-env.ts
12499
12672
  function claudeBin() {
@@ -12989,7 +13162,7 @@ app10.post("/set-pin", async (c) => {
12989
13162
  const hash = hashPin2(body.pin);
12990
13163
  const account = resolveAccount();
12991
13164
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
12992
- const userId = existingOwnerUserId ?? randomUUID7();
13165
+ const userId = existingOwnerUserId ?? randomUUID8();
12993
13166
  if (existingOwnerUserId) {
12994
13167
  console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
12995
13168
  } else {
@@ -13381,7 +13554,7 @@ app11.post("/", async (c) => {
13381
13554
  var client_error_default = app11;
13382
13555
 
13383
13556
  // server/routes/admin/session.ts
13384
- import { randomUUID as randomUUID8 } from "crypto";
13557
+ import { randomUUID as randomUUID9 } from "crypto";
13385
13558
  async function resolveUserIdentity(accountId, userId) {
13386
13559
  const result = await loadAdminUserName(userId);
13387
13560
  if (result.source === "neo4j") {
@@ -13395,7 +13568,7 @@ async function resolveUserIdentity(accountId, userId) {
13395
13568
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
13396
13569
  const account = resolveAccount();
13397
13570
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
13398
- const signedSessionToken = randomUUID8();
13571
+ const signedSessionToken = randomUUID9();
13399
13572
  const cacheKey = fingerprintSessionKey(signedSessionToken);
13400
13573
  registerSession(cacheKey, "admin", accountId, void 0, userId, userName ?? void 0, role);
13401
13574
  if (userId) setWantsPriorConversation(cacheKey);
@@ -13437,15 +13610,15 @@ function operatorRoleFor(config, userId) {
13437
13610
  async function buildAccountOptionList(accounts, userId) {
13438
13611
  const houseAccount = accounts.find((a) => a.config.role === "house");
13439
13612
  console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
13440
- const resolve36 = async (a) => {
13613
+ const resolve37 = async (a) => {
13441
13614
  const businessName = await fetchAccountName(a.accountId) || void 0;
13442
13615
  return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
13443
13616
  };
13444
13617
  const houseOptions = await Promise.all(
13445
- accounts.filter((a) => a.config.role === "house").map(resolve36)
13618
+ accounts.filter((a) => a.config.role === "house").map(resolve37)
13446
13619
  );
13447
13620
  const nonHouseOptions = await Promise.all(
13448
- accounts.filter((a) => a.config.role !== "house").map(resolve36)
13621
+ accounts.filter((a) => a.config.role !== "house").map(resolve37)
13449
13622
  );
13450
13623
  const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
13451
13624
  nonHouseOptions.sort((x, y) => {
@@ -14929,10 +15102,10 @@ function readTunnelState(brandConfigDir) {
14929
15102
  }
14930
15103
  }
14931
15104
  function resolveTunnelUrl() {
14932
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
15105
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve21(process.cwd(), "..");
14933
15106
  let brand;
14934
15107
  try {
14935
- brand = JSON.parse(readFileSync30(resolve21(platformRoot4, "config", "brand.json"), "utf-8"));
15108
+ brand = JSON.parse(readFileSync30(resolve21(platformRoot5, "config", "brand.json"), "utf-8"));
14936
15109
  } catch {
14937
15110
  return null;
14938
15111
  }
@@ -15141,18 +15314,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
15141
15314
  ]);
15142
15315
  var app21 = new Hono();
15143
15316
  app21.post("/", async (c) => {
15144
- const TAG45 = "[admin:events]";
15317
+ const TAG46 = "[admin:events]";
15145
15318
  let body;
15146
15319
  try {
15147
15320
  body = await c.req.json();
15148
15321
  } catch (err) {
15149
15322
  const detail = err instanceof Error ? err.message : String(err);
15150
- console.error(`${TAG45} reject reason=body-not-json detail=${detail}`);
15323
+ console.error(`${TAG46} reject reason=body-not-json detail=${detail}`);
15151
15324
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
15152
15325
  }
15153
15326
  const event = typeof body.event === "string" ? body.event : "";
15154
15327
  if (!ALLOWED_EVENTS.has(event)) {
15155
- console.error(`${TAG45} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15328
+ console.error(`${TAG46} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15156
15329
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
15157
15330
  }
15158
15331
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -15176,8 +15349,8 @@ var events_default = app21;
15176
15349
  // server/routes/admin/files.ts
15177
15350
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync27 } from "fs";
15178
15351
  import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, unlink as unlink2, rename, rmdir } from "fs/promises";
15179
- import { realpathSync as realpathSync5 } from "fs";
15180
- import { randomUUID as randomUUID9 } from "crypto";
15352
+ import { realpathSync as realpathSync6 } from "fs";
15353
+ import { randomUUID as randomUUID10 } from "crypto";
15181
15354
  import { basename as basename9, dirname as dirname11, join as join30, relative as relative4, resolve as resolve23, sep as sep6 } from "path";
15182
15355
  import { Readable as Readable2 } from "stream";
15183
15356
 
@@ -16542,8 +16715,8 @@ async function resolveUploadTarget(c, accountId, uid) {
16542
16715
  const info = await stat4(destDir).catch(() => null);
16543
16716
  if (!info || !info.isDirectory()) return { ok: false, status: 400, error: "Upload destination is not a directory" };
16544
16717
  }
16545
- const dataRootReal = realpathSync5(DATA_ROOT);
16546
- const destDirReal = realpathSync5(destDir);
16718
+ const dataRootReal = realpathSync6(DATA_ROOT);
16719
+ const destDirReal = realpathSync6(destDir);
16547
16720
  if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep6)) {
16548
16721
  console.error(`[data-upload] op=path-traversal-blocked uid=${uid} requested="${destRel}" resolved="${destDirReal}"`);
16549
16722
  return { ok: false, status: 403, error: "Upload destination escapes DATA_ROOT" };
@@ -16569,7 +16742,7 @@ app22.post("/upload", requireAdminSession, async (c) => {
16569
16742
  if (c.req.query("uploadId") !== void 0) {
16570
16743
  return handleChunkUpload(c, accountId);
16571
16744
  }
16572
- const uid = `${Date.now()}-${randomUUID9().slice(0, 8)}`;
16745
+ const uid = `${Date.now()}-${randomUUID10().slice(0, 8)}`;
16573
16746
  const declaredBytes = c.req.header("content-length") ?? "unknown";
16574
16747
  const target = await resolveUploadTarget(c, accountId, uid);
16575
16748
  if (!target.ok) return c.json({ error: target.error }, target.status);
@@ -19644,9 +19817,9 @@ app34.get("/", requireAdminSession, async (c) => {
19644
19817
  var session_usage_default = app34;
19645
19818
 
19646
19819
  // server/routes/admin/session-rc-spawn.ts
19647
- import { randomUUID as randomUUID10 } from "crypto";
19820
+ import { randomUUID as randomUUID11 } from "crypto";
19648
19821
  var ACCOUNT_UUID_RE3 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
19649
- function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID10, adminUserId, authenticatedName, callerAccountId) {
19822
+ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, adminUserId, authenticatedName, callerAccountId) {
19650
19823
  const operator = isOperatorHost(host, operatorDomains);
19651
19824
  const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
19652
19825
  const name = typeof body.name === "string" && body.name.length > 0 ? body.name : void 0;
@@ -19701,8 +19874,8 @@ app35.post("/", requireAdminSession, async (c) => {
19701
19874
  const adminUserId = cacheKey ? getUserIdForSession(cacheKey) : void 0;
19702
19875
  const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
19703
19876
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
19704
- const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID10, adminUserId, authenticatedName, accountId);
19705
- const spawnReqId = randomUUID10();
19877
+ const plan = resolveSpawnPlan(host, getOperatorDomains(), body, randomUUID11, adminUserId, authenticatedName, accountId);
19878
+ const spawnReqId = randomUUID11();
19706
19879
  console.log(
19707
19880
  `[chat-spawn] op=request spawnReqId=${spawnReqId} origin=${plan.origin} kind=${plan.kind} sessionId=${plan.kind === "new" ? "new" : plan.sessionId}`
19708
19881
  );
@@ -19774,8 +19947,8 @@ app35.post("/", requireAdminSession, async (c) => {
19774
19947
  var session_rc_spawn_default = app35;
19775
19948
 
19776
19949
  // server/routes/admin/session-reseat.ts
19777
- import { randomUUID as randomUUID11 } from "crypto";
19778
- function resolveReseatPlan(body, mintId = randomUUID11, adminUserId, authenticatedName) {
19950
+ import { randomUUID as randomUUID12 } from "crypto";
19951
+ function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName) {
19779
19952
  const sessionId = mintId();
19780
19953
  const key = `session:${sessionId}`;
19781
19954
  const payload = {
@@ -19829,11 +20002,11 @@ app36.post("/", requireAdminSession, async (c) => {
19829
20002
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
19830
20003
  const plan = resolveReseatPlan(
19831
20004
  { fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
19832
- randomUUID11,
20005
+ randomUUID12,
19833
20006
  adminUserId,
19834
20007
  authenticatedName
19835
20008
  );
19836
- const reseatReqId = randomUUID11();
20009
+ const reseatReqId = randomUUID12();
19837
20010
  console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
19838
20011
  let res;
19839
20012
  try {
@@ -20076,7 +20249,7 @@ app38.get("/", async (c) => {
20076
20249
  var health_default2 = app38;
20077
20250
 
20078
20251
  // server/routes/admin/linkedin-ingest.ts
20079
- import { randomUUID as randomUUID12 } from "crypto";
20252
+ import { randomUUID as randomUUID13 } from "crypto";
20080
20253
 
20081
20254
  // app/lib/channel-pty-bridge/manager-client.ts
20082
20255
  function managerBase2() {
@@ -20329,7 +20502,7 @@ app39.post("/", requireAdminSession, async (c) => {
20329
20502
  );
20330
20503
  const initialMessage = buildInitialMessage(envelope);
20331
20504
  const spawnStart = Date.now();
20332
- const sessionId = randomUUID12();
20505
+ const sessionId = randomUUID13();
20333
20506
  console.log(TAG27 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
20334
20507
  const spawned = await managerRcSpawn({
20335
20508
  sessionId,
@@ -21367,7 +21540,7 @@ app47.get("/", requireAdminSession, async (c) => {
21367
21540
  var tasks_list_default = app47;
21368
21541
 
21369
21542
  // server/routes/admin/task-timer-start.ts
21370
- import { randomUUID as randomUUID13 } from "crypto";
21543
+ import { randomUUID as randomUUID14 } from "crypto";
21371
21544
  var TAG34 = "[task-timer]";
21372
21545
  var app48 = new Hono();
21373
21546
  app48.post("/", requireAdminSession, async (c) => {
@@ -21405,7 +21578,7 @@ app48.post("/", requireAdminSession, async (c) => {
21405
21578
  console.log(`${TAG34} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
21406
21579
  return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
21407
21580
  }
21408
- const entryId = randomUUID13();
21581
+ const entryId = randomUUID14();
21409
21582
  await session.run(
21410
21583
  `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
21411
21584
  CREATE (te:TimeEntry {entryId: $entryId, taskId: $taskId, accountId: $accountId, startedAt: $now, endedAt: null})
@@ -21532,7 +21705,7 @@ app50.post("/", requireAdminSession, async (c) => {
21532
21705
  var task_complete_default = app50;
21533
21706
 
21534
21707
  // server/routes/admin/task-time-adjust.ts
21535
- import { randomUUID as randomUUID14 } from "crypto";
21708
+ import { randomUUID as randomUUID15 } from "crypto";
21536
21709
  import neo4j6 from "neo4j-driver";
21537
21710
  var TAG37 = "[task-time-adjust]";
21538
21711
  var app51 = new Hono();
@@ -21557,7 +21730,7 @@ app51.post("/", requireAdminSession, async (c) => {
21557
21730
  console.error(`${TAG37} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
21558
21731
  return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
21559
21732
  }
21560
- const adjustmentId = randomUUID14();
21733
+ const adjustmentId = randomUUID15();
21561
21734
  const now = (/* @__PURE__ */ new Date()).toISOString();
21562
21735
  console.log(`${TAG37} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
21563
21736
  const session = getSession();
@@ -21891,7 +22064,7 @@ app55.route("/request-magic-link", request_magic_link_default);
21891
22064
  var access_default = app55;
21892
22065
 
21893
22066
  // server/routes/sites.ts
21894
- import { existsSync as existsSync31, readFileSync as readFileSync34, realpathSync as realpathSync6, statSync as statSync12 } from "fs";
22067
+ import { existsSync as existsSync31, readFileSync as readFileSync34, realpathSync as realpathSync7, statSync as statSync12 } from "fs";
21895
22068
  import { resolve as resolve28 } from "path";
21896
22069
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
21897
22070
  var MIME = {
@@ -21983,8 +22156,8 @@ app56.get("/:rel{.*}", (c) => {
21983
22156
  let realPath;
21984
22157
  let realRoot;
21985
22158
  try {
21986
- realPath = realpathSync6(filePath);
21987
- realRoot = realpathSync6(rootDir);
22159
+ realPath = realpathSync7(filePath);
22160
+ realRoot = realpathSync7(rootDir);
21988
22161
  } catch {
21989
22162
  console.error(`[sites] not-found path=${reqPath} status=404`);
21990
22163
  return c.text("Not found", 404);
@@ -22114,9 +22287,9 @@ var cachedAttempted = false;
22114
22287
  function readBrandConfig() {
22115
22288
  if (cachedAttempted) return cached2;
22116
22289
  cachedAttempted = true;
22117
- const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
22118
- if (!platformRoot4) return null;
22119
- const brandPath = join35(platformRoot4, "config", "brand.json");
22290
+ const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
22291
+ if (!platformRoot5) return null;
22292
+ const brandPath = join35(platformRoot5, "config", "brand.json");
22120
22293
  if (!existsSync32(brandPath)) return null;
22121
22294
  try {
22122
22295
  cached2 = JSON.parse(readFileSync36(brandPath, "utf-8"));
@@ -24453,16 +24626,16 @@ var WebchatGateway = class _WebchatGateway {
24453
24626
  * The public /api/chat route awaits this to return the reply on the same SSE
24454
24627
  * response, preserving the pre-756 POST→single-blob browser contract. */
24455
24628
  awaitReply(key, timeoutMs) {
24456
- return new Promise((resolve36) => {
24629
+ return new Promise((resolve37) => {
24457
24630
  const timer2 = setTimeout(() => {
24458
24631
  this.replyAwaiters.delete(key);
24459
- resolve36({ timeout: true });
24632
+ resolve37({ timeout: true });
24460
24633
  }, timeoutMs);
24461
24634
  if (timer2.unref) timer2.unref();
24462
24635
  this.replyAwaiters.set(key, (r) => {
24463
24636
  clearTimeout(timer2);
24464
24637
  this.replyAwaiters.delete(key);
24465
- resolve36(r);
24638
+ resolve37(r);
24466
24639
  });
24467
24640
  });
24468
24641
  }
@@ -24473,11 +24646,11 @@ var WebchatGateway = class _WebchatGateway {
24473
24646
  * terminal dialog stays answerable). */
24474
24647
  awaitPermissionVerdict(p, timeoutMs) {
24475
24648
  const k = _WebchatGateway.promptKey(p.key, p.requestId);
24476
- return new Promise((resolve36) => {
24649
+ return new Promise((resolve37) => {
24477
24650
  this.pendingPrompts.get(k)?.resolve({ timeout: true });
24478
24651
  const timer2 = setTimeout(() => {
24479
24652
  this.pendingPrompts.delete(k);
24480
- resolve36({ timeout: true });
24653
+ resolve37({ timeout: true });
24481
24654
  }, timeoutMs);
24482
24655
  if (timer2.unref) timer2.unref();
24483
24656
  this.pendingPrompts.set(k, {
@@ -24486,7 +24659,7 @@ var WebchatGateway = class _WebchatGateway {
24486
24659
  resolve: (r) => {
24487
24660
  clearTimeout(timer2);
24488
24661
  this.pendingPrompts.delete(k);
24489
- resolve36(r);
24662
+ resolve37(r);
24490
24663
  }
24491
24664
  });
24492
24665
  console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
@@ -24727,7 +24900,9 @@ var WebchatGateway = class _WebchatGateway {
24727
24900
  this.recovering = false;
24728
24901
  }
24729
24902
  }
24730
- /** Handle one inbound webchat message (browser POST → here). */
24903
+ /** Handle one inbound webchat message (browser POST → here). Resolves when
24904
+ * the hub holds the message; the cold-start spawn is returned as a handle
24905
+ * (Task 1715 — see WebchatInboundResult). */
24731
24906
  async handleInbound(input) {
24732
24907
  const bytes = Buffer.byteLength(input.text, "utf8");
24733
24908
  const hadSubscriber = this.hub.hasSubscriber(input.key);
@@ -24761,63 +24936,60 @@ var WebchatGateway = class _WebchatGateway {
24761
24936
  }
24762
24937
  const existing = this.publicSessions.get(input.key);
24763
24938
  if (existing) existing.lastActivityAt = Date.now();
24939
+ let spawn4 = null;
24764
24940
  if (!hadSubscriber && !this.spawning.has(input.key)) {
24765
24941
  this.spawning.add(input.key);
24766
24942
  console.error(`[webchat:inbound] op=spawn-trigger key=${input.key} role=${input.role}`);
24767
- try {
24768
- const spawned = await this.deps.ensureChannelSession({
24943
+ spawn4 = this.runSpawn(input);
24944
+ spawn4.catch(() => {
24945
+ });
24946
+ }
24947
+ return { spawn: spawn4 };
24948
+ }
24949
+ /** Task 1715 — the cold-start spawn, run off the accept's path. Rejects on a
24950
+ * failed spawn so the public caller's SSE still errors; logs op=spawn-failed
24951
+ * first, because on the admin path the 200 has already been returned and this
24952
+ * line is the only attribution the failure gets. */
24953
+ async runSpawn(input) {
24954
+ const startedAt = Date.now();
24955
+ try {
24956
+ const spawned = await this.deps.ensureChannelSession({
24957
+ accountId: input.accountId,
24958
+ key: input.key,
24959
+ role: input.role,
24960
+ gatewayUrl: this.deps.gatewayUrl,
24961
+ serverPath: this.deps.serverPath,
24962
+ // Task 800 — only present on a session-targeted launch, so the
24963
+ // default-path call shape stays byte-identical.
24964
+ ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {},
24965
+ // The authenticated admin's userId on an admin bootstrap inbound; the
24966
+ // default-path call shape stays byte-identical when absent.
24967
+ ...input.adminUserId !== void 0 ? { adminUserId: input.adminUserId } : {},
24968
+ // Task 756 — public spawn params on a role:'public' inbound.
24969
+ ...input.public !== void 0 ? { public: input.public } : {}
24970
+ });
24971
+ if (input.role === "public" && input.public && spawned && "sessionId" in spawned) {
24972
+ this.publicSessions.set(input.key, {
24973
+ sessionId: spawned.sessionId,
24769
24974
  accountId: input.accountId,
24770
- key: input.key,
24771
- role: input.role,
24772
- gatewayUrl: this.deps.gatewayUrl,
24773
- serverPath: this.deps.serverPath,
24774
- // Task 800 — only present on a session-targeted launch, so the
24775
- // default-path call shape stays byte-identical.
24776
- ...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {},
24777
- // The authenticated admin's userId on an admin bootstrap inbound; the
24778
- // default-path call shape stays byte-identical when absent.
24779
- ...input.adminUserId !== void 0 ? { adminUserId: input.adminUserId } : {},
24780
- // Task 756 — public spawn params on a role:'public' inbound.
24781
- ...input.public !== void 0 ? { public: input.public } : {}
24975
+ agentSlug: input.public.agentSlug,
24976
+ sliceToken: input.public.sliceToken,
24977
+ personId: input.public.personId,
24978
+ lastActivityAt: Date.now()
24782
24979
  });
24783
- if (input.role === "public" && input.public && spawned && "sessionId" in spawned) {
24784
- this.publicSessions.set(input.key, {
24785
- sessionId: spawned.sessionId,
24786
- accountId: input.accountId,
24787
- agentSlug: input.public.agentSlug,
24788
- sliceToken: input.public.sliceToken,
24789
- personId: input.public.personId,
24790
- lastActivityAt: Date.now()
24791
- });
24792
- }
24793
- } finally {
24794
- this.spawning.delete(input.key);
24795
24980
  }
24981
+ } catch (err) {
24982
+ const reason = (err instanceof Error ? err.message : String(err)).replace(/\s+/g, " ");
24983
+ console.error(
24984
+ `[webchat:inbound] op=spawn-failed key=${keyDisplay(input.key)} role=${input.role} ms=${Date.now() - startedAt} reason=${reason}`
24985
+ );
24986
+ throw err;
24987
+ } finally {
24988
+ this.spawning.delete(input.key);
24796
24989
  }
24797
24990
  }
24798
24991
  };
24799
24992
 
24800
- // app/lib/webchat/gateway/public-spawn-request.ts
24801
- function buildPublicWebchatSpawnRequest(input) {
24802
- const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
24803
- return {
24804
- senderId: input.key,
24805
- role: "public",
24806
- channel: "webchat",
24807
- accountId: input.accountId,
24808
- agentSlug,
24809
- model: SONNET_MODEL,
24810
- // sliceToken/personId are present only for a gated visitor; open-mode
24811
- // public agents carry neither (mirrors the bridge's open-mode key).
24812
- ...sliceToken ? { sliceToken } : {},
24813
- ...personId ? { personId } : {},
24814
- ...visitorId ? { visitorId } : {},
24815
- ...input.previousContext ? { previousContext: input.previousContext } : {},
24816
- name,
24817
- webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
24818
- };
24819
- }
24820
-
24821
24993
  // app/lib/channel-pty-bridge/follower.ts
24822
24994
  function followerPendingMaxMs() {
24823
24995
  return Number(process.env.CHANNEL_PTY_FOLLOWER_PENDING_MAX_MS ?? String(3e5));
@@ -24825,6 +24997,13 @@ function followerPendingMaxMs() {
24825
24997
  function followerRetryMs() {
24826
24998
  return Number(process.env.CHANNEL_PTY_FOLLOWER_RETRY_MS ?? String(1e3));
24827
24999
  }
25000
+ function toolResultText(content) {
25001
+ if (typeof content === "string") return content;
25002
+ if (!Array.isArray(content)) return "";
25003
+ return content.map(
25004
+ (b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : ""
25005
+ ).join("");
25006
+ }
24828
25007
  function startFollower(opts) {
24829
25008
  const abort = new AbortController();
24830
25009
  const { entry, tag } = opts;
@@ -24867,6 +25046,8 @@ function startFollower(opts) {
24867
25046
  const decoder = new TextDecoder("utf8");
24868
25047
  let buffered = "";
24869
25048
  const fileDelivery = opts.fileDelivery ?? null;
25049
+ const captureVerdicts = typeof fileDelivery?.onFileToolResult === "function";
25050
+ const awaitingVerdict = /* @__PURE__ */ new Set();
24870
25051
  let firedFileTools = [];
24871
25052
  let suppressing = opts.suppressResumeReplay === true;
24872
25053
  let discardedTurns = 0;
@@ -24900,6 +25081,28 @@ function startFollower(opts) {
24900
25081
  continue;
24901
25082
  }
24902
25083
  if (event.type === "user") {
25084
+ if (captureVerdicts && awaitingVerdict.size > 0 && !suppressing) {
25085
+ const umsg = event.message;
25086
+ if (umsg && Array.isArray(umsg.content)) {
25087
+ for (const block of umsg.content) {
25088
+ if (block?.type !== "tool_result") continue;
25089
+ const id = block.tool_use_id;
25090
+ if (typeof id !== "string" || !awaitingVerdict.has(id)) continue;
25091
+ awaitingVerdict.delete(id);
25092
+ try {
25093
+ await fileDelivery.onFileToolResult({
25094
+ toolUseId: id,
25095
+ isError: block.is_error === true,
25096
+ text: toolResultText(block.content)
25097
+ });
25098
+ } catch (err) {
25099
+ console.error(
25100
+ `${tag} file-delivery-error sessionId=${sid} phase=tool-result message=${err instanceof Error ? err.message : String(err)}`
25101
+ );
25102
+ }
25103
+ }
25104
+ }
25105
+ }
24903
25106
  entry.pendingTurnText = "";
24904
25107
  firedFileTools = [];
24905
25108
  continue;
@@ -24921,8 +25124,10 @@ function startFollower(opts) {
24921
25124
  continue;
24922
25125
  }
24923
25126
  firedFileTools.push(block.name);
25127
+ const toolUseId = typeof block.id === "string" ? block.id : "";
25128
+ if (captureVerdicts && toolUseId) awaitingVerdict.add(toolUseId);
24924
25129
  try {
24925
- await fileDelivery.onFileToolUse({ toolName: block.name, input: block.input });
25130
+ await fileDelivery.onFileToolUse({ toolName: block.name, toolUseId, input: block.input });
24926
25131
  } catch (err) {
24927
25132
  console.error(
24928
25133
  `${tag} file-delivery-error sessionId=${sid} tool=${block.name} message=${err instanceof Error ? err.message : String(err)}`
@@ -24944,9 +25149,10 @@ function startFollower(opts) {
24944
25149
  if (flush.trim()) {
24945
25150
  await fanOut(entry.subscribers, flush, opts.onError, tag);
24946
25151
  }
24947
- if (fileDelivery && firedFileTools.length > 0) {
25152
+ if (fileDelivery && (firedFileTools.length > 0 || captureVerdicts)) {
24948
25153
  const fired = firedFileTools;
24949
25154
  firedFileTools = [];
25155
+ awaitingVerdict.clear();
24950
25156
  try {
24951
25157
  fileDelivery.onTurnEnd(fired);
24952
25158
  } catch (err) {
@@ -24984,28 +25190,55 @@ async function fanOut(subscribers, text, onError, tag) {
24984
25190
  );
24985
25191
  }
24986
25192
 
25193
+ // app/lib/webchat/file-delivery.ts
25194
+ import { realpathSync as realpathSync8 } from "fs";
25195
+ import { resolve as resolve32 } from "path";
25196
+
24987
25197
  // app/lib/channel-pty-bridge/file-delivery.ts
24988
25198
  var SEND_USER_FILE = "SendUserFile";
24989
25199
  function makeFileDelivery(opts) {
24990
- const { entry, tag, channel, sendFile } = opts;
25200
+ const { entry, tag, channel, sendFile, deferUntilVerdict = false } = opts;
24991
25201
  let failedFiles = [];
24992
25202
  let attempts = 0;
24993
- return {
25203
+ const pending = /* @__PURE__ */ new Map();
25204
+ function parseInput(input) {
25205
+ const i = input ?? {};
25206
+ return {
25207
+ // A non-array `files` yields zero files. This is the observed refused-call
25208
+ // shape: with the tool absent from the enabled set its schema is absent
25209
+ // from the request, so the model emits a JSON-encoded string. Never parse
25210
+ // it back — the harness refused the call.
25211
+ files: Array.isArray(i.files) ? i.files.filter((f) => typeof f === "string") : [],
25212
+ caption: typeof i.caption === "string" ? i.caption : void 0
25213
+ };
25214
+ }
25215
+ async function forward(call) {
25216
+ for (let i = 0; i < call.files.length; i++) {
25217
+ attempts++;
25218
+ const result = await sendFile(call.files[i], i === 0 ? call.caption : void 0);
25219
+ const name = call.files[i].split("/").pop() ?? call.files[i];
25220
+ console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
25221
+ if (!result.ok) failedFiles.push(call.files[i]);
25222
+ }
25223
+ }
25224
+ function unreconciledCall(reason, files, detail) {
25225
+ const d = detail ? ` detail="${detail.replace(/\s+/g, " ").slice(0, 120)}"` : "";
25226
+ console.error(
25227
+ `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${entry.sessionId.slice(0, 8)} tool=${SEND_USER_FILE} reason=${reason} files=${files}${d}`
25228
+ );
25229
+ }
25230
+ const handler = {
24994
25231
  isFileDeliveryTool(toolName) {
24995
25232
  return toolName === SEND_USER_FILE;
24996
25233
  },
24997
25234
  async onFileToolUse(use) {
24998
25235
  if (use.toolName !== SEND_USER_FILE) return;
24999
- const input = use.input ?? {};
25000
- const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
25001
- const caption = typeof input.caption === "string" ? input.caption : void 0;
25002
- for (let i = 0; i < files.length; i++) {
25003
- attempts++;
25004
- const result = await sendFile(files[i], i === 0 ? caption : void 0);
25005
- const name = files[i].split("/").pop() ?? files[i];
25006
- console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
25007
- if (!result.ok) failedFiles.push(files[i]);
25236
+ const call = parseInput(use.input);
25237
+ if (deferUntilVerdict) {
25238
+ pending.set(use.toolUseId, call);
25239
+ return;
25008
25240
  }
25241
+ await forward(call);
25009
25242
  },
25010
25243
  onTurnEnd(firedTools) {
25011
25244
  const failed = failedFiles;
@@ -25018,6 +25251,11 @@ function makeFileDelivery(opts) {
25018
25251
  `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
25019
25252
  );
25020
25253
  }
25254
+ if (deferUntilVerdict) {
25255
+ for (const call of pending.values()) unreconciledCall("no-verdict", call.files.length);
25256
+ pending.clear();
25257
+ return;
25258
+ }
25021
25259
  if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
25022
25260
  console.error(
25023
25261
  `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
@@ -25025,13 +25263,123 @@ function makeFileDelivery(opts) {
25025
25263
  }
25026
25264
  }
25027
25265
  };
25266
+ if (deferUntilVerdict) {
25267
+ handler.onFileToolResult = async (result) => {
25268
+ const call = pending.get(result.toolUseId);
25269
+ if (!call) return;
25270
+ pending.delete(result.toolUseId);
25271
+ if (result.isError) {
25272
+ unreconciledCall("harness-refused", call.files.length, result.text);
25273
+ return;
25274
+ }
25275
+ if (call.files.length === 0) {
25276
+ unreconciledCall("no-files", 0);
25277
+ return;
25278
+ }
25279
+ await forward(call);
25280
+ };
25281
+ }
25282
+ return handler;
25283
+ }
25284
+
25285
+ // app/lib/webchat/file-delivery.ts
25286
+ var TAG41 = "[webchat-adaptor]";
25287
+ function platformRoot2() {
25288
+ return process.env.MAXY_PLATFORM_ROOT || "";
25289
+ }
25290
+ function makeWebchatSendFile(entry) {
25291
+ return async (filePath) => {
25292
+ if (!entry.accountId) {
25293
+ console.error(`${TAG41} file-delivery reject reason=no-account sender=${entry.senderId}`);
25294
+ return { ok: false, error: "no-account" };
25295
+ }
25296
+ const accountDir = resolve32(platformRoot2(), "..", "data/accounts", entry.accountId);
25297
+ try {
25298
+ const resolved = realpathSync8(filePath);
25299
+ const accountResolved = realpathSync8(accountDir);
25300
+ if (!resolved.startsWith(accountResolved + "/")) {
25301
+ console.error(`${TAG41} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
25302
+ return { ok: false, error: "outside-account" };
25303
+ }
25304
+ return { ok: true };
25305
+ } catch (err) {
25306
+ const code = err.code;
25307
+ console.error(
25308
+ `${TAG41} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
25309
+ );
25310
+ return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
25311
+ }
25312
+ };
25313
+ }
25314
+ function makeWebchatFileDelivery(entry) {
25315
+ return makeFileDelivery({
25316
+ entry,
25317
+ tag: TAG41,
25318
+ channel: "webchat",
25319
+ sendFile: makeWebchatSendFile(entry),
25320
+ deferUntilVerdict: true
25321
+ });
25322
+ }
25323
+
25324
+ // app/lib/webchat/gateway/native-file-follower.ts
25325
+ function startWebchatNativeFileFollower(input) {
25326
+ const entry = {
25327
+ sessionId: input.sessionId,
25328
+ role: "admin",
25329
+ channel: "webchat",
25330
+ accountId: input.accountId,
25331
+ senderId: input.key,
25332
+ agentSlug: "admin",
25333
+ lastInboundAt: Date.now(),
25334
+ writeChain: Promise.resolve(),
25335
+ subscribers: /* @__PURE__ */ new Set(),
25336
+ pendingTurnText: "",
25337
+ followerAbort: null,
25338
+ followerRunning: false,
25339
+ sliceToken: "",
25340
+ personId: null,
25341
+ name: null,
25342
+ // Webchat renders from the transcript; there is no separate reply target.
25343
+ replyTarget: null
25344
+ };
25345
+ return startFollower({
25346
+ entry,
25347
+ tag: "[webchat-adaptor]",
25348
+ fileDelivery: makeWebchatFileDelivery(entry),
25349
+ // A resumed session's JSONL already holds prior SendUserFile tool_uses;
25350
+ // suppress replay so historical calls are not re-reconciled on attach.
25351
+ suppressResumeReplay: true,
25352
+ onClose: input.onClose ?? (() => {
25353
+ })
25354
+ });
25355
+ }
25356
+
25357
+ // app/lib/webchat/gateway/public-spawn-request.ts
25358
+ function buildPublicWebchatSpawnRequest(input) {
25359
+ const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
25360
+ return {
25361
+ senderId: input.key,
25362
+ role: "public",
25363
+ channel: "webchat",
25364
+ accountId: input.accountId,
25365
+ agentSlug,
25366
+ model: SONNET_MODEL,
25367
+ // sliceToken/personId are present only for a gated visitor; open-mode
25368
+ // public agents carry neither (mirrors the bridge's open-mode key).
25369
+ ...sliceToken ? { sliceToken } : {},
25370
+ ...personId ? { personId } : {},
25371
+ ...visitorId ? { visitorId } : {},
25372
+ ...input.previousContext ? { previousContext: input.previousContext } : {},
25373
+ name,
25374
+ webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
25375
+ };
25028
25376
  }
25029
25377
 
25030
25378
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
25031
- var TAG41 = "[whatsapp-adaptor]";
25379
+ var TAG42 = "[whatsapp-adaptor]";
25032
25380
  var SEND_USER_FILE2 = "SendUserFile";
25033
25381
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
25034
- function platformRoot2() {
25382
+ function platformRoot3() {
25035
25383
  return process.env.MAXY_PLATFORM_ROOT || "";
25036
25384
  }
25037
25385
  function makeWhatsAppSendFile(entry, maxyAccountId) {
@@ -25042,11 +25390,11 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25042
25390
  caption,
25043
25391
  accountId: entry.accountId,
25044
25392
  maxyAccountId,
25045
- platformRoot: platformRoot2()
25393
+ platformRoot: platformRoot3()
25046
25394
  });
25047
25395
  if (result.ok) return { ok: true };
25048
25396
  console.error(
25049
- `${TAG41} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25397
+ `${TAG42} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25050
25398
  );
25051
25399
  return { ok: false, error: result.error };
25052
25400
  };
@@ -25054,7 +25402,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25054
25402
  function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25055
25403
  const shared = makeFileDelivery({
25056
25404
  entry,
25057
- tag: TAG41,
25405
+ tag: TAG42,
25058
25406
  channel: "whatsapp",
25059
25407
  sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
25060
25408
  });
@@ -25089,7 +25437,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25089
25437
  if (!delivered) {
25090
25438
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
25091
25439
  console.error(
25092
- `${TAG41} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25440
+ `${TAG42} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25093
25441
  );
25094
25442
  }
25095
25443
  }
@@ -25480,35 +25828,35 @@ function buildTelegramSpawnRequest(input) {
25480
25828
  }
25481
25829
 
25482
25830
  // app/lib/telegram/outbound/send-document.ts
25483
- import { realpathSync as realpathSync7 } from "fs";
25831
+ import { realpathSync as realpathSync9 } from "fs";
25484
25832
  import { readFile as readFile7, stat as stat7 } from "fs/promises";
25485
- import { resolve as resolve32, basename as basename11 } from "path";
25486
- var TAG42 = "[telegram:outbound]";
25833
+ import { resolve as resolve33, basename as basename11 } from "path";
25834
+ var TAG43 = "[telegram:outbound]";
25487
25835
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
25488
25836
  async function sendTelegramDocument(input) {
25489
- const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
25837
+ const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
25490
25838
  if (!botToken || !filePath) {
25491
25839
  return { ok: false, status: 400, error: "Missing required fields: botToken, filePath" };
25492
25840
  }
25493
- if (!maxyAccountId || !platformRoot4) {
25841
+ if (!maxyAccountId || !platformRoot5) {
25494
25842
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
25495
25843
  }
25496
- const accountDir = resolve32(platformRoot4, "..", "data/accounts", maxyAccountId);
25844
+ const accountDir = resolve33(platformRoot5, "..", "data/accounts", maxyAccountId);
25497
25845
  let resolvedPath;
25498
25846
  try {
25499
- resolvedPath = realpathSync7(filePath);
25500
- const accountResolved = realpathSync7(accountDir);
25847
+ resolvedPath = realpathSync9(filePath);
25848
+ const accountResolved = realpathSync9(accountDir);
25501
25849
  if (!resolvedPath.startsWith(accountResolved + "/")) {
25502
- console.error(`${TAG42} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25850
+ console.error(`${TAG43} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25503
25851
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
25504
25852
  }
25505
25853
  } catch (err) {
25506
25854
  const code = err.code;
25507
25855
  if (code === "ENOENT") {
25508
- console.error(`${TAG42} document ENOENT path=${filePath}`);
25856
+ console.error(`${TAG43} document ENOENT path=${filePath}`);
25509
25857
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
25510
25858
  }
25511
- console.error(`${TAG42} document path error: ${String(err)}`);
25859
+ console.error(`${TAG43} document path error: ${String(err)}`);
25512
25860
  return { ok: false, status: 500, error: String(err) };
25513
25861
  }
25514
25862
  const fileStat = await stat7(resolvedPath);
@@ -25541,14 +25889,14 @@ async function sendTelegramDocument(input) {
25541
25889
  error = e instanceof Error ? e.message : String(e);
25542
25890
  }
25543
25891
  console.error(
25544
- `${TAG42} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25892
+ `${TAG43} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25545
25893
  );
25546
25894
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
25547
25895
  }
25548
25896
 
25549
25897
  // app/lib/telegram/outbound/file-delivery.ts
25550
- var TAG43 = "[telegram:outbound]";
25551
- function platformRoot3() {
25898
+ var TAG44 = "[telegram:outbound]";
25899
+ function platformRoot4() {
25552
25900
  return process.env.MAXY_PLATFORM_ROOT || "";
25553
25901
  }
25554
25902
  function makeTelegramSendFile(entry) {
@@ -25559,11 +25907,11 @@ function makeTelegramSendFile(entry) {
25559
25907
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
25560
25908
  }
25561
25909
  if (!botToken) {
25562
- console.error(`${TAG43} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25910
+ console.error(`${TAG44} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25563
25911
  return { ok: false, error: "no-bot-token" };
25564
25912
  }
25565
25913
  if (entry.replyTarget == null) {
25566
- console.error(`${TAG43} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25914
+ console.error(`${TAG44} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25567
25915
  return { ok: false, error: "no-reply-target" };
25568
25916
  }
25569
25917
  const result = await sendTelegramDocument({
@@ -25572,7 +25920,7 @@ function makeTelegramSendFile(entry) {
25572
25920
  filePath,
25573
25921
  caption,
25574
25922
  maxyAccountId: entry.accountId,
25575
- platformRoot: platformRoot3()
25923
+ platformRoot: platformRoot4()
25576
25924
  });
25577
25925
  return result.ok ? { ok: true } : { ok: false, error: result.error };
25578
25926
  };
@@ -25580,7 +25928,7 @@ function makeTelegramSendFile(entry) {
25580
25928
  function makeTelegramFileDelivery(entry) {
25581
25929
  return makeFileDelivery({
25582
25930
  entry,
25583
- tag: TAG43,
25931
+ tag: TAG44,
25584
25932
  channel: "telegram",
25585
25933
  sendFile: makeTelegramSendFile(entry)
25586
25934
  });
@@ -25620,17 +25968,17 @@ function startTelegramNativeFileFollower(input) {
25620
25968
  }
25621
25969
 
25622
25970
  // server/telegram-descriptor.ts
25623
- import { resolve as resolve33, join as join37 } from "path";
25971
+ import { resolve as resolve34, join as join37 } from "path";
25624
25972
  function telegramGatewayUrl() {
25625
25973
  return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
25626
25974
  }
25627
25975
  function telegramServerPath() {
25628
- return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/telegram-channel/dist/server.js");
25976
+ return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve34(process.env.MAXY_PLATFORM_ROOT ?? join37(__dirname, ".."), "services/telegram-channel/dist/server.js");
25629
25977
  }
25630
25978
 
25631
25979
  // app/lib/channel-pty-bridge/public-session-end-review.ts
25632
- import { randomUUID as randomUUID15 } from "crypto";
25633
- var TAG44 = "[public-session-review]";
25980
+ import { randomUUID as randomUUID16 } from "crypto";
25981
+ var TAG45 = "[public-session-review]";
25634
25982
  var CONSUMED_CAP = 500;
25635
25983
  var consumed = /* @__PURE__ */ new Set();
25636
25984
  function consumeOnce(sessionId) {
@@ -25657,7 +26005,7 @@ async function fetchJsonl(sessionId) {
25657
26005
  return await res.text();
25658
26006
  } catch (err) {
25659
26007
  console.error(
25660
- `${TAG44} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
26008
+ `${TAG45} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
25661
26009
  );
25662
26010
  return null;
25663
26011
  }
@@ -25681,7 +26029,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
25681
26029
  const res = await fetch(url);
25682
26030
  if (!res.ok) {
25683
26031
  console.error(
25684
- `${TAG44} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
26032
+ `${TAG45} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
25685
26033
  );
25686
26034
  return [];
25687
26035
  }
@@ -25689,7 +26037,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
25689
26037
  return Array.isArray(body.writes) ? body.writes : [];
25690
26038
  } catch (err) {
25691
26039
  console.error(
25692
- `${TAG44} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
26040
+ `${TAG45} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
25693
26041
  );
25694
26042
  return [];
25695
26043
  }
@@ -25717,8 +26065,8 @@ function composeInitialMessage(input) {
25717
26065
  ].join("\n");
25718
26066
  }
25719
26067
  async function dispatchReviewer(input, initialMessage) {
25720
- const sessionId = randomUUID15();
25721
- console.log(`${TAG44} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
26068
+ const sessionId = randomUUID16();
26069
+ console.log(`${TAG45} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
25722
26070
  const spawned = await managerRcSpawn({
25723
26071
  sessionId,
25724
26072
  initialMessage,
@@ -25738,21 +26086,21 @@ async function firePublicSessionEndReview(input) {
25738
26086
  const dispatchedAt = Date.now();
25739
26087
  if (consumed.has(input.sessionId)) {
25740
26088
  console.log(
25741
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26089
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
25742
26090
  );
25743
26091
  return;
25744
26092
  }
25745
26093
  const jsonl = await fetchJsonl(input.sessionId);
25746
26094
  if (!jsonl) {
25747
26095
  console.log(
25748
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
26096
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
25749
26097
  );
25750
26098
  return;
25751
26099
  }
25752
26100
  const operatorTurns = countOperatorTurns(jsonl);
25753
26101
  if (operatorTurns === 0) {
25754
26102
  console.log(
25755
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
26103
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
25756
26104
  );
25757
26105
  return;
25758
26106
  }
@@ -25766,19 +26114,19 @@ async function firePublicSessionEndReview(input) {
25766
26114
  });
25767
26115
  if (!consumeOnce(input.sessionId)) {
25768
26116
  console.log(
25769
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26117
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
25770
26118
  );
25771
26119
  return;
25772
26120
  }
25773
26121
  const dispatched = await dispatchReviewer(input, initialMessage);
25774
26122
  if (!dispatched.ok) {
25775
26123
  console.error(
25776
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
26124
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
25777
26125
  );
25778
26126
  return;
25779
26127
  }
25780
26128
  console.log(
25781
- `${TAG44} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
26129
+ `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
25782
26130
  );
25783
26131
  }
25784
26132
 
@@ -25888,7 +26236,7 @@ function broadcastAdminShutdown(reason) {
25888
26236
  // ../lib/entitlement/src/index.ts
25889
26237
  import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
25890
26238
  import { existsSync as existsSync35, readFileSync as readFileSync37, statSync as statSync13 } from "fs";
25891
- import { resolve as resolve34 } from "path";
26239
+ import { resolve as resolve35 } from "path";
25892
26240
 
25893
26241
  // ../lib/entitlement/src/canonicalize.ts
25894
26242
  function canonicalize(value) {
@@ -25923,7 +26271,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
25923
26271
  var GRACE_DAYS = 7;
25924
26272
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
25925
26273
  function pubkeyPath(brand) {
25926
- return resolve34(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
26274
+ return resolve35(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
25927
26275
  }
25928
26276
  var memo = null;
25929
26277
  function memoKey(mtimeMs, account) {
@@ -25935,7 +26283,7 @@ function resolveEntitlement(brand, account) {
25935
26283
  if (brand.commercialMode !== true) {
25936
26284
  return logResolved(implicitTrust(account), null);
25937
26285
  }
25938
- const entitlementPath = resolve34(brand.configDir, "entitlement.json");
26286
+ const entitlementPath = resolve35(brand.configDir, "entitlement.json");
25939
26287
  if (!existsSync35(entitlementPath)) {
25940
26288
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
25941
26289
  }
@@ -26195,6 +26543,7 @@ function isPublicHost(host) {
26195
26543
  }
26196
26544
  var app62 = new Hono();
26197
26545
  var nativeFileFollowers = /* @__PURE__ */ new Map();
26546
+ var webchatFileFollowers = /* @__PURE__ */ new Map();
26198
26547
  async function fetchAccountStandingRules(accountId) {
26199
26548
  const session = getSession();
26200
26549
  try {
@@ -26232,7 +26581,7 @@ async function recordPassiveDispatch(input) {
26232
26581
  var waGateway = new WaGateway({
26233
26582
  fetchStandingRules: fetchAccountStandingRules,
26234
26583
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
26235
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
26584
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
26236
26585
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
26237
26586
  // path-validation scope) is the sender's effective SESSION account, resolved
26238
26587
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -26247,7 +26596,7 @@ var waGateway = new WaGateway({
26247
26596
  caption,
26248
26597
  accountId,
26249
26598
  maxyAccountId,
26250
- platformRoot: resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."))
26599
+ platformRoot: resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, ".."))
26251
26600
  });
26252
26601
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
26253
26602
  },
@@ -26405,6 +26754,20 @@ var webchatGateway = new WebchatGateway({
26405
26754
  console.error(`[webchat:inbound] op=spawn-failed key=${key} error=${result.error} status=${result.status}`);
26406
26755
  return;
26407
26756
  }
26757
+ const priorWebchatFollower = webchatFileFollowers.get(key);
26758
+ if (priorWebchatFollower) priorWebchatFollower.abort();
26759
+ let wac;
26760
+ wac = startWebchatNativeFileFollower({
26761
+ sessionId: result.sessionId,
26762
+ key,
26763
+ // The session's own resolved account (house or sub-account) — the
26764
+ // file-path validation scope, exactly as passed to the spawn above.
26765
+ accountId,
26766
+ onClose: () => {
26767
+ if (webchatFileFollowers.get(key) === wac) webchatFileFollowers.delete(key);
26768
+ }
26769
+ });
26770
+ webchatFileFollowers.set(key, wac);
26408
26771
  return { sessionId: result.sessionId };
26409
26772
  },
26410
26773
  // Task-962 — the recovery pass asks the manager to respawn a wedged already-
@@ -26866,8 +27229,8 @@ app62.get("/agent-assets/:slug/:filename", (c) => {
26866
27229
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
26867
27230
  return c.text("Not found", 404);
26868
27231
  }
26869
- const filePath = resolve35(account.accountDir, "agents", slug, "assets", filename);
26870
- const expectedDir = resolve35(account.accountDir, "agents", slug, "assets");
27232
+ const filePath = resolve36(account.accountDir, "agents", slug, "assets", filename);
27233
+ const expectedDir = resolve36(account.accountDir, "agents", slug, "assets");
26871
27234
  if (!filePath.startsWith(expectedDir + "/")) {
26872
27235
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
26873
27236
  return c.text("Forbidden", 403);
@@ -26896,8 +27259,8 @@ app62.get("/generated/:filename", (c) => {
26896
27259
  console.error(`[generated] serve file=${filename} status=404`);
26897
27260
  return c.text("Not found", 404);
26898
27261
  }
26899
- const filePath = resolve35(account.accountDir, "generated", filename);
26900
- const expectedDir = resolve35(account.accountDir, "generated");
27262
+ const filePath = resolve36(account.accountDir, "generated", filename);
27263
+ const expectedDir = resolve36(account.accountDir, "generated");
26901
27264
  if (!filePath.startsWith(expectedDir + "/")) {
26902
27265
  console.error(`[generated] serve file=${filename} status=403`);
26903
27266
  return c.text("Forbidden", 403);
@@ -26951,11 +27314,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
26951
27314
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
26952
27315
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
26953
27316
  var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
26954
- (p) => existsSync36(resolve35(process.cwd(), "public", p.replace(/^\//, "")))
27317
+ (p) => existsSync36(resolve36(process.cwd(), "public", p.replace(/^\//, "")))
26955
27318
  );
26956
27319
  var SW_SOURCE = (() => {
26957
27320
  try {
26958
- return readFileSync38(resolve35(process.cwd(), "public", "sw.js"), "utf-8");
27321
+ return readFileSync38(resolve36(process.cwd(), "public", "sw.js"), "utf-8");
26959
27322
  } catch {
26960
27323
  return null;
26961
27324
  }
@@ -27006,7 +27369,7 @@ var clientErrorReporterScript = `<script>
27006
27369
  function cachedHtml(file) {
27007
27370
  let html = htmlCache.get(file);
27008
27371
  if (!html) {
27009
- html = readFileSync38(resolve35(process.cwd(), "public", file), "utf-8");
27372
+ html = readFileSync38(resolve36(process.cwd(), "public", file), "utf-8");
27010
27373
  const productNameEsc = escapeHtml(BRAND.productName);
27011
27374
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
27012
27375
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -27124,7 +27487,7 @@ app62.use("/vnc-popout.html", logViewerFetch);
27124
27487
  app62.get("/vnc-popout.html", (c) => {
27125
27488
  let html = htmlCache.get("vnc-popout.html");
27126
27489
  if (!html) {
27127
- html = readFileSync38(resolve35(process.cwd(), "public", "vnc-popout.html"), "utf-8");
27490
+ html = readFileSync38(resolve36(process.cwd(), "public", "vnc-popout.html"), "utf-8");
27128
27491
  const name = escapeHtml(BRAND.productName);
27129
27492
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
27130
27493
  html = html.replace("</head>", ` ${brandScript}
@@ -27257,7 +27620,7 @@ var httpServer = serve({ fetch: app62.fetch, port, hostname });
27257
27620
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27258
27621
  {
27259
27622
  const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27260
- const reconcileScript = resolve35(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
27623
+ const reconcileScript = resolve36(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
27261
27624
  const RECONCILE_INTERVAL_MS = 12e4;
27262
27625
  const runReconcile = () => {
27263
27626
  if (!existsSync36(reconcileScript)) return;
@@ -27279,7 +27642,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27279
27642
  }
27280
27643
  {
27281
27644
  const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27282
- const outlookScript = resolve35(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
27645
+ const outlookScript = resolve36(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
27283
27646
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
27284
27647
  const runOutlookComplete = () => {
27285
27648
  if (!existsSync36(outlookScript)) return;
@@ -27301,7 +27664,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27301
27664
  }
27302
27665
  {
27303
27666
  const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27304
- const strandedAccountsDir = resolve35(auditRoot, "..", "data/accounts");
27667
+ const strandedAccountsDir = resolve36(auditRoot, "..", "data/accounts");
27305
27668
  const STRANDED_AUDIT_INTERVAL_MS = 3e5;
27306
27669
  const STRANDED_AGE_MS = 16 * 6e4;
27307
27670
  const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -27311,7 +27674,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27311
27674
  const now = Date.now();
27312
27675
  for (const name of readdirSync21(strandedAccountsDir)) {
27313
27676
  if (!STRANDED_UUID_RE.test(name)) continue;
27314
- const pendingPath2 = resolve35(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
27677
+ const pendingPath2 = resolve36(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
27315
27678
  if (!existsSync36(pendingPath2)) continue;
27316
27679
  const ageMs = now - statSync14(pendingPath2).mtimeMs;
27317
27680
  if (ageMs > STRANDED_AGE_MS) {
@@ -27339,10 +27702,19 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27339
27702
  auditFirst.unref();
27340
27703
  const auditLoop = setInterval(runStorageAuditSafe, STORAGE_AUDIT_INTERVAL_MS);
27341
27704
  auditLoop.unref();
27705
+ const runPagesAuditSafe = () => {
27706
+ runPagesAudit(auditPlatformRoot).catch(
27707
+ (err) => console.error(`[pages-audit] error="${err.message}"`)
27708
+ );
27709
+ };
27710
+ const pagesAuditFirst = setTimeout(runPagesAuditSafe, 35e3);
27711
+ pagesAuditFirst.unref();
27712
+ const pagesAuditLoop = setInterval(runPagesAuditSafe, STORAGE_AUDIT_INTERVAL_MS);
27713
+ pagesAuditLoop.unref();
27342
27714
  }
27343
27715
  {
27344
27716
  const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27345
- const publishScript = resolve35(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
27717
+ const publishScript = resolve36(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
27346
27718
  const PUBLISH_INTERVAL_MS = 3e5;
27347
27719
  const currentAccount = () => {
27348
27720
  try {
@@ -27371,7 +27743,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27371
27743
  }
27372
27744
  };
27373
27745
  const auditSnapshotAge = (account) => {
27374
- const availPath = resolve35(account.accountDir, "calendar-availability.json");
27746
+ const availPath = resolve36(account.accountDir, "calendar-availability.json");
27375
27747
  let hasBookingSite = false;
27376
27748
  try {
27377
27749
  if (existsSync36(availPath)) {
@@ -27381,7 +27753,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27381
27753
  } catch {
27382
27754
  }
27383
27755
  if (!hasBookingSite) return;
27384
- const statePath = resolve35(account.accountDir, "state", "booking-availability", "last-publish.json");
27756
+ const statePath = resolve36(account.accountDir, "state", "booking-availability", "last-publish.json");
27385
27757
  let lastSuccessAt = null;
27386
27758
  if (existsSync36(statePath)) {
27387
27759
  try {
@@ -27410,7 +27782,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
27410
27782
  startTimeEntryCensus(() => getSession());
27411
27783
  {
27412
27784
  const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..");
27413
- const auditScript = resolve35(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
27785
+ const auditScript = resolve36(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
27414
27786
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
27415
27787
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
27416
27788
  const runConnectorAudit = () => {
@@ -27679,7 +28051,7 @@ if (bootAccountConfig?.whatsapp) {
27679
28051
  }
27680
28052
  init({
27681
28053
  configDir: configDirForWhatsApp,
27682
- platformRoot: resolve35(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..")),
28054
+ platformRoot: resolve36(process.env.MAXY_PLATFORM_ROOT ?? join38(__dirname, "..")),
27683
28055
  accountConfig: bootAccountConfig,
27684
28056
  onMessage: async (msg) => {
27685
28057
  if (msg.isOwnerMirror) {