@rubytech/create-maxy-code 0.1.330 → 0.1.332

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 (156) hide show
  1. package/dist/index.js +19 -2
  2. package/package.json +1 -1
  3. package/payload/platform/config/brand.json +1 -0
  4. package/payload/platform/lib/account-enumeration/dist/index.d.ts +33 -0
  5. package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/account-enumeration/dist/index.js +76 -0
  7. package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -1
  8. package/payload/platform/lib/account-enumeration/src/index.ts +78 -0
  9. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +24 -24
  10. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +109 -75
  11. package/payload/platform/lib/mcp-spawn-tee/dist/index.js.map +1 -1
  12. package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +56 -19
  13. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +112 -77
  14. package/payload/platform/neo4j/schema.cypher +58 -4
  15. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  16. package/payload/platform/plugins/admin/PLUGIN.md +13 -1
  17. package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +109 -75
  18. package/payload/platform/plugins/admin/mcp/dist/index.js +102 -0
  19. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  20. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts +2 -0
  21. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts.map +1 -0
  22. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js +57 -0
  23. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js.map +1 -0
  24. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +34 -0
  25. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -0
  26. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +78 -0
  27. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -0
  28. package/payload/platform/plugins/admin/skills/plainly/SKILL.md +7 -0
  29. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  30. package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +109 -75
  31. package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +109 -75
  32. package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +109 -75
  33. package/payload/platform/plugins/contacts/mcp/dist/index.js +1 -1
  34. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  35. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts +2 -0
  36. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts.map +1 -0
  37. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js +71 -0
  38. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js.map +1 -0
  39. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.d.ts +2 -0
  40. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.d.ts.map +1 -0
  41. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.js +112 -0
  42. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-create.test.js.map +1 -0
  43. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.d.ts +2 -0
  44. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.d.ts.map +1 -0
  45. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.js +102 -0
  46. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/group-manage.test.js.map +1 -0
  47. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +1 -0
  48. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
  49. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +10 -3
  50. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
  51. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.d.ts.map +1 -1
  52. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js +7 -2
  53. package/payload/platform/plugins/contacts/mcp/dist/tools/group-create.js.map +1 -1
  54. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.d.ts.map +1 -1
  55. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js +5 -4
  56. package/payload/platform/plugins/contacts/mcp/dist/tools/group-manage.js.map +1 -1
  57. package/payload/platform/plugins/contacts/mcp/package.json +4 -2
  58. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  59. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +109 -75
  60. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  61. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  62. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +109 -75
  63. package/payload/platform/plugins/memory/mcp/dist/index.js +3 -1
  64. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  65. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js +39 -0
  66. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js.map +1 -1
  67. package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.d.ts.map +1 -1
  68. package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js +13 -1
  69. package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js.map +1 -1
  70. package/payload/platform/plugins/memory/references/schema-estate-agent.md +17 -5
  71. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +109 -75
  72. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +109 -75
  73. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +109 -75
  74. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +109 -75
  75. package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +109 -75
  76. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +109 -75
  77. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +109 -75
  78. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +109 -75
  79. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +109 -75
  80. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +40 -0
  81. package/payload/platform/scripts/__tests__/sweep-durability.test.sh +103 -0
  82. package/payload/platform/scripts/lib/provision-account-dir.sh +319 -0
  83. package/payload/platform/scripts/lib/resolve-account-dir.sh +79 -132
  84. package/payload/platform/scripts/seed-neo4j.sh +18 -0
  85. package/payload/platform/scripts/setup-account.sh +28 -361
  86. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  87. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +4 -0
  88. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/config.d.ts +18 -0
  90. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/config.js +63 -7
  92. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  93. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/http-server.js +30 -2
  95. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/index.js +16 -1
  97. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  99. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
  100. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  101. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +5 -0
  102. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +8 -0
  104. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts +6 -0
  106. package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts.map +1 -1
  107. package/payload/platform/services/claude-session-manager/dist/stuck-turn.js +17 -5
  108. package/payload/platform/services/claude-session-manager/dist/stuck-turn.js.map +1 -1
  109. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  110. package/payload/platform/services/webchat-channel/dist/instructions.js +25 -1
  111. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  112. package/payload/platform/services/webchat-channel/dist/notification.d.ts +17 -0
  113. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  114. package/payload/platform/services/webchat-channel/dist/notification.js +14 -0
  115. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  116. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  117. package/payload/platform/services/webchat-channel/dist/server.js +66 -2
  118. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  119. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +28 -0
  120. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -0
  121. package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -0
  122. package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -0
  123. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  124. package/payload/platform/services/whatsapp-channel/dist/notification.js +24 -1
  125. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  126. package/payload/platform/templates/account.json +2 -0
  127. package/payload/premium-plugins/writer-craft/lib/mcp-spawn-tee/index.js +109 -75
  128. package/payload/server/public/assets/AdminLoginScreens-DexSvtjD.js +1 -0
  129. package/payload/server/public/assets/AdminShell-Bf9tndNg.js +1 -0
  130. package/payload/server/public/assets/{Checkbox-1flpB9H0.js → Checkbox-D58GsKoQ.js} +1 -1
  131. package/payload/server/public/assets/{OperatorConversations-DrPqB9Ym.css → OperatorConversations-CDdp2nVn.css} +1 -1
  132. package/payload/server/public/assets/OperatorConversations-RmqANYz8.js +9 -0
  133. package/payload/server/public/assets/{admin-BWJTvagN.js → admin-CH5Vkkt4.js} +1 -1
  134. package/payload/server/public/assets/{browser-XM5e-fn1.js → browser-BwDtzSlr.js} +1 -1
  135. package/payload/server/public/assets/chat-3GY0LmYH.js +1 -0
  136. package/payload/server/public/assets/data-CttrzhfL.js +1 -0
  137. package/payload/server/public/assets/{graph-CIIz9Dmh.js → graph-CZSdF2jv.js} +3 -3
  138. package/payload/server/public/assets/{graph-labels-BRKvhE0e.js → graph-labels-Bi0fu8Ns.js} +1 -1
  139. package/payload/server/public/assets/operator-BEivxmRy.js +1 -0
  140. package/payload/server/public/assets/page-DUvT07SB.js +30 -0
  141. package/payload/server/public/assets/{public-BPlZnopr.js → public-B3eIlVbd.js} +1 -1
  142. package/payload/server/public/browser.html +4 -4
  143. package/payload/server/public/chat.html +5 -5
  144. package/payload/server/public/data.html +4 -4
  145. package/payload/server/public/graph.html +6 -6
  146. package/payload/server/public/index.html +6 -6
  147. package/payload/server/public/operator.html +7 -7
  148. package/payload/server/public/public.html +5 -5
  149. package/payload/server/server.js +605 -435
  150. package/payload/server/public/assets/AdminLoginScreens-BnuaXvxN.js +0 -1
  151. package/payload/server/public/assets/AdminShell-Br0DJQNu.js +0 -1
  152. package/payload/server/public/assets/OperatorConversations-C8sITz2_.js +0 -9
  153. package/payload/server/public/assets/chat-COFMxaoC.js +0 -1
  154. package/payload/server/public/assets/data-BHtJozT2.js +0 -1
  155. package/payload/server/public/assets/operator-9hbxrXKm.js +0 -1
  156. package/payload/server/public/assets/page-CXkBfOhG.js +0 -30
@@ -1293,8 +1293,8 @@ var serveStatic = (options = { root: "" }) => {
1293
1293
  };
1294
1294
 
1295
1295
  // server/index.ts
1296
- import { readFileSync as readFileSync31, existsSync as existsSync31, watchFile } from "fs";
1297
- import { resolve as resolve33, join as join28, basename as basename10 } from "path";
1296
+ import { readFileSync as readFileSync32, existsSync as existsSync31, watchFile } from "fs";
1297
+ import { resolve as resolve33, join as join29, basename as basename10 } from "path";
1298
1298
  import { homedir as homedir3 } from "os";
1299
1299
  import { monitorEventLoopDelay } from "perf_hooks";
1300
1300
 
@@ -1650,7 +1650,7 @@ async function ensureAuth() {
1650
1650
  }
1651
1651
 
1652
1652
  // server/routes/health.ts
1653
- import { existsSync as existsSync3, readFileSync as readFileSync6 } from "fs";
1653
+ import { existsSync as existsSync3, readFileSync as readFileSync7 } from "fs";
1654
1654
  import { createConnection as createConnection2 } from "net";
1655
1655
 
1656
1656
  // app/lib/network.ts
@@ -3297,6 +3297,23 @@ function enumerateValidAccountIds(accountsDir) {
3297
3297
  cache.set(accountsDir, valid);
3298
3298
  return valid;
3299
3299
  }
3300
+ function resolveHouseOrSoleAccountId(accountsDir) {
3301
+ const valid = enumerateValidAccountIds(accountsDir);
3302
+ const houses = [];
3303
+ for (const id of valid) {
3304
+ const configPath2 = resolve3(accountsDir, id, "account.json");
3305
+ try {
3306
+ const cfg = JSON.parse(readFileSync4(configPath2, "utf-8"));
3307
+ if (cfg.role === "house") houses.push(id);
3308
+ } catch {
3309
+ }
3310
+ }
3311
+ if (houses.length === 1) return houses[0];
3312
+ if (houses.length === 0 && valid.length === 1) return valid[0];
3313
+ throw new Error(
3314
+ `[account-registry] resolveHouseOrSoleAccountId: houses=${houses.length} total=${valid.length} under ${accountsDir} \u2014 expected exactly one role:"house" (or a single unlabelled account). Loud-fail rather than picking one silently.`
3315
+ );
3316
+ }
3300
3317
  function getAccountsDirFromEnv() {
3301
3318
  const root = process.env.MAXY_PLATFORM_ROOT;
3302
3319
  if (!root) {
@@ -3324,26 +3341,39 @@ var cached = null;
3324
3341
  var cachedAccountsDir = null;
3325
3342
  function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
3326
3343
  if (cached !== null && cachedAccountsDir === accountsDir) return cached;
3327
- const valid = enumerateValidAccountIds(accountsDir);
3328
- if (valid.length === 0) {
3329
- throw new Error(
3330
- `[whatsapp-persist] resolvePlatformAccountId: no platform account found under ${accountsDir} \u2014 corrupt install? Cannot stamp n.accountId without a valid UUID.`
3331
- );
3344
+ const resolved = resolveHouseOrSoleAccountId(accountsDir);
3345
+ cached = resolved;
3346
+ cachedAccountsDir = accountsDir;
3347
+ return cached;
3348
+ }
3349
+
3350
+ // app/lib/managed-service.ts
3351
+ import { join as join3 } from "path";
3352
+ import { readFileSync as readFileSync5 } from "fs";
3353
+ function isHouseManagedService(accountsDir = ACCOUNTS_DIR) {
3354
+ let houseId;
3355
+ try {
3356
+ houseId = resolvePlatformAccountId(accountsDir);
3357
+ } catch {
3358
+ return false;
3332
3359
  }
3333
- if (valid.length > 1) {
3334
- throw new Error(
3335
- `[whatsapp-persist] resolvePlatformAccountId: multiple platform accounts found under ${accountsDir} (${valid.join(", ")}) \u2014 Phase 0 invariant requires exactly one. Loud-fail rather than picking one silently.`
3360
+ try {
3361
+ const cfg = JSON.parse(
3362
+ readFileSync5(join3(accountsDir, houseId, "account.json"), "utf-8")
3336
3363
  );
3364
+ return cfg.managedService === true;
3365
+ } catch {
3366
+ return false;
3337
3367
  }
3338
- cached = valid[0];
3339
- cachedAccountsDir = accountsDir;
3340
- return cached;
3368
+ }
3369
+ function isCustomerTurnSuppressed(role, managedService) {
3370
+ return managedService === true && role !== "admin";
3341
3371
  }
3342
3372
 
3343
3373
  // app/lib/whatsapp/inbound/media.ts
3344
3374
  import { randomUUID as randomUUID3 } from "crypto";
3345
3375
  import { writeFile, mkdir } from "fs/promises";
3346
- import { join as join3 } from "path";
3376
+ import { join as join4 } from "path";
3347
3377
  import {
3348
3378
  downloadMediaMessage,
3349
3379
  downloadContentFromMessage,
@@ -3429,7 +3459,7 @@ async function downloadInboundMedia(msg, sock, opts) {
3429
3459
  await mkdir(MEDIA_DIR, { recursive: true });
3430
3460
  const ext = mimeToExt(mimetype ?? "application/octet-stream");
3431
3461
  const filename = `${randomUUID3()}.${ext}`;
3432
- const filePath = join3(MEDIA_DIR, filename);
3462
+ const filePath = join4(MEDIA_DIR, filename);
3433
3463
  await writeFile(filePath, buffer);
3434
3464
  const sizeKB = (buffer.length / 1024).toFixed(0);
3435
3465
  console.error(`${TAG9} media downloaded type=${mimetype ?? "unknown"} size=${sizeKB}KB path=${filePath}`);
@@ -4471,6 +4501,12 @@ async function handleInboundMessage(conn, msg) {
4471
4501
  `${TAG13} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (mediaResult ? ` mediaPath=${mediaResult.path}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
4472
4502
  );
4473
4503
  if (!accessResult.allowed) return;
4504
+ if (isCustomerTurnSuppressed(accessResult.agentType, isHouseManagedService())) {
4505
+ console.error(
4506
+ `[channel-house] op=inbound from=${senderPhone} accountId=${conn.platformAccountId} autoReply=suppressed`
4507
+ );
4508
+ return;
4509
+ }
4474
4510
  const sendReceipts = whatsAppConfig.accounts?.[conn.accountId]?.sendReadReceipts ?? whatsAppConfig.sendReadReceipts ?? true;
4475
4511
  if (sendReceipts && msg.key.id) {
4476
4512
  sendReadReceipt(conn.sock, remoteJid, [msg.key.id], isGroup ? senderJid : void 0);
@@ -4541,7 +4577,7 @@ async function handleInboundMessage(conn, msg) {
4541
4577
  // app/lib/vnc.ts
4542
4578
  import { spawnSync, execFileSync } from "child_process";
4543
4579
  import { createConnection } from "net";
4544
- import { mkdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
4580
+ import { mkdirSync, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
4545
4581
  import { resolve as resolve4 } from "path";
4546
4582
  var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
4547
4583
  var VNC_SCRIPT = resolve4(PLATFORM_ROOT3, "scripts/vnc.sh");
@@ -4599,7 +4635,7 @@ function discoverNativeDisplay() {
4599
4635
  const leaderPid = leaderResult.stdout?.trim();
4600
4636
  if (leaderPid) {
4601
4637
  try {
4602
- const environ = readFileSync5(`/proc/${leaderPid}/environ`, "utf8");
4638
+ const environ = readFileSync6(`/proc/${leaderPid}/environ`, "utf8");
4603
4639
  const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
4604
4640
  if (match) waylandDisplay = match.split("=")[1];
4605
4641
  } catch {
@@ -4859,7 +4895,7 @@ app.get("/", async (c) => {
4859
4895
  let pinConfigured = false;
4860
4896
  try {
4861
4897
  if (existsSync3(USERS_FILE)) {
4862
- const raw = readFileSync6(USERS_FILE, "utf-8").trim();
4898
+ const raw = readFileSync7(USERS_FILE, "utf-8").trim();
4863
4899
  if (raw) {
4864
4900
  const users = JSON.parse(raw);
4865
4901
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -5117,7 +5153,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
5117
5153
  // app/lib/stt/voice-note.ts
5118
5154
  import { writeFile as writeFile3, mkdtemp, rm } from "fs/promises";
5119
5155
  import { tmpdir } from "os";
5120
- import { join as join4 } from "path";
5156
+ import { join as join5 } from "path";
5121
5157
  var TAG14 = "[voice]";
5122
5158
  var AUDIO_MIME_TYPES = /* @__PURE__ */ new Set([
5123
5159
  "audio/ogg",
@@ -5161,9 +5197,9 @@ async function transcribeVoiceNote(file, source) {
5161
5197
  let tempDir;
5162
5198
  let tempPath;
5163
5199
  try {
5164
- tempDir = await mkdtemp(join4(tmpdir(), "voice-"));
5200
+ tempDir = await mkdtemp(join5(tmpdir(), "voice-"));
5165
5201
  const ext = audioExtension(mimeType);
5166
- tempPath = join4(tempDir, `recording${ext}`);
5202
+ tempPath = join5(tempDir, `recording${ext}`);
5167
5203
  const buffer = Buffer.from(await file.arrayBuffer());
5168
5204
  await writeFile3(tempPath, buffer);
5169
5205
  } catch (err) {
@@ -5240,7 +5276,53 @@ function composePublicSessionTitle(input) {
5240
5276
  return segments.join(" \xB7 ");
5241
5277
  }
5242
5278
 
5279
+ // app/lib/webchat/persist-customer-inbound.ts
5280
+ var TAG15 = "[channel-house]";
5281
+ async function persistCustomerWebchatInbound(input) {
5282
+ if (!input.text) return null;
5283
+ try {
5284
+ const conv = await ensureConversation(
5285
+ input.houseAccountId,
5286
+ "public",
5287
+ input.sessionKey,
5288
+ input.visitorId,
5289
+ input.agentSlug,
5290
+ void 0,
5291
+ "webchat",
5292
+ input.sessionKey
5293
+ );
5294
+ if (!conv.sessionId) {
5295
+ console.error(
5296
+ `${TAG15} op=persist-skip reason=conversation-merge-failed accountId=${input.houseAccountId} key=${input.sessionKey}`
5297
+ );
5298
+ return null;
5299
+ }
5300
+ const messageId = await persistMessage(
5301
+ conv.sessionId,
5302
+ "user",
5303
+ input.text,
5304
+ input.houseAccountId
5305
+ );
5306
+ if (!messageId) {
5307
+ console.error(
5308
+ `${TAG15} op=persist-skip reason=message-write-failed accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)}`
5309
+ );
5310
+ return null;
5311
+ }
5312
+ console.error(
5313
+ `${TAG15} op=persist accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)} messageId=${messageId.slice(0, 8)} bytes=${Buffer.byteLength(input.text, "utf8")}`
5314
+ );
5315
+ return { sessionId: conv.sessionId, messageId };
5316
+ } catch (err) {
5317
+ console.error(
5318
+ `${TAG15} op=persist-fail accountId=${input.houseAccountId} key=${input.sessionKey} reason=${err instanceof Error ? err.message : String(err)}`
5319
+ );
5320
+ return null;
5321
+ }
5322
+ }
5323
+
5243
5324
  // server/routes/chat.ts
5325
+ var MANAGED_SERVICE_ACK = "Thanks, we've received your message and someone will be in touch shortly.";
5244
5326
  var WEBCHAT_TAG = "[webchat-adaptor]";
5245
5327
  var SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
5246
5328
  function webchatTurnTimeoutMs() {
@@ -5416,6 +5498,38 @@ ${result.result.text}` : result.result.text;
5416
5498
  console.error(
5417
5499
  `${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
5418
5500
  );
5501
+ if (isCustomerTurnSuppressed("public", isHouseManagedService())) {
5502
+ if (!isGreeting) {
5503
+ await persistCustomerWebchatInbound({
5504
+ houseAccountId: account.accountId,
5505
+ sessionKey: session_key,
5506
+ text: message,
5507
+ ...agentSlug ? { agentSlug } : {},
5508
+ ...visitorId ? { visitorId } : {}
5509
+ });
5510
+ console.error(
5511
+ `[channel-house] op=inbound from=${sk} accountId=${account.accountId} autoReply=suppressed`
5512
+ );
5513
+ }
5514
+ const ackStream = new ReadableStream({
5515
+ start(controller) {
5516
+ if (!isGreeting) {
5517
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify({ text: MANAGED_SERVICE_ACK })}
5518
+
5519
+ `));
5520
+ }
5521
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
5522
+ controller.close();
5523
+ }
5524
+ });
5525
+ return new Response(ackStream, {
5526
+ headers: {
5527
+ "Content-Type": "text/event-stream",
5528
+ "Cache-Control": "no-cache",
5529
+ Connection: "keep-alive"
5530
+ }
5531
+ });
5532
+ }
5419
5533
  const readable = new ReadableStream({
5420
5534
  async start(controller) {
5421
5535
  try {
@@ -5483,23 +5597,23 @@ ${result.result.text}` : result.result.text;
5483
5597
  }
5484
5598
 
5485
5599
  // server/routes/whatsapp.ts
5486
- import { join as join7, resolve as resolve9 } from "path";
5487
- import { readdirSync as readdirSync3, readFileSync as readFileSync8, existsSync as existsSync5 } from "fs";
5600
+ import { join as join8, resolve as resolve9 } from "path";
5601
+ import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync5 } from "fs";
5488
5602
 
5489
5603
  // app/lib/whatsapp/login.ts
5490
5604
  import { randomUUID as randomUUID6 } from "crypto";
5491
5605
 
5492
5606
  // app/lib/whatsapp/config-persist.ts
5493
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
5494
- import { resolve as resolve7, join as join5 } from "path";
5495
- var TAG15 = "[whatsapp:config]";
5607
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, existsSync as existsSync4 } from "fs";
5608
+ import { resolve as resolve7, join as join6 } from "path";
5609
+ var TAG16 = "[whatsapp:config]";
5496
5610
  function configPath(accountDir) {
5497
5611
  return resolve7(accountDir, "account.json");
5498
5612
  }
5499
5613
  function readConfig(accountDir) {
5500
5614
  const path2 = configPath(accountDir);
5501
5615
  if (!existsSync4(path2)) throw new Error(`account.json not found at ${path2}`);
5502
- return JSON.parse(readFileSync7(path2, "utf-8"));
5616
+ return JSON.parse(readFileSync8(path2, "utf-8"));
5503
5617
  }
5504
5618
  function writeConfig(accountDir, config) {
5505
5619
  const path2 = configPath(accountDir);
@@ -5509,9 +5623,9 @@ function reloadManagerConfig(accountDir) {
5509
5623
  try {
5510
5624
  const config = readConfig(accountDir);
5511
5625
  reloadConfig(config);
5512
- console.error(`${TAG15} reloaded manager config`);
5626
+ console.error(`${TAG16} reloaded manager config`);
5513
5627
  } catch (err) {
5514
- console.error(`${TAG15} manager config reload failed: ${String(err)}`);
5628
+ console.error(`${TAG16} manager config reload failed: ${String(err)}`);
5515
5629
  }
5516
5630
  }
5517
5631
  var E164_PATTERN = /^\+\d{7,15}$/;
@@ -5537,25 +5651,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
5537
5651
  const adminPhones = wa.adminPhones;
5538
5652
  if (!adminPhones.includes(normalized)) {
5539
5653
  adminPhones.push(normalized);
5540
- console.error(`${TAG15} added selfPhone=${normalized} to adminPhones`);
5654
+ console.error(`${TAG16} added selfPhone=${normalized} to adminPhones`);
5541
5655
  }
5542
5656
  } else {
5543
- console.error(`${TAG15} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
5657
+ console.error(`${TAG16} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
5544
5658
  }
5545
5659
  const parsed = WhatsAppConfigSchema.safeParse(wa);
5546
5660
  if (!parsed.success) {
5547
5661
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
5548
- console.error(`${TAG15} validation failed after pairing: ${msg}`);
5662
+ console.error(`${TAG16} validation failed after pairing: ${msg}`);
5549
5663
  return { ok: false, error: `Validation failed: ${msg}` };
5550
5664
  }
5551
5665
  config.whatsapp = parsed.data;
5552
5666
  writeConfig(accountDir, config);
5553
- console.error(`${TAG15} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
5667
+ console.error(`${TAG16} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
5554
5668
  reloadManagerConfig(accountDir);
5555
5669
  return { ok: true };
5556
5670
  } catch (err) {
5557
5671
  const msg = err instanceof Error ? err.message : String(err);
5558
- console.error(`${TAG15} persist failed account=${accountId}: ${msg}`);
5672
+ console.error(`${TAG16} persist failed account=${accountId}: ${msg}`);
5559
5673
  return { ok: false, error: msg };
5560
5674
  }
5561
5675
  }
@@ -5585,12 +5699,12 @@ function addAdminPhone(accountDir, phone) {
5585
5699
  }
5586
5700
  config.whatsapp = parsed.data;
5587
5701
  writeConfig(accountDir, config);
5588
- console.error(`${TAG15} added admin phone=${normalized}`);
5702
+ console.error(`${TAG16} added admin phone=${normalized}`);
5589
5703
  reloadManagerConfig(accountDir);
5590
5704
  return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
5591
5705
  } catch (err) {
5592
5706
  const msg = err instanceof Error ? err.message : String(err);
5593
- console.error(`${TAG15} addAdminPhone failed: ${msg}`);
5707
+ console.error(`${TAG16} addAdminPhone failed: ${msg}`);
5594
5708
  return { ok: false, error: msg };
5595
5709
  }
5596
5710
  }
@@ -5618,12 +5732,12 @@ function removeAdminPhone(accountDir, phone) {
5618
5732
  }
5619
5733
  config.whatsapp = parsed.data;
5620
5734
  writeConfig(accountDir, config);
5621
- console.error(`${TAG15} removed admin phone=${normalized}`);
5735
+ console.error(`${TAG16} removed admin phone=${normalized}`);
5622
5736
  reloadManagerConfig(accountDir);
5623
5737
  return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
5624
5738
  } catch (err) {
5625
5739
  const msg = err instanceof Error ? err.message : String(err);
5626
- console.error(`${TAG15} removeAdminPhone failed: ${msg}`);
5740
+ console.error(`${TAG16} removeAdminPhone failed: ${msg}`);
5627
5741
  return { ok: false, error: msg };
5628
5742
  }
5629
5743
  }
@@ -5643,7 +5757,7 @@ function setPublicAgent(accountDir, slug) {
5643
5757
  if (!trimmed) {
5644
5758
  return { ok: false, error: "Agent slug cannot be empty." };
5645
5759
  }
5646
- const agentConfigPath = join5(accountDir, "agents", trimmed, "config.json");
5760
+ const agentConfigPath = join6(accountDir, "agents", trimmed, "config.json");
5647
5761
  if (!existsSync4(agentConfigPath)) {
5648
5762
  return { ok: false, error: `Agent "${trimmed}" not found \u2014 no config.json at ${agentConfigPath}. Check the agent slug and try again.` };
5649
5763
  }
@@ -5661,12 +5775,12 @@ function setPublicAgent(accountDir, slug) {
5661
5775
  }
5662
5776
  config.whatsapp = parsed.data;
5663
5777
  writeConfig(accountDir, config);
5664
- console.error(`${TAG15} publicAgent set to ${trimmed}`);
5778
+ console.error(`${TAG16} publicAgent set to ${trimmed}`);
5665
5779
  reloadManagerConfig(accountDir);
5666
5780
  return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
5667
5781
  } catch (err) {
5668
5782
  const msg = err instanceof Error ? err.message : String(err);
5669
- console.error(`${TAG15} setPublicAgent failed: ${msg}`);
5783
+ console.error(`${TAG16} setPublicAgent failed: ${msg}`);
5670
5784
  return { ok: false, error: msg };
5671
5785
  }
5672
5786
  }
@@ -5707,17 +5821,17 @@ function updateConfig(accountDir, fields) {
5707
5821
  const parsed = WhatsAppConfigSchema.safeParse(wa);
5708
5822
  if (!parsed.success) {
5709
5823
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
5710
- console.error(`${TAG15} update validation failed: ${msg}`);
5824
+ console.error(`${TAG16} update validation failed: ${msg}`);
5711
5825
  return { ok: false, error: `Validation failed: ${msg}` };
5712
5826
  }
5713
5827
  config.whatsapp = parsed.data;
5714
5828
  writeConfig(accountDir, config);
5715
- console.error(`${TAG15} updated fields=[${fieldNames.join(",")}]`);
5829
+ console.error(`${TAG16} updated fields=[${fieldNames.join(",")}]`);
5716
5830
  reloadManagerConfig(accountDir);
5717
5831
  return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
5718
5832
  } catch (err) {
5719
5833
  const msg = err instanceof Error ? err.message : String(err);
5720
- console.error(`${TAG15} updateConfig failed: ${msg}`);
5834
+ console.error(`${TAG16} updateConfig failed: ${msg}`);
5721
5835
  return { ok: false, error: msg };
5722
5836
  }
5723
5837
  }
@@ -5754,17 +5868,17 @@ function migrateRemovedConfigKeys(accountDir) {
5754
5868
  }
5755
5869
  writeConfig(accountDir, config);
5756
5870
  console.error(
5757
- `${TAG15} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
5871
+ `${TAG16} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
5758
5872
  );
5759
5873
  return { dropped: result.droppedKeys };
5760
5874
  } catch (err) {
5761
- console.error(`${TAG15} migration failed: ${String(err)}`);
5875
+ console.error(`${TAG16} migration failed: ${String(err)}`);
5762
5876
  return { dropped: [] };
5763
5877
  }
5764
5878
  }
5765
5879
 
5766
5880
  // app/lib/whatsapp/login.ts
5767
- var TAG16 = "[whatsapp:login]";
5881
+ var TAG17 = "[whatsapp:login]";
5768
5882
  function maskPhone(digits) {
5769
5883
  if (digits.length <= 4) return "*".repeat(digits.length);
5770
5884
  return digits.slice(0, 2) + "*".repeat(digits.length - 4) + digits.slice(-2);
@@ -5775,7 +5889,7 @@ function closeSocket(sock) {
5775
5889
  try {
5776
5890
  sock.ws?.close?.();
5777
5891
  } catch (err) {
5778
- console.warn(`${TAG16} socket close error during cleanup: ${String(err)}`);
5892
+ console.warn(`${TAG17} socket close error during cleanup: ${String(err)}`);
5779
5893
  }
5780
5894
  }
5781
5895
  function resetActiveLogin(accountId) {
@@ -5802,18 +5916,18 @@ async function runPostPairing(login) {
5802
5916
  const masked = selfPhone ? `${selfPhone.slice(0, 4)}***` : "null";
5803
5917
  console.error(`[whatsapp-persist] wa-persist-on-connect account=${login.accountId} phone=${masked}`);
5804
5918
  } else {
5805
- console.error(`${TAG16} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
5919
+ console.error(`${TAG17} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
5806
5920
  }
5807
5921
  } catch (err) {
5808
- console.error(`${TAG16} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
5922
+ console.error(`${TAG17} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
5809
5923
  }
5810
5924
  } else {
5811
- console.error(`${TAG16} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
5925
+ console.error(`${TAG17} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
5812
5926
  }
5813
5927
  try {
5814
5928
  await registerLoginSocket(login.accountId, login.sock, login.authDir);
5815
5929
  } catch (err) {
5816
- console.error(`${TAG16} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
5930
+ console.error(`${TAG17} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
5817
5931
  }
5818
5932
  }
5819
5933
  async function loginConnectionLoop(accountId, login) {
@@ -5825,7 +5939,7 @@ async function loginConnectionLoop(accountId, login) {
5825
5939
  if (current?.id === login.id) {
5826
5940
  await runPostPairing(current);
5827
5941
  current.connected = true;
5828
- console.error(`${TAG16} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
5942
+ console.error(`${TAG17} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
5829
5943
  }
5830
5944
  return;
5831
5945
  } catch (err) {
@@ -5835,7 +5949,7 @@ async function loginConnectionLoop(accountId, login) {
5835
5949
  if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
5836
5950
  if (attempt >= LOGIN_MAX_RECONNECTS) {
5837
5951
  console.error(
5838
- `${TAG16} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
5952
+ `${TAG17} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
5839
5953
  );
5840
5954
  current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
5841
5955
  } else {
@@ -5847,7 +5961,7 @@ async function loginConnectionLoop(accountId, login) {
5847
5961
  attempt++;
5848
5962
  const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
5849
5963
  console.error(
5850
- `${TAG16} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
5964
+ `${TAG17} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
5851
5965
  );
5852
5966
  closeSocket(current.sock);
5853
5967
  await new Promise((r) => setTimeout(r, delay));
@@ -5858,7 +5972,7 @@ async function loginConnectionLoop(accountId, login) {
5858
5972
  current.sock = newSock;
5859
5973
  } catch (sockErr) {
5860
5974
  console.error(
5861
- `${TAG16} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
5975
+ `${TAG17} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
5862
5976
  );
5863
5977
  current.error = `Reconnection failed: ${String(sockErr)}`;
5864
5978
  return;
@@ -5872,7 +5986,7 @@ async function startLogin(opts) {
5872
5986
  const hasAuth = await authExists(authDir);
5873
5987
  const selfId = readSelfId(authDir);
5874
5988
  console.error(
5875
- `${TAG16} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasCode=${!!existing0.pairingCode}}` : " activeLogin=none")
5989
+ `${TAG17} startLogin account=${accountId} force=${!!force} hasAuth=${hasAuth}` + (existing0 ? ` activeLogin={id=${existing0.id.slice(0, 8)},age=${Math.round((Date.now() - existing0.startedAt) / 1e3)}s,hasCode=${!!existing0.pairingCode}}` : " activeLogin=none")
5876
5990
  );
5877
5991
  if (hasAuth && !force) {
5878
5992
  const who = selfId.e164 ?? selfId.jid ?? "unknown";
@@ -5887,7 +6001,7 @@ async function startLogin(opts) {
5887
6001
  }
5888
6002
  const existing = activeLogins.get(accountId);
5889
6003
  if (existing && isLoginFresh(existing) && existing.pairingCode && !force) {
5890
- console.error(`${TAG16} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
6004
+ console.error(`${TAG17} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
5891
6005
  return {
5892
6006
  pairingCode: existing.pairingCode,
5893
6007
  phone: existing.phone,
@@ -5895,7 +6009,7 @@ async function startLogin(opts) {
5895
6009
  };
5896
6010
  }
5897
6011
  if (existing) {
5898
- console.error(`${TAG16} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
6012
+ console.error(`${TAG17} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
5899
6013
  }
5900
6014
  resetActiveLogin(accountId);
5901
6015
  await clearAuth(authDir);
@@ -5925,7 +6039,7 @@ async function startLogin(opts) {
5925
6039
  const current = activeLogins.get(accountId);
5926
6040
  if (current?.id !== login.id) return;
5927
6041
  if (!current.pairingCode) current.pairingCode = code;
5928
- console.error(`${TAG16} pairing-code-issued account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length}`);
6042
+ console.error(`${TAG17} pairing-code-issued account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length}`);
5929
6043
  resolveCode?.(code);
5930
6044
  }).catch((err) => {
5931
6045
  clearTimeout(codeTimer);
@@ -5954,7 +6068,7 @@ async function startLogin(opts) {
5954
6068
  const ttlTimer = setTimeout(() => {
5955
6069
  const current = activeLogins.get(accountId);
5956
6070
  if (current?.id === login.id && !current.connected) {
5957
- console.error(`${TAG16} pairing-window-elapsed account=${accountId} (ttl sweep)`);
6071
+ console.error(`${TAG17} pairing-window-elapsed account=${accountId} (ttl sweep)`);
5958
6072
  resetActiveLogin(accountId);
5959
6073
  }
5960
6074
  }, ACTIVE_LOGIN_TTL_MS);
@@ -5962,7 +6076,7 @@ async function startLogin(opts) {
5962
6076
  ttlTimer.unref();
5963
6077
  }
5964
6078
  loginConnectionLoop(accountId, login).catch((err) => {
5965
- console.error(`${TAG16} loginConnectionLoop unexpected error: ${String(err)}`);
6079
+ console.error(`${TAG17} loginConnectionLoop unexpected error: ${String(err)}`);
5966
6080
  const current = activeLogins.get(accountId);
5967
6081
  if (current?.id === login.id) {
5968
6082
  current.error = `Unexpected login error: ${String(err)}`;
@@ -5987,13 +6101,13 @@ async function waitForLogin(opts) {
5987
6101
  const { accountId, timeoutMs = 6e4 } = opts;
5988
6102
  const login = activeLogins.get(accountId);
5989
6103
  console.error(
5990
- `${TAG16} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasCode=${!!login.pairingCode}}` : " login=none")
6104
+ `${TAG17} waitForLogin account=${accountId} timeout=${timeoutMs}ms` + (login ? ` login={id=${login.id.slice(0, 8)},age=${Math.round((Date.now() - login.startedAt) / 1e3)}s,connected=${login.connected},hasCode=${!!login.pairingCode}}` : " login=none")
5991
6105
  );
5992
6106
  if (!login) {
5993
6107
  return { connected: false, message: "No active WhatsApp login in progress.", configPersisted: false };
5994
6108
  }
5995
6109
  if (!isLoginFresh(login)) {
5996
- console.error(`${TAG16} pairing-window-elapsed account=${accountId}`);
6110
+ console.error(`${TAG17} pairing-window-elapsed account=${accountId}`);
5997
6111
  resetActiveLogin(accountId);
5998
6112
  return { connected: false, message: "The pairing window expired. Ask me to generate a new code.", configPersisted: false };
5999
6113
  }
@@ -6001,8 +6115,8 @@ async function waitForLogin(opts) {
6001
6115
  while (Date.now() < deadline) {
6002
6116
  if (login.connected) {
6003
6117
  const selfId = readSelfId(login.authDir);
6004
- console.error(`${TAG16} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
6005
- console.error(`${TAG16} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
6118
+ console.error(`${TAG17} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
6119
+ console.error(`${TAG17} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
6006
6120
  const configPersisted = login.configPersisted;
6007
6121
  activeLogins.delete(accountId);
6008
6122
  return {
@@ -6020,7 +6134,7 @@ async function waitForLogin(opts) {
6020
6134
  await new Promise((r) => setTimeout(r, 1e3));
6021
6135
  }
6022
6136
  const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
6023
- console.error(`${TAG16} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
6137
+ console.error(`${TAG17} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
6024
6138
  return { connected: false, message: "Still waiting for you to enter the pairing code. The code is still valid \u2014 ask me to keep waiting, or request a new code.", configPersisted: false };
6025
6139
  }
6026
6140
 
@@ -6028,7 +6142,7 @@ async function waitForLogin(opts) {
6028
6142
  import { realpathSync as realpathSync3 } from "fs";
6029
6143
  import { readFile, stat as stat2 } from "fs/promises";
6030
6144
  import { resolve as resolve8, basename } from "path";
6031
- var TAG17 = "[whatsapp:outbound]";
6145
+ var TAG18 = "[whatsapp:outbound]";
6032
6146
  var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
6033
6147
  var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
6034
6148
  var lastRouteDocumentOutboundAt = /* @__PURE__ */ new Map();
@@ -6062,16 +6176,16 @@ async function sendWhatsAppDocument(input) {
6062
6176
  const accountResolved = realpathSync3(accountDir);
6063
6177
  if (!resolvedPath.startsWith(accountResolved + "/")) {
6064
6178
  const sanitised = filePath.replace(accountDir, "<account>/");
6065
- console.error(`${TAG17} document REJECTED path=${sanitised} reason=outside_account_directory`);
6179
+ console.error(`${TAG18} document REJECTED path=${sanitised} reason=outside_account_directory`);
6066
6180
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
6067
6181
  }
6068
6182
  } catch (err) {
6069
6183
  const code = err.code;
6070
6184
  if (code === "ENOENT") {
6071
- console.error(`${TAG17} document ENOENT path=${filePath}`);
6185
+ console.error(`${TAG18} document ENOENT path=${filePath}`);
6072
6186
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
6073
6187
  }
6074
- console.error(`${TAG17} document path error: ${String(err)}`);
6188
+ console.error(`${TAG18} document path error: ${String(err)}`);
6075
6189
  return { ok: false, status: 500, error: String(err) };
6076
6190
  }
6077
6191
  const fileStat = await stat2(resolvedPath);
@@ -6086,7 +6200,7 @@ async function sendWhatsAppDocument(input) {
6086
6200
  const jid = normalizeJid2(to);
6087
6201
  const sock = getSocket(accountId);
6088
6202
  if (!sock) {
6089
- console.error(`${TAG17} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=not-connected`);
6203
+ console.error(`${TAG18} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=not-connected`);
6090
6204
  return { ok: false, status: 503, error: `WhatsApp account "${accountId}" is not connected` };
6091
6205
  }
6092
6206
  const buffer = Buffer.from(await readFile(resolvedPath));
@@ -6098,7 +6212,7 @@ async function sendWhatsAppDocument(input) {
6098
6212
  { accountId }
6099
6213
  );
6100
6214
  console.error(
6101
- `${TAG17} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
6215
+ `${TAG18} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
6102
6216
  );
6103
6217
  if (result.success) {
6104
6218
  recordDocumentOutbound(to);
@@ -6217,11 +6331,11 @@ function serializeWhatsAppSchema() {
6217
6331
 
6218
6332
  // app/lib/whatsapp/status-reconcile.ts
6219
6333
  import { readdirSync as readdirSync2 } from "fs";
6220
- import { join as join6 } from "path";
6221
- var TAG18 = "[whatsapp:reconcile]";
6334
+ import { join as join7 } from "path";
6335
+ var TAG19 = "[whatsapp:reconcile]";
6222
6336
  function listCredsAccountIds(credsRoot) {
6223
6337
  try {
6224
- return readdirSync2(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join6(credsRoot, e.name))).map((e) => e.name);
6338
+ return readdirSync2(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(join7(credsRoot, e.name))).map((e) => e.name);
6225
6339
  } catch {
6226
6340
  return [];
6227
6341
  }
@@ -6236,18 +6350,18 @@ function reconcileCredsOnDisk(opts) {
6236
6350
  const entries = [];
6237
6351
  for (const accountId of listCredsAccountIds(credsRoot)) {
6238
6352
  if (liveAccountIds.has(accountId)) continue;
6239
- const authDir = join6(credsRoot, accountId);
6353
+ const authDir = join7(credsRoot, accountId);
6240
6354
  const selfPhone = readSelfId(authDir).e164 ?? void 0;
6241
6355
  const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
6242
6356
  console.error(
6243
- `${TAG18} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
6357
+ `${TAG19} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
6244
6358
  );
6245
6359
  if (!configured && accountDir) {
6246
6360
  const result = persistAfterPairing(accountDir, accountId, selfPhone ?? null);
6247
6361
  if (result.ok) {
6248
- console.error(`${TAG18} self-healed account=${accountId}`);
6362
+ console.error(`${TAG19} self-healed account=${accountId}`);
6249
6363
  } else {
6250
- console.error(`${TAG18} self-heal FAILED account=${accountId}: ${result.error}`);
6364
+ console.error(`${TAG19} self-heal FAILED account=${accountId}: ${result.error}`);
6251
6365
  }
6252
6366
  }
6253
6367
  entries.push({ accountId, selfPhone, connected: false, linkedUnconfigured: !configured });
@@ -6256,22 +6370,22 @@ function reconcileCredsOnDisk(opts) {
6256
6370
  }
6257
6371
 
6258
6372
  // server/routes/whatsapp.ts
6259
- var TAG19 = "[whatsapp:api]";
6373
+ var TAG20 = "[whatsapp:api]";
6260
6374
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
6261
6375
  var app2 = new Hono();
6262
6376
  app2.get("/status", (c) => {
6263
6377
  try {
6264
6378
  const live = getStatus();
6265
6379
  const liveIds = new Set(live.map((a) => a.accountId));
6266
- const credsRoot = join7(MAXY_DIR, "credentials", "whatsapp");
6380
+ const credsRoot = join8(MAXY_DIR, "credentials", "whatsapp");
6267
6381
  const accountDir = resolveAccount()?.accountDir ?? null;
6268
6382
  const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds });
6269
6383
  const accounts = [...live, ...reconciled];
6270
6384
  const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}`).join(", ");
6271
- console.error(`${TAG19} status accounts=${accounts.length} [${summary}]`);
6385
+ console.error(`${TAG20} status accounts=${accounts.length} [${summary}]`);
6272
6386
  return c.json({ accounts });
6273
6387
  } catch (err) {
6274
- console.error(`${TAG19} status error: ${String(err)}`);
6388
+ console.error(`${TAG20} status error: ${String(err)}`);
6275
6389
  return c.json({ error: String(err) }, 500);
6276
6390
  }
6277
6391
  });
@@ -6284,13 +6398,13 @@ app2.post("/login/start", async (c) => {
6284
6398
  if (!phone) {
6285
6399
  return c.json({ error: 'Missing required field "phone" (E.164, e.g. +441234567890).' }, 400);
6286
6400
  }
6287
- const authDir = join7(MAXY_DIR, "credentials", "whatsapp", accountId);
6401
+ const authDir = join8(MAXY_DIR, "credentials", "whatsapp", accountId);
6288
6402
  const accountDir = resolveAccount()?.accountDir ?? null;
6289
6403
  const result = await startLogin({ accountId, authDir, phone, accountDir, force });
6290
- console.error(`${TAG19} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
6404
+ console.error(`${TAG20} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
6291
6405
  return c.json(result);
6292
6406
  } catch (err) {
6293
- console.error(`${TAG19} login/start error: ${String(err)}`);
6407
+ console.error(`${TAG20} login/start error: ${String(err)}`);
6294
6408
  return c.json({ error: String(err) }, 500);
6295
6409
  }
6296
6410
  });
@@ -6300,7 +6414,7 @@ app2.post("/login/wait", async (c) => {
6300
6414
  const accountId = validateAccountId(body.accountId);
6301
6415
  const timeoutMs = body.timeoutMs ?? 6e4;
6302
6416
  const result = await waitForLogin({ accountId, timeoutMs });
6303
- console.error(`${TAG19} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
6417
+ console.error(`${TAG20} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
6304
6418
  return c.json({
6305
6419
  connected: result.connected,
6306
6420
  message: result.message,
@@ -6308,7 +6422,7 @@ app2.post("/login/wait", async (c) => {
6308
6422
  configPersisted: result.configPersisted
6309
6423
  });
6310
6424
  } catch (err) {
6311
- console.error(`${TAG19} login/wait error: ${String(err)}`);
6425
+ console.error(`${TAG20} login/wait error: ${String(err)}`);
6312
6426
  return c.json({ error: String(err) }, 500);
6313
6427
  }
6314
6428
  });
@@ -6319,7 +6433,7 @@ app2.post("/disconnect", async (c) => {
6319
6433
  await stopConnection(accountId);
6320
6434
  return c.json({ disconnected: true, accountId });
6321
6435
  } catch (err) {
6322
- console.error(`${TAG19} disconnect error: ${String(err)}`);
6436
+ console.error(`${TAG20} disconnect error: ${String(err)}`);
6323
6437
  return c.json({ error: String(err) }, 500);
6324
6438
  }
6325
6439
  });
@@ -6330,7 +6444,7 @@ app2.post("/reconnect", async (c) => {
6330
6444
  await startConnection(accountId);
6331
6445
  return c.json({ reconnecting: true, accountId });
6332
6446
  } catch (err) {
6333
- console.error(`${TAG19} reconnect error: ${String(err)}`);
6447
+ console.error(`${TAG20} reconnect error: ${String(err)}`);
6334
6448
  return c.json({ error: String(err) }, 500);
6335
6449
  }
6336
6450
  });
@@ -6351,7 +6465,7 @@ app2.post("/config", async (c) => {
6351
6465
  return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
6352
6466
  }
6353
6467
  const result = addAdminPhone(account.accountDir, phone);
6354
- console.error(`${TAG19} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
6468
+ console.error(`${TAG20} config action=add-admin-phone phone=${phone} ok=${result.ok}`);
6355
6469
  return c.json(result, result.ok ? 200 : 400);
6356
6470
  }
6357
6471
  case "remove-admin-phone": {
@@ -6359,12 +6473,12 @@ app2.post("/config", async (c) => {
6359
6473
  return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
6360
6474
  }
6361
6475
  const result = removeAdminPhone(account.accountDir, phone);
6362
- console.error(`${TAG19} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
6476
+ console.error(`${TAG20} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
6363
6477
  return c.json(result, result.ok ? 200 : 400);
6364
6478
  }
6365
6479
  case "list-admin-phones": {
6366
6480
  const phones = readAdminPhones(account.accountDir);
6367
- console.error(`${TAG19} config action=list-admin-phones count=${phones.length}`);
6481
+ console.error(`${TAG20} config action=list-admin-phones count=${phones.length}`);
6368
6482
  return c.json({ ok: true, phones });
6369
6483
  }
6370
6484
  case "set-public-agent": {
@@ -6372,13 +6486,13 @@ app2.post("/config", async (c) => {
6372
6486
  return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
6373
6487
  }
6374
6488
  const result = setPublicAgent(account.accountDir, slug);
6375
- console.error(`${TAG19} config action=set-public-agent slug=${slug} ok=${result.ok}`);
6489
+ console.error(`${TAG20} config action=set-public-agent slug=${slug} ok=${result.ok}`);
6376
6490
  return c.json(result, result.ok ? 200 : 400);
6377
6491
  }
6378
6492
  case "get-public-agent": {
6379
6493
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
6380
6494
  const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount });
6381
- console.error(`${TAG19} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
6495
+ console.error(`${TAG20} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
6382
6496
  return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
6383
6497
  }
6384
6498
  case "list-public-agents": {
@@ -6392,29 +6506,29 @@ app2.post("/config", async (c) => {
6392
6506
  const configPath2 = resolve9(agentsDir, entry.name, "config.json");
6393
6507
  if (!existsSync5(configPath2)) continue;
6394
6508
  try {
6395
- const config = JSON.parse(readFileSync8(configPath2, "utf-8"));
6509
+ const config = JSON.parse(readFileSync9(configPath2, "utf-8"));
6396
6510
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
6397
6511
  } catch {
6398
- console.error(`${TAG19} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
6512
+ console.error(`${TAG20} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
6399
6513
  }
6400
6514
  }
6401
6515
  } catch (err) {
6402
- console.error(`${TAG19} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
6516
+ console.error(`${TAG20} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
6403
6517
  }
6404
6518
  }
6405
- console.error(`${TAG19} config action=list-public-agents count=${agents.length}`);
6519
+ console.error(`${TAG20} config action=list-public-agents count=${agents.length}`);
6406
6520
  return c.json({ ok: true, agents });
6407
6521
  }
6408
6522
  case "schema": {
6409
6523
  const text = serializeWhatsAppSchema();
6410
- console.error(`${TAG19} config action=schema`);
6524
+ console.error(`${TAG20} config action=schema`);
6411
6525
  return c.json({ ok: true, text });
6412
6526
  }
6413
6527
  case "list-groups": {
6414
6528
  const groupAccountId = accountId ?? "default";
6415
6529
  const sock = getSocket(groupAccountId);
6416
6530
  if (!sock) {
6417
- console.error(`${TAG19} config action=list-groups error="not connected" accountId=${groupAccountId}`);
6531
+ console.error(`${TAG20} config action=list-groups error="not connected" accountId=${groupAccountId}`);
6418
6532
  return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
6419
6533
  }
6420
6534
  try {
@@ -6424,10 +6538,10 @@ app2.post("/config", async (c) => {
6424
6538
  name: g.subject ?? g.id,
6425
6539
  participantCount: Array.isArray(g.participants) ? g.participants.length : 0
6426
6540
  }));
6427
- console.error(`${TAG19} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
6541
+ console.error(`${TAG20} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
6428
6542
  return c.json({ ok: true, groups });
6429
6543
  } catch (err) {
6430
- console.error(`${TAG19} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
6544
+ console.error(`${TAG20} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
6431
6545
  return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
6432
6546
  }
6433
6547
  }
@@ -6437,12 +6551,12 @@ app2.post("/config", async (c) => {
6437
6551
  }
6438
6552
  const result = updateConfig(account.accountDir, fields);
6439
6553
  const fieldNames = Object.keys(fields);
6440
- console.error(`${TAG19} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
6554
+ console.error(`${TAG20} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
6441
6555
  return c.json(result, result.ok ? 200 : 400);
6442
6556
  }
6443
6557
  case "get-config": {
6444
6558
  const waConfig = getConfig(account.accountDir);
6445
- console.error(`${TAG19} config action=get-config`);
6559
+ console.error(`${TAG20} config action=get-config`);
6446
6560
  return c.json({ ok: true, config: waConfig });
6447
6561
  }
6448
6562
  default:
@@ -6452,7 +6566,7 @@ app2.post("/config", async (c) => {
6452
6566
  );
6453
6567
  }
6454
6568
  } catch (err) {
6455
- console.error(`${TAG19} config error: ${String(err)}`);
6569
+ console.error(`${TAG20} config error: ${String(err)}`);
6456
6570
  return c.json({ ok: false, error: String(err) }, 500);
6457
6571
  }
6458
6572
  });
@@ -6473,7 +6587,7 @@ app2.post("/send-document", async (c) => {
6473
6587
  recordRouteDocumentOutbound(to, filePath);
6474
6588
  return c.json({ success: true, messageId: result.messageId });
6475
6589
  } catch (err) {
6476
- console.error(`${TAG19} send-document error: ${String(err)}`);
6590
+ console.error(`${TAG20} send-document error: ${String(err)}`);
6477
6591
  return c.json({ error: String(err) }, 500);
6478
6592
  }
6479
6593
  });
@@ -6483,11 +6597,11 @@ app2.get("/activity", (c) => {
6483
6597
  const result = getChannelActivity(accountId);
6484
6598
  const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
6485
6599
  console.error(
6486
- `${TAG19} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
6600
+ `${TAG20} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
6487
6601
  );
6488
6602
  return c.json(result);
6489
6603
  } catch (err) {
6490
- console.error(`${TAG19} activity error: ${String(err)}`);
6604
+ console.error(`${TAG20} activity error: ${String(err)}`);
6491
6605
  return c.json({ error: String(err) }, 500);
6492
6606
  }
6493
6607
  });
@@ -6506,10 +6620,10 @@ app2.get("/conversations", (c) => {
6506
6620
  };
6507
6621
  });
6508
6622
  conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
6509
- console.error(`${TAG19} conversations account=${accountId} count=${conversations.length}`);
6623
+ console.error(`${TAG20} conversations account=${accountId} count=${conversations.length}`);
6510
6624
  return c.json({ conversations });
6511
6625
  } catch (err) {
6512
- console.error(`${TAG19} conversations error: ${String(err)}`);
6626
+ console.error(`${TAG20} conversations error: ${String(err)}`);
6513
6627
  return c.json({ error: String(err) }, 500);
6514
6628
  }
6515
6629
  });
@@ -6524,10 +6638,10 @@ app2.get("/messages", (c) => {
6524
6638
  const limit = limitParam ? parseInt(limitParam, 10) : void 0;
6525
6639
  const effectiveLimit = limit && !Number.isNaN(limit) && limit > 0 ? limit : void 0;
6526
6640
  const messages = getMessages(accountId, jid, effectiveLimit);
6527
- console.error(`${TAG19} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
6641
+ console.error(`${TAG20} messages account=${accountId} jid=${jid} limit=${effectiveLimit ?? "all"} returned=${messages.length}`);
6528
6642
  return c.json({ messages });
6529
6643
  } catch (err) {
6530
- console.error(`${TAG19} messages error: ${String(err)}`);
6644
+ console.error(`${TAG20} messages error: ${String(err)}`);
6531
6645
  return c.json({ error: String(err) }, 500);
6532
6646
  }
6533
6647
  });
@@ -6608,7 +6722,7 @@ app2.get("/conversation-graph-state", async (c) => {
6608
6722
  }
6609
6723
  } catch (err) {
6610
6724
  const msg = err instanceof Error ? err.message : String(err);
6611
- console.error(`${TAG19} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
6725
+ console.error(`${TAG20} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
6612
6726
  return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
6613
6727
  }
6614
6728
  const ms = Date.now() - t0;
@@ -6621,7 +6735,7 @@ app2.get("/conversation-graph-state", async (c) => {
6621
6735
  ms
6622
6736
  });
6623
6737
  } catch (err) {
6624
- console.error(`${TAG19} conversation-graph-state error: ${String(err)}`);
6738
+ console.error(`${TAG20} conversation-graph-state error: ${String(err)}`);
6625
6739
  return c.json({ error: String(err) }, 500);
6626
6740
  }
6627
6741
  });
@@ -6633,12 +6747,12 @@ app2.get("/group-info", async (c) => {
6633
6747
  return c.json({ error: "Missing required parameter: jid" }, 400);
6634
6748
  }
6635
6749
  if (!isGroupJid(jid)) {
6636
- console.error(`${TAG19} group-info error="not a group JID" jid=${jid} account=${accountId}`);
6750
+ console.error(`${TAG20} group-info error="not a group JID" jid=${jid} account=${accountId}`);
6637
6751
  return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
6638
6752
  }
6639
6753
  const sock = getSocket(accountId);
6640
6754
  if (!sock) {
6641
- console.error(`${TAG19} group-info error="not connected" account=${accountId}`);
6755
+ console.error(`${TAG20} group-info error="not connected" account=${accountId}`);
6642
6756
  return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
6643
6757
  }
6644
6758
  const meta = await sock.groupMetadata(jid);
@@ -6651,22 +6765,22 @@ app2.get("/group-info", async (c) => {
6651
6765
  participantCount: meta.participants.length,
6652
6766
  participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
6653
6767
  };
6654
- console.error(`${TAG19} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
6768
+ console.error(`${TAG20} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
6655
6769
  return c.json(result);
6656
6770
  } catch (err) {
6657
- console.error(`${TAG19} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
6771
+ console.error(`${TAG20} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
6658
6772
  return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
6659
6773
  }
6660
6774
  });
6661
6775
  var whatsapp_default = app2;
6662
6776
 
6663
6777
  // server/routes/whatsapp-reader.ts
6664
- import { readFileSync as readFileSync12, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync7 } from "fs";
6665
- import { basename as basename2, dirname, isAbsolute, join as join11, relative as relative2, resolve as resolve11, sep as sep3 } from "path";
6778
+ import { readFileSync as readFileSync13, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync7 } from "fs";
6779
+ import { basename as basename2, dirname, isAbsolute, join as join12, relative as relative2, resolve as resolve11, sep as sep3 } from "path";
6666
6780
 
6667
6781
  // server/routes/admin/sidebar-sessions.ts
6668
- import { readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
6669
- import { join as join8, resolve as resolve10 } from "path";
6782
+ import { readdirSync as readdirSync4, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
6783
+ import { join as join9, resolve as resolve10 } from "path";
6670
6784
 
6671
6785
  // app/lib/whatsapp-reader/select-sessions.ts
6672
6786
  function isReaderChannelSession(role, channel) {
@@ -6725,7 +6839,7 @@ function enumerateJsonls(projectsRoot) {
6725
6839
  throw err;
6726
6840
  }
6727
6841
  for (const slug of slugs) {
6728
- const slugDir = join8(projectsRoot, slug);
6842
+ const slugDir = join9(projectsRoot, slug);
6729
6843
  let entries;
6730
6844
  try {
6731
6845
  entries = readdirSync4(slugDir, { withFileTypes: true });
@@ -6736,9 +6850,9 @@ function enumerateJsonls(projectsRoot) {
6736
6850
  }
6737
6851
  for (const entry of entries) {
6738
6852
  if (entry.isFile() && SESSION_ID_RE.test(entry.name)) {
6739
- out.push({ path: join8(slugDir, entry.name), isSubagent: false, archived: false });
6853
+ out.push({ path: join9(slugDir, entry.name), isSubagent: false, archived: false });
6740
6854
  } else if (entry.isDirectory() && entry.name === "subagents") {
6741
- const subDir = join8(slugDir, entry.name);
6855
+ const subDir = join9(slugDir, entry.name);
6742
6856
  let subEntries;
6743
6857
  try {
6744
6858
  subEntries = readdirSync4(subDir, { withFileTypes: true });
@@ -6749,11 +6863,11 @@ function enumerateJsonls(projectsRoot) {
6749
6863
  }
6750
6864
  for (const sub of subEntries) {
6751
6865
  if (sub.isFile() && SESSION_ID_RE.test(sub.name)) {
6752
- out.push({ path: join8(subDir, sub.name), isSubagent: true, archived: false });
6866
+ out.push({ path: join9(subDir, sub.name), isSubagent: true, archived: false });
6753
6867
  }
6754
6868
  }
6755
6869
  } else if (entry.isDirectory() && entry.name === "archive") {
6756
- const archiveDir = join8(slugDir, entry.name);
6870
+ const archiveDir = join9(slugDir, entry.name);
6757
6871
  let archiveEntries;
6758
6872
  try {
6759
6873
  archiveEntries = readdirSync4(archiveDir, { withFileTypes: true });
@@ -6764,7 +6878,7 @@ function enumerateJsonls(projectsRoot) {
6764
6878
  }
6765
6879
  for (const arc of archiveEntries) {
6766
6880
  if (arc.isFile() && SESSION_ID_RE.test(arc.name)) {
6767
- out.push({ path: join8(archiveDir, arc.name), isSubagent: false, archived: true });
6881
+ out.push({ path: join9(archiveDir, arc.name), isSubagent: false, archived: true });
6768
6882
  }
6769
6883
  }
6770
6884
  }
@@ -6797,10 +6911,10 @@ async function fetchLiveSessions() {
6797
6911
  function loadUserTitles(accountDir) {
6798
6912
  const out = /* @__PURE__ */ new Map();
6799
6913
  if (!accountDir) return out;
6800
- const path2 = join8(accountDir, "session-titles.json");
6914
+ const path2 = join9(accountDir, "session-titles.json");
6801
6915
  let raw;
6802
6916
  try {
6803
- raw = readFileSync9(path2, "utf8");
6917
+ raw = readFileSync10(path2, "utf8");
6804
6918
  } catch {
6805
6919
  return out;
6806
6920
  }
@@ -6823,10 +6937,10 @@ function loadUserTitles(accountDir) {
6823
6937
  return out;
6824
6938
  }
6825
6939
  function readSidecarMeta(metaPath) {
6826
- const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null };
6940
+ const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null, accountId: null };
6827
6941
  let raw;
6828
6942
  try {
6829
- raw = readFileSync9(metaPath, "utf8");
6943
+ raw = readFileSync10(metaPath, "utf8");
6830
6944
  } catch {
6831
6945
  return empty;
6832
6946
  }
@@ -6848,7 +6962,8 @@ function readSidecarMeta(metaPath) {
6848
6962
  personId: typeof r.personId === "string" ? r.personId : null,
6849
6963
  adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null,
6850
6964
  visitorId: typeof r.visitorId === "string" ? r.visitorId : null,
6851
- agentSlug: typeof r.agentSlug === "string" ? r.agentSlug : null
6965
+ agentSlug: typeof r.agentSlug === "string" ? r.agentSlug : null,
6966
+ accountId: typeof r.accountId === "string" ? r.accountId : null
6852
6967
  };
6853
6968
  }
6854
6969
  function startsWithCliMarker(s) {
@@ -6915,12 +7030,33 @@ function resolveTitle(body, sessionId, userTitles) {
6915
7030
  }
6916
7031
  app3.get("/", requireAdminSession, async (c) => {
6917
7032
  const accountId = process.env.ACCOUNT_ID ?? null;
7033
+ const switcherUserId = (() => {
7034
+ const cacheKey = c.get("cacheKey");
7035
+ return cacheKey ? getUserIdForSession(cacheKey) : void 0;
7036
+ })();
7037
+ const accounts = [];
7038
+ if (switcherUserId) {
7039
+ for (const a of resolveUserAccounts(switcherUserId)) {
7040
+ let businessName = null;
7041
+ try {
7042
+ const branding = await fetchBranding(a.accountId);
7043
+ businessName = branding?.name || null;
7044
+ } catch {
7045
+ }
7046
+ accounts.push({
7047
+ accountId: a.accountId,
7048
+ role: a.config.role ?? "",
7049
+ businessName,
7050
+ isHouse: a.config.role === "house"
7051
+ });
7052
+ }
7053
+ }
6918
7054
  const configDir2 = claudeConfigDir();
6919
7055
  if (!configDir2) {
6920
7056
  console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
6921
- return c.json({ sessions: [], accountId });
7057
+ return c.json({ sessions: [], accountId, accounts });
6922
7058
  }
6923
- const projectsRoot = join8(configDir2, "projects");
7059
+ const projectsRoot = join9(configDir2, "projects");
6924
7060
  const jsonls = enumerateJsonls(projectsRoot);
6925
7061
  const liveSessions = await fetchLiveSessions();
6926
7062
  const accountDir = accountId ? resolve10(ACCOUNTS_DIR, accountId) : null;
@@ -6947,7 +7083,7 @@ app3.get("/", requireAdminSession, async (c) => {
6947
7083
  let body;
6948
7084
  let mtimeMs;
6949
7085
  try {
6950
- body = readFileSync9(path2, "utf8");
7086
+ body = readFileSync10(path2, "utf8");
6951
7087
  mtimeMs = statSync2(path2).mtimeMs;
6952
7088
  } catch {
6953
7089
  continue;
@@ -6956,8 +7092,8 @@ app3.get("/", requireAdminSession, async (c) => {
6956
7092
  const pid = live ? liveSessions.get(sessionId) ?? null : null;
6957
7093
  const resolved = resolveTitle(body, sessionId, userTitles);
6958
7094
  titleSourceCounts[resolved.source] += 1;
6959
- const metaPath = join8(projectDir, `${sessionId}.meta.json`);
6960
- const { bridgeIds, role, channel: sidecarChannel, personId, adminUserId } = readSidecarMeta(metaPath);
7095
+ const metaPath = join9(projectDir, `${sessionId}.meta.json`);
7096
+ const { bridgeIds, role, channel: sidecarChannel, personId, adminUserId, accountId: rowAccountId } = readSidecarMeta(metaPath);
6961
7097
  if (isReaderChannelSession(role, sidecarChannel)) {
6962
7098
  excludedChannelCount += 1;
6963
7099
  continue;
@@ -6979,7 +7115,8 @@ app3.get("/", requireAdminSession, async (c) => {
6979
7115
  archived,
6980
7116
  channel: resolved.channel,
6981
7117
  personName: null,
6982
- model: lastAssistantModel(body)
7118
+ model: lastAssistantModel(body),
7119
+ accountId: rowAccountId
6983
7120
  });
6984
7121
  if (live) liveCount += 1;
6985
7122
  if (isSubagent) subagentCount += 1;
@@ -7015,20 +7152,20 @@ app3.get("/", requireAdminSession, async (c) => {
7015
7152
  console.log(
7016
7153
  `[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} excludedChannel=${excludedChannelCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} personNames=${personNameCount} adminNames=${adminNameCount} accountId=${accountId ? accountId.slice(0, 8) : "unset"}`
7017
7154
  );
7018
- return c.json({ sessions: rows, accountId });
7155
+ return c.json({ sessions: rows, accountId, accounts });
7019
7156
  });
7020
7157
  var sidebar_sessions_default = app3;
7021
7158
 
7022
7159
  // app/lib/admin-identity/pin-validator.ts
7023
7160
  import { createHash } from "crypto";
7024
- import { existsSync as existsSync6, readFileSync as readFileSync10, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
7025
- import { join as join9 } from "path";
7161
+ import { existsSync as existsSync6, readFileSync as readFileSync11, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
7162
+ import { join as join10 } from "path";
7026
7163
  function hashPin(pin) {
7027
7164
  return createHash("sha256").update(pin).digest("hex");
7028
7165
  }
7029
7166
  function readUsersFile(usersFilePath) {
7030
7167
  if (!existsSync6(usersFilePath)) return null;
7031
- const raw = readFileSync10(usersFilePath, "utf-8").trim();
7168
+ const raw = readFileSync11(usersFilePath, "utf-8").trim();
7032
7169
  if (!raw) return [];
7033
7170
  return JSON.parse(raw);
7034
7171
  }
@@ -7056,16 +7193,16 @@ function scanForOrphanedAccountAdmins(users, accountsDir) {
7056
7193
  }
7057
7194
  for (const entry of entries) {
7058
7195
  if (entry.startsWith(".")) continue;
7059
- const accountDir = join9(accountsDir, entry);
7196
+ const accountDir = join10(accountsDir, entry);
7060
7197
  try {
7061
7198
  if (!statSync3(accountDir).isDirectory()) continue;
7062
7199
  } catch {
7063
7200
  continue;
7064
7201
  }
7065
- const accountJsonPath = join9(accountDir, "account.json");
7202
+ const accountJsonPath = join10(accountDir, "account.json");
7066
7203
  if (!existsSync6(accountJsonPath)) continue;
7067
7204
  try {
7068
- const config = JSON.parse(readFileSync10(accountJsonPath, "utf-8"));
7205
+ const config = JSON.parse(readFileSync11(accountJsonPath, "utf-8"));
7069
7206
  const admins = config.admins ?? [];
7070
7207
  for (const a of admins) {
7071
7208
  if (typeof a.userId === "string" && !known.has(a.userId)) orphans.push(a.userId);
@@ -7321,8 +7458,8 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
7321
7458
  }
7322
7459
 
7323
7460
  // app/lib/whatsapp-reader/enrich-attachments.ts
7324
- import { readFileSync as readFileSync11, readdirSync as readdirSync6 } from "fs";
7325
- import { join as join10 } from "path";
7461
+ import { readFileSync as readFileSync12, readdirSync as readdirSync6 } from "fs";
7462
+ import { join as join11 } from "path";
7326
7463
  var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
7327
7464
  function adminAttachmentIds(text) {
7328
7465
  const ids = [];
@@ -7335,7 +7472,7 @@ function adminAttachmentIds(text) {
7335
7472
  function adminAttachmentsFromText(text, uploadsBase) {
7336
7473
  const out = [];
7337
7474
  for (const id of adminAttachmentIds(text)) {
7338
- const meta = readAttachmentMeta(join10(uploadsBase, id));
7475
+ const meta = readAttachmentMeta(join11(uploadsBase, id));
7339
7476
  if (meta) out.push(meta);
7340
7477
  }
7341
7478
  return out;
@@ -7343,7 +7480,7 @@ function adminAttachmentsFromText(text, uploadsBase) {
7343
7480
  function readSidecar(dir, attachmentId) {
7344
7481
  let parsed;
7345
7482
  try {
7346
- parsed = JSON.parse(readFileSync11(join10(dir, `${attachmentId}.meta.json`), "utf8"));
7483
+ parsed = JSON.parse(readFileSync12(join11(dir, `${attachmentId}.meta.json`), "utf8"));
7347
7484
  } catch {
7348
7485
  return null;
7349
7486
  }
@@ -7372,7 +7509,7 @@ function listSessionAttachmentsInDir(dir) {
7372
7509
  }
7373
7510
  const sidecars = [];
7374
7511
  for (const name of entries) {
7375
- const sidecar = readSidecar(join10(dir, name), name);
7512
+ const sidecar = readSidecar(join11(dir, name), name);
7376
7513
  if (sidecar) sidecars.push(sidecar);
7377
7514
  }
7378
7515
  sidecars.sort((a, b) => a.storedAt.localeCompare(b.storedAt));
@@ -7407,17 +7544,17 @@ var app4 = new Hono();
7407
7544
  app4.get("/conversations", requireAdminSession, async (c) => {
7408
7545
  const cfg = claudeConfigDir();
7409
7546
  if (!cfg) return c.json({ conversations: [] });
7410
- const projectsRoot = join11(cfg, "projects");
7547
+ const projectsRoot = join12(cfg, "projects");
7411
7548
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
7412
7549
  const rows = [];
7413
7550
  for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
7414
7551
  const sessionId = basename2(path2).replace(/\.jsonl$/, "");
7415
7552
  const projectDir = dirname(path2);
7416
- const meta = readSidecarMeta(join11(projectDir, `${sessionId}.meta.json`));
7553
+ const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
7417
7554
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
7418
7555
  let body = "";
7419
7556
  try {
7420
- body = readFileSync12(path2, "utf8");
7557
+ body = readFileSync13(path2, "utf8");
7421
7558
  } catch {
7422
7559
  }
7423
7560
  const { title } = resolveTitle(body, sessionId, userTitles);
@@ -7521,11 +7658,11 @@ app4.get("/stream", requireAdminSession, (c) => {
7521
7658
  const sessionId = c.req.query("sessionId") ?? "";
7522
7659
  const projectDir = c.req.query("projectDir") ?? "";
7523
7660
  const cfg = claudeConfigDir();
7524
- const projectsRoot = cfg ? resolve11(join11(cfg, "projects")) : null;
7661
+ const projectsRoot = cfg ? resolve11(join12(cfg, "projects")) : null;
7525
7662
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
7526
7663
  return c.json({ error: "bad session reference" }, 400);
7527
7664
  }
7528
- const jsonlPath = resolve11(join11(projectDir, `${sessionId}.jsonl`));
7665
+ const jsonlPath = resolve11(join12(projectDir, `${sessionId}.jsonl`));
7529
7666
  if (!jsonlPath.startsWith(projectsRoot + sep3)) {
7530
7667
  return c.json({ error: "bad session reference" }, 400);
7531
7668
  }
@@ -7638,7 +7775,7 @@ app4.get("/directives", requireAdminSession, (c) => {
7638
7775
  const pid = Number(pidPart);
7639
7776
  let len = 0;
7640
7777
  try {
7641
- len = statSync4(join11(dir, name)).size;
7778
+ len = statSync4(join12(dir, name)).size;
7642
7779
  } catch {
7643
7780
  }
7644
7781
  return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
@@ -7653,11 +7790,11 @@ app4.get("/directive", requireAdminSession, (c) => {
7653
7790
  }
7654
7791
  const dir = directiveStoreDir(sessionId);
7655
7792
  if (!dir) return c.json({ error: "no account" }, 404);
7656
- const path2 = resolve11(join11(dir, name));
7793
+ const path2 = resolve11(join12(dir, name));
7657
7794
  if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
7658
7795
  let body;
7659
7796
  try {
7660
- body = readFileSync12(path2, "utf8");
7797
+ body = readFileSync13(path2, "utf8");
7661
7798
  } catch {
7662
7799
  return c.json({ error: "not found" }, 404);
7663
7800
  }
@@ -7666,8 +7803,8 @@ app4.get("/directive", requireAdminSession, (c) => {
7666
7803
  var whatsapp_reader_default = app4;
7667
7804
 
7668
7805
  // server/routes/public-reader.ts
7669
- import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
7670
- import { basename as basename3, dirname as dirname2, join as join12, resolve as resolve12, sep as sep4 } from "path";
7806
+ import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync14, readdirSync as readdirSync8 } from "fs";
7807
+ import { basename as basename3, dirname as dirname2, join as join13, resolve as resolve12, sep as sep4 } from "path";
7671
7808
 
7672
7809
  // app/lib/whatsapp-reader/delivered-kinds.ts
7673
7810
  var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
@@ -7714,7 +7851,7 @@ function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
7714
7851
 
7715
7852
  // server/routes/public-reader.ts
7716
7853
  var app5 = new Hono();
7717
- var TAG20 = "[public-webchat]";
7854
+ var TAG21 = "[public-webchat]";
7718
7855
  function parseAccessSessionId(cookieHeader) {
7719
7856
  if (!cookieHeader) return null;
7720
7857
  const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
@@ -7741,10 +7878,10 @@ function enumeratePublicRows() {
7741
7878
  const cfg = claudeConfigDir();
7742
7879
  if (!cfg) return [];
7743
7880
  const rows = [];
7744
- for (const { path: path2 } of enumerateJsonls(join12(cfg, "projects"))) {
7881
+ for (const { path: path2 } of enumerateJsonls(join13(cfg, "projects"))) {
7745
7882
  const sessionId = basename3(path2).replace(/\.jsonl$/, "");
7746
7883
  const projectDir = dirname2(path2);
7747
- const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
7884
+ const meta = readSidecarMeta(join13(projectDir, `${sessionId}.meta.json`));
7748
7885
  rows.push({
7749
7886
  sessionId,
7750
7887
  projectDir,
@@ -7762,7 +7899,7 @@ function enumeratePublicRows() {
7762
7899
  app5.get("/session", (c) => {
7763
7900
  const visitor = resolveVisitor(c);
7764
7901
  if (!visitor) {
7765
- console.error(`${TAG20} op=reader-refused reason=no-anchor path=/session`);
7902
+ console.error(`${TAG21} op=reader-refused reason=no-anchor path=/session`);
7766
7903
  return c.json({ error: "gate-required" }, 401);
7767
7904
  }
7768
7905
  const rows = enumeratePublicRows();
@@ -7781,11 +7918,11 @@ app5.get("/session", (c) => {
7781
7918
  found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
7782
7919
  }
7783
7920
  if (found) {
7784
- console.log(`${TAG20} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
7921
+ console.log(`${TAG21} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
7785
7922
  return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
7786
7923
  }
7787
7924
  const sessionKey = crypto.randomUUID();
7788
- console.log(`${TAG20} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
7925
+ console.log(`${TAG21} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
7789
7926
  return c.json({ sessionId: null, projectDir: null, sessionKey });
7790
7927
  });
7791
7928
  function publicUploadsDir(accountId, sessionId) {
@@ -7814,30 +7951,30 @@ function readFrom2(path2, from) {
7814
7951
  app5.get("/stream", (c) => {
7815
7952
  const visitor = resolveVisitor(c);
7816
7953
  if (!visitor) {
7817
- console.error(`${TAG20} op=reader-refused reason=no-anchor path=/stream`);
7954
+ console.error(`${TAG21} op=reader-refused reason=no-anchor path=/stream`);
7818
7955
  return c.json({ error: "gate-required" }, 401);
7819
7956
  }
7820
7957
  const sessionId = c.req.query("sessionId") ?? "";
7821
7958
  const projectDir = c.req.query("projectDir") ?? "";
7822
7959
  const cfg = claudeConfigDir();
7823
- const projectsRoot = cfg ? resolve12(join12(cfg, "projects")) : null;
7960
+ const projectsRoot = cfg ? resolve12(join13(cfg, "projects")) : null;
7824
7961
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
7825
7962
  return c.json({ error: "bad session reference" }, 400);
7826
7963
  }
7827
- const jsonlPath = resolve12(join12(projectDir, `${sessionId}.jsonl`));
7964
+ const jsonlPath = resolve12(join13(projectDir, `${sessionId}.jsonl`));
7828
7965
  if (!jsonlPath.startsWith(projectsRoot + sep4)) {
7829
7966
  return c.json({ error: "bad session reference" }, 400);
7830
7967
  }
7831
- const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
7968
+ const meta = readSidecarMeta(join13(projectDir, `${sessionId}.meta.json`));
7832
7969
  const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
7833
7970
  const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
7834
7971
  if (!(isPublicWebchat && owns)) {
7835
- console.error(`${TAG20} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
7972
+ console.error(`${TAG21} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
7836
7973
  return c.json({ error: "forbidden" }, 403);
7837
7974
  }
7838
7975
  const senderShort = (meta.senderId ?? "").slice(0, 8);
7839
7976
  const encoder = new TextEncoder();
7840
- console.log(`${TAG20} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
7977
+ console.log(`${TAG21} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
7841
7978
  const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
7842
7979
  data: ${JSON.stringify(turn)}
7843
7980
 
@@ -7852,7 +7989,7 @@ data: ${JSON.stringify(turn)}
7852
7989
  const { turns, dropped } = filterDeliveredFrames(lines);
7853
7990
  if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
7854
7991
  for (const turn of turns) send(controller, turn, offset);
7855
- if (dropped > 0) console.log(`${TAG20} op=reader-filtered key=${senderShort} dropped=${dropped}`);
7992
+ if (dropped > 0) console.log(`${TAG21} op=reader-filtered key=${senderShort} dropped=${dropped}`);
7856
7993
  };
7857
7994
  try {
7858
7995
  const { buf, end } = readFrom2(jsonlPath, 0);
@@ -7887,7 +8024,7 @@ data: ${JSON.stringify(turn)}
7887
8024
  watcher?.close();
7888
8025
  clearInterval(poll);
7889
8026
  clearInterval(heartbeat);
7890
- console.log(`${TAG20} op=reader-close key=${senderShort}`);
8027
+ console.log(`${TAG21} op=reader-close key=${senderShort}`);
7891
8028
  try {
7892
8029
  controller.close();
7893
8030
  } catch {
@@ -7943,7 +8080,7 @@ app5.get("/attachment/:attachmentId", (c) => {
7943
8080
  try {
7944
8081
  dataFile = readdirSync8(dir).find((f) => !f.endsWith(".meta.json"));
7945
8082
  if (!dataFile) throw new Error("no data file");
7946
- buffer = readFileSync13(resolve12(dir, dataFile));
8083
+ buffer = readFileSync14(resolve12(dir, dataFile));
7947
8084
  } catch {
7948
8085
  console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
7949
8086
  return new Response("Not found", { status: 404 });
@@ -7962,20 +8099,20 @@ var public_reader_default = app5;
7962
8099
 
7963
8100
  // server/routes/webchat.ts
7964
8101
  import { basename as basename4, dirname as dirname3 } from "path";
7965
- import { join as join15 } from "path";
7966
- import { existsSync as existsSync9, readdirSync as readdirSync10, readFileSync as readFileSync16, renameSync as renameSync3, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
8102
+ import { join as join16 } from "path";
8103
+ import { existsSync as existsSync9, readdirSync as readdirSync10, readFileSync as readFileSync17, renameSync as renameSync3, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
7967
8104
 
7968
8105
  // server/canonical-webchat-override.ts
7969
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync5, renameSync } from "fs";
7970
- import { join as join13 } from "path";
8106
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync5, renameSync } from "fs";
8107
+ import { join as join14 } from "path";
7971
8108
  var FILE = "canonical-webchat-session.json";
7972
8109
  var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
7973
8110
  function canonicalOverridePath(accountDir) {
7974
- return join13(accountDir, FILE);
8111
+ return join14(accountDir, FILE);
7975
8112
  }
7976
8113
  function readRaw(accountDir) {
7977
8114
  try {
7978
- return JSON.parse(readFileSync14(canonicalOverridePath(accountDir), "utf8"));
8115
+ return JSON.parse(readFileSync15(canonicalOverridePath(accountDir), "utf8"));
7979
8116
  } catch {
7980
8117
  return null;
7981
8118
  }
@@ -8062,8 +8199,8 @@ function isNewSessionEffortLevel(value) {
8062
8199
  }
8063
8200
 
8064
8201
  // ../services/claude-session-manager/src/sidecar-store.ts
8065
- import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync9, readFileSync as readFileSync15, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
8066
- import { join as join14 } from "path";
8202
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync9, readFileSync as readFileSync16, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
8203
+ import { join as join15 } from "path";
8067
8204
  function escapeRegExp(s) {
8068
8205
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8069
8206
  }
@@ -8071,7 +8208,7 @@ function createSidecarStore(config) {
8071
8208
  const { suffix, validate: validate2 } = config;
8072
8209
  const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
8073
8210
  function pathFor(sessionsDir, id) {
8074
- return join14(sessionsDir, `${id}${suffix}`);
8211
+ return join15(sessionsDir, `${id}${suffix}`);
8075
8212
  }
8076
8213
  function write(sessionsDir, id, record) {
8077
8214
  mkdirSync2(sessionsDir, { recursive: true });
@@ -8092,7 +8229,7 @@ function createSidecarStore(config) {
8092
8229
  function readFileAt(path2) {
8093
8230
  let raw;
8094
8231
  try {
8095
- raw = readFileSync15(path2, "utf8");
8232
+ raw = readFileSync16(path2, "utf8");
8096
8233
  } catch {
8097
8234
  return null;
8098
8235
  }
@@ -8115,10 +8252,10 @@ function createSidecarStore(config) {
8115
8252
  const out = [];
8116
8253
  for (const name of names) {
8117
8254
  if (!suffixRe.test(name)) continue;
8118
- const path2 = join14(sessionsDir, name);
8255
+ const path2 = join15(sessionsDir, name);
8119
8256
  let raw;
8120
8257
  try {
8121
- raw = readFileSync15(path2, "utf8");
8258
+ raw = readFileSync16(path2, "utf8");
8122
8259
  } catch {
8123
8260
  onSkip?.(name, "unreadable");
8124
8261
  continue;
@@ -8192,10 +8329,10 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
8192
8329
  function locateSession(sessionId) {
8193
8330
  const cfg = claudeConfigDir();
8194
8331
  if (!cfg) return { projectDir: null, channel: null };
8195
- for (const { path: path2 } of enumerateJsonls(join15(cfg, "projects"))) {
8332
+ for (const { path: path2 } of enumerateJsonls(join16(cfg, "projects"))) {
8196
8333
  if (basename4(path2) === `${sessionId}.jsonl`) {
8197
8334
  const dir = dirname3(path2);
8198
- const meta = readSidecarMeta(join15(dir, `${sessionId}.meta.json`));
8335
+ const meta = readSidecarMeta(join16(dir, `${sessionId}.meta.json`));
8199
8336
  return { projectDir: dir, channel: meta.channel };
8200
8337
  }
8201
8338
  }
@@ -8204,7 +8341,7 @@ function locateSession(sessionId) {
8204
8341
  function locateSidecar(sessionId) {
8205
8342
  const cfg = claudeConfigDir();
8206
8343
  if (!cfg) return null;
8207
- const projectsRoot = join15(cfg, "projects");
8344
+ const projectsRoot = join16(cfg, "projects");
8208
8345
  let slugs;
8209
8346
  try {
8210
8347
  slugs = readdirSync10(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -8212,7 +8349,7 @@ function locateSidecar(sessionId) {
8212
8349
  return null;
8213
8350
  }
8214
8351
  for (const slug of slugs) {
8215
- const metaPath = join15(projectsRoot, slug, `${sessionId}.meta.json`);
8352
+ const metaPath = join16(projectsRoot, slug, `${sessionId}.meta.json`);
8216
8353
  if (existsSync9(metaPath)) {
8217
8354
  const meta = readSidecarMeta(metaPath);
8218
8355
  return { channel: meta.channel, adminUserId: meta.adminUserId };
@@ -8225,7 +8362,7 @@ function readTargetOwner(sessionId) {
8225
8362
  if (sidecar !== null) return { found: true, owner: sidecar.adminUserId };
8226
8363
  const { projectDir } = locateSession(sessionId);
8227
8364
  if (projectDir !== null) {
8228
- return { found: true, owner: readSidecarMeta(join15(projectDir, `${sessionId}.meta.json`)).adminUserId };
8365
+ return { found: true, owner: readSidecarMeta(join16(projectDir, `${sessionId}.meta.json`)).adminUserId };
8229
8366
  }
8230
8367
  return { found: false, owner: null };
8231
8368
  }
@@ -8239,7 +8376,7 @@ function sessionSidecarExists(sessionId) {
8239
8376
  function jsonlSizeBytes(projectDir, sessionId) {
8240
8377
  if (projectDir === null) return null;
8241
8378
  try {
8242
- return statSync6(join15(projectDir, `${sessionId}.jsonl`)).size;
8379
+ return statSync6(join16(projectDir, `${sessionId}.jsonl`)).size;
8243
8380
  } catch {
8244
8381
  return null;
8245
8382
  }
@@ -8327,10 +8464,10 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId) {
8327
8464
  const cfg = claudeConfigDir();
8328
8465
  if (!cfg) return null;
8329
8466
  let best = null;
8330
- for (const { path: path2, isSubagent, archived } of enumerateJsonls(join15(cfg, "projects"))) {
8467
+ for (const { path: path2, isSubagent, archived } of enumerateJsonls(join16(cfg, "projects"))) {
8331
8468
  if (isSubagent || archived) continue;
8332
8469
  const id = basename4(path2).slice(0, -".jsonl".length);
8333
- const meta = readSidecarMeta(join15(dirname3(path2), `${id}.meta.json`));
8470
+ const meta = readSidecarMeta(join16(dirname3(path2), `${id}.meta.json`));
8334
8471
  if (meta.role !== "admin" || meta.channel !== "webchat") continue;
8335
8472
  let mtimeMs;
8336
8473
  try {
@@ -8581,9 +8718,9 @@ ${note}` : note;
8581
8718
  console.error(`[webchat:settings] op=${op} outcome=refused value=${value} reason=account-unresolved`);
8582
8719
  return c.json({ error: "account unresolved" }, 503);
8583
8720
  }
8584
- const accountJsonPath = join15(account.accountDir, "account.json");
8721
+ const accountJsonPath = join16(account.accountDir, "account.json");
8585
8722
  try {
8586
- const parsed = JSON.parse(readFileSync16(accountJsonPath, "utf8"));
8723
+ const parsed = JSON.parse(readFileSync17(accountJsonPath, "utf8"));
8587
8724
  if (op === "model") parsed.adminModel = value;
8588
8725
  else if (op === "effort") parsed.effort = value;
8589
8726
  else parsed.adminPermissionMode = value;
@@ -8628,7 +8765,7 @@ ${note}` : note;
8628
8765
  const cfg2 = claudeConfigDir();
8629
8766
  if (cfg2) {
8630
8767
  const source2 = resolveBridgeSource(
8631
- join15(cfg2, "sessions"),
8768
+ join16(cfg2, "sessions"),
8632
8769
  target,
8633
8770
  (id) => locateSession(id).projectDir !== null
8634
8771
  );
@@ -8660,7 +8797,7 @@ ${note}` : note;
8660
8797
  const cfg = claudeConfigDir();
8661
8798
  let projectDir = null;
8662
8799
  if (cfg) {
8663
- for (const { path: path2 } of enumerateJsonls(join15(cfg, "projects"))) {
8800
+ for (const { path: path2 } of enumerateJsonls(join16(cfg, "projects"))) {
8664
8801
  if (basename4(path2) === `${sessionId}.jsonl`) {
8665
8802
  projectDir = dirname3(path2);
8666
8803
  break;
@@ -8668,7 +8805,7 @@ ${note}` : note;
8668
8805
  }
8669
8806
  }
8670
8807
  if (source === "latest" && requesterUserId !== null) {
8671
- const owner = readSidecarMeta(join15(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
8808
+ const owner = readSidecarMeta(join16(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
8672
8809
  if (owner !== null && owner !== requesterUserId && requesterUserId !== primaryUserId) {
8673
8810
  console.error(`[webchat:inbound] op=session-resolve-foreign key=${sessionId.slice(0, 8)} owner=${owner.slice(0, 8)} requester=${requesterUserId.slice(0, 8)}`);
8674
8811
  }
@@ -8700,8 +8837,8 @@ ${note}` : note;
8700
8837
  import { resolve as resolve13 } from "path";
8701
8838
 
8702
8839
  // app/lib/claude-agent/specialist-roster.ts
8703
- import { existsSync as existsSync10, readFileSync as readFileSync17, readdirSync as readdirSync11 } from "fs";
8704
- import { join as join16 } from "path";
8840
+ import { existsSync as existsSync10, readFileSync as readFileSync18, readdirSync as readdirSync11 } from "fs";
8841
+ import { join as join17 } from "path";
8705
8842
  function field(fm, name) {
8706
8843
  const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
8707
8844
  if (!m) return null;
@@ -8720,7 +8857,7 @@ function readSpecialistRoster(specialistsDir) {
8720
8857
  if (!file.endsWith(".md")) continue;
8721
8858
  let raw;
8722
8859
  try {
8723
- raw = readFileSync17(join16(specialistsDir, file), "utf-8");
8860
+ raw = readFileSync18(join17(specialistsDir, file), "utf-8");
8724
8861
  } catch (err) {
8725
8862
  skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
8726
8863
  continue;
@@ -8837,8 +8974,8 @@ var webchat_greeting_default = app6;
8837
8974
 
8838
8975
  // server/routes/telegram.ts
8839
8976
  import { homedir } from "os";
8840
- import { resolve as resolve17, join as join17 } from "path";
8841
- import { existsSync as existsSync11, readFileSync as readFileSync18 } from "fs";
8977
+ import { resolve as resolve17, join as join18 } from "path";
8978
+ import { existsSync as existsSync11, readFileSync as readFileSync19 } from "fs";
8842
8979
 
8843
8980
  // app/lib/channel-pty-bridge/bridge.ts
8844
8981
  import { resolve as resolve16 } from "path";
@@ -8992,7 +9129,7 @@ function classifyTimeout(ndjson, writeAtMs) {
8992
9129
 
8993
9130
  // app/lib/channel-pty-bridge/public-session-end-review.ts
8994
9131
  import { randomUUID as randomUUID7 } from "crypto";
8995
- var TAG21 = "[public-session-review]";
9132
+ var TAG22 = "[public-session-review]";
8996
9133
  var CONSUMED_CAP = 500;
8997
9134
  var consumed = /* @__PURE__ */ new Set();
8998
9135
  function consumeOnce(sessionId) {
@@ -9019,7 +9156,7 @@ async function fetchJsonl(sessionId) {
9019
9156
  return await res.text();
9020
9157
  } catch (err) {
9021
9158
  console.error(
9022
- `${TAG21} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
9159
+ `${TAG22} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
9023
9160
  );
9024
9161
  return null;
9025
9162
  }
@@ -9043,7 +9180,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
9043
9180
  const res = await fetch(url);
9044
9181
  if (!res.ok) {
9045
9182
  console.error(
9046
- `${TAG21} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
9183
+ `${TAG22} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
9047
9184
  );
9048
9185
  return [];
9049
9186
  }
@@ -9051,7 +9188,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
9051
9188
  return Array.isArray(body.writes) ? body.writes : [];
9052
9189
  } catch (err) {
9053
9190
  console.error(
9054
- `${TAG21} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
9191
+ `${TAG22} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
9055
9192
  );
9056
9193
  return [];
9057
9194
  }
@@ -9080,7 +9217,7 @@ function composeInitialMessage(input) {
9080
9217
  }
9081
9218
  async function dispatchReviewer(input, initialMessage) {
9082
9219
  const sessionId = randomUUID7();
9083
- console.log(`${TAG21} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
9220
+ console.log(`${TAG22} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
9084
9221
  const spawned = await managerRcSpawn({
9085
9222
  sessionId,
9086
9223
  initialMessage,
@@ -9100,21 +9237,21 @@ async function firePublicSessionEndReview(input) {
9100
9237
  const dispatchedAt = Date.now();
9101
9238
  if (consumed.has(input.sessionId)) {
9102
9239
  console.log(
9103
- `${TAG21} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
9240
+ `${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
9104
9241
  );
9105
9242
  return;
9106
9243
  }
9107
9244
  const jsonl = await fetchJsonl(input.sessionId);
9108
9245
  if (!jsonl) {
9109
9246
  console.log(
9110
- `${TAG21} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
9247
+ `${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
9111
9248
  );
9112
9249
  return;
9113
9250
  }
9114
9251
  const operatorTurns = countOperatorTurns(jsonl);
9115
9252
  if (operatorTurns === 0) {
9116
9253
  console.log(
9117
- `${TAG21} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
9254
+ `${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
9118
9255
  );
9119
9256
  return;
9120
9257
  }
@@ -9128,19 +9265,19 @@ async function firePublicSessionEndReview(input) {
9128
9265
  });
9129
9266
  if (!consumeOnce(input.sessionId)) {
9130
9267
  console.log(
9131
- `${TAG21} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
9268
+ `${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
9132
9269
  );
9133
9270
  return;
9134
9271
  }
9135
9272
  const dispatched = await dispatchReviewer(input, initialMessage);
9136
9273
  if (!dispatched.ok) {
9137
9274
  console.error(
9138
- `${TAG21} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
9275
+ `${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
9139
9276
  );
9140
9277
  return;
9141
9278
  }
9142
9279
  console.log(
9143
- `${TAG21} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
9280
+ `${TAG22} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
9144
9281
  );
9145
9282
  }
9146
9283
 
@@ -9354,7 +9491,7 @@ function makeFileDelivery(opts) {
9354
9491
  }
9355
9492
 
9356
9493
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
9357
- var TAG22 = "[whatsapp-adaptor]";
9494
+ var TAG23 = "[whatsapp-adaptor]";
9358
9495
  var SEND_USER_FILE2 = "SendUserFile";
9359
9496
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
9360
9497
  function platformRoot() {
@@ -9367,7 +9504,7 @@ function makeWhatsAppSendFile(entry) {
9367
9504
  maxyAccountId = resolvePlatformAccountId();
9368
9505
  } catch (err) {
9369
9506
  console.error(
9370
- `${TAG22} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9507
+ `${TAG23} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9371
9508
  );
9372
9509
  return { ok: false, error: "account-unresolved" };
9373
9510
  }
@@ -9381,7 +9518,7 @@ function makeWhatsAppSendFile(entry) {
9381
9518
  });
9382
9519
  if (result.ok) return { ok: true };
9383
9520
  console.error(
9384
- `${TAG22} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
9521
+ `${TAG23} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
9385
9522
  );
9386
9523
  return { ok: false, error: result.error };
9387
9524
  };
@@ -9389,7 +9526,7 @@ function makeWhatsAppSendFile(entry) {
9389
9526
  function makeWhatsAppFileDelivery(entry) {
9390
9527
  const shared = makeFileDelivery({
9391
9528
  entry,
9392
- tag: TAG22,
9529
+ tag: TAG23,
9393
9530
  channel: "whatsapp",
9394
9531
  sendFile: makeWhatsAppSendFile(entry)
9395
9532
  });
@@ -9424,7 +9561,7 @@ function makeWhatsAppFileDelivery(entry) {
9424
9561
  if (!delivered) {
9425
9562
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
9426
9563
  console.error(
9427
- `${TAG22} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
9564
+ `${TAG23} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
9428
9565
  );
9429
9566
  }
9430
9567
  }
@@ -9436,7 +9573,7 @@ function makeWhatsAppFileDelivery(entry) {
9436
9573
  import { realpathSync as realpathSync4 } from "fs";
9437
9574
  import { readFile as readFile2, stat as stat3 } from "fs/promises";
9438
9575
  import { resolve as resolve14, basename as basename5 } from "path";
9439
- var TAG23 = "[telegram:outbound]";
9576
+ var TAG24 = "[telegram:outbound]";
9440
9577
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
9441
9578
  async function sendTelegramDocument(input) {
9442
9579
  const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
@@ -9452,16 +9589,16 @@ async function sendTelegramDocument(input) {
9452
9589
  resolvedPath = realpathSync4(filePath);
9453
9590
  const accountResolved = realpathSync4(accountDir);
9454
9591
  if (!resolvedPath.startsWith(accountResolved + "/")) {
9455
- console.error(`${TAG23} document REJECTED reason=outside_account_directory`);
9592
+ console.error(`${TAG24} document REJECTED reason=outside_account_directory`);
9456
9593
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
9457
9594
  }
9458
9595
  } catch (err) {
9459
9596
  const code = err.code;
9460
9597
  if (code === "ENOENT") {
9461
- console.error(`${TAG23} document ENOENT path=${filePath}`);
9598
+ console.error(`${TAG24} document ENOENT path=${filePath}`);
9462
9599
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
9463
9600
  }
9464
- console.error(`${TAG23} document path error: ${String(err)}`);
9601
+ console.error(`${TAG24} document path error: ${String(err)}`);
9465
9602
  return { ok: false, status: 500, error: String(err) };
9466
9603
  }
9467
9604
  const fileStat = await stat3(resolvedPath);
@@ -9494,13 +9631,13 @@ async function sendTelegramDocument(input) {
9494
9631
  error = e instanceof Error ? e.message : String(e);
9495
9632
  }
9496
9633
  console.error(
9497
- `${TAG23} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
9634
+ `${TAG24} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
9498
9635
  );
9499
9636
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
9500
9637
  }
9501
9638
 
9502
9639
  // app/lib/telegram/outbound/file-delivery.ts
9503
- var TAG24 = "[telegram:outbound]";
9640
+ var TAG25 = "[telegram:outbound]";
9504
9641
  function platformRoot2() {
9505
9642
  return process.env.MAXY_PLATFORM_ROOT || "";
9506
9643
  }
@@ -9512,11 +9649,11 @@ function makeTelegramSendFile(entry) {
9512
9649
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
9513
9650
  }
9514
9651
  if (!botToken) {
9515
- console.error(`${TAG24} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
9652
+ console.error(`${TAG25} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
9516
9653
  return { ok: false, error: "no-bot-token" };
9517
9654
  }
9518
9655
  if (entry.replyTarget == null) {
9519
- console.error(`${TAG24} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
9656
+ console.error(`${TAG25} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
9520
9657
  return { ok: false, error: "no-reply-target" };
9521
9658
  }
9522
9659
  let maxyAccountId;
@@ -9524,7 +9661,7 @@ function makeTelegramSendFile(entry) {
9524
9661
  maxyAccountId = resolvePlatformAccountId();
9525
9662
  } catch (err) {
9526
9663
  console.error(
9527
- `${TAG24} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9664
+ `${TAG25} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9528
9665
  );
9529
9666
  return { ok: false, error: "account-unresolved" };
9530
9667
  }
@@ -9542,7 +9679,7 @@ function makeTelegramSendFile(entry) {
9542
9679
  function makeTelegramFileDelivery(entry) {
9543
9680
  return makeFileDelivery({
9544
9681
  entry,
9545
- tag: TAG24,
9682
+ tag: TAG25,
9546
9683
  channel: "telegram",
9547
9684
  sendFile: makeTelegramSendFile(entry)
9548
9685
  });
@@ -9551,7 +9688,7 @@ function makeTelegramFileDelivery(entry) {
9551
9688
  // app/lib/webchat/file-delivery.ts
9552
9689
  import { realpathSync as realpathSync5 } from "fs";
9553
9690
  import { resolve as resolve15 } from "path";
9554
- var TAG25 = "[webchat-adaptor]";
9691
+ var TAG26 = "[webchat-adaptor]";
9555
9692
  function platformRoot3() {
9556
9693
  return process.env.MAXY_PLATFORM_ROOT || "";
9557
9694
  }
@@ -9562,7 +9699,7 @@ function makeWebchatSendFile(entry) {
9562
9699
  maxyAccountId = resolvePlatformAccountId();
9563
9700
  } catch (err) {
9564
9701
  console.error(
9565
- `${TAG25} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9702
+ `${TAG26} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9566
9703
  );
9567
9704
  return { ok: false, error: "account-unresolved" };
9568
9705
  }
@@ -9571,14 +9708,14 @@ function makeWebchatSendFile(entry) {
9571
9708
  const resolved = realpathSync5(filePath);
9572
9709
  const accountResolved = realpathSync5(accountDir);
9573
9710
  if (!resolved.startsWith(accountResolved + "/")) {
9574
- console.error(`${TAG25} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
9711
+ console.error(`${TAG26} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
9575
9712
  return { ok: false, error: "outside-account" };
9576
9713
  }
9577
9714
  return { ok: true };
9578
9715
  } catch (err) {
9579
9716
  const code = err.code;
9580
9717
  console.error(
9581
- `${TAG25} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
9718
+ `${TAG26} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
9582
9719
  );
9583
9720
  return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
9584
9721
  }
@@ -9587,7 +9724,7 @@ function makeWebchatSendFile(entry) {
9587
9724
  function makeWebchatFileDelivery(entry) {
9588
9725
  return makeFileDelivery({
9589
9726
  entry,
9590
- tag: TAG25,
9727
+ tag: TAG26,
9591
9728
  channel: "webchat",
9592
9729
  sendFile: makeWebchatSendFile(entry)
9593
9730
  });
@@ -9955,14 +10092,14 @@ function routeTelegramUpdate(input) {
9955
10092
  }
9956
10093
 
9957
10094
  // server/routes/telegram.ts
9958
- var TAG26 = "[telegram-inbound]";
10095
+ var TAG27 = "[telegram-inbound]";
9959
10096
  var DISPATCH_TIMEOUT_MS = 12e4;
9960
10097
  function configDirName() {
9961
10098
  const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
9962
- const brandPath = join17(platformRoot4, "config", "brand.json");
10099
+ const brandPath = join18(platformRoot4, "config", "brand.json");
9963
10100
  if (existsSync11(brandPath)) {
9964
10101
  try {
9965
- return JSON.parse(readFileSync18(brandPath, "utf-8")).configDir ?? ".maxy";
10102
+ return JSON.parse(readFileSync19(brandPath, "utf-8")).configDir ?? ".maxy";
9966
10103
  } catch {
9967
10104
  }
9968
10105
  }
@@ -9990,18 +10127,18 @@ app7.post("/", async (c) => {
9990
10127
  const botParam = c.req.query("bot");
9991
10128
  const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
9992
10129
  if (!botType) {
9993
- console.error(`${TAG26} op=reject reason=missing-bot-param`);
10130
+ console.error(`${TAG27} op=reject reason=missing-bot-param`);
9994
10131
  return c.json({ ok: false }, 400);
9995
10132
  }
9996
10133
  const sp = secretPath(botType);
9997
10134
  if (!existsSync11(sp)) {
9998
- console.error(`${TAG26} op=reject reason=no-secret-file botType=${botType}`);
10135
+ console.error(`${TAG27} op=reject reason=no-secret-file botType=${botType}`);
9999
10136
  return c.json({ ok: false }, 401);
10000
10137
  }
10001
- const expected = readFileSync18(sp, "utf-8").trim();
10138
+ const expected = readFileSync19(sp, "utf-8").trim();
10002
10139
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
10003
10140
  if (got !== expected) {
10004
- console.error(`${TAG26} op=reject reason=bad-secret botType=${botType}`);
10141
+ console.error(`${TAG27} op=reject reason=bad-secret botType=${botType}`);
10005
10142
  return c.json({ ok: false }, 401);
10006
10143
  }
10007
10144
  let update;
@@ -10012,25 +10149,25 @@ app7.post("/", async (c) => {
10012
10149
  }
10013
10150
  const account = resolveAccount();
10014
10151
  if (!account) {
10015
- console.error(`${TAG26} op=reject reason=no-account`);
10152
+ console.error(`${TAG27} op=reject reason=no-account`);
10016
10153
  return c.json({ ok: true }, 200);
10017
10154
  }
10018
10155
  const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
10019
10156
  if (decision.kind === "ignore") {
10020
- console.error(`${TAG26} op=ignore reason=${decision.reason}`);
10157
+ console.error(`${TAG27} op=ignore reason=${decision.reason}`);
10021
10158
  return c.json({ ok: true }, 200);
10022
10159
  }
10023
10160
  if (decision.kind === "denied") {
10024
- console.error(`${TAG26} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
10161
+ console.error(`${TAG27} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
10025
10162
  return c.json({ ok: true }, 200);
10026
10163
  }
10027
10164
  const role = decision.agentType === "admin" ? "admin" : "public";
10028
10165
  const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
10029
10166
  if (!agentSlug) {
10030
- console.error(`${TAG26} op=reject reason=no-default-agent`);
10167
+ console.error(`${TAG27} op=reject reason=no-default-agent`);
10031
10168
  return c.json({ ok: true }, 200);
10032
10169
  }
10033
- console.error(`${TAG26} op=update accountId=${account.accountId} from=${decision.senderId}`);
10170
+ console.error(`${TAG27} op=update accountId=${account.accountId} from=${decision.senderId}`);
10034
10171
  const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
10035
10172
  const { senderId, chatId, text } = decision;
10036
10173
  void (async () => {
@@ -10047,67 +10184,67 @@ app7.post("/", async (c) => {
10047
10184
  timeoutMs: DISPATCH_TIMEOUT_MS
10048
10185
  });
10049
10186
  if ("error" in result) {
10050
- console.error(`${TAG26} op=dispatch-failed from=${senderId} error=${result.error}`);
10187
+ console.error(`${TAG27} op=dispatch-failed from=${senderId} error=${result.error}`);
10051
10188
  return;
10052
10189
  }
10053
10190
  if (!botToken) {
10054
10191
  const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
10055
- console.error(`${TAG26} op=reply-dropped reason=${reason} botType=${botType}`);
10192
+ console.error(`${TAG27} op=reply-dropped reason=${reason} botType=${botType}`);
10056
10193
  return;
10057
10194
  }
10058
10195
  const sent = await sendTelegram(botToken, chatId, result.turnText);
10059
- console.error(`${TAG26} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
10196
+ console.error(`${TAG27} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
10060
10197
  })();
10061
10198
  return c.json({ ok: true }, 200);
10062
10199
  });
10063
10200
  var telegram_default = app7;
10064
10201
 
10065
10202
  // server/routes/quickbooks.ts
10066
- import { join as join18 } from "path";
10067
- import { existsSync as existsSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync, renameSync as renameSync4 } from "fs";
10068
- var TAG27 = "[quickbooks]";
10203
+ import { join as join19 } from "path";
10204
+ import { existsSync as existsSync12, readFileSync as readFileSync20, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync, renameSync as renameSync4 } from "fs";
10205
+ var TAG28 = "[quickbooks]";
10069
10206
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
10070
10207
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
10071
10208
  function pendingPath(accountDir, state) {
10072
- return join18(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
10209
+ return join19(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
10073
10210
  }
10074
10211
  function storePath(accountDir) {
10075
- return join18(accountDir, "secrets", "quickbooks.json");
10212
+ return join19(accountDir, "secrets", "quickbooks.json");
10076
10213
  }
10077
10214
  async function completeConsent(args) {
10078
10215
  const { accountDir, code, realmId } = args;
10079
10216
  const state = args.state;
10080
10217
  if (!state || !STATE_RE.test(state) || !existsSync12(pendingPath(accountDir, state))) {
10081
- console.error(`${TAG27} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
10218
+ console.error(`${TAG28} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
10082
10219
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
10083
10220
  }
10084
10221
  const claimFile = `${pendingPath(accountDir, state)}.claimed`;
10085
10222
  try {
10086
10223
  renameSync4(pendingPath(accountDir, state), claimFile);
10087
10224
  } catch {
10088
- console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
10225
+ console.error(`${TAG28} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
10089
10226
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
10090
10227
  }
10091
10228
  try {
10092
10229
  let pending;
10093
10230
  try {
10094
- pending = JSON.parse(readFileSync19(claimFile, "utf-8"));
10231
+ pending = JSON.parse(readFileSync20(claimFile, "utf-8"));
10095
10232
  } catch {
10096
- console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
10233
+ console.error(`${TAG28} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
10097
10234
  return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
10098
10235
  }
10099
10236
  if (Date.now() > pending.expiresAt) {
10100
- console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
10237
+ console.error(`${TAG28} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
10101
10238
  return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
10102
10239
  }
10103
- console.error(`${TAG27} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
10240
+ console.error(`${TAG28} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
10104
10241
  if (!code || !realmId) {
10105
10242
  return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
10106
10243
  }
10107
10244
  if (!existsSync12(storePath(accountDir))) {
10108
10245
  return { ok: false, status: 500, message: "No QuickBooks credentials are stored for this account.", stateValid: true };
10109
10246
  }
10110
- const store2 = JSON.parse(readFileSync19(storePath(accountDir), "utf-8"));
10247
+ const store2 = JSON.parse(readFileSync20(storePath(accountDir), "utf-8"));
10111
10248
  const form = new URLSearchParams({
10112
10249
  grant_type: "authorization_code",
10113
10250
  code,
@@ -10124,12 +10261,12 @@ async function completeConsent(args) {
10124
10261
  });
10125
10262
  const text = await res.text();
10126
10263
  if (!res.ok) {
10127
- console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=false`);
10264
+ console.error(`${TAG28} op=token-exchange realmId=${realmId} persisted=false`);
10128
10265
  return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
10129
10266
  }
10130
10267
  const body = JSON.parse(text);
10131
10268
  if (!body.refresh_token) {
10132
- console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=false`);
10269
+ console.error(`${TAG28} op=token-exchange realmId=${realmId} persisted=false`);
10133
10270
  return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
10134
10271
  }
10135
10272
  const now = Date.now();
@@ -10139,10 +10276,10 @@ async function completeConsent(args) {
10139
10276
  refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
10140
10277
  lastRefresh: now
10141
10278
  };
10142
- mkdirSync3(join18(accountDir, "secrets"), { recursive: true });
10279
+ mkdirSync3(join19(accountDir, "secrets"), { recursive: true });
10143
10280
  writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
10144
- const persisted = JSON.parse(readFileSync19(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
10145
- console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=${persisted}`);
10281
+ const persisted = JSON.parse(readFileSync20(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
10282
+ console.error(`${TAG28} op=token-exchange realmId=${realmId} persisted=${persisted}`);
10146
10283
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
10147
10284
  } finally {
10148
10285
  rmSync(claimFile, { force: true });
@@ -10155,7 +10292,7 @@ var app8 = new Hono();
10155
10292
  app8.get("/callback", async (c) => {
10156
10293
  const account = resolveAccount();
10157
10294
  if (!account) {
10158
- console.error(`${TAG27} op=callback stateValid=false realmId= reason=no-account`);
10295
+ console.error(`${TAG28} op=callback stateValid=false realmId= reason=no-account`);
10159
10296
  return c.html(page("QuickBooks", "This install has no account configured."), 500);
10160
10297
  }
10161
10298
  const result = await completeConsent({
@@ -10172,12 +10309,12 @@ var quickbooks_default = app8;
10172
10309
 
10173
10310
  // server/routes/onboarding.ts
10174
10311
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
10175
- import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as readFileSync21, unlinkSync as unlinkSync2 } from "fs";
10312
+ import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as readFileSync22, unlinkSync as unlinkSync2 } from "fs";
10176
10313
  import { createHash as createHash3, randomUUID as randomUUID8 } from "crypto";
10177
10314
 
10178
10315
  // ../lib/admins-write/src/index.ts
10179
- import { existsSync as existsSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync12, statSync as statSync7 } from "fs";
10180
- import { dirname as dirname4, join as join19 } from "path";
10316
+ import { existsSync as existsSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync12, statSync as statSync7 } from "fs";
10317
+ import { dirname as dirname4, join as join20 } from "path";
10181
10318
  function logLine(input, result) {
10182
10319
  const userIdShort = input.userId.slice(0, 8);
10183
10320
  console.error(
@@ -10195,7 +10332,7 @@ function writeAdminEntry(input) {
10195
10332
  try {
10196
10333
  let users = [];
10197
10334
  if (existsSync13(input.usersFile)) {
10198
- const raw = readFileSync20(input.usersFile, "utf-8").trim();
10335
+ const raw = readFileSync21(input.usersFile, "utf-8").trim();
10199
10336
  if (raw) {
10200
10337
  const parsed = JSON.parse(raw);
10201
10338
  if (!Array.isArray(parsed)) {
@@ -10219,11 +10356,11 @@ function writeAdminEntry(input) {
10219
10356
  return result;
10220
10357
  }
10221
10358
  try {
10222
- const accountJsonPath = join19(input.accountDir, "account.json");
10359
+ const accountJsonPath = join20(input.accountDir, "account.json");
10223
10360
  if (!existsSync13(accountJsonPath)) {
10224
10361
  throw new Error(`account.json not found at ${accountJsonPath}`);
10225
10362
  }
10226
- const config = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
10363
+ const config = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
10227
10364
  const admins = config.admins ?? [];
10228
10365
  if (admins.some((a) => a.userId === input.userId)) {
10229
10366
  result.accountJsonResult = "noop";
@@ -10249,7 +10386,7 @@ function checkAdminAuthInvariant(input) {
10249
10386
  const usersUserIds = /* @__PURE__ */ new Set();
10250
10387
  if (existsSync13(input.usersFile)) {
10251
10388
  try {
10252
- const raw = readFileSync20(input.usersFile, "utf-8").trim();
10389
+ const raw = readFileSync21(input.usersFile, "utf-8").trim();
10253
10390
  if (raw) {
10254
10391
  const users = JSON.parse(raw);
10255
10392
  for (const u of users) {
@@ -10274,17 +10411,17 @@ function checkAdminAuthInvariant(input) {
10274
10411
  }
10275
10412
  for (const entry of entries) {
10276
10413
  if (entry.startsWith(".")) continue;
10277
- const accountDir = join19(input.accountsDir, entry);
10414
+ const accountDir = join20(input.accountsDir, entry);
10278
10415
  try {
10279
10416
  if (!statSync7(accountDir).isDirectory()) continue;
10280
10417
  } catch {
10281
10418
  continue;
10282
10419
  }
10283
- const accountJsonPath = join19(accountDir, "account.json");
10420
+ const accountJsonPath = join20(accountDir, "account.json");
10284
10421
  if (!existsSync13(accountJsonPath)) continue;
10285
10422
  let admins = [];
10286
10423
  try {
10287
- const config = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
10424
+ const config = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
10288
10425
  admins = config.admins ?? [];
10289
10426
  } catch (err) {
10290
10427
  const msg = err instanceof Error ? err.message : String(err);
@@ -10325,7 +10462,7 @@ function hashPin2(pin) {
10325
10462
  }
10326
10463
  function readUsersFile2() {
10327
10464
  if (!existsSync14(USERS_FILE)) return null;
10328
- const raw = readFileSync21(USERS_FILE, "utf-8").trim();
10465
+ const raw = readFileSync22(USERS_FILE, "utf-8").trim();
10329
10466
  if (!raw) return [];
10330
10467
  return JSON.parse(raw);
10331
10468
  }
@@ -10487,7 +10624,7 @@ app9.post("/set-pin", async (c) => {
10487
10624
  let installOwner = null;
10488
10625
  if (existsSync14(INSTALL_OWNER_FILE)) {
10489
10626
  try {
10490
- const raw = readFileSync21(INSTALL_OWNER_FILE, "utf-8").trim();
10627
+ const raw = readFileSync22(INSTALL_OWNER_FILE, "utf-8").trim();
10491
10628
  if (raw.length > 0) installOwner = raw;
10492
10629
  } catch (err) {
10493
10630
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -10569,8 +10706,8 @@ var onboarding_default = app9;
10569
10706
 
10570
10707
  // server/routes/client-error.ts
10571
10708
  import { appendFileSync, existsSync as existsSync15, renameSync as renameSync6, statSync as statSync8 } from "fs";
10572
- import { join as join20 } from "path";
10573
- var CLIENT_ERRORS_LOG = join20(LOG_DIR, "client-errors.log");
10709
+ import { join as join21 } from "path";
10710
+ var CLIENT_ERRORS_LOG = join21(LOG_DIR, "client-errors.log");
10574
10711
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
10575
10712
  var MAX_BODY_SIZE = 8 * 1024;
10576
10713
  var MAX_STACK_LEN = 2e3;
@@ -10895,17 +11032,17 @@ app11.post("/", async (c) => {
10895
11032
  var session_default = app11;
10896
11033
 
10897
11034
  // server/routes/admin/logs.ts
10898
- import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
11035
+ import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync23, statSync as statSync9 } from "fs";
10899
11036
  import { resolve as resolve18, basename as basename6 } from "path";
10900
11037
 
10901
11038
  // app/lib/logs-read-resolve.ts
10902
11039
  import { existsSync as existsSync16 } from "fs";
10903
- import { join as join21 } from "path";
11040
+ import { join as join22 } from "path";
10904
11041
  function resolveSessionLogPaths(filename, logDirs) {
10905
11042
  const tried = [filename];
10906
11043
  const hits = [];
10907
11044
  for (const dir of logDirs) {
10908
- const fullPath = join21(dir, filename);
11045
+ const fullPath = join22(dir, filename);
10909
11046
  if (existsSync16(fullPath)) {
10910
11047
  hits.push({ path: fullPath, dir });
10911
11048
  }
@@ -10934,7 +11071,7 @@ app12.get("/", async (c) => {
10934
11071
  const filePath = resolve18(dir, safe);
10935
11072
  searched.push(filePath);
10936
11073
  try {
10937
- const buffer = readFileSync22(filePath);
11074
+ const buffer = readFileSync23(filePath);
10938
11075
  const onDiskBytes = statSync9(filePath).size;
10939
11076
  const headers = {
10940
11077
  "Content-Type": "text/plain; charset=utf-8",
@@ -10999,7 +11136,7 @@ app12.get("/", async (c) => {
10999
11136
  console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
11000
11137
  try {
11001
11138
  const filename = basename6(hit.path);
11002
- const buffer = readFileSync22(hit.path);
11139
+ const buffer = readFileSync23(hit.path);
11003
11140
  const onDiskBytes = statSync9(hit.path).size;
11004
11141
  const headers = {
11005
11142
  "Content-Type": "text/plain; charset=utf-8",
@@ -11046,7 +11183,7 @@ app12.get("/", async (c) => {
11046
11183
  files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
11047
11184
  seen.add(name);
11048
11185
  try {
11049
- const content = readFileSync22(resolve18(dir, name));
11186
+ const content = readFileSync23(resolve18(dir, name));
11050
11187
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
11051
11188
  logs[name] = tail.trim() || "(empty)";
11052
11189
  } catch (err) {
@@ -11131,7 +11268,7 @@ var attachment_default = app14;
11131
11268
 
11132
11269
  // server/routes/admin/agents.ts
11133
11270
  import { resolve as resolve20 } from "path";
11134
- import { readdirSync as readdirSync14, readFileSync as readFileSync23, existsSync as existsSync19, rmSync as rmSync2 } from "fs";
11271
+ import { readdirSync as readdirSync14, readFileSync as readFileSync24, existsSync as existsSync19, rmSync as rmSync2 } from "fs";
11135
11272
  var app15 = new Hono();
11136
11273
  app15.get("/", (c) => {
11137
11274
  const account = resolveAccount();
@@ -11147,7 +11284,7 @@ app15.get("/", (c) => {
11147
11284
  const configPath2 = resolve20(agentsDir, entry.name, "config.json");
11148
11285
  if (!existsSync19(configPath2)) continue;
11149
11286
  try {
11150
- const config = JSON.parse(readFileSync23(configPath2, "utf-8"));
11287
+ const config = JSON.parse(readFileSync24(configPath2, "utf-8"));
11151
11288
  agents.push({
11152
11289
  slug: entry.name,
11153
11290
  displayName: config.displayName ?? entry.name,
@@ -11906,8 +12043,8 @@ app16.put("/:id/label", requireAdminSession, async (c) => {
11906
12043
  var sessions_default = app16;
11907
12044
 
11908
12045
  // app/lib/claude-agent/spawn-context.ts
11909
- import { existsSync as existsSync21, readFileSync as readFileSync24, readdirSync as readdirSync15, statSync as statSync10 } from "fs";
11910
- import { dirname as dirname5, resolve as resolve21, join as join22 } from "path";
12046
+ import { existsSync as existsSync21, readFileSync as readFileSync25, readdirSync as readdirSync15, statSync as statSync10 } from "fs";
12047
+ import { dirname as dirname5, resolve as resolve21, join as join23 } from "path";
11911
12048
  async function resolveOwnerProfileBlock(accountId, userId) {
11912
12049
  if (!userId) return { ok: false, reason: "missing-user-id" };
11913
12050
  try {
@@ -11948,14 +12085,14 @@ function frontmatterField(manifest, field2) {
11948
12085
  function extractPluginToolDescriptions(pluginDir) {
11949
12086
  const out = /* @__PURE__ */ new Map();
11950
12087
  const candidates = [
11951
- join22(pluginDir, "mcp/dist/index.js"),
11952
- join22(pluginDir, "mcp/src/index.ts")
12088
+ join23(pluginDir, "mcp/dist/index.js"),
12089
+ join23(pluginDir, "mcp/src/index.ts")
11953
12090
  ];
11954
12091
  let raw = null;
11955
12092
  for (const path2 of candidates) {
11956
12093
  if (!existsSync21(path2)) continue;
11957
12094
  try {
11958
- raw = readFileSync24(path2, "utf-8");
12095
+ raw = readFileSync25(path2, "utf-8");
11959
12096
  break;
11960
12097
  } catch {
11961
12098
  }
@@ -12007,7 +12144,7 @@ function listPluginDirs() {
12007
12144
  const platformPlugins = resolve21(PLATFORM_ROOT, "plugins");
12008
12145
  if (existsSync21(platformPlugins)) {
12009
12146
  for (const name of readdirSync15(platformPlugins)) {
12010
- const dir = join22(platformPlugins, name);
12147
+ const dir = join23(platformPlugins, name);
12011
12148
  try {
12012
12149
  if (statSync10(dir).isDirectory()) out.set(name, dir);
12013
12150
  } catch {
@@ -12017,11 +12154,11 @@ function listPluginDirs() {
12017
12154
  const premiumRoot = resolve21(dirname5(PLATFORM_ROOT), "premium-plugins");
12018
12155
  if (existsSync21(premiumRoot)) {
12019
12156
  for (const bundle of readdirSync15(premiumRoot)) {
12020
- const bundlePlugins = join22(premiumRoot, bundle, "plugins");
12157
+ const bundlePlugins = join23(premiumRoot, bundle, "plugins");
12021
12158
  if (!existsSync21(bundlePlugins)) continue;
12022
12159
  try {
12023
12160
  for (const name of readdirSync15(bundlePlugins)) {
12024
- const dir = join22(bundlePlugins, name);
12161
+ const dir = join23(bundlePlugins, name);
12025
12162
  try {
12026
12163
  if (statSync10(dir).isDirectory()) out.set(name, dir);
12027
12164
  } catch {
@@ -12037,7 +12174,7 @@ function readEnabledPlugins(accountId) {
12037
12174
  const accountFile = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
12038
12175
  if (!existsSync21(accountFile)) return /* @__PURE__ */ new Set();
12039
12176
  try {
12040
- const parsed = JSON.parse(readFileSync24(accountFile, "utf-8"));
12177
+ const parsed = JSON.parse(readFileSync25(accountFile, "utf-8"));
12041
12178
  return new Set(
12042
12179
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
12043
12180
  );
@@ -12049,7 +12186,7 @@ function readFrontmatter(path2) {
12049
12186
  if (!existsSync21(path2)) return null;
12050
12187
  let raw;
12051
12188
  try {
12052
- raw = readFileSync24(path2, "utf-8");
12189
+ raw = readFileSync25(path2, "utf-8");
12053
12190
  } catch {
12054
12191
  return null;
12055
12192
  }
@@ -12064,7 +12201,7 @@ function computeActivePlugins(accountId) {
12064
12201
  for (const name of Array.from(enabled).sort()) {
12065
12202
  const dir = pluginDirs.get(name);
12066
12203
  if (!dir) continue;
12067
- const fm = readFrontmatter(join22(dir, "PLUGIN.md"));
12204
+ const fm = readFrontmatter(join23(dir, "PLUGIN.md"));
12068
12205
  if (!fm) continue;
12069
12206
  const description = frontmatterField(`---
12070
12207
  ${fm}
@@ -12081,7 +12218,7 @@ ${fm}
12081
12218
  `plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
12082
12219
  );
12083
12220
  if (toolNames.length > 0 && descMap.size === 0) {
12084
- const hasSource = existsSync21(join22(dir, "mcp/dist/index.js")) || existsSync21(join22(dir, "mcp/src/index.ts"));
12221
+ const hasSource = existsSync21(join23(dir, "mcp/dist/index.js")) || existsSync21(join23(dir, "mcp/src/index.ts"));
12085
12222
  if (hasSource) {
12086
12223
  console.warn(
12087
12224
  `[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
@@ -12091,7 +12228,7 @@ ${fm}
12091
12228
  const skillPaths = parseSkillPathsFromFrontmatter(fm);
12092
12229
  const skills = [];
12093
12230
  for (const relPath of skillPaths) {
12094
- const skillPath = join22(dir, relPath);
12231
+ const skillPath = join23(dir, relPath);
12095
12232
  const skillFm = readFrontmatter(skillPath);
12096
12233
  if (!skillFm) continue;
12097
12234
  const skillName = frontmatterField(`---
@@ -12135,7 +12272,7 @@ function computeSpecialistDomains(accountId) {
12135
12272
  const out = [];
12136
12273
  for (const file of entries.sort()) {
12137
12274
  if (!file.endsWith(".md")) continue;
12138
- const fm = readFrontmatter(join22(specialistsDir, file));
12275
+ const fm = readFrontmatter(join23(specialistsDir, file));
12139
12276
  if (!fm) continue;
12140
12277
  const wrapped = `---
12141
12278
  ${fm}
@@ -12157,7 +12294,7 @@ function computeDormantPlugins(accountId) {
12157
12294
  if (!existsSync21(accountFile)) return [];
12158
12295
  let enabled;
12159
12296
  try {
12160
- const raw = readFileSync24(accountFile, "utf-8");
12297
+ const raw = readFileSync25(accountFile, "utf-8");
12161
12298
  const parsed = JSON.parse(raw);
12162
12299
  enabled = new Set(
12163
12300
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
@@ -12183,7 +12320,7 @@ function computeDormantPlugins(accountId) {
12183
12320
  if (!existsSync21(manifestPath)) continue;
12184
12321
  let manifest;
12185
12322
  try {
12186
- manifest = readFileSync24(manifestPath, "utf-8");
12323
+ manifest = readFileSync25(manifestPath, "utf-8");
12187
12324
  } catch {
12188
12325
  continue;
12189
12326
  }
@@ -12197,13 +12334,13 @@ function computeDormantPlugins(accountId) {
12197
12334
  }
12198
12335
 
12199
12336
  // server/routes/admin/claude-sessions.ts
12200
- import { existsSync as existsSync22, readFileSync as readFileSync25 } from "fs";
12337
+ import { existsSync as existsSync22, readFileSync as readFileSync26 } from "fs";
12201
12338
  import { resolve as resolve22 } from "path";
12202
12339
  function readTunnelState(brandConfigDir) {
12203
12340
  const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
12204
12341
  if (!existsSync22(statePath)) return null;
12205
12342
  try {
12206
- const parsed = JSON.parse(readFileSync25(statePath, "utf-8"));
12343
+ const parsed = JSON.parse(readFileSync26(statePath, "utf-8"));
12207
12344
  const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
12208
12345
  const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
12209
12346
  const domain = typeof parsed.domain === "string" ? parsed.domain : null;
@@ -12217,7 +12354,7 @@ function resolveTunnelUrl() {
12217
12354
  const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
12218
12355
  let brand;
12219
12356
  try {
12220
- brand = JSON.parse(readFileSync25(resolve22(platformRoot4, "config", "brand.json"), "utf-8"));
12357
+ brand = JSON.parse(readFileSync26(resolve22(platformRoot4, "config", "brand.json"), "utf-8"));
12221
12358
  } catch {
12222
12359
  return null;
12223
12360
  }
@@ -12228,7 +12365,7 @@ function resolveTunnelUrl() {
12228
12365
  if (!state) return null;
12229
12366
  return `https://${hostname2}.${state.domain}`;
12230
12367
  }
12231
- var TAG28 = "[claude-session-manager:wrapper]";
12368
+ var TAG29 = "[claude-session-manager:wrapper]";
12232
12369
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
12233
12370
  const auth = await ensureAuth();
12234
12371
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -12246,12 +12383,12 @@ async function performSpawnWithInitialMessage(args) {
12246
12383
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
12247
12384
  const ownerMs = Date.now() - ownerStart;
12248
12385
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
12249
- console.log(`${TAG28} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
12386
+ console.log(`${TAG29} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
12250
12387
  const dormantPlugins = computeDormantPlugins(args.senderId);
12251
12388
  const activePlugins = computeActivePlugins(args.senderId);
12252
12389
  const specialistDomains = computeSpecialistDomains(args.senderId);
12253
12390
  const tunnelUrl = resolveTunnelUrl();
12254
- console.log(`${TAG28} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
12391
+ console.log(`${TAG29} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
12255
12392
  const upstreamPayload = JSON.stringify({
12256
12393
  senderId: args.senderId,
12257
12394
  // Task 205 — pass userId through to the manager so it lands as
@@ -12278,24 +12415,24 @@ async function performSpawnWithInitialMessage(args) {
12278
12415
  // unshapely values.
12279
12416
  conversationNodeId: args.conversationNodeId
12280
12417
  });
12281
- console.log(`${TAG28} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
12418
+ console.log(`${TAG29} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
12282
12419
  const forwardStart = Date.now();
12283
12420
  const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
12284
12421
  method: "POST",
12285
12422
  headers: { "content-type": "application/json" },
12286
12423
  body: upstreamPayload
12287
12424
  }).catch((err) => {
12288
- console.error(`${TAG28} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
12425
+ console.error(`${TAG29} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
12289
12426
  return null;
12290
12427
  });
12291
12428
  if (!upstream) return {
12292
12429
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
12293
12430
  claudeSessionId: null
12294
12431
  };
12295
- console.log(`${TAG28} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
12432
+ console.log(`${TAG29} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
12296
12433
  if (args.initialMessage) {
12297
12434
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
12298
- console.log(`${TAG28} initial-message-inlined bytes=${inputBytes}`);
12435
+ console.log(`${TAG29} initial-message-inlined bytes=${inputBytes}`);
12299
12436
  }
12300
12437
  const bodyText = await upstream.text().catch(() => "");
12301
12438
  let claudeSessionId = null;
@@ -12330,7 +12467,7 @@ app17.post("/", async (c) => {
12330
12467
  if (refusal) return refusal;
12331
12468
  const senderId = getAccountIdForSession(cacheKey) ?? "";
12332
12469
  if (!senderId) {
12333
- console.error(`${TAG28} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
12470
+ console.error(`${TAG29} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
12334
12471
  return c.json({ error: "admin-account-not-resolved" }, 500);
12335
12472
  }
12336
12473
  const userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -12339,7 +12476,7 @@ app17.post("/", async (c) => {
12339
12476
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
12340
12477
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
12341
12478
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
12342
- console.log(`${TAG28} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
12479
+ console.log(`${TAG29} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
12343
12480
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
12344
12481
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
12345
12482
  senderId,
@@ -12358,13 +12495,13 @@ app17.post("/", async (c) => {
12358
12495
  claudeSessionId,
12359
12496
  senderId
12360
12497
  );
12361
- console.log(`${TAG28} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
12498
+ console.log(`${TAG29} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
12362
12499
  return response;
12363
12500
  });
12364
12501
  var claude_sessions_default = app17;
12365
12502
 
12366
12503
  // server/routes/admin/log-ingest.ts
12367
- var TAG29 = "[log-ingest]";
12504
+ var TAG30 = "[log-ingest]";
12368
12505
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
12369
12506
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
12370
12507
  var MAX_LINE_BYTES = 4096;
@@ -12375,7 +12512,7 @@ function isLoopbackAddr(addr) {
12375
12512
  app18.post("/", async (c) => {
12376
12513
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
12377
12514
  if (!isLoopbackAddr(remoteAddr)) {
12378
- console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12515
+ console.error(`${TAG30} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12379
12516
  return c.json({ error: "log-ingest-loopback-only" }, 403);
12380
12517
  }
12381
12518
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -12384,7 +12521,7 @@ app18.post("/", async (c) => {
12384
12521
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
12385
12522
  const offender = tokens.find((t) => !isLoopbackAddr(t));
12386
12523
  if (offender !== void 0) {
12387
- console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12524
+ console.error(`${TAG30} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12388
12525
  return c.json({ error: "log-ingest-loopback-only" }, 403);
12389
12526
  }
12390
12527
  }
@@ -12426,18 +12563,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
12426
12563
  ]);
12427
12564
  var app19 = new Hono();
12428
12565
  app19.post("/", async (c) => {
12429
- const TAG38 = "[admin:events]";
12566
+ const TAG39 = "[admin:events]";
12430
12567
  let body;
12431
12568
  try {
12432
12569
  body = await c.req.json();
12433
12570
  } catch (err) {
12434
12571
  const detail = err instanceof Error ? err.message : String(err);
12435
- console.error(`${TAG38} reject reason=body-not-json detail=${detail}`);
12572
+ console.error(`${TAG39} reject reason=body-not-json detail=${detail}`);
12436
12573
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
12437
12574
  }
12438
12575
  const event = typeof body.event === "string" ? body.event : "";
12439
12576
  if (!ALLOWED_EVENTS.has(event)) {
12440
- console.error(`${TAG38} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
12577
+ console.error(`${TAG39} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
12441
12578
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
12442
12579
  }
12443
12580
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -12463,7 +12600,7 @@ import { createReadStream as createReadStream2, createWriteStream as createWrite
12463
12600
  import { readdir as readdir3, readFile as readFile5, stat as stat5, mkdir as mkdir3, unlink as unlink2, rename } from "fs/promises";
12464
12601
  import { realpathSync as realpathSync6 } from "fs";
12465
12602
  import { randomUUID as randomUUID10 } from "crypto";
12466
- import { basename as basename8, dirname as dirname6, join as join24, resolve as resolve24, sep as sep6 } from "path";
12603
+ import { basename as basename8, dirname as dirname6, join as join25, resolve as resolve24, sep as sep6 } from "path";
12467
12604
  import { Readable as Readable2 } from "stream";
12468
12605
 
12469
12606
  // ../lib/graph-trash/src/index.ts
@@ -12781,7 +12918,7 @@ async function cascadeDeleteDocument(params) {
12781
12918
 
12782
12919
  // app/lib/file-index.ts
12783
12920
  import * as fsp from "fs/promises";
12784
- import { resolve as resolve23, relative as relative3, join as join23, basename as basename7, extname as extname2, sep as sep5 } from "path";
12921
+ import { resolve as resolve23, relative as relative3, join as join24, basename as basename7, extname as extname2, sep as sep5 } from "path";
12785
12922
  import { tmpdir as tmpdir2 } from "os";
12786
12923
  import { execFile as execFile2 } from "child_process";
12787
12924
  import { promisify as promisify2 } from "util";
@@ -12863,7 +13000,7 @@ async function extractPdf(absolute) {
12863
13000
  return stdout.trim();
12864
13001
  }
12865
13002
  async function ocrPdf(absolute) {
12866
- const outPdf = join23(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
13003
+ const outPdf = join24(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
12867
13004
  try {
12868
13005
  await execFileAsync2(
12869
13006
  "ocrmypdf",
@@ -12925,7 +13062,7 @@ async function readDisplayName(absolute) {
12925
13062
  const stem = dot === -1 ? base : base.slice(0, dot);
12926
13063
  if (base === `${stem}.meta.json`) return null;
12927
13064
  try {
12928
- const raw = await fsp.readFile(join23(dir, `${stem}.meta.json`), "utf-8");
13065
+ const raw = await fsp.readFile(join24(dir, `${stem}.meta.json`), "utf-8");
12929
13066
  const meta = JSON.parse(raw);
12930
13067
  const dn = meta.displayName ?? meta.filename;
12931
13068
  return typeof dn === "string" && dn.length > 0 ? dn : null;
@@ -12946,7 +13083,7 @@ async function walkSubtree(root, dataRoot, out) {
12946
13083
  for (const entry of entries) {
12947
13084
  if (entry.isSymbolicLink()) continue;
12948
13085
  if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
12949
- const abs = join23(root, entry.name);
13086
+ const abs = join24(root, entry.name);
12950
13087
  if (entry.isDirectory()) {
12951
13088
  const sub = await walkSubtree(abs, dataRoot, out);
12952
13089
  if (!sub.clean) clean = false;
@@ -13219,7 +13356,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
13219
13356
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
13220
13357
  async function readMeta(absDir, baseName) {
13221
13358
  try {
13222
- const raw = await readFile5(join24(absDir, `${baseName}.meta.json`), "utf8");
13359
+ const raw = await readFile5(join25(absDir, `${baseName}.meta.json`), "utf8");
13223
13360
  const parsed = JSON.parse(raw);
13224
13361
  if (typeof parsed?.filename === "string") {
13225
13362
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -13257,7 +13394,7 @@ async function readAccountNames() {
13257
13394
  }
13258
13395
  async function enrich(absolute, entry, accountNames) {
13259
13396
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
13260
- const meta = await readMeta(join24(absolute, entry.name), entry.name);
13397
+ const meta = await readMeta(join25(absolute, entry.name), entry.name);
13261
13398
  if (meta?.filename) {
13262
13399
  entry.displayName = meta.filename;
13263
13400
  entry.mimeType = meta.mimeType;
@@ -13421,7 +13558,7 @@ app20.get("/", requireAdminSession, async (c) => {
13421
13558
  const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
13422
13559
  const protectedEntry = isProtectedFromDeletion(childRel).protected;
13423
13560
  try {
13424
- const entryPath = join24(absolute, name);
13561
+ const entryPath = join25(absolute, name);
13425
13562
  const s = await stat5(entryPath);
13426
13563
  entries.push({
13427
13564
  name,
@@ -13562,7 +13699,7 @@ app20.post("/upload", requireAdminSession, async (c) => {
13562
13699
  const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
13563
13700
  if (!base.ok) return c.json({ error: base.error }, base.status);
13564
13701
  const relDir = relpath ? dirname6(relpath) : ".";
13565
- const destRel = relDir === "." ? base.relative : join24(base.relative, relDir);
13702
+ const destRel = relDir === "." ? base.relative : join25(base.relative, relDir);
13566
13703
  if (crossesForeignAccountPartition(destRel, accountId)) {
13567
13704
  console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
13568
13705
  return c.json({ error: "Not found" }, 404);
@@ -13739,7 +13876,7 @@ app20.delete("/", requireAdminSession, async (c) => {
13739
13876
  }
13740
13877
  const dot = base.lastIndexOf(".");
13741
13878
  const stem = dot === -1 ? base : base.slice(0, dot);
13742
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join24(dirname6(absolute), `${stem}.meta.json`) : null;
13879
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join25(dirname6(absolute), `${stem}.meta.json`) : null;
13743
13880
  await unlink2(absolute);
13744
13881
  if (sidecarPath) {
13745
13882
  try {
@@ -16215,6 +16352,7 @@ var session_rename_default = app31;
16215
16352
 
16216
16353
  // server/routes/admin/session-rc-spawn.ts
16217
16354
  import { randomUUID as randomUUID11 } from "crypto";
16355
+ var ACCOUNT_UUID_RE2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
16218
16356
  function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, adminUserId, authenticatedName) {
16219
16357
  const operator = isOperatorHost(host, operatorDomains);
16220
16358
  const resumeId = typeof body.sessionId === "string" && body.sessionId.length > 0 ? body.sessionId : void 0;
@@ -16225,6 +16363,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, ad
16225
16363
  const pickedModel = kind === "new" && typeof body.model === "string" && isSelectableModel(body.model) ? body.model : null;
16226
16364
  const pickedEffort = kind === "new" && typeof body.effort === "string" && isNewSessionEffortLevel(body.effort) ? body.effort : null;
16227
16365
  const paramsSource = pickedModel !== null && pickedEffort !== null ? "picker" : "default";
16366
+ const targetAccountId = typeof body.targetAccountId === "string" && ACCOUNT_UUID_RE2.test(body.targetAccountId) ? body.targetAccountId : void 0;
16228
16367
  const payload = {};
16229
16368
  let sessionId;
16230
16369
  if (webchatBind) {
@@ -16239,6 +16378,7 @@ function resolveSpawnPlan(host, operatorDomains, body, mintId = randomUUID11, ad
16239
16378
  if (pickedModel) payload.model = pickedModel;
16240
16379
  if (pickedEffort) payload.effort = pickedEffort;
16241
16380
  if (adminUserId) payload.adminUserId = adminUserId;
16381
+ if (targetAccountId) payload.targetAccountId = targetAccountId;
16242
16382
  if (authenticatedName) payload.authenticatedName = authenticatedName;
16243
16383
  } else {
16244
16384
  sessionId = resumeId;
@@ -16549,14 +16689,14 @@ app34.get("/", async (c) => {
16549
16689
  var system_stats_default = app34;
16550
16690
 
16551
16691
  // server/routes/admin/health.ts
16552
- import { existsSync as existsSync25, readFileSync as readFileSync26 } from "fs";
16553
- import { resolve as resolve26, join as join25 } from "path";
16692
+ import { existsSync as existsSync25, readFileSync as readFileSync27 } from "fs";
16693
+ import { resolve as resolve26, join as join26 } from "path";
16554
16694
  var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
16555
16695
  var brandHostname = "maxy";
16556
- var brandJsonPath = join25(PLATFORM_ROOT6, "config", "brand.json");
16696
+ var brandJsonPath = join26(PLATFORM_ROOT6, "config", "brand.json");
16557
16697
  if (existsSync25(brandJsonPath)) {
16558
16698
  try {
16559
- const brand = JSON.parse(readFileSync26(brandJsonPath, "utf-8"));
16699
+ const brand = JSON.parse(readFileSync27(brandJsonPath, "utf-8"));
16560
16700
  if (brand.hostname) brandHostname = brand.hostname;
16561
16701
  } catch {
16562
16702
  }
@@ -16566,7 +16706,7 @@ var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
16566
16706
  var PROBE_TIMEOUT_MS = 1e3;
16567
16707
  function readVersion() {
16568
16708
  if (!existsSync25(VERSION_FILE)) return "unknown";
16569
- return readFileSync26(VERSION_FILE, "utf-8").trim() || "unknown";
16709
+ return readFileSync27(VERSION_FILE, "utf-8").trim() || "unknown";
16570
16710
  }
16571
16711
  async function probeConversationDb() {
16572
16712
  let session;
@@ -16618,7 +16758,7 @@ var health_default2 = app35;
16618
16758
 
16619
16759
  // server/routes/admin/linkedin-ingest.ts
16620
16760
  import { randomUUID as randomUUID13 } from "crypto";
16621
- var TAG30 = "[linkedin-ingest-route]";
16761
+ var TAG31 = "[linkedin-ingest-route]";
16622
16762
  var UUID2 = /^[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}$/;
16623
16763
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
16624
16764
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -16722,29 +16862,29 @@ app36.post("/", requireAdminSession, async (c) => {
16722
16862
  try {
16723
16863
  body = await c.req.json();
16724
16864
  } catch {
16725
- console.error(TAG30 + " rejected status=400 reason=schema:body-not-json");
16865
+ console.error(TAG31 + " rejected status=400 reason=schema:body-not-json");
16726
16866
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
16727
16867
  }
16728
16868
  const v = validate(body);
16729
16869
  if (!v.ok) {
16730
- console.error(TAG30 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
16870
+ console.error(TAG31 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
16731
16871
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
16732
16872
  }
16733
16873
  const envelope = v.envelope;
16734
16874
  const cacheKey = c.var.cacheKey ?? "";
16735
16875
  const senderId = getAccountIdForSession(cacheKey) ?? "";
16736
16876
  if (!senderId) {
16737
- console.error(TAG30 + " rejected status=500 reason=admin-account-not-resolved");
16877
+ console.error(TAG31 + " rejected status=500 reason=admin-account-not-resolved");
16738
16878
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
16739
16879
  }
16740
16880
  const payloadBytes = JSON.stringify(envelope).length;
16741
16881
  console.log(
16742
- TAG30 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
16882
+ TAG31 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
16743
16883
  );
16744
16884
  const initialMessage = buildInitialMessage(envelope);
16745
16885
  const spawnStart = Date.now();
16746
16886
  const sessionId = randomUUID13();
16747
- console.log(TAG30 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
16887
+ console.log(TAG31 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
16748
16888
  const spawned = await managerRcSpawn({
16749
16889
  sessionId,
16750
16890
  initialMessage,
@@ -16753,12 +16893,12 @@ app36.post("/", requireAdminSession, async (c) => {
16753
16893
  });
16754
16894
  if ("error" in spawned) {
16755
16895
  console.error(
16756
- TAG30 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
16896
+ TAG31 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
16757
16897
  );
16758
16898
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
16759
16899
  }
16760
16900
  console.log(
16761
- TAG30 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
16901
+ TAG31 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
16762
16902
  );
16763
16903
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
16764
16904
  });
@@ -16766,7 +16906,7 @@ var linkedin_ingest_default = app36;
16766
16906
 
16767
16907
  // server/routes/admin/post-turn-context.ts
16768
16908
  import neo4j3 from "neo4j-driver";
16769
- var TAG31 = "[post-turn-context]";
16909
+ var TAG32 = "[post-turn-context]";
16770
16910
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
16771
16911
  "embedding",
16772
16912
  "passwordHash",
@@ -16808,7 +16948,7 @@ var app37 = new Hono();
16808
16948
  app37.get("/", async (c) => {
16809
16949
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16810
16950
  if (!isLoopbackAddr2(remoteAddr)) {
16811
- console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16951
+ console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16812
16952
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
16813
16953
  }
16814
16954
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16817,7 +16957,7 @@ app37.get("/", async (c) => {
16817
16957
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16818
16958
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
16819
16959
  if (offender !== void 0) {
16820
- console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16960
+ console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16821
16961
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
16822
16962
  }
16823
16963
  }
@@ -16849,7 +16989,7 @@ app37.get("/", async (c) => {
16849
16989
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
16850
16990
  const total = Date.now() - started;
16851
16991
  console.log(
16852
- `${TAG31} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
16992
+ `${TAG32} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
16853
16993
  );
16854
16994
  return c.json({
16855
16995
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -16858,7 +16998,7 @@ app37.get("/", async (c) => {
16858
16998
  const elapsed = Date.now() - started;
16859
16999
  const message = err instanceof Error ? err.message : String(err);
16860
17000
  console.error(
16861
- `${TAG31} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
17001
+ `${TAG32} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
16862
17002
  );
16863
17003
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
16864
17004
  } finally {
@@ -16971,7 +17111,7 @@ function formatPreviousContext(writes) {
16971
17111
  }
16972
17112
 
16973
17113
  // server/routes/admin/public-session-context.ts
16974
- var TAG32 = "[public-session-context]";
17114
+ var TAG33 = "[public-session-context]";
16975
17115
  function isLoopbackAddr3(addr) {
16976
17116
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
16977
17117
  }
@@ -16979,7 +17119,7 @@ var app38 = new Hono();
16979
17119
  app38.get("/", async (c) => {
16980
17120
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16981
17121
  if (!isLoopbackAddr3(remoteAddr)) {
16982
- console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17122
+ console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16983
17123
  return c.json({ error: "public-session-context-loopback-only" }, 403);
16984
17124
  }
16985
17125
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16988,7 +17128,7 @@ app38.get("/", async (c) => {
16988
17128
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16989
17129
  const offender = tokens.find((t) => !isLoopbackAddr3(t));
16990
17130
  if (offender !== void 0) {
16991
- console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17131
+ console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16992
17132
  return c.json({ error: "public-session-context-loopback-only" }, 403);
16993
17133
  }
16994
17134
  }
@@ -17002,14 +17142,14 @@ app38.get("/", async (c) => {
17002
17142
  const writes = await fetchSliceWrites(session, sliceToken, accountId);
17003
17143
  const total = Date.now() - started;
17004
17144
  console.log(
17005
- `${TAG32} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
17145
+ `${TAG33} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
17006
17146
  );
17007
17147
  return c.json({ writes });
17008
17148
  } catch (err) {
17009
17149
  const elapsed = Date.now() - started;
17010
17150
  const message = err instanceof Error ? err.message : String(err);
17011
17151
  console.error(
17012
- `${TAG32} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
17152
+ `${TAG33} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
17013
17153
  );
17014
17154
  return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
17015
17155
  } finally {
@@ -17019,7 +17159,7 @@ app38.get("/", async (c) => {
17019
17159
  var public_session_context_default = app38;
17020
17160
 
17021
17161
  // server/routes/admin/public-session-exit.ts
17022
- var TAG33 = "[public-session-exit-route]";
17162
+ var TAG34 = "[public-session-exit-route]";
17023
17163
  function isLoopbackAddr4(addr) {
17024
17164
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
17025
17165
  }
@@ -17027,7 +17167,7 @@ var app39 = new Hono();
17027
17167
  app39.post("/", async (c) => {
17028
17168
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
17029
17169
  if (!isLoopbackAddr4(remoteAddr)) {
17030
- console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17170
+ console.error(`${TAG34} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17031
17171
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
17032
17172
  }
17033
17173
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -17036,7 +17176,7 @@ app39.post("/", async (c) => {
17036
17176
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
17037
17177
  const offender = tokens.find((t) => !isLoopbackAddr4(t));
17038
17178
  if (offender !== void 0) {
17039
- console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17179
+ console.error(`${TAG34} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17040
17180
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
17041
17181
  }
17042
17182
  }
@@ -17054,7 +17194,7 @@ app39.post("/", async (c) => {
17054
17194
  var public_session_exit_default = app39;
17055
17195
 
17056
17196
  // server/routes/admin/access-session-evict.ts
17057
- var TAG34 = "[access-session-evict]";
17197
+ var TAG35 = "[access-session-evict]";
17058
17198
  function isLoopbackAddr5(addr) {
17059
17199
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
17060
17200
  }
@@ -17062,7 +17202,7 @@ var app40 = new Hono();
17062
17202
  app40.post("/", async (c) => {
17063
17203
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
17064
17204
  if (!isLoopbackAddr5(remoteAddr)) {
17065
- console.error(`${TAG34} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17205
+ console.error(`${TAG35} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17066
17206
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
17067
17207
  }
17068
17208
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -17071,7 +17211,7 @@ app40.post("/", async (c) => {
17071
17211
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
17072
17212
  const offender = tokens.find((t) => !isLoopbackAddr5(t));
17073
17213
  if (offender !== void 0) {
17074
- console.error(`${TAG34} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17214
+ console.error(`${TAG35} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17075
17215
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
17076
17216
  }
17077
17217
  }
@@ -17084,13 +17224,13 @@ app40.post("/", async (c) => {
17084
17224
  const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
17085
17225
  if (!grantId) return c.json({ error: "grantId required" }, 400);
17086
17226
  const dropped = evictAccessSessionsByGrant(grantId);
17087
- console.log(`${TAG34} grantId=${grantId} dropped=${dropped}`);
17227
+ console.log(`${TAG35} grantId=${grantId} dropped=${dropped}`);
17088
17228
  return c.json({ ok: true, dropped });
17089
17229
  });
17090
17230
  var access_session_evict_default = app40;
17091
17231
 
17092
17232
  // server/routes/admin/enrol-person.ts
17093
- var TAG35 = "[enrol]";
17233
+ var TAG36 = "[enrol]";
17094
17234
  function isLoopbackAddr6(addr) {
17095
17235
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
17096
17236
  }
@@ -17099,7 +17239,7 @@ var app41 = new Hono();
17099
17239
  app41.post("/", async (c) => {
17100
17240
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
17101
17241
  if (!isLoopbackAddr6(remoteAddr)) {
17102
- console.error(`${TAG35} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17242
+ console.error(`${TAG36} reject reason=non-loopback remoteAddr=${remoteAddr}`);
17103
17243
  return c.json({ error: "enrol-person-loopback-only" }, 403);
17104
17244
  }
17105
17245
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -17108,7 +17248,7 @@ app41.post("/", async (c) => {
17108
17248
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
17109
17249
  const offender = tokens.find((t) => !isLoopbackAddr6(t));
17110
17250
  if (offender !== void 0) {
17111
- console.error(`${TAG35} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17251
+ console.error(`${TAG36} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
17112
17252
  return c.json({ error: "enrol-person-loopback-only" }, 403);
17113
17253
  }
17114
17254
  }
@@ -17133,7 +17273,7 @@ app41.post("/", async (c) => {
17133
17273
  }
17134
17274
  const accountId = process.env.ACCOUNT_ID ?? "";
17135
17275
  if (!accountId) {
17136
- console.error(`${TAG35} op=person result=no-account-id`);
17276
+ console.error(`${TAG36} op=person result=no-account-id`);
17137
17277
  return c.json({ error: "no-account-id" }, 500);
17138
17278
  }
17139
17279
  let personId;
@@ -17141,7 +17281,7 @@ app41.post("/", async (c) => {
17141
17281
  personId = await enrolPerson({ accountId, phone, email, agentSlug });
17142
17282
  } catch (err) {
17143
17283
  console.error(
17144
- `${TAG35} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
17284
+ `${TAG36} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
17145
17285
  );
17146
17286
  return c.json({ error: "enrol-failed" }, 500);
17147
17287
  }
@@ -17151,11 +17291,11 @@ app41.post("/", async (c) => {
17151
17291
  if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
17152
17292
  } catch (err) {
17153
17293
  console.error(
17154
- `${TAG35} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
17294
+ `${TAG36} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
17155
17295
  );
17156
17296
  }
17157
17297
  console.log(
17158
- `${TAG35} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
17298
+ `${TAG36} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
17159
17299
  );
17160
17300
  return c.json({ personId, grant }, 200);
17161
17301
  });
@@ -17231,7 +17371,7 @@ app43.route("/enrol-person", enrol_person_default);
17231
17371
  var admin_default = app43;
17232
17372
 
17233
17373
  // server/routes/access/verify-token.ts
17234
- var TAG36 = "[access-verify]";
17374
+ var TAG37 = "[access-verify]";
17235
17375
  var MINT_TAG = "[access-session-mint]";
17236
17376
  var COOKIE_NAME = "__access_session";
17237
17377
  var app44 = new Hono();
@@ -17250,39 +17390,39 @@ app44.post("/", async (c) => {
17250
17390
  }
17251
17391
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
17252
17392
  if (rateMsg) {
17253
- console.error(`${TAG36} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
17393
+ console.error(`${TAG37} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
17254
17394
  return c.json({ error: rateMsg }, 429);
17255
17395
  }
17256
17396
  const grant = await findGrantByMagicToken(token);
17257
17397
  if (!grant) {
17258
17398
  recordAccessFailedAttempt(ip, agentSlug);
17259
- console.error(`${TAG36} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
17399
+ console.error(`${TAG37} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
17260
17400
  return c.json({ error: "invalid-or-expired-link" }, 401);
17261
17401
  }
17262
17402
  if (grant.agentSlug !== agentSlug) {
17263
17403
  recordAccessFailedAttempt(ip, agentSlug);
17264
17404
  console.error(
17265
- `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
17405
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
17266
17406
  );
17267
17407
  return c.json({ error: "invalid-or-expired-link" }, 401);
17268
17408
  }
17269
17409
  if (grant.status === "expired" || grant.status === "revoked") {
17270
17410
  recordAccessFailedAttempt(ip, agentSlug);
17271
17411
  console.error(
17272
- `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
17412
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
17273
17413
  );
17274
17414
  return c.json({ error: "access-no-longer-valid" }, 401);
17275
17415
  }
17276
17416
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
17277
17417
  recordAccessFailedAttempt(ip, agentSlug);
17278
17418
  console.error(
17279
- `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
17419
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
17280
17420
  );
17281
17421
  return c.json({ error: "access-no-longer-valid" }, 401);
17282
17422
  }
17283
17423
  if (!grant.sliceToken) {
17284
17424
  console.error(
17285
- `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
17425
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
17286
17426
  );
17287
17427
  return c.json({ error: "grant-misconfigured" }, 500);
17288
17428
  }
@@ -17298,12 +17438,12 @@ app44.post("/", async (c) => {
17298
17438
  await consumeMagicTokenAndActivate(grant.grantId);
17299
17439
  } catch (err) {
17300
17440
  console.error(
17301
- `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
17441
+ `${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
17302
17442
  );
17303
17443
  return c.json({ error: "verification-failed" }, 500);
17304
17444
  }
17305
17445
  clearAccessRateLimit(ip, agentSlug);
17306
- console.log(`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
17446
+ console.log(`${TAG37} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
17307
17447
  console.log(
17308
17448
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
17309
17449
  );
@@ -17379,7 +17519,7 @@ async function sendMagicLinkEmail(payload) {
17379
17519
  }
17380
17520
 
17381
17521
  // server/routes/access/request-magic-link.ts
17382
- var TAG37 = "[access-request-link]";
17522
+ var TAG38 = "[access-request-link]";
17383
17523
  var app45 = new Hono();
17384
17524
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
17385
17525
  app45.post("/", async (c) => {
@@ -17396,18 +17536,18 @@ app45.post("/", async (c) => {
17396
17536
  }
17397
17537
  const rateMsg = checkRequestLinkRateLimit(contactValue);
17398
17538
  if (rateMsg) {
17399
- console.error(`${TAG37} contactValue=${maskContact(contactValue)} result=rate-limited`);
17539
+ console.error(`${TAG38} contactValue=${maskContact(contactValue)} result=rate-limited`);
17400
17540
  return c.json({ error: rateMsg }, 429);
17401
17541
  }
17402
17542
  recordRequestLinkAttempt(contactValue);
17403
17543
  const accountId = process.env.ACCOUNT_ID ?? "";
17404
17544
  if (!accountId) {
17405
- console.error(`${TAG37} contactValue=${maskContact(contactValue)} result=no-account-id`);
17545
+ console.error(`${TAG38} contactValue=${maskContact(contactValue)} result=no-account-id`);
17406
17546
  return c.json({ message: VISITOR_MESSAGE }, 200);
17407
17547
  }
17408
17548
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
17409
17549
  if (!grant) {
17410
- console.log(`${TAG37} contactValue=${maskContact(contactValue)} result=notfound`);
17550
+ console.log(`${TAG38} contactValue=${maskContact(contactValue)} result=notfound`);
17411
17551
  return c.json({ message: VISITOR_MESSAGE }, 200);
17412
17552
  }
17413
17553
  let token;
@@ -17415,7 +17555,7 @@ app45.post("/", async (c) => {
17415
17555
  token = await generateNewMagicToken(grant.grantId);
17416
17556
  } catch (err) {
17417
17557
  console.error(
17418
- `${TAG37} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
17558
+ `${TAG38} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
17419
17559
  );
17420
17560
  return c.json({ message: VISITOR_MESSAGE }, 200);
17421
17561
  }
@@ -17447,12 +17587,12 @@ app45.post("/", async (c) => {
17447
17587
  });
17448
17588
  if (!sendResult.ok) {
17449
17589
  console.error(
17450
- `${TAG37} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
17590
+ `${TAG38} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
17451
17591
  );
17452
17592
  return c.json({ message: VISITOR_MESSAGE }, 200);
17453
17593
  }
17454
17594
  console.log(
17455
- `${TAG37} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
17595
+ `${TAG38} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
17456
17596
  );
17457
17597
  return c.json({ message: VISITOR_MESSAGE }, 200);
17458
17598
  });
@@ -17465,7 +17605,7 @@ app46.route("/request-magic-link", request_magic_link_default);
17465
17605
  var access_default = app46;
17466
17606
 
17467
17607
  // server/routes/sites.ts
17468
- import { existsSync as existsSync26, readFileSync as readFileSync27, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
17608
+ import { existsSync as existsSync26, readFileSync as readFileSync28, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
17469
17609
  import { resolve as resolve28 } from "path";
17470
17610
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
17471
17611
  var MIME = {
@@ -17569,7 +17709,7 @@ app47.get("/:rel{.*}", (c) => {
17569
17709
  }
17570
17710
  let body;
17571
17711
  try {
17572
- body = readFileSync27(realPath);
17712
+ body = readFileSync28(realPath);
17573
17713
  } catch (err) {
17574
17714
  const code = err?.code;
17575
17715
  if (code === "EISDIR") {
@@ -17605,7 +17745,7 @@ var sites_default = app47;
17605
17745
 
17606
17746
  // app/lib/visitor-token.ts
17607
17747
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
17608
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync28, writeFileSync as writeFileSync11 } from "fs";
17748
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync29, writeFileSync as writeFileSync11 } from "fs";
17609
17749
  import { dirname as dirname7 } from "path";
17610
17750
  var TOKEN_PREFIX = "v1.";
17611
17751
  var SECRET_BYTES = 32;
@@ -17614,7 +17754,7 @@ var cachedSecret = null;
17614
17754
  function getSecret() {
17615
17755
  if (cachedSecret) return cachedSecret;
17616
17756
  try {
17617
- const hex2 = readFileSync28(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
17757
+ const hex2 = readFileSync29(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
17618
17758
  if (hex2.length === SECRET_BYTES * 2) {
17619
17759
  cachedSecret = Buffer.from(hex2, "hex");
17620
17760
  return cachedSecret;
@@ -17628,7 +17768,7 @@ function getSecret() {
17628
17768
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
17629
17769
  } catch {
17630
17770
  }
17631
- const hex = readFileSync28(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
17771
+ const hex = readFileSync29(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
17632
17772
  cachedSecret = Buffer.from(hex, "hex");
17633
17773
  return cachedSecret;
17634
17774
  }
@@ -17681,8 +17821,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
17681
17821
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
17682
17822
 
17683
17823
  // app/lib/brand-config.ts
17684
- import { existsSync as existsSync27, readFileSync as readFileSync29 } from "fs";
17685
- import { join as join26 } from "path";
17824
+ import { existsSync as existsSync27, readFileSync as readFileSync30 } from "fs";
17825
+ import { join as join27 } from "path";
17686
17826
  var cached2 = null;
17687
17827
  var cachedAttempted = false;
17688
17828
  function readBrandConfig() {
@@ -17690,10 +17830,10 @@ function readBrandConfig() {
17690
17830
  cachedAttempted = true;
17691
17831
  const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
17692
17832
  if (!platformRoot4) return null;
17693
- const brandPath = join26(platformRoot4, "config", "brand.json");
17833
+ const brandPath = join27(platformRoot4, "config", "brand.json");
17694
17834
  if (!existsSync27(brandPath)) return null;
17695
17835
  try {
17696
- cached2 = JSON.parse(readFileSync29(brandPath, "utf-8"));
17836
+ cached2 = JSON.parse(readFileSync30(brandPath, "utf-8"));
17697
17837
  return cached2;
17698
17838
  } catch {
17699
17839
  return null;
@@ -18494,7 +18634,7 @@ function startGraphHealthTimer() {
18494
18634
  // app/lib/file-watcher.ts
18495
18635
  import * as fsp2 from "fs/promises";
18496
18636
  import { resolve as resolve30, sep as sep8 } from "path";
18497
- var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18637
+ var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18498
18638
  var DEFAULT_COALESCE_MS = 500;
18499
18639
  var ROOTS = ["accounts"];
18500
18640
  function _routeEvent(rootName, filename) {
@@ -18502,7 +18642,7 @@ function _routeEvent(rootName, filename) {
18502
18642
  const segs = filename.split(sep8).filter(Boolean);
18503
18643
  if (segs.length < 2) return null;
18504
18644
  const accountId = segs[0];
18505
- if (!ACCOUNT_UUID_RE2.test(accountId)) return null;
18645
+ if (!ACCOUNT_UUID_RE3.test(accountId)) return null;
18506
18646
  return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
18507
18647
  }
18508
18648
  async function startFileWatcher(opts = {}) {
@@ -18597,7 +18737,7 @@ async function startFileWatcher(opts = {}) {
18597
18737
  // app/lib/migrate-uploads.ts
18598
18738
  import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
18599
18739
  import { dirname as dirname8, relative as relative5, resolve as resolve31 } from "path";
18600
- var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18740
+ var ACCOUNT_UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18601
18741
  async function walkFiles(dir) {
18602
18742
  const out = [];
18603
18743
  let entries;
@@ -18673,7 +18813,7 @@ async function migrateUploads(opts = {}) {
18673
18813
  try {
18674
18814
  for (const entry of topEntries) {
18675
18815
  const name = entry.name;
18676
- if (ACCOUNT_UUID_RE3.test(name)) {
18816
+ if (ACCOUNT_UUID_RE4.test(name)) {
18677
18817
  moved += await relocateTree(
18678
18818
  resolve31(oldRoot, name),
18679
18819
  resolve31(dataRoot, "accounts", name, "uploads"),
@@ -18720,7 +18860,7 @@ async function migrateUploads(opts = {}) {
18720
18860
  // app/lib/migrate-admin-webchat-sidecars.ts
18721
18861
  import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
18722
18862
  import { existsSync as existsSync29 } from "fs";
18723
- import { join as join27 } from "path";
18863
+ import { join as join28 } from "path";
18724
18864
  var ADMIN_ROLE2 = "admin";
18725
18865
  var WEBCHAT_CHANNEL2 = "webchat";
18726
18866
  var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
@@ -18765,7 +18905,7 @@ async function collectLiveSidecars(projectsRoot) {
18765
18905
  }
18766
18906
  for (const slug of slugs) {
18767
18907
  if (!slug.isDirectory()) continue;
18768
- const slugDir = join27(projectsRoot, slug.name);
18908
+ const slugDir = join28(projectsRoot, slug.name);
18769
18909
  let entries;
18770
18910
  try {
18771
18911
  entries = await readdir6(slugDir, { withFileTypes: true });
@@ -18774,9 +18914,9 @@ async function collectLiveSidecars(projectsRoot) {
18774
18914
  }
18775
18915
  for (const entry of entries) {
18776
18916
  if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
18777
- out.push(join27(slugDir, entry.name));
18917
+ out.push(join28(slugDir, entry.name));
18778
18918
  } else if (entry.isDirectory() && entry.name === "subagents") {
18779
- const subDir = join27(slugDir, entry.name);
18919
+ const subDir = join28(slugDir, entry.name);
18780
18920
  let subs;
18781
18921
  try {
18782
18922
  subs = await readdir6(subDir, { withFileTypes: true });
@@ -18784,7 +18924,7 @@ async function collectLiveSidecars(projectsRoot) {
18784
18924
  continue;
18785
18925
  }
18786
18926
  for (const s of subs) {
18787
- if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join27(subDir, s.name));
18927
+ if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join28(subDir, s.name));
18788
18928
  }
18789
18929
  }
18790
18930
  }
@@ -18796,7 +18936,7 @@ function shortId(path2) {
18796
18936
  return base.slice(0, 8);
18797
18937
  }
18798
18938
  async function migrateAdminWebchatSidecars(opts = {}) {
18799
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join27(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
18939
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join28(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
18800
18940
  const usersFile = opts.usersFile ?? USERS_FILE;
18801
18941
  const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
18802
18942
  const resolveName = opts.resolveName ?? defaultResolveName;
@@ -19622,6 +19762,26 @@ function createWebchatChannelRoutes(deps) {
19622
19762
  deps.onReceived?.(key, messageId);
19623
19763
  return c.json({ ok: true });
19624
19764
  });
19765
+ app53.post("/webchat-channel/turn-end", async (c) => {
19766
+ const body = await c.req.json().catch(() => null);
19767
+ const key = body?.key;
19768
+ const sessionId = body?.sessionId;
19769
+ const replied = body?.replied;
19770
+ if (typeof key !== "string" || typeof sessionId !== "string" || typeof replied !== "boolean") {
19771
+ return c.json({ error: "key, sessionId and replied required" }, 400);
19772
+ }
19773
+ const k = key.slice(0, 8);
19774
+ const sid = sessionId.slice(0, 8);
19775
+ if (replied) {
19776
+ return c.json({ ok: true, delivered: "reply" });
19777
+ }
19778
+ const finalBytes = typeof body?.finalBytes === "number" && Number.isFinite(body.finalBytes) ? body.finalBytes : 0;
19779
+ if (finalBytes <= 0) {
19780
+ return c.json({ ok: true, delivered: "none" });
19781
+ }
19782
+ console.error(`[webchat-native] op=turn-undelivered channel=webchat key=${k} sessionId=${sid} bytes=${finalBytes}`);
19783
+ return c.json({ ok: true, delivered: "undelivered" });
19784
+ });
19625
19785
  return app53;
19626
19786
  }
19627
19787
 
@@ -20114,7 +20274,7 @@ function broadcastAdminShutdown(reason) {
20114
20274
 
20115
20275
  // ../lib/entitlement/src/index.ts
20116
20276
  import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
20117
- import { existsSync as existsSync30, readFileSync as readFileSync30, statSync as statSync12 } from "fs";
20277
+ import { existsSync as existsSync30, readFileSync as readFileSync31, statSync as statSync12 } from "fs";
20118
20278
  import { resolve as resolve32 } from "path";
20119
20279
 
20120
20280
  // ../lib/entitlement/src/canonicalize.ts
@@ -20178,7 +20338,7 @@ function resolveEntitlement(brand, account) {
20178
20338
  function verifyAndResolve(brand, entitlementPath, account) {
20179
20339
  let pubkeyPem;
20180
20340
  try {
20181
- pubkeyPem = readFileSync30(pubkeyPath(brand), "utf-8");
20341
+ pubkeyPem = readFileSync31(pubkeyPath(brand), "utf-8");
20182
20342
  } catch (err) {
20183
20343
  return logResolved(anonymousFallback("pubkey-missing"), {
20184
20344
  reason: "pubkey-missing"
@@ -20192,7 +20352,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
20192
20352
  }
20193
20353
  let envelope;
20194
20354
  try {
20195
- envelope = JSON.parse(readFileSync30(entitlementPath, "utf-8"));
20355
+ envelope = JSON.parse(readFileSync31(entitlementPath, "utf-8"));
20196
20356
  } catch {
20197
20357
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
20198
20358
  }
@@ -20353,14 +20513,14 @@ function clientFrom(c) {
20353
20513
  );
20354
20514
  }
20355
20515
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
20356
- var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join28(PLATFORM_ROOT8, "config", "brand.json") : "";
20516
+ var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join29(PLATFORM_ROOT8, "config", "brand.json") : "";
20357
20517
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
20358
20518
  if (BRAND_JSON_PATH && !existsSync31(BRAND_JSON_PATH)) {
20359
20519
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
20360
20520
  }
20361
20521
  if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
20362
20522
  try {
20363
- const parsed = JSON.parse(readFileSync31(BRAND_JSON_PATH, "utf-8"));
20523
+ const parsed = JSON.parse(readFileSync32(BRAND_JSON_PATH, "utf-8"));
20364
20524
  BRAND = { ...BRAND, ...parsed };
20365
20525
  } catch (err) {
20366
20526
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -20379,11 +20539,11 @@ var brandLoginOpts = {
20379
20539
  bodyFont: BRAND.defaultFonts?.body,
20380
20540
  logoContainsName: !!BRAND.logoContainsName
20381
20541
  };
20382
- var ALIAS_DOMAINS_PATH = join28(homedir3(), BRAND.configDir, "alias-domains.json");
20542
+ var ALIAS_DOMAINS_PATH = join29(homedir3(), BRAND.configDir, "alias-domains.json");
20383
20543
  function loadAliasDomains() {
20384
20544
  try {
20385
20545
  if (!existsSync31(ALIAS_DOMAINS_PATH)) return null;
20386
- const parsed = JSON.parse(readFileSync31(ALIAS_DOMAINS_PATH, "utf-8"));
20546
+ const parsed = JSON.parse(readFileSync32(ALIAS_DOMAINS_PATH, "utf-8"));
20387
20547
  if (!Array.isArray(parsed)) {
20388
20548
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
20389
20549
  return null;
@@ -20411,7 +20571,7 @@ var app52 = new Hono();
20411
20571
  var nativeFileFollowers = /* @__PURE__ */ new Map();
20412
20572
  var waGateway = new WaGateway({
20413
20573
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
20414
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
20574
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
20415
20575
  // Task 751 — file delivery on the native channel: resolve the platform
20416
20576
  // account + path validation here and funnel through the shared send core.
20417
20577
  sendDocument: async ({ senderId, accountId, filePath, caption }) => {
@@ -20427,7 +20587,7 @@ var waGateway = new WaGateway({
20427
20587
  caption,
20428
20588
  accountId,
20429
20589
  maxyAccountId,
20430
- platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, ".."))
20590
+ platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, ".."))
20431
20591
  });
20432
20592
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
20433
20593
  },
@@ -20902,7 +21062,7 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
20902
21062
  const ext = "." + filename.split(".").pop()?.toLowerCase();
20903
21063
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
20904
21064
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
20905
- const body = readFileSync31(filePath);
21065
+ const body = readFileSync32(filePath);
20906
21066
  return c.body(body, 200, {
20907
21067
  "Content-Type": contentType,
20908
21068
  "Cache-Control": "public, max-age=3600"
@@ -20932,7 +21092,7 @@ app52.get("/generated/:filename", (c) => {
20932
21092
  const ext = "." + filename.split(".").pop()?.toLowerCase();
20933
21093
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
20934
21094
  console.log(`[generated] serve file=${filename} status=200`);
20935
- const body = readFileSync31(filePath);
21095
+ const body = readFileSync32(filePath);
20936
21096
  return c.body(body, 200, {
20937
21097
  "Content-Type": contentType,
20938
21098
  "Cache-Control": "public, max-age=86400"
@@ -20947,19 +21107,29 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
20947
21107
  var brandIconPath = "/brand/maxy-monochrome.png";
20948
21108
  if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
20949
21109
  try {
20950
- const fullBrand = JSON.parse(readFileSync31(BRAND_JSON_PATH, "utf-8"));
21110
+ const fullBrand = JSON.parse(readFileSync32(BRAND_JSON_PATH, "utf-8"));
20951
21111
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
20952
21112
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
20953
21113
  } catch {
20954
21114
  }
20955
21115
  }
21116
+ var DEFAULT_PRIMARY_CONTAINER = { label: "Project", term: "Projects" };
21117
+ var rawPrimaryContainer = BRAND.primaryContainer;
21118
+ var primaryContainerValid = !!rawPrimaryContainer && typeof rawPrimaryContainer.label === "string" && typeof rawPrimaryContainer.term === "string";
21119
+ var brandPrimaryContainer = primaryContainerValid ? rawPrimaryContainer : DEFAULT_PRIMARY_CONTAINER;
21120
+ if (primaryContainerValid) {
21121
+ console.log(`[brand] op=inject primaryContainer=${brandPrimaryContainer.label}/${brandPrimaryContainer.term}`);
21122
+ } else {
21123
+ console.log(`[brand] op=primaryContainer-default reason=${rawPrimaryContainer ? "malformed" : "absent"} brand=${BRAND.hostname}`);
21124
+ }
20956
21125
  var brandScript = `<script>window.__BRAND__=${JSON.stringify({
20957
21126
  productName: BRAND.productName,
20958
21127
  hostname: BRAND.hostname,
20959
21128
  marketingUrl: BRAND.marketingUrl,
20960
21129
  logo: brandLogoPath,
20961
21130
  icon: brandIconPath,
20962
- logoContainsName: !!BRAND.logoContainsName
21131
+ logoContainsName: !!BRAND.logoContainsName,
21132
+ primaryContainer: brandPrimaryContainer
20963
21133
  })}</script>`;
20964
21134
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
20965
21135
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
@@ -20967,7 +21137,7 @@ var brandIconFsPath = resolve33(process.cwd(), "public", brandIconPath.replace(/
20967
21137
  var brandIconExists = existsSync31(brandIconFsPath);
20968
21138
  var SW_SOURCE = (() => {
20969
21139
  try {
20970
- return readFileSync31(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
21140
+ return readFileSync32(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
20971
21141
  } catch {
20972
21142
  return null;
20973
21143
  }
@@ -20975,9 +21145,9 @@ var SW_SOURCE = (() => {
20975
21145
  function readInstalledVersion() {
20976
21146
  try {
20977
21147
  if (!PLATFORM_ROOT8) return "unknown";
20978
- const versionFile = join28(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
21148
+ const versionFile = join29(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
20979
21149
  if (!existsSync31(versionFile)) return "unknown";
20980
- const content = readFileSync31(versionFile, "utf-8").trim();
21150
+ const content = readFileSync32(versionFile, "utf-8").trim();
20981
21151
  return content || "unknown";
20982
21152
  } catch {
20983
21153
  return "unknown";
@@ -21018,7 +21188,7 @@ var clientErrorReporterScript = `<script>
21018
21188
  function cachedHtml(file) {
21019
21189
  let html = htmlCache.get(file);
21020
21190
  if (!html) {
21021
- html = readFileSync31(resolve33(process.cwd(), "public", file), "utf-8");
21191
+ html = readFileSync32(resolve33(process.cwd(), "public", file), "utf-8");
21022
21192
  const productNameEsc = escapeHtml(BRAND.productName);
21023
21193
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
21024
21194
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -21035,13 +21205,13 @@ ${clientErrorReporterScript}
21035
21205
  return html;
21036
21206
  }
21037
21207
  function loadBrandingCache(agentSlug) {
21038
- const configDir2 = join28(homedir3(), BRAND.configDir);
21208
+ const configDir2 = join29(homedir3(), BRAND.configDir);
21039
21209
  try {
21040
21210
  const accountId = getDefaultAccountId();
21041
21211
  if (!accountId) return null;
21042
- const cachePath = join28(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21212
+ const cachePath = join29(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
21043
21213
  if (!existsSync31(cachePath)) return null;
21044
- return JSON.parse(readFileSync31(cachePath, "utf-8"));
21214
+ return JSON.parse(readFileSync32(cachePath, "utf-8"));
21045
21215
  } catch {
21046
21216
  return null;
21047
21217
  }
@@ -21136,7 +21306,7 @@ app52.use("/vnc-popout.html", logViewerFetch);
21136
21306
  app52.get("/vnc-popout.html", (c) => {
21137
21307
  let html = htmlCache.get("vnc-popout.html");
21138
21308
  if (!html) {
21139
- html = readFileSync31(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21309
+ html = readFileSync32(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21140
21310
  const name = escapeHtml(BRAND.productName);
21141
21311
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
21142
21312
  html = html.replace("</head>", ` ${brandScript}
@@ -21347,7 +21517,7 @@ async function runAdminUserReconcileTick() {
21347
21517
  console.error("[adminuser-self-heal] skip reason=no-users-file");
21348
21518
  return;
21349
21519
  }
21350
- const usersRaw = readFileSync31(USERS_FILE, "utf-8").trim();
21520
+ const usersRaw = readFileSync32(USERS_FILE, "utf-8").trim();
21351
21521
  if (!usersRaw) {
21352
21522
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
21353
21523
  return;
@@ -21461,7 +21631,7 @@ if (bootAccountConfig?.whatsapp) {
21461
21631
  }
21462
21632
  init({
21463
21633
  configDir: configDirForWhatsApp,
21464
- platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, "..")),
21634
+ platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join29(__dirname, "..")),
21465
21635
  accountConfig: bootAccountConfig,
21466
21636
  onMessage: async (msg) => {
21467
21637
  if (msg.isOwnerMirror) {