@rubytech/create-maxy-code 0.1.463 → 0.1.465

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 (114) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/docs/superpowers/plans/2026-07-17-task-1736-retire-mcp-stderr-tee.md +397 -0
  3. package/payload/platform/docs/superpowers/specs/2026-07-17-task-1736-retire-mcp-stderr-tee-design.md +128 -0
  4. package/payload/platform/lib/graph-mcp/dist/index.js +5 -24
  5. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  6. package/payload/platform/lib/graph-mcp/src/index.ts +7 -25
  7. package/payload/platform/lib/mcp-spawn-tee/dist/index.d.ts +2 -2
  8. package/payload/platform/lib/mcp-spawn-tee/dist/index.js +2 -2
  9. package/payload/platform/lib/mcp-spawn-tee/src/__tests__/spawn-tee.test.ts +0 -25
  10. package/payload/platform/lib/mcp-spawn-tee/src/index.ts +2 -2
  11. package/payload/platform/lib/models/dist/index.d.ts.map +1 -1
  12. package/payload/platform/lib/models/dist/index.js +7 -2
  13. package/payload/platform/lib/models/dist/index.js.map +1 -1
  14. package/payload/platform/lib/models/src/index.ts +7 -2
  15. package/payload/platform/package.json +2 -2
  16. package/payload/platform/plugins/admin/lib/mcp-spawn-tee/index.js +2 -2
  17. package/payload/platform/plugins/admin/mcp/dist/index.js +0 -2
  18. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  19. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +27 -36
  20. package/payload/platform/plugins/aeo/lib/mcp-spawn-tee/index.js +2 -2
  21. package/payload/platform/plugins/aeo/mcp/dist/index.js +0 -2
  22. package/payload/platform/plugins/aeo/mcp/dist/index.js.map +1 -1
  23. package/payload/platform/plugins/browser/lib/mcp-spawn-tee/index.js +2 -2
  24. package/payload/platform/plugins/browser/mcp/dist/index.js +0 -2
  25. package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
  26. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +10 -1
  27. package/payload/platform/plugins/connector/lib/mcp-spawn-tee/index.js +2 -2
  28. package/payload/platform/plugins/connector/mcp/dist/index.js +0 -2
  29. package/payload/platform/plugins/connector/mcp/dist/index.js.map +1 -1
  30. package/payload/platform/plugins/contacts/lib/mcp-spawn-tee/index.js +2 -2
  31. package/payload/platform/plugins/contacts/mcp/dist/index.js +0 -2
  32. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  33. package/payload/platform/plugins/docs/references/claudeai-connectors.md +16 -7
  34. package/payload/platform/plugins/docs/references/plugins-guide.md +10 -28
  35. package/payload/platform/plugins/email/lib/mcp-spawn-tee/index.js +2 -2
  36. package/payload/platform/plugins/email/mcp/dist/index.js +0 -2
  37. package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
  38. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +2 -2
  39. package/payload/platform/plugins/filesystem/mcp/dist/index.js +0 -2
  40. package/payload/platform/plugins/filesystem/mcp/dist/index.js.map +1 -1
  41. package/payload/platform/plugins/graph-viewer/lib/mcp-spawn-tee/index.js +2 -2
  42. package/payload/platform/plugins/graph-viewer/mcp/dist/index.js +0 -2
  43. package/payload/platform/plugins/graph-viewer/mcp/dist/index.js.map +1 -1
  44. package/payload/platform/plugins/memory/lib/mcp-spawn-tee/index.js +2 -2
  45. package/payload/platform/plugins/memory/mcp/dist/index.js +0 -2
  46. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/scripts/boot-smoke.sh +0 -1
  48. package/payload/platform/plugins/outlook/lib/mcp-spawn-tee/index.js +2 -2
  49. package/payload/platform/plugins/quickbooks/lib/mcp-spawn-tee/index.js +2 -2
  50. package/payload/platform/plugins/quickbooks/mcp/dist/index.js +0 -2
  51. package/payload/platform/plugins/quickbooks/mcp/dist/index.js.map +1 -1
  52. package/payload/platform/plugins/replicate/lib/mcp-spawn-tee/index.js +2 -2
  53. package/payload/platform/plugins/replicate/mcp/dist/index.js +0 -2
  54. package/payload/platform/plugins/replicate/mcp/dist/index.js.map +1 -1
  55. package/payload/platform/plugins/scheduling/PLUGIN.md +21 -9
  56. package/payload/platform/plugins/scheduling/lib/mcp-spawn-tee/index.js +2 -2
  57. package/payload/platform/plugins/scheduling/mcp/dist/index.js +0 -2
  58. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  59. package/payload/platform/plugins/storage-broker/lib/mcp-spawn-tee/index.js +2 -2
  60. package/payload/platform/plugins/storage-broker/mcp/dist/index.js +0 -2
  61. package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
  62. package/payload/platform/plugins/telegram/lib/mcp-spawn-tee/index.js +2 -2
  63. package/payload/platform/plugins/telegram/mcp/dist/index.js +0 -2
  64. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
  65. package/payload/platform/plugins/url-get/lib/mcp-spawn-tee/index.js +2 -2
  66. package/payload/platform/plugins/url-get/mcp/dist/index.js +0 -2
  67. package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
  68. package/payload/platform/plugins/voice-mirror/lib/mcp-spawn-tee/index.js +2 -2
  69. package/payload/platform/plugins/voice-mirror/mcp/dist/index.d.ts.map +1 -1
  70. package/payload/platform/plugins/voice-mirror/mcp/dist/index.js +0 -2
  71. package/payload/platform/plugins/voice-mirror/mcp/dist/index.js.map +1 -1
  72. package/payload/platform/plugins/whatsapp/lib/mcp-spawn-tee/index.js +2 -2
  73. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +0 -2
  74. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  75. package/payload/platform/plugins/work/lib/mcp-spawn-tee/index.js +2 -2
  76. package/payload/platform/plugins/work/mcp/dist/index.js +0 -2
  77. package/payload/platform/plugins/work/mcp/dist/index.js.map +1 -1
  78. package/payload/platform/plugins/workflows/lib/mcp-spawn-tee/index.js +2 -2
  79. package/payload/platform/plugins/workflows/mcp/dist/index.js +0 -2
  80. package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
  81. package/payload/platform/scripts/__tests__/logs-rotate.test.sh +2 -2
  82. package/payload/platform/scripts/check-architecture-skill-no-drift.mjs +21 -14
  83. package/payload/platform/scripts/logs-rotate.sh +6 -6
  84. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  85. package/payload/platform/services/claude-session-manager/dist/http-server.js +23 -2
  86. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  87. package/payload/platform/services/claude-session-manager/dist/index.js +27 -2
  88. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +8 -0
  90. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +11 -0
  92. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  93. package/payload/platform/services/claude-session-manager/dist/passive-print-surface.d.ts +10 -0
  94. package/payload/platform/services/claude-session-manager/dist/passive-print-surface.d.ts.map +1 -0
  95. package/payload/platform/services/claude-session-manager/dist/passive-print-surface.js +31 -0
  96. package/payload/platform/services/claude-session-manager/dist/passive-print-surface.js.map +1 -0
  97. package/payload/platform/services/claude-session-manager/dist/print-child-launcher.d.ts +8 -0
  98. package/payload/platform/services/claude-session-manager/dist/print-child-launcher.d.ts.map +1 -0
  99. package/payload/platform/services/claude-session-manager/dist/print-child-launcher.js +50 -0
  100. package/payload/platform/services/claude-session-manager/dist/print-child-launcher.js.map +1 -0
  101. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +10 -0
  102. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +23 -2
  104. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +2 -2
  106. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  107. package/payload/server/server.js +699 -559
  108. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +0 -69
  109. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +0 -1
  110. package/payload/platform/lib/mcp-stderr-tee/dist/index.js +0 -189
  111. package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +0 -1
  112. package/payload/platform/lib/mcp-stderr-tee/package.json +0 -7
  113. package/payload/platform/lib/mcp-stderr-tee/src/index.ts +0 -197
  114. package/payload/platform/lib/mcp-stderr-tee/tsconfig.json +0 -8
@@ -1422,10 +1422,10 @@ var createStreamBody = (stream2) => {
1422
1422
  });
1423
1423
  return body;
1424
1424
  };
1425
- var getStats = (path2) => {
1425
+ var getStats = (path3) => {
1426
1426
  let stats;
1427
1427
  try {
1428
- stats = statSync(path2);
1428
+ stats = statSync(path3);
1429
1429
  } catch {
1430
1430
  }
1431
1431
  return stats;
@@ -1468,21 +1468,21 @@ var serveStatic = (options = { root: "" }) => {
1468
1468
  return next();
1469
1469
  }
1470
1470
  }
1471
- let path2 = join(
1471
+ let path3 = join(
1472
1472
  root,
1473
1473
  !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename
1474
1474
  );
1475
- let stats = getStats(path2);
1475
+ let stats = getStats(path3);
1476
1476
  if (stats && stats.isDirectory()) {
1477
1477
  const indexFile2 = options.index ?? "index.html";
1478
- path2 = join(path2, indexFile2);
1479
- stats = getStats(path2);
1478
+ path3 = join(path3, indexFile2);
1479
+ stats = getStats(path3);
1480
1480
  }
1481
1481
  if (!stats) {
1482
- await options.onNotFound?.(path2, c);
1482
+ await options.onNotFound?.(path3, c);
1483
1483
  return next();
1484
1484
  }
1485
- const mimeType = getMimeType(path2);
1485
+ const mimeType = getMimeType(path3);
1486
1486
  c.header("Content-Type", mimeType || "application/octet-stream");
1487
1487
  if (options.precompressed && (!mimeType || COMPRESSIBLE_CONTENT_TYPE_REGEX.test(mimeType))) {
1488
1488
  const acceptEncodingSet = new Set(
@@ -1492,12 +1492,12 @@ var serveStatic = (options = { root: "" }) => {
1492
1492
  if (!acceptEncodingSet.has(encoding)) {
1493
1493
  continue;
1494
1494
  }
1495
- const precompressedStats = getStats(path2 + ENCODINGS[encoding]);
1495
+ const precompressedStats = getStats(path3 + ENCODINGS[encoding]);
1496
1496
  if (precompressedStats) {
1497
1497
  c.header("Content-Encoding", encoding);
1498
1498
  c.header("Vary", "Accept-Encoding", { append: true });
1499
1499
  stats = precompressedStats;
1500
- path2 = path2 + ENCODINGS[encoding];
1500
+ path3 = path3 + ENCODINGS[encoding];
1501
1501
  break;
1502
1502
  }
1503
1503
  }
@@ -1511,7 +1511,7 @@ var serveStatic = (options = { root: "" }) => {
1511
1511
  result = c.body(null);
1512
1512
  } else if (!range) {
1513
1513
  c.header("Content-Length", size.toString());
1514
- result = c.body(createStreamBody(createReadStream(path2)), 200);
1514
+ result = c.body(createStreamBody(createReadStream(path3)), 200);
1515
1515
  } else {
1516
1516
  c.header("Accept-Ranges", "bytes");
1517
1517
  c.header("Date", stats.birthtime.toUTCString());
@@ -1522,12 +1522,12 @@ var serveStatic = (options = { root: "" }) => {
1522
1522
  end = size - 1;
1523
1523
  }
1524
1524
  const chunksize = end - start + 1;
1525
- const stream2 = createReadStream(path2, { start, end });
1525
+ const stream2 = createReadStream(path3, { start, end });
1526
1526
  c.header("Content-Length", chunksize.toString());
1527
1527
  c.header("Content-Range", `bytes ${start}-${end}/${stats.size}`);
1528
1528
  result = c.body(createStreamBody(stream2), 206);
1529
1529
  }
1530
- await options.onFound?.(path2, c);
1530
+ await options.onFound?.(path3, c);
1531
1531
  return result;
1532
1532
  };
1533
1533
  };
@@ -2176,7 +2176,7 @@ function listCredentialAccountIds(configDir2) {
2176
2176
  // app/lib/whatsapp/session.ts
2177
2177
  import { randomUUID } from "crypto";
2178
2178
  import fsSync2 from "fs";
2179
- import fs2 from "fs/promises";
2179
+ import fs3 from "fs/promises";
2180
2180
  import { inspect } from "util";
2181
2181
  import {
2182
2182
  Browsers,
@@ -2187,7 +2187,63 @@ import {
2187
2187
  useMultiFileAuthState
2188
2188
  } from "@whiskeysockets/baileys";
2189
2189
  import { createRequire } from "module";
2190
+
2191
+ // app/lib/whatsapp/baileys-debug-window.ts
2192
+ import fs2 from "fs";
2193
+ import path2 from "path";
2190
2194
  var TAG2 = "[whatsapp:session]";
2195
+ var SENTINEL_BASENAME = "baileys-debug-until";
2196
+ var STAT_INTERVAL_MS = 1e3;
2197
+ var VALID_LEVELS = /* @__PURE__ */ new Set(["trace", "debug", "info"]);
2198
+ var INACTIVE = { active: false, level: "warn" };
2199
+ var cache = /* @__PURE__ */ new Map();
2200
+ function sentinelPath(authDir) {
2201
+ return path2.join(authDir, SENTINEL_BASENAME);
2202
+ }
2203
+ function readSentinel(authDir, nowMs) {
2204
+ let raw;
2205
+ try {
2206
+ raw = fs2.readFileSync(sentinelPath(authDir), "utf-8");
2207
+ } catch {
2208
+ return { state: INACTIVE, deadlineMs: null };
2209
+ }
2210
+ const line = raw.split("\n", 1)[0].trim();
2211
+ if (!line) return { state: INACTIVE, deadlineMs: null };
2212
+ const tokens = line.split(/\s+/);
2213
+ const deadlineMs = Date.parse(tokens[0]);
2214
+ if (Number.isNaN(deadlineMs) || deadlineMs <= nowMs) {
2215
+ return { state: INACTIVE, deadlineMs: null };
2216
+ }
2217
+ let level = "trace";
2218
+ if (tokens.length > 1) {
2219
+ if (!VALID_LEVELS.has(tokens[1])) return { state: INACTIVE, deadlineMs: null };
2220
+ level = tokens[1];
2221
+ }
2222
+ return { state: { active: true, level }, deadlineMs };
2223
+ }
2224
+ function resolveDebugWindow(authDir, nowMs = Date.now()) {
2225
+ if (!authDir) return INACTIVE;
2226
+ const prev = cache.get(authDir);
2227
+ if (prev && nowMs - prev.checkedAtMs < STAT_INTERVAL_MS) {
2228
+ return prev.state;
2229
+ }
2230
+ const { state, deadlineMs } = readSentinel(authDir, nowMs);
2231
+ const wasActive = prev?.state.active ?? false;
2232
+ if (!wasActive && state.active) {
2233
+ console.error(
2234
+ `${TAG2} op=baileys-debug-window-open account=${path2.basename(authDir)} level=${state.level} until=${new Date(deadlineMs).toISOString()}`
2235
+ );
2236
+ } else if (wasActive && !state.active) {
2237
+ console.error(
2238
+ `${TAG2} op=baileys-debug-window-close account=${path2.basename(authDir)}`
2239
+ );
2240
+ }
2241
+ cache.set(authDir, { checkedAtMs: nowMs, state });
2242
+ return state;
2243
+ }
2244
+
2245
+ // app/lib/whatsapp/session.ts
2246
+ var TAG3 = "[whatsapp:session]";
2191
2247
  var WIRE_TAG = "[whatsapp:wire]";
2192
2248
  var baileysPkgVersion = (() => {
2193
2249
  try {
@@ -2197,7 +2253,7 @@ var baileysPkgVersion = (() => {
2197
2253
  return "unknown";
2198
2254
  }
2199
2255
  })();
2200
- console.error(`${TAG2} baileys package loaded version=${baileysPkgVersion}`);
2256
+ console.error(`${TAG3} baileys package loaded version=${baileysPkgVersion}`);
2201
2257
  var __BAILEYS_TAG = "[whatsapp:baileys]";
2202
2258
  var INSPECT_OPTS = {
2203
2259
  depth: null,
@@ -2222,7 +2278,8 @@ function bindingsToPrefix(bindings) {
2222
2278
  }
2223
2279
  return parts.join(" ");
2224
2280
  }
2225
- function createBaileysLogger(bindings = {}) {
2281
+ var LEVEL_RANK = { trace: 10, debug: 20, info: 30 };
2282
+ function createBaileysLogger(bindings = {}, authDir) {
2226
2283
  const prefix = Object.keys(bindings).length > 0 ? bindingsToPrefix(bindings) : "";
2227
2284
  const emit = (level, args) => {
2228
2285
  const serialized = args.map(serializeArg).join(" ");
@@ -2232,23 +2289,32 @@ function createBaileysLogger(bindings = {}) {
2232
2289
  console.error(__BAILEYS_TAG, level, serialized);
2233
2290
  }
2234
2291
  };
2292
+ const emitIfWindowed = (rank, level, args) => {
2293
+ const w = resolveDebugWindow(authDir);
2294
+ if (w.active && rank >= LEVEL_RANK[w.level]) {
2295
+ emit(level, args);
2296
+ }
2297
+ };
2235
2298
  return {
2236
- level: "warn",
2237
- trace: () => {
2238
- },
2239
- debug: () => {
2240
- },
2241
- info: () => {
2299
+ // Live getter: Baileys reads config.logger.level per-frame and (verified on
2300
+ // the pinned version) never shallow-copies the logger, so the window's level
2301
+ // reaches socket.js:449/465 as it changes. A Baileys that spread the logger
2302
+ // would freeze this to one value — revisit on upgrade.
2303
+ get level() {
2304
+ return resolveDebugWindow(authDir).level;
2242
2305
  },
2306
+ trace: (...args) => emitIfWindowed(LEVEL_RANK.trace, "TRACE", args),
2307
+ debug: (...args) => emitIfWindowed(LEVEL_RANK.debug, "DEBUG", args),
2308
+ info: (...args) => emitIfWindowed(LEVEL_RANK.info, "INFO", args),
2243
2309
  warn: (...args) => emit("WARN", args),
2244
2310
  error: (...args) => emit("ERROR", args),
2245
2311
  fatal: (...args) => emit("FATAL", args),
2246
- child: (childBindings) => createBaileysLogger({ ...bindings, ...childBindings ?? {} })
2312
+ child: (childBindings) => createBaileysLogger({ ...bindings, ...childBindings ?? {} }, authDir)
2247
2313
  };
2248
2314
  }
2249
2315
  var credsSaveQueue = Promise.resolve();
2250
2316
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2251
- console.error(`${TAG2} draining credential save queue\u2026`);
2317
+ console.error(`${TAG3} draining credential save queue\u2026`);
2252
2318
  const timer2 = new Promise(
2253
2319
  (resolve37) => setTimeout(() => resolve37("timeout"), timeoutMs)
2254
2320
  );
@@ -2257,9 +2323,9 @@ async function drainCredsSaveQueue(timeoutMs = 5e3) {
2257
2323
  timer2
2258
2324
  ]);
2259
2325
  if (result === "timeout") {
2260
- console.error(`${TAG2} credential save queue drain timed out after ${timeoutMs}ms`);
2326
+ console.error(`${TAG3} credential save queue drain timed out after ${timeoutMs}ms`);
2261
2327
  } else {
2262
- console.error(`${TAG2} credential save queue drained`);
2328
+ console.error(`${TAG3} credential save queue drained`);
2263
2329
  }
2264
2330
  }
2265
2331
  function readCredsJsonRaw2(filePath) {
@@ -2282,53 +2348,53 @@ async function safeSaveCreds(authDir, saveCreds) {
2282
2348
  JSON.parse(raw);
2283
2349
  fsSync2.copyFileSync(credsPath, backupPath);
2284
2350
  } catch (err) {
2285
- console.warn(`${TAG2} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
2351
+ console.warn(`${TAG3} backup pre-copy failed (parse or write) authDir=${authDir}: ${String(err)}`);
2286
2352
  }
2287
2353
  }
2288
2354
  } catch (err) {
2289
- console.warn(`${TAG2} backup preparation failed authDir=${authDir}: ${String(err)}`);
2355
+ console.warn(`${TAG3} backup preparation failed authDir=${authDir}: ${String(err)}`);
2290
2356
  }
2291
2357
  try {
2292
2358
  await Promise.resolve(saveCreds());
2293
2359
  const credsPath = resolveCredsPath(authDir);
2294
2360
  try {
2295
- const stats = await fs2.stat(credsPath);
2361
+ const stats = await fs3.stat(credsPath);
2296
2362
  if (stats.size <= 1) {
2297
- console.error(`${TAG2} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
2363
+ console.error(`${TAG3} WARNING: creds file empty/truncated after save path=${credsPath} size=${stats.size}`);
2298
2364
  } else {
2299
- console.error(`${TAG2} creds saved and verified path=${credsPath} size=${stats.size}`);
2365
+ console.error(`${TAG3} creds saved and verified path=${credsPath} size=${stats.size}`);
2300
2366
  }
2301
2367
  } catch (statErr) {
2302
- console.error(`${TAG2} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
2368
+ console.error(`${TAG3} creds NOT found on disk after save path=${credsPath}: ${String(statErr)}`);
2303
2369
  }
2304
2370
  } catch (err) {
2305
- console.error(`${TAG2} failed saving creds to ${authDir}: ${String(err)}`);
2371
+ console.error(`${TAG3} failed saving creds to ${authDir}: ${String(err)}`);
2306
2372
  }
2307
2373
  }
2308
2374
  function enqueueSaveCreds(authDir, saveCreds) {
2309
2375
  credsSaveQueue = credsSaveQueue.then(() => safeSaveCreds(authDir, saveCreds)).catch((err) => {
2310
- console.error(`${TAG2} creds save queue error: ${String(err)}`);
2376
+ console.error(`${TAG3} creds save queue error: ${String(err)}`);
2311
2377
  });
2312
2378
  }
2313
2379
  async function createWaSocket(opts) {
2314
2380
  const { authDir, onQr, onConnectionUpdate, silent, correlationId, account, getCodeIssuedTs } = opts;
2315
2381
  const cid = correlationId ?? "none";
2316
2382
  const acct = account ?? "unknown";
2317
- await fs2.mkdir(authDir, { recursive: true });
2383
+ await fs3.mkdir(authDir, { recursive: true });
2318
2384
  maybeRestoreCredsFromBackup(authDir);
2319
2385
  const { state, saveCreds } = await useMultiFileAuthState(authDir);
2320
2386
  const { version } = await fetchLatestBaileysVersion();
2321
2387
  if (!silent) {
2322
2388
  const preCensus = credsCensus(authDir);
2323
2389
  console.error(
2324
- `${TAG2} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
2390
+ `${TAG3} op=creds-pre cid=${cid} account=${acct} credsPresent=${preCensus.credsPresent} registered=${preCensus.registered} hasAccount=${preCensus.hasAccountSignature}`
2325
2391
  );
2326
2392
  }
2327
2393
  const openTs = Date.now();
2328
2394
  console.error(
2329
- `${TAG2} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
2395
+ `${TAG3} op=socket-open cid=${cid} account=${acct} baileysVersion=${baileysPkgVersion} waProtocolVersion=${version.join(".")} openTs=${openTs}`
2330
2396
  );
2331
- const baileysLogger = createBaileysLogger();
2397
+ const baileysLogger = createBaileysLogger({}, authDir);
2332
2398
  const sock = makeWASocket({
2333
2399
  auth: {
2334
2400
  creds: state.creds,
@@ -2343,7 +2409,7 @@ async function createWaSocket(opts) {
2343
2409
  fireInitQueries: false
2344
2410
  });
2345
2411
  sock.ev.on("creds.update", () => {
2346
- console.error(`${TAG2} creds.update received \u2014 saving to ${authDir}`);
2412
+ console.error(`${TAG3} creds.update received \u2014 saving to ${authDir}`);
2347
2413
  enqueueSaveCreds(authDir, saveCreds);
2348
2414
  });
2349
2415
  let qrSequence = 0;
@@ -2363,14 +2429,14 @@ async function createWaSocket(opts) {
2363
2429
  parts.push(`disconnectReason=${formatError(update.lastDisconnect.error)}`);
2364
2430
  }
2365
2431
  if (!silent && parts.length > 0) {
2366
- console.error(`${TAG2} connection.update ${parts.join(" ")}`);
2432
+ console.error(`${TAG3} connection.update ${parts.join(" ")}`);
2367
2433
  }
2368
2434
  if (update.qr && !silent) {
2369
2435
  const now = Date.now();
2370
2436
  const sinceLastRefMs = lastQrTs ? now - lastQrTs : null;
2371
2437
  lastQrTs = now;
2372
2438
  console.error(
2373
- `${TAG2} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
2439
+ `${TAG3} op=qr cid=${cid} qr=#${qrSequence} sinceOpenMs=${now - openTs} sinceLastRefMs=${sinceLastRefMs ?? "n/a"}`
2374
2440
  );
2375
2441
  }
2376
2442
  if (update.isNewLogin === true && !pairSuccessLogged && !silent) {
@@ -2379,7 +2445,7 @@ async function createWaSocket(opts) {
2379
2445
  const sinceCodeIssuedMs = codeIssuedTs ? Date.now() - codeIssuedTs : null;
2380
2446
  const afterCensus = credsCensus(authDir);
2381
2447
  console.error(
2382
- `${TAG2} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
2448
+ `${TAG3} op=pair-success cid=${cid} account=${acct} sinceCodeIssuedMs=${sinceCodeIssuedMs ?? "n/a"} hasAccountAfter=${afterCensus.hasAccountSignature}`
2383
2449
  );
2384
2450
  }
2385
2451
  if (update.qr && onQr) {
@@ -2389,25 +2455,25 @@ async function createWaSocket(opts) {
2389
2455
  const status = getStatusCode(update.lastDisconnect?.error);
2390
2456
  if (!silent) {
2391
2457
  console.error(
2392
- `${TAG2} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
2458
+ `${TAG3} op=close cid=${cid} account=${acct} status=${status ?? "unknown"} reason="${formatError(update.lastDisconnect?.error)}" socketLifeMs=${Date.now() - openTs} kind=${closeKind(status, everOpen)}`
2393
2459
  );
2394
2460
  }
2395
2461
  if (status === DisconnectReason.loggedOut && !silent) {
2396
- console.error(`${TAG2} session logged out (401) \u2014 re-link required`);
2462
+ console.error(`${TAG3} session logged out (401) \u2014 re-link required`);
2397
2463
  }
2398
2464
  }
2399
2465
  if (update.connection === "open") {
2400
2466
  everOpen = true;
2401
- if (!silent) console.error(`${TAG2} connected`);
2467
+ if (!silent) console.error(`${TAG3} connected`);
2402
2468
  }
2403
2469
  onConnectionUpdate?.(update);
2404
2470
  } catch (err) {
2405
- console.error(`${TAG2} connection.update handler error: ${String(err)}`);
2471
+ console.error(`${TAG3} connection.update handler error: ${String(err)}`);
2406
2472
  }
2407
2473
  });
2408
2474
  if (sock.ws && typeof sock.ws.on === "function") {
2409
2475
  sock.ws.on("error", (err) => {
2410
- console.error(`${TAG2} WebSocket error: ${String(err)}`);
2476
+ console.error(`${TAG3} WebSocket error: ${String(err)}`);
2411
2477
  });
2412
2478
  sock.ws.on("CB:message", (node) => {
2413
2479
  const attrs = node?.attrs ?? {};
@@ -2547,7 +2613,7 @@ function withTimeout(label, promise, timeoutMs) {
2547
2613
  }
2548
2614
 
2549
2615
  // app/lib/whatsapp/init-queries.ts
2550
- var TAG3 = "[whatsapp:init]";
2616
+ var TAG4 = "[whatsapp:init]";
2551
2617
  var DEFAULT_QUERY_TIMEOUT_MS = 15e3;
2552
2618
  var INSPECT_OPTS2 = {
2553
2619
  depth: null,
@@ -2575,20 +2641,20 @@ async function runInitQueries(sock, ctx) {
2575
2641
  elapsedMs: 0
2576
2642
  };
2577
2643
  console.error(
2578
- `${TAG3} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
2644
+ `${TAG4} runInitQueries start account=${ctx.accountId} attempt=${ctx.attempt} selfPhone=${ctx.selfPhone ?? "unknown"} authDir=${ctx.authDir} queryTimeoutMs=${queryTimeoutMs}`
2579
2645
  );
2580
2646
  console.error(
2581
- `${TAG3} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
2647
+ `${TAG4} fetchProps SKIPPED account=${ctx.accountId} reason=no-platform-consumer`
2582
2648
  );
2583
2649
  if (ctx.signal?.aborted) {
2584
2650
  result.aborted = true;
2585
2651
  result.elapsedMs = Date.now() - startedAt;
2586
2652
  console.error(
2587
- `${TAG3} runInitQueries aborted before start account=${ctx.accountId}`
2653
+ `${TAG4} runInitQueries aborted before start account=${ctx.accountId}`
2588
2654
  );
2589
2655
  return result;
2590
2656
  }
2591
- console.error(`${TAG3} fetchBlocklist start account=${ctx.accountId}`);
2657
+ console.error(`${TAG4} fetchBlocklist start account=${ctx.accountId}`);
2592
2658
  try {
2593
2659
  const blocklist = await withTimeout(
2594
2660
  "fetchBlocklist",
@@ -2598,24 +2664,24 @@ async function runInitQueries(sock, ctx) {
2598
2664
  const count = Array.isArray(blocklist) ? blocklist.length : 0;
2599
2665
  result.blocklistOk = true;
2600
2666
  console.error(
2601
- `${TAG3} fetchBlocklist ok account=${ctx.accountId} count=${count}`
2667
+ `${TAG4} fetchBlocklist ok account=${ctx.accountId} count=${count}`
2602
2668
  );
2603
2669
  } catch (err) {
2604
2670
  const formatted = formatErr(err);
2605
2671
  result.errors.blocklist = formatted;
2606
2672
  console.error(
2607
- `${TAG3} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
2673
+ `${TAG4} fetchBlocklist FAILED account=${ctx.accountId}: ${formatted}`
2608
2674
  );
2609
2675
  }
2610
2676
  if (ctx.signal?.aborted) {
2611
2677
  result.aborted = true;
2612
2678
  result.elapsedMs = Date.now() - startedAt;
2613
2679
  console.error(
2614
- `${TAG3} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
2680
+ `${TAG4} runInitQueries aborted mid-sequence account=${ctx.accountId} after=fetchBlocklist`
2615
2681
  );
2616
2682
  return result;
2617
2683
  }
2618
- console.error(`${TAG3} fetchPrivacySettings start account=${ctx.accountId}`);
2684
+ console.error(`${TAG4} fetchPrivacySettings start account=${ctx.accountId}`);
2619
2685
  try {
2620
2686
  const settings = await withTimeout(
2621
2687
  "fetchPrivacySettings",
@@ -2625,18 +2691,18 @@ async function runInitQueries(sock, ctx) {
2625
2691
  );
2626
2692
  result.privacySettingsOk = true;
2627
2693
  console.error(
2628
- `${TAG3} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
2694
+ `${TAG4} fetchPrivacySettings ok account=${ctx.accountId} keys=${settings ? Object.keys(settings).join(",") : ""}`
2629
2695
  );
2630
2696
  } catch (err) {
2631
2697
  const formatted = formatErr(err);
2632
2698
  result.errors.privacySettings = formatted;
2633
2699
  console.error(
2634
- `${TAG3} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
2700
+ `${TAG4} fetchPrivacySettings FAILED account=${ctx.accountId}: ${formatted}`
2635
2701
  );
2636
2702
  }
2637
2703
  result.elapsedMs = Date.now() - startedAt;
2638
2704
  console.error(
2639
- `${TAG3} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
2705
+ `${TAG4} runInitQueries done account=${ctx.accountId} blocklistOk=${result.blocklistOk} privacySettingsOk=${result.privacySettingsOk} elapsedMs=${result.elapsedMs}`
2640
2706
  );
2641
2707
  return result;
2642
2708
  }
@@ -3143,6 +3209,12 @@ function readAllConversations(platformAccountId) {
3143
3209
  return out;
3144
3210
  }
3145
3211
 
3212
+ // app/lib/whatsapp/echo-already-recorded.ts
3213
+ function echoAlreadyRecorded(args) {
3214
+ const messageId = `whatsapp-live:${args.accountId}:${args.remoteJid}:${args.msgKeyId}`;
3215
+ return args.readConversation(args.platformAccountId, args.canonicalRemoteJid).some((r) => r.messageId === messageId);
3216
+ }
3217
+
3146
3218
  // app/lib/whatsapp/managed-account.ts
3147
3219
  function normalizeEntry(v) {
3148
3220
  return typeof v === "string" ? { account: v, mode: "active" } : { account: v.managesAccount, mode: v.mode ?? "active" };
@@ -3527,17 +3599,17 @@ var INBOUND_TTL_MS = 20 * 6e4;
3527
3599
  var INBOUND_MAX = 5e3;
3528
3600
  var agentSentCache = /* @__PURE__ */ new Map();
3529
3601
  var inboundCache = /* @__PURE__ */ new Map();
3530
- function pruneCache(cache2, ttl, max) {
3602
+ function pruneCache(cache3, ttl, max) {
3531
3603
  const now = Date.now();
3532
- for (const [key, entry] of cache2) {
3533
- if (now - entry.ts > ttl) cache2.delete(key);
3604
+ for (const [key, entry] of cache3) {
3605
+ if (now - entry.ts > ttl) cache3.delete(key);
3534
3606
  }
3535
- if (cache2.size > max) {
3536
- const excess = cache2.size - max;
3607
+ if (cache3.size > max) {
3608
+ const excess = cache3.size - max;
3537
3609
  let count = 0;
3538
- for (const key of cache2.keys()) {
3610
+ for (const key of cache3.keys()) {
3539
3611
  if (count >= excess) break;
3540
- cache2.delete(key);
3612
+ cache3.delete(key);
3541
3613
  count++;
3542
3614
  }
3543
3615
  }
@@ -3571,7 +3643,7 @@ function isDuplicateInbound(key) {
3571
3643
  }
3572
3644
 
3573
3645
  // app/lib/whatsapp/inbound/self-chat.ts
3574
- var TAG4 = "[whatsapp:self-chat]";
3646
+ var TAG5 = "[whatsapp:self-chat]";
3575
3647
  function normalizeJid(jid) {
3576
3648
  const m = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|lid)$/i);
3577
3649
  return m ? `${m[1]}@${m[2].toLowerCase()}` : jid;
@@ -3591,10 +3663,10 @@ function evaluateSelfChatCommand(input) {
3591
3663
  const connAtMs = input.lastConnectedAtMs ?? 0;
3592
3664
  const msgId = input.msgId ?? "?";
3593
3665
  console.error(
3594
- `${TAG4} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
3666
+ `${TAG5} op=seen account=${input.accountId} msgId=${msgId} tsMs=${tsMs} connAtMs=${connAtMs} fromMe=true`
3595
3667
  );
3596
3668
  const skip = (reason) => {
3597
- console.error(`${TAG4} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
3669
+ console.error(`${TAG5} op=skip account=${input.accountId} msgId=${msgId} reason=${reason}`);
3598
3670
  return { kind: "skip", reason };
3599
3671
  };
3600
3672
  if (input.msgId && isAgentSentMessage(input.msgId)) return skip("echo");
@@ -3602,13 +3674,13 @@ function evaluateSelfChatCommand(input) {
3602
3674
  if (isDuplicateInbound(`${input.accountId}:${input.remoteJid}:${input.msgId}`)) return skip("duplicate");
3603
3675
  if (!input.text) return skip("no-text");
3604
3676
  console.error(
3605
- `${TAG4} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
3677
+ `${TAG5} op=dispatch account=${input.accountId} msgId=${msgId} bytes=${Buffer.byteLength(input.text, "utf8")} agentType=admin`
3606
3678
  );
3607
3679
  return { kind: "dispatch", text: input.text };
3608
3680
  }
3609
3681
 
3610
3682
  // app/lib/whatsapp/activity.ts
3611
- var TAG5 = "[whatsapp:activity]";
3683
+ var TAG6 = "[whatsapp:activity]";
3612
3684
  var RECENT_EVENTS_MAX = 200;
3613
3685
  var counters = /* @__PURE__ */ new Map();
3614
3686
  var recentEvents = [];
@@ -3636,10 +3708,10 @@ function recordActivity(event) {
3636
3708
  recentEvents.splice(0, recentEvents.length - RECENT_EVENTS_MAX);
3637
3709
  }
3638
3710
  console.error(
3639
- `${TAG5} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
3711
+ `${TAG6} channel-activity: direction=${direction} account=${accountId} jid=${jid}` + (messageType ? ` type=${messageType}` : "")
3640
3712
  );
3641
3713
  } catch (err) {
3642
- console.error(`${TAG5} recording failed: ${String(err)}`);
3714
+ console.error(`${TAG6} recording failed: ${String(err)}`);
3643
3715
  }
3644
3716
  }
3645
3717
  function getChannelActivity(accountId) {
@@ -3660,7 +3732,7 @@ function getChannelActivity(accountId) {
3660
3732
  }
3661
3733
 
3662
3734
  // app/lib/whatsapp/outbound/send.ts
3663
- var TAG6 = "[whatsapp:outbound]";
3735
+ var TAG7 = "[whatsapp:outbound]";
3664
3736
  function buildQuoted(q) {
3665
3737
  return {
3666
3738
  key: {
@@ -3681,14 +3753,14 @@ async function sendTextMessage(sock, to, text, opts) {
3681
3753
  const messageId = result?.key?.id;
3682
3754
  if (messageId) {
3683
3755
  trackAgentSentMessage(messageId);
3684
- console.error(`${TAG6} sent text to=${jid} id=${messageId}`);
3756
+ console.error(`${TAG7} sent text to=${jid} id=${messageId}`);
3685
3757
  }
3686
3758
  if (opts?.accountId) {
3687
3759
  recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: "text" });
3688
3760
  }
3689
3761
  return { success: true, messageId: messageId ?? void 0 };
3690
3762
  } catch (err) {
3691
- console.error(`${TAG6} send failed to=${to}: ${String(err)}`);
3763
+ console.error(`${TAG7} send failed to=${to}: ${String(err)}`);
3692
3764
  return { success: false, error: String(err) };
3693
3765
  }
3694
3766
  }
@@ -3758,20 +3830,20 @@ async function sendMediaMessage(sock, to, media, opts) {
3758
3830
  const messageId = result?.key?.id;
3759
3831
  if (messageId) {
3760
3832
  trackAgentSentMessage(messageId);
3761
- console.error(`${TAG6} sent ${media.type} to=${jid} id=${messageId}`);
3833
+ console.error(`${TAG7} sent ${media.type} to=${jid} id=${messageId}`);
3762
3834
  }
3763
3835
  if (opts?.accountId) {
3764
3836
  recordActivity({ accountId: opts.accountId, direction: "outbound", jid, messageType: media.type });
3765
3837
  }
3766
3838
  return { success: true, messageId: messageId ?? void 0 };
3767
3839
  } catch (err) {
3768
- console.error(`${TAG6} send media failed to=${to}: ${String(err)}`);
3840
+ console.error(`${TAG7} send media failed to=${to}: ${String(err)}`);
3769
3841
  return { success: false, error: String(err) };
3770
3842
  }
3771
3843
  }
3772
3844
 
3773
3845
  // app/lib/whatsapp/persist-message.ts
3774
- var TAG7 = "[whatsapp-persist]";
3846
+ var TAG8 = "[whatsapp-persist]";
3775
3847
  var sessionWriteLocks = /* @__PURE__ */ new Map();
3776
3848
  async function persistWhatsAppMessage(input) {
3777
3849
  if (!input.body && !input.attachmentId || !input.msgKeyId) return null;
@@ -3822,14 +3894,14 @@ async function persistWhatsAppMessage(input) {
3822
3894
  };
3823
3895
  const result = await session.run(cypher, params);
3824
3896
  if (result.records.length === 0) {
3825
- console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
3897
+ console.error(`${TAG8} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
3826
3898
  return null;
3827
3899
  }
3828
3900
  const rec = result.records[0];
3829
3901
  const senderElementId = rec.get("senderElementId");
3830
3902
  const senderReused = rec.get("senderReused") === true;
3831
3903
  console.error(
3832
- `${TAG7} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
3904
+ `${TAG8} sender-resolved telephone=${senderTelephone} nodeKind=Person elementId=${senderElementId} reused=${senderReused}`
3833
3905
  );
3834
3906
  const diskRecord = {
3835
3907
  messageId,
@@ -3851,18 +3923,18 @@ async function persistWhatsAppMessage(input) {
3851
3923
  const { created } = appendMessage(input.platformAccountId, diskRecord, altDedupeRemoteJids);
3852
3924
  const ms = Date.now() - t0;
3853
3925
  if (!created) {
3854
- console.error(`${TAG7} noop reason=existing-message messageId=${messageId}`);
3926
+ console.error(`${TAG8} noop reason=existing-message messageId=${messageId}`);
3855
3927
  return { messageId, created: false, senderElementId };
3856
3928
  }
3857
3929
  console.error(
3858
- `${TAG7} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
3930
+ `${TAG8} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} attachment=${input.attachmentId ?? "none"} ms=${ms}`
3859
3931
  );
3860
3932
  return { messageId, created: true, senderElementId };
3861
3933
  } catch (err) {
3862
3934
  const ms = Date.now() - t0;
3863
3935
  const reason = sanitizeReason(err);
3864
3936
  console.error(
3865
- `${TAG7} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
3937
+ `${TAG8} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
3866
3938
  );
3867
3939
  return null;
3868
3940
  } finally {
@@ -3895,7 +3967,7 @@ function sanitizeReason(err) {
3895
3967
  }
3896
3968
 
3897
3969
  // app/lib/whatsapp/ensure-conversation.ts
3898
- var TAG8 = "[whatsapp-persist]";
3970
+ var TAG9 = "[whatsapp-persist]";
3899
3971
  async function ensureWhatsAppConversation(input) {
3900
3972
  const t0 = Date.now();
3901
3973
  try {
@@ -3907,19 +3979,19 @@ async function ensureWhatsAppConversation(input) {
3907
3979
  const ms = Date.now() - t0;
3908
3980
  if (!result.sessionId) {
3909
3981
  console.error(
3910
- `${TAG8} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
3982
+ `${TAG9} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-sessionId ms=${ms}`
3911
3983
  );
3912
3984
  return null;
3913
3985
  }
3914
3986
  console.error(
3915
- `${TAG8} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp created=${result.created} ms=${ms}`
3987
+ `${TAG9} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp created=${result.created} ms=${ms}`
3916
3988
  );
3917
3989
  return { sessionId: result.sessionId, created: result.created };
3918
3990
  } catch (err) {
3919
3991
  const ms = Date.now() - t0;
3920
3992
  const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
3921
3993
  console.error(
3922
- `${TAG8} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
3994
+ `${TAG9} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
3923
3995
  );
3924
3996
  return null;
3925
3997
  }
@@ -3929,16 +4001,16 @@ async function ensureWhatsAppConversation(input) {
3929
4001
  import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
3930
4002
  import { resolve as resolve3 } from "path";
3931
4003
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3932
- var cache = /* @__PURE__ */ new Map();
4004
+ var cache2 = /* @__PURE__ */ new Map();
3933
4005
  function enumerateValidAccountIds(accountsDir) {
3934
- const cached3 = cache.get(accountsDir);
4006
+ const cached3 = cache2.get(accountsDir);
3935
4007
  if (cached3 !== void 0) return cached3;
3936
4008
  let names;
3937
4009
  try {
3938
4010
  names = readdirSync2(accountsDir);
3939
4011
  } catch (err) {
3940
4012
  if (err.code === "ENOENT") {
3941
- cache.set(accountsDir, []);
4013
+ cache2.set(accountsDir, []);
3942
4014
  return [];
3943
4015
  }
3944
4016
  throw err;
@@ -3955,7 +4027,7 @@ function enumerateValidAccountIds(accountsDir) {
3955
4027
  if (code === "ENOENT") continue;
3956
4028
  }
3957
4029
  }
3958
- cache.set(accountsDir, valid);
4030
+ cache2.set(accountsDir, valid);
3959
4031
  return valid;
3960
4032
  }
3961
4033
  function resolveHouseOrSoleAccountId(accountsDir) {
@@ -4553,7 +4625,7 @@ async function storeComponentArtefact(accountId, attachmentId, mimeType, content
4553
4625
  }
4554
4626
 
4555
4627
  // app/lib/whatsapp/inbound/media.ts
4556
- var TAG9 = "[whatsapp:media]";
4628
+ var TAG10 = "[whatsapp:media]";
4557
4629
  var MEDIA_DIR = "/tmp/maxy-media";
4558
4630
  var DOWNLOAD_TIMEOUT_MS = 45e3;
4559
4631
  function deterministicAttachmentId(seed) {
@@ -4641,25 +4713,25 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
4641
4713
  }
4642
4714
  );
4643
4715
  if (!buffer || buffer.length === 0) {
4644
- console.error(`${TAG9} primary download returned empty, trying direct fallback`);
4716
+ console.error(`${TAG10} primary download returned empty, trying direct fallback`);
4645
4717
  const downloadable = getDownloadableContent(content);
4646
4718
  if (downloadable) {
4647
4719
  try {
4648
4720
  const stream2 = await downloadContentFromMessage(downloadable.downloadable, downloadable.mediaType);
4649
4721
  buffer = await streamToBuffer(stream2);
4650
4722
  } catch (fallbackErr) {
4651
- console.error(`${TAG9} direct download fallback failed: ${String(fallbackErr)}`);
4723
+ console.error(`${TAG10} direct download fallback failed: ${String(fallbackErr)}`);
4652
4724
  }
4653
4725
  }
4654
4726
  }
4655
4727
  if (!buffer || buffer.length === 0) {
4656
- console.error(`${TAG9} download failed: empty buffer for ${mimetype ?? "unknown"}`);
4728
+ console.error(`${TAG10} download failed: empty buffer for ${mimetype ?? "unknown"}`);
4657
4729
  return void 0;
4658
4730
  }
4659
4731
  if (buffer.length > maxBytes) {
4660
4732
  const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
4661
4733
  const limitMB = (maxBytes / (1024 * 1024)).toFixed(0);
4662
- console.error(`${TAG9} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
4734
+ console.error(`${TAG10} media too large type=${mimetype ?? "unknown"} size=${sizeMB}MB limit=${limitMB}MB`);
4663
4735
  return void 0;
4664
4736
  }
4665
4737
  return {
@@ -4671,7 +4743,7 @@ async function downloadInboundMediaBuffer(msg, sock, opts) {
4671
4743
  timeoutMs
4672
4744
  );
4673
4745
  } catch (err) {
4674
- console.error(`${TAG9} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
4746
+ console.error(`${TAG10} media download failed type=${mimetype ?? "unknown"} error=${String(err)}`);
4675
4747
  return void 0;
4676
4748
  }
4677
4749
  }
@@ -4683,7 +4755,7 @@ async function downloadInboundMedia(msg, sock, opts) {
4683
4755
  const filePath = join6(MEDIA_DIR, `${randomUUID4()}.${ext}`);
4684
4756
  await writeFile2(filePath, res.buffer);
4685
4757
  const sizeKB = (res.buffer.length / 1024).toFixed(0);
4686
- console.error(`${TAG9} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
4758
+ console.error(`${TAG10} media downloaded type=${res.mimetype} size=${sizeKB}KB path=${filePath}`);
4687
4759
  return { path: filePath, mimetype: res.mimetype, size: res.buffer.length };
4688
4760
  }
4689
4761
  async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
@@ -4692,19 +4764,27 @@ async function storeInboundMediaServable(msg, sock, accountId, seed, opts) {
4692
4764
  if (attachmentExists(accountId, attachmentId)) return attachmentId;
4693
4765
  const res = await downloadInboundMediaBufferCached(msg, sock, opts);
4694
4766
  if (!res) {
4695
- console.error(`${TAG9} servable-store skip reason=no-buffer id=${attachmentId}`);
4767
+ console.error(`${TAG10} servable-store skip reason=no-buffer id=${attachmentId}`);
4696
4768
  return null;
4697
4769
  }
4698
4770
  const filename = res.filename ?? `${attachmentId}.${mimeToExt(res.mimetype)}`;
4699
4771
  await storeMediaBuffer(accountId, attachmentId, filename, res.mimetype, res.buffer);
4700
- console.error(`${TAG9} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
4772
+ console.error(`${TAG10} servable-store ok id=${attachmentId} mime=${res.mimetype} bytes=${res.buffer.length}`);
4701
4773
  return attachmentId;
4702
4774
  } catch (err) {
4703
- console.error(`${TAG9} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
4775
+ console.error(`${TAG10} servable-store FAIL id=${attachmentId} reason=${String(err).slice(0, 120)}`);
4704
4776
  return null;
4705
4777
  }
4706
4778
  }
4707
4779
 
4780
+ // app/lib/whatsapp/outbound/document-limits.ts
4781
+ var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
4782
+
4783
+ // app/lib/whatsapp/echo-store-max-bytes.ts
4784
+ function echoStoreMaxBytes(args) {
4785
+ return args.fromMe ? WHATSAPP_DOCUMENT_MAX_BYTES : (args.mediaMaxMb ?? 50) * 1024 * 1024;
4786
+ }
4787
+
4708
4788
  // app/lib/whatsapp/inbound/bounded-queue.ts
4709
4789
  function createBoundedQueue(limit, opts) {
4710
4790
  if (!Number.isInteger(limit) || limit < 1) {
@@ -4740,7 +4820,7 @@ function createBoundedQueue(limit, opts) {
4740
4820
  }
4741
4821
 
4742
4822
  // app/lib/whatsapp/inbound/debounce.ts
4743
- var TAG10 = "[whatsapp:debounce]";
4823
+ var TAG11 = "[whatsapp:debounce]";
4744
4824
  var STT_TAG = "[whatsapp:stt-await]";
4745
4825
  function createInboundDebouncer(opts) {
4746
4826
  const { debounceMs, buildKey, onFlush, onError } = opts;
@@ -4771,7 +4851,7 @@ function createInboundDebouncer(opts) {
4771
4851
  pending.delete(key);
4772
4852
  const batchSize = batch.entries.length;
4773
4853
  try {
4774
- console.error(`${TAG10} debounce flush key=${key} batchSize=${batchSize}`);
4854
+ console.error(`${TAG11} debounce flush key=${key} batchSize=${batchSize}`);
4775
4855
  const result = onFlush(batch.entries);
4776
4856
  if (result && typeof result.catch === "function") {
4777
4857
  result.catch(onError);
@@ -4851,7 +4931,7 @@ function combineInboundBatch(entries) {
4851
4931
  }
4852
4932
 
4853
4933
  // app/lib/whatsapp/opening-hours.ts
4854
- var TAG11 = "[whatsapp:hours]";
4934
+ var TAG12 = "[whatsapp:hours]";
4855
4935
  async function isBusinessOpen(accountId) {
4856
4936
  try {
4857
4937
  const timezone = await resolveTimezone(accountId);
@@ -4869,7 +4949,7 @@ async function isBusinessOpen(accountId) {
4869
4949
  { accountId, dayOfWeek, previousDayOfWeek }
4870
4950
  );
4871
4951
  if (result.records.length === 0) {
4872
- console.error(`${TAG11} [${accountId}] business hours check: no opening hours configured, treating as open`);
4952
+ console.error(`${TAG12} [${accountId}] business hours check: no opening hours configured, treating as open`);
4873
4953
  return { open: true, reason: "no opening hours configured" };
4874
4954
  }
4875
4955
  const specs = result.records.filter((r) => r.get("opens") != null && r.get("closes") != null).map((r) => ({
@@ -4878,7 +4958,7 @@ async function isBusinessOpen(accountId) {
4878
4958
  closes: String(r.get("closes")).trim()
4879
4959
  }));
4880
4960
  if (specs.length === 0) {
4881
- console.error(`${TAG11} [${accountId}] business hours check: no opening hours configured, treating as open`);
4961
+ console.error(`${TAG12} [${accountId}] business hours check: no opening hours configured, treating as open`);
4882
4962
  return { open: true, reason: "no opening hours configured" };
4883
4963
  }
4884
4964
  for (const spec of specs) {
@@ -4888,7 +4968,7 @@ async function isBusinessOpen(accountId) {
4888
4968
  if (spec.opens > spec.closes && currentTime < spec.closes) {
4889
4969
  const hoursStr = `${spec.opens}-${spec.closes} (${spec.day})`;
4890
4970
  console.error(
4891
- `${TAG11} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
4971
+ `${TAG12} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
4892
4972
  );
4893
4973
  return {
4894
4974
  open: true,
@@ -4901,7 +4981,7 @@ async function isBusinessOpen(accountId) {
4901
4981
  } else if (isTimeInRange(currentTime, spec.opens, spec.closes)) {
4902
4982
  const hoursStr = `${spec.opens}-${spec.closes}`;
4903
4983
  console.error(
4904
- `${TAG11} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
4984
+ `${TAG12} [${accountId}] business hours check: open (day=${dayOfWeek}, time=${currentTime}, hours=${hoursStr})`
4905
4985
  );
4906
4986
  return {
4907
4987
  open: true,
@@ -4915,7 +4995,7 @@ async function isBusinessOpen(accountId) {
4915
4995
  const todaySpecs = specs.filter((s) => s.day === dayOfWeek);
4916
4996
  const allHours = todaySpecs.map((s) => `${s.opens}-${s.closes}`).join(", ") || "none today";
4917
4997
  console.error(
4918
- `${TAG11} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
4998
+ `${TAG12} [${accountId}] business hours check: closed (day=${dayOfWeek}, time=${currentTime}, hours=${allHours})`
4919
4999
  );
4920
5000
  return {
4921
5001
  open: false,
@@ -4929,7 +5009,7 @@ async function isBusinessOpen(accountId) {
4929
5009
  }
4930
5010
  } catch (err) {
4931
5011
  console.error(
4932
- `${TAG11} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
5012
+ `${TAG12} [${accountId}] business hours check failed, treating as open: ${err instanceof Error ? err.message : String(err)}`
4933
5013
  );
4934
5014
  return { open: true, reason: "hours check failed (treating as open)" };
4935
5015
  }
@@ -4975,7 +5055,7 @@ import { execFile } from "child_process";
4975
5055
  import { unlink, stat } from "fs/promises";
4976
5056
  import { promisify } from "util";
4977
5057
  var execFileAsync = promisify(execFile);
4978
- var TAG12 = "[stt]";
5058
+ var TAG13 = "[stt]";
4979
5059
  var WHISPER_BINARY = process.env.WHISPER_BINARY ?? "/opt/whisper.cpp/build/bin/whisper-cli";
4980
5060
  var WHISPER_MODEL = process.env.WHISPER_MODEL ?? "/opt/whisper.cpp/models/ggml-base.bin";
4981
5061
  var WHISPER_TIMEOUT_MS = 20 * 60 * 1e3;
@@ -4994,11 +5074,11 @@ async function transcribe(audioPath, mimetype) {
4994
5074
  const s = await stat(audioPath);
4995
5075
  audioBytes = s.size;
4996
5076
  } catch {
4997
- console.error(`${TAG12} failed: file not readable path=${audioPath}`);
5077
+ console.error(`${TAG13} failed: file not readable path=${audioPath}`);
4998
5078
  return void 0;
4999
5079
  }
5000
5080
  console.error(
5001
- `${TAG12} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
5081
+ `${TAG13} start provider=whisper-local audio_bytes=${audioBytes} mimetype=${mimetype} path=${audioPath}`
5002
5082
  );
5003
5083
  const wavPath = audioPath.replace(/\.[^.]+$/, "") + ".wav";
5004
5084
  try {
@@ -5017,7 +5097,7 @@ async function transcribe(audioPath, mimetype) {
5017
5097
  ], { timeout: 3e4 });
5018
5098
  } catch (err) {
5019
5099
  const reason = err instanceof Error ? err.message : String(err);
5020
- console.error(`${TAG12} failed: ffmpeg conversion error=${reason}`);
5100
+ console.error(`${TAG13} failed: ffmpeg conversion error=${reason}`);
5021
5101
  return void 0;
5022
5102
  }
5023
5103
  try {
@@ -5033,20 +5113,20 @@ async function transcribe(audioPath, mimetype) {
5033
5113
  const durationMs = Date.now() - startMs;
5034
5114
  const { text, count: stripped } = stripSpecialTokens(stdout.trim());
5035
5115
  if (!text) {
5036
- console.error(`${TAG12} failed: whisper returned empty output duration_ms=${durationMs}`);
5116
+ console.error(`${TAG13} failed: whisper returned empty output duration_ms=${durationMs}`);
5037
5117
  return void 0;
5038
5118
  }
5039
5119
  const langMatch = stderr.match(/auto-detected language:\s*(\w+)/);
5040
5120
  const language = langMatch?.[1] ?? "unknown";
5041
5121
  const words = text.split(/\s+/).filter(Boolean).length;
5042
5122
  console.error(
5043
- `${TAG12} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
5123
+ `${TAG13} done provider=whisper-local duration_ms=${durationMs} words=${words} stripped=${stripped} lang=${language}`
5044
5124
  );
5045
5125
  return { text, language, durationMs };
5046
5126
  } catch (err) {
5047
5127
  const durationMs = Date.now() - startMs;
5048
5128
  const reason = err instanceof Error ? err.message : String(err);
5049
- console.error(`${TAG12} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
5129
+ console.error(`${TAG13} failed provider=whisper-local duration_ms=${durationMs} error=${reason}`);
5050
5130
  return void 0;
5051
5131
  } finally {
5052
5132
  unlink(wavPath).catch(() => {
@@ -5065,7 +5145,7 @@ var WaReplyError = class extends Error {
5065
5145
  };
5066
5146
 
5067
5147
  // app/lib/whatsapp/manager.ts
5068
- var TAG13 = "[whatsapp:manager]";
5148
+ var TAG14 = "[whatsapp:manager]";
5069
5149
  var MAX_RECONNECT_ATTEMPTS = 10;
5070
5150
  var MESSAGE_STORE_MAX = 500;
5071
5151
  var APPEND_MEDIA_CONCURRENCY = 3;
@@ -5092,7 +5172,7 @@ function storeMessage(storeKey, entry) {
5092
5172
  if (entries.length > MESSAGE_STORE_MAX) {
5093
5173
  const trimmed = entries.length - MESSAGE_STORE_MAX;
5094
5174
  entries.splice(0, trimmed);
5095
- console.error(`${TAG13} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
5175
+ console.error(`${TAG14} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
5096
5176
  }
5097
5177
  }
5098
5178
  function deriveCacheKey(input) {
@@ -5135,7 +5215,7 @@ function buildSelfChatDispatch(input) {
5135
5215
  }
5136
5216
  async function init(opts) {
5137
5217
  if (initialized) {
5138
- console.error(`${TAG13} already initialized`);
5218
+ console.error(`${TAG14} already initialized`);
5139
5219
  return;
5140
5220
  }
5141
5221
  configDir = opts.configDir;
@@ -5143,20 +5223,20 @@ async function init(opts) {
5143
5223
  loadConfig(opts.accountConfig);
5144
5224
  const accountIds = listCredentialAccountIds(configDir);
5145
5225
  if (accountIds.length === 0) {
5146
- console.error(`${TAG13} init: no stored WhatsApp credentials found`);
5226
+ console.error(`${TAG14} init: no stored WhatsApp credentials found`);
5147
5227
  initialized = true;
5148
5228
  return;
5149
5229
  }
5150
- console.error(`${TAG13} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
5230
+ console.error(`${TAG14} init: found ${accountIds.length} credentialed account(s): ${accountIds.join(", ")}`);
5151
5231
  initialized = true;
5152
5232
  for (const accountId of accountIds) {
5153
5233
  const accountCfg = whatsAppConfig.accounts?.[accountId];
5154
5234
  if (accountCfg?.enabled === false) {
5155
- console.error(`${TAG13} skipping disabled account=${accountId}`);
5235
+ console.error(`${TAG14} skipping disabled account=${accountId}`);
5156
5236
  continue;
5157
5237
  }
5158
5238
  startConnection(accountId).catch((err) => {
5159
- console.error(`${TAG13} failed to auto-start account=${accountId}: ${formatError(err)}`);
5239
+ console.error(`${TAG14} failed to auto-start account=${accountId}: ${formatError(err)}`);
5160
5240
  });
5161
5241
  }
5162
5242
  }
@@ -5165,7 +5245,7 @@ async function startConnection(accountId) {
5165
5245
  const authDir = resolveAuthDir(configDir, accountId);
5166
5246
  const hasAuth = await authExists(authDir);
5167
5247
  if (!hasAuth) {
5168
- console.error(`${TAG13} no credentials for account=${accountId}`);
5248
+ console.error(`${TAG14} no credentials for account=${accountId}`);
5169
5249
  return;
5170
5250
  }
5171
5251
  let platformAccountId;
@@ -5216,11 +5296,11 @@ async function stopConnection(accountId) {
5216
5296
  conn.sock.ev.removeAllListeners("creds.update");
5217
5297
  conn.sock.ws?.close?.();
5218
5298
  } catch (err) {
5219
- console.warn(`${TAG13} socket cleanup error during stop account=${accountId}: ${String(err)}`);
5299
+ console.warn(`${TAG14} socket cleanup error during stop account=${accountId}: ${String(err)}`);
5220
5300
  }
5221
5301
  }
5222
5302
  connections.delete(accountId);
5223
- console.error(`${TAG13} stopped account=${accountId}`);
5303
+ console.error(`${TAG14} stopped account=${accountId}`);
5224
5304
  }
5225
5305
  function getStatus() {
5226
5306
  return Array.from(connections.values()).map((conn) => ({
@@ -5299,9 +5379,9 @@ async function registerLoginSocket(accountId, sock, authDir) {
5299
5379
  monitorInbound(conn);
5300
5380
  try {
5301
5381
  await sock.sendPresenceUpdate("available");
5302
- console.error(`${TAG13} presence set to available account=${accountId}`);
5382
+ console.error(`${TAG14} presence set to available account=${accountId}`);
5303
5383
  } catch (err) {
5304
- console.error(`${TAG13} presence update failed account=${accountId}: ${String(err)}`);
5384
+ console.error(`${TAG14} presence update failed account=${accountId}: ${String(err)}`);
5305
5385
  }
5306
5386
  await runInitQueries(sock, {
5307
5387
  accountId,
@@ -5312,7 +5392,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
5312
5392
  });
5313
5393
  watchForDisconnect(conn);
5314
5394
  attachSelfIdRefreshOnCredsUpdate(conn);
5315
- console.error(`${TAG13} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
5395
+ console.error(`${TAG14} registered login socket for account=${accountId} phone=${conn.selfPhone ?? "unknown"}`);
5316
5396
  }
5317
5397
  function attachSelfIdRefreshOnCredsUpdate(conn) {
5318
5398
  if (!conn.sock) return;
@@ -5357,7 +5437,7 @@ async function shutdown() {
5357
5437
  const ids = Array.from(connections.keys());
5358
5438
  await Promise.all(ids.map((id) => stopConnection(id)));
5359
5439
  initialized = false;
5360
- console.error(`${TAG13} shutdown complete`);
5440
+ console.error(`${TAG14} shutdown complete`);
5361
5441
  }
5362
5442
  function loadConfig(accountConfig) {
5363
5443
  try {
@@ -5368,19 +5448,19 @@ function loadConfig(accountConfig) {
5368
5448
  if (result.ok) {
5369
5449
  whatsAppConfig = result.data;
5370
5450
  if (result.droppedKeys.length > 0) {
5371
- console.error(`${TAG13} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
5451
+ console.error(`${TAG14} config: dropped unknown keys=[${result.droppedKeys.join(",")}] preserved known config`);
5372
5452
  }
5373
5453
  } else {
5374
- console.error(`${TAG13} config validation failed: ${result.error.message}`);
5454
+ console.error(`${TAG14} config validation failed: ${result.error.message}`);
5375
5455
  whatsAppConfig = {};
5376
5456
  }
5377
5457
  const diskAdmin = Array.isArray(wa.adminPhones) ? wa.adminPhones : [];
5378
5458
  if (diskAdmin.length > 0 && (whatsAppConfig.adminPhones?.length ?? 0) === 0) {
5379
- console.error(`${TAG13} WARN adminPhones present on disk but empty after load`);
5459
+ console.error(`${TAG14} WARN adminPhones present on disk but empty after load`);
5380
5460
  }
5381
5461
  }
5382
5462
  } catch (err) {
5383
- console.error(`${TAG13} config load error: ${String(err)}`);
5463
+ console.error(`${TAG14} config load error: ${String(err)}`);
5384
5464
  whatsAppConfig = {};
5385
5465
  }
5386
5466
  }
@@ -5391,7 +5471,7 @@ async function connectWithReconnect(conn) {
5391
5471
  let cycleError = null;
5392
5472
  let connectedAt;
5393
5473
  try {
5394
- console.error(`${TAG13} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
5474
+ console.error(`${TAG14} connecting account=${conn.accountId} attempt=${conn.reconnectAttempts}`);
5395
5475
  if (conn.debouncer) {
5396
5476
  await conn.debouncer.destroy();
5397
5477
  conn.debouncer = null;
@@ -5404,7 +5484,7 @@ async function connectWithReconnect(conn) {
5404
5484
  conn.sock = sock;
5405
5485
  applySelfIdentity(conn, sock, "pre-listener");
5406
5486
  monitorInbound(conn);
5407
- console.error(`${TAG13} socket created account=${conn.accountId} \u2014 waiting for connection`);
5487
+ console.error(`${TAG14} socket created account=${conn.accountId} \u2014 waiting for connection`);
5408
5488
  await waitForConnection(sock);
5409
5489
  connectedAt = Date.now();
5410
5490
  conn.connected = true;
@@ -5412,12 +5492,12 @@ async function connectWithReconnect(conn) {
5412
5492
  conn.lastError = void 0;
5413
5493
  conn.terminalReason = void 0;
5414
5494
  applySelfIdentity(conn, sock, "post-connect");
5415
- console.error(`${TAG13} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
5495
+ console.error(`${TAG14} connected account=${conn.accountId} phone=${conn.selfPhone ?? "unknown"}`);
5416
5496
  try {
5417
5497
  await sock.sendPresenceUpdate("available");
5418
- console.error(`${TAG13} presence set to available account=${conn.accountId}`);
5498
+ console.error(`${TAG14} presence set to available account=${conn.accountId}`);
5419
5499
  } catch (err) {
5420
- console.error(`${TAG13} presence update failed account=${conn.accountId}: ${String(err)}`);
5500
+ console.error(`${TAG14} presence update failed account=${conn.accountId}: ${String(err)}`);
5421
5501
  }
5422
5502
  await runInitQueries(sock, {
5423
5503
  accountId: conn.accountId,
@@ -5440,10 +5520,10 @@ async function connectWithReconnect(conn) {
5440
5520
  }
5441
5521
  const classification = classifyDisconnect(err);
5442
5522
  conn.lastError = classification.message;
5443
- console.error(`${TAG13} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
5523
+ console.error(`${TAG14} disconnect account=${conn.accountId}: ${classification.kind} \u2014 ${classification.message}`);
5444
5524
  if (!classification.shouldRetry) {
5445
5525
  conn.terminalReason = classification.message;
5446
- console.error(`${TAG13} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
5526
+ console.error(`${TAG14} terminal disconnect for account=${conn.accountId}, stopping reconnection`);
5447
5527
  return;
5448
5528
  }
5449
5529
  }
@@ -5456,7 +5536,7 @@ async function connectWithReconnect(conn) {
5456
5536
  if (decision.action === "abort") {
5457
5537
  const stuckReason = `GIVING UP account=${conn.accountId} attempts=${decision.finalAttempts}/${maxAttempts} uptimeMsLast=${uptimeMs} stableThresholdMs=${STABLE_UPTIME_MS} lastError=${conn.lastError ?? "(none)"}`;
5458
5538
  console.error(
5459
- `${TAG13} ${stuckReason} \u2014 re-pair via QR or restart required; WhatsApp will not reconnect automatically`
5539
+ `${TAG14} ${stuckReason} \u2014 re-pair via QR or restart required; WhatsApp will not reconnect automatically`
5460
5540
  );
5461
5541
  conn.sessionStuckReason = stuckReason;
5462
5542
  conn.lastError = stuckReason;
@@ -5465,17 +5545,17 @@ async function connectWithReconnect(conn) {
5465
5545
  conn.reconnectAttempts = decision.nextAttempts;
5466
5546
  if (decision.reason === "short-lived") {
5467
5547
  console.error(
5468
- `${TAG13} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
5548
+ `${TAG14} short-lived session account=${conn.accountId} uptimeMs=${uptimeMs} attempt=${decision.nextAttempts}/${maxAttempts} lastError=${conn.lastError ?? "(clean disconnect)"}`
5469
5549
  );
5470
5550
  } else {
5471
5551
  console.error(
5472
- `${TAG13} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
5552
+ `${TAG14} session stable account=${conn.accountId} uptimeMs=${uptimeMs} \u2014 reconnect counter reset`
5473
5553
  );
5474
5554
  }
5475
5555
  if (decision.reason === "short-lived" || cycleError) {
5476
5556
  const delay = computeBackoff(Math.max(1, decision.nextAttempts));
5477
5557
  console.error(
5478
- `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5558
+ `${TAG14} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
5479
5559
  );
5480
5560
  await new Promise((resolve37) => {
5481
5561
  const timer2 = setTimeout(resolve37, delay);
@@ -5509,11 +5589,11 @@ function watchForDisconnect(conn) {
5509
5589
  conn.sock.ev.on("connection.update", (update) => {
5510
5590
  if (update.connection === "close") {
5511
5591
  if (connections.get(conn.accountId) !== conn) return;
5512
- console.error(`${TAG13} socket disconnected for account=${conn.accountId}`);
5592
+ console.error(`${TAG14} socket disconnected for account=${conn.accountId}`);
5513
5593
  conn.connected = false;
5514
5594
  conn.sock = null;
5515
5595
  connectWithReconnect(conn).catch((err) => {
5516
- console.error(`${TAG13} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
5596
+ console.error(`${TAG14} reconnection failed for account=${conn.accountId}: ${formatError(err)}`);
5517
5597
  });
5518
5598
  }
5519
5599
  });
@@ -5539,7 +5619,7 @@ function monitorInbound(conn) {
5539
5619
  if (!conn.sock || !onInboundMessage) return;
5540
5620
  const sock = conn.sock;
5541
5621
  const debounceMs = whatsAppConfig.accounts?.[conn.accountId]?.debounceMs ?? whatsAppConfig.debounceMs ?? 0;
5542
- console.error(`${TAG13} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
5622
+ console.error(`${TAG14} monitorInbound started account=${conn.accountId} debounceMs=${debounceMs}`);
5543
5623
  conn.debouncer = createInboundDebouncer({
5544
5624
  debounceMs,
5545
5625
  buildKey: (payload) => {
@@ -5549,12 +5629,12 @@ function monitorInbound(conn) {
5549
5629
  onFlush: (entries) => {
5550
5630
  if (!onInboundMessage) return;
5551
5631
  if (entries.length > 1) {
5552
- console.error(`${TAG13} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
5632
+ console.error(`${TAG14} debounce: combining ${entries.length} messages account=${conn.accountId} from=${entries[0].senderPhone}`);
5553
5633
  }
5554
5634
  onInboundMessage(combineInboundBatch(entries));
5555
5635
  },
5556
5636
  onError: (err) => {
5557
- console.error(`${TAG13} debounce flush error account=${conn.accountId}: ${String(err)}`);
5637
+ console.error(`${TAG14} debounce flush error account=${conn.accountId}: ${String(err)}`);
5558
5638
  }
5559
5639
  });
5560
5640
  sock.ev.on("messages.upsert", async (upsert) => {
@@ -5586,7 +5666,7 @@ function monitorInbound(conn) {
5586
5666
  }
5587
5667
  const entries = messageStore.get(storeKey);
5588
5668
  console.error(
5589
- `${TAG13} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) account=${conn.platformAccountId}`
5669
+ `${TAG14} stored message ${msg.key.id ?? "?"} for ${remoteJid} (type: ${upsert.type}, store size: ${entries?.length ?? 0}/${MESSAGE_STORE_MAX}) account=${conn.platformAccountId}`
5590
5670
  );
5591
5671
  recordActivity({
5592
5672
  accountId: conn.accountId,
@@ -5621,9 +5701,25 @@ function monitorInbound(conn) {
5621
5701
  remoteJid,
5622
5702
  conn.lidMapping
5623
5703
  );
5624
- const wantsServableMedia = mediaServable && !fromMe && !!conn.sock;
5704
+ const alreadyRecorded = fromMe && echoAlreadyRecorded({
5705
+ readConversation,
5706
+ platformAccountId: conn.platformAccountId,
5707
+ canonicalRemoteJid,
5708
+ accountId: conn.accountId,
5709
+ remoteJid,
5710
+ msgKeyId: msg.key.id
5711
+ });
5712
+ const wantsServableMedia = mediaServable && !!conn.sock && (!fromMe || !alreadyRecorded);
5713
+ if (fromMe && mediaServable && !!conn.sock) {
5714
+ console.error(
5715
+ `${TAG14} fromMe-media-echo msgKey=${msg.key.id} decision=${alreadyRecorded ? "skip" : "store"} reason=${alreadyRecorded ? "already-recorded" : "no-prior-record"}`
5716
+ );
5717
+ }
5625
5718
  const mediaSeed = `whatsapp-live:${conn.accountId}:${canonicalRemoteJid}:${msg.key.id}`;
5626
- const maxBytes = (whatsAppConfig.mediaMaxMb ?? 50) * 1024 * 1024;
5719
+ const maxBytes = echoStoreMaxBytes({
5720
+ fromMe,
5721
+ mediaMaxMb: whatsAppConfig.mediaMaxMb
5722
+ });
5627
5723
  const basePersist = {
5628
5724
  accountId: conn.accountId,
5629
5725
  platformAccountId: conn.platformAccountId,
@@ -5668,7 +5764,7 @@ function monitorInbound(conn) {
5668
5764
  receiptDeferredToWorker = true;
5669
5765
  if (!accepted) {
5670
5766
  console.error(
5671
- `${TAG13} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
5767
+ `${TAG14} append-media overflow account=${conn.platformAccountId} msgKey=${msgKeyId ?? "?"} \u2014 pending cap ${APPEND_MEDIA_MAX_PENDING} reached, text-fallback persist, inline media shed`
5672
5768
  );
5673
5769
  const persisted = await persistWhatsAppMessage({
5674
5770
  ...basePersist,
@@ -5739,14 +5835,16 @@ function monitorInbound(conn) {
5739
5835
  }));
5740
5836
  continue;
5741
5837
  }
5838
+ const skippedSenderJid = isGroupJid(remoteJid) ? msg.key.participant ?? remoteJid : remoteJid;
5839
+ const skippedSenderPhone = await resolveJidToE164(skippedSenderJid, conn.lidMapping) ?? skippedSenderJid;
5742
5840
  console.error(
5743
- `${TAG13} append-type message ${msg.key.id ?? "?"} stored, dispatch skipped account=${conn.accountId}`
5841
+ `${TAG14} append-type message ${msg.key.id ?? "?"} dispatch skipped from=${skippedSenderPhone}${msg.pushName ? ` name=${msg.pushName}` : ""} account=${conn.accountId} (persistence not confirmed here)`
5744
5842
  );
5745
5843
  continue;
5746
5844
  }
5747
5845
  await handleInboundMessage(conn, msg);
5748
5846
  } catch (err) {
5749
- console.error(`${TAG13} inbound handler error account=${conn.accountId}: ${String(err)}`);
5847
+ console.error(`${TAG14} inbound handler error account=${conn.accountId}: ${String(err)}`);
5750
5848
  }
5751
5849
  }
5752
5850
  });
@@ -5802,31 +5900,31 @@ async function handleInboundMessage(conn, msg) {
5802
5900
  const remoteJid = msg.key.remoteJid;
5803
5901
  if (!remoteJid) return;
5804
5902
  if (remoteJid === "status@broadcast") {
5805
- console.error(`${TAG13} drop: status broadcast account=${conn.accountId}`);
5903
+ console.error(`${TAG14} drop: status broadcast account=${conn.accountId}`);
5806
5904
  return;
5807
5905
  }
5808
5906
  if (!msg.message) {
5809
- console.error(`${TAG13} drop: empty message account=${conn.accountId} from=${remoteJid}`);
5907
+ console.error(`${TAG14} drop: empty message account=${conn.accountId} from=${remoteJid}`);
5810
5908
  return;
5811
5909
  }
5812
5910
  const dedupKey = `${conn.accountId}:${remoteJid}:${msg.key.id}`;
5813
5911
  if (isDuplicateInbound(dedupKey)) {
5814
- console.error(`${TAG13} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
5912
+ console.error(`${TAG14} drop: duplicate account=${conn.accountId} key=${dedupKey}`);
5815
5913
  return;
5816
5914
  }
5817
5915
  if (msg.key.fromMe) {
5818
5916
  if (msg.key.id && isAgentSentMessage(msg.key.id)) {
5819
- console.error(`${TAG13} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
5917
+ console.error(`${TAG14} drop: echo suppression account=${conn.accountId} msgId=${msg.key.id}`);
5820
5918
  return;
5821
5919
  }
5822
5920
  const extracted2 = extractMessage(msg);
5823
5921
  if (!extracted2.text) {
5824
- console.error(`${TAG13} owner reply skipped \u2014 no text content account=${conn.accountId}`);
5922
+ console.error(`${TAG14} owner reply skipped \u2014 no text content account=${conn.accountId}`);
5825
5923
  return;
5826
5924
  }
5827
5925
  const isGroup2 = isGroupJid(remoteJid);
5828
5926
  const senderPhone2 = conn.selfPhone ?? "owner";
5829
- console.error(`${TAG13} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
5927
+ console.error(`${TAG14} owner reply mirrored to session from=${senderPhone2} account=${conn.accountId}`);
5830
5928
  const reply2 = async (text) => {
5831
5929
  const currentSock = conn.sock;
5832
5930
  if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
@@ -5860,7 +5958,7 @@ async function handleInboundMessage(conn, msg) {
5860
5958
  }
5861
5959
  const extracted = extractMessage(msg);
5862
5960
  if (!extracted.text && !extracted.mediaType) {
5863
- console.error(`${TAG13} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
5961
+ console.error(`${TAG14} drop: no text or media account=${conn.accountId} from=${remoteJid}`);
5864
5962
  return;
5865
5963
  }
5866
5964
  let mediaResult;
@@ -5870,7 +5968,7 @@ async function handleInboundMessage(conn, msg) {
5870
5968
  maxBytes: maxMb * 1024 * 1024
5871
5969
  });
5872
5970
  if (!mediaResult) {
5873
- console.error(`${TAG13} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
5971
+ console.error(`${TAG14} media download returned undefined account=${conn.accountId} type=${extracted.mediaType} from=${remoteJid}`);
5874
5972
  }
5875
5973
  }
5876
5974
  const isGroup = isGroupJid(remoteJid);
@@ -5911,7 +6009,7 @@ async function handleInboundMessage(conn, msg) {
5911
6009
  });
5912
6010
  }
5913
6011
  console.error(
5914
- `${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}` : "")
6012
+ `${TAG14} 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}` : "")
5915
6013
  );
5916
6014
  if (!accessResult.allowed && accessResult.reason === "account-manager-unresolved") {
5917
6015
  console.error(
@@ -5937,7 +6035,7 @@ async function handleInboundMessage(conn, msg) {
5937
6035
  }
5938
6036
  const reply = async (text) => {
5939
6037
  if (isGroupJid(remoteJid)) {
5940
- console.error(`${TAG13} op=group-reply-blocked jid=${remoteJid}`);
6038
+ console.error(`${TAG14} op=group-reply-blocked jid=${remoteJid}`);
5941
6039
  throw new WaReplyError("group reply blocked \u2014 observe-only", false);
5942
6040
  }
5943
6041
  const currentSock = conn.sock;
@@ -5984,15 +6082,15 @@ async function handleInboundMessage(conn, msg) {
5984
6082
  if (accessResult.agentType === "public") {
5985
6083
  const hoursResult = await isBusinessOpen(conn.accountId);
5986
6084
  if (!hoursResult.open) {
5987
- console.error(`${TAG13} [${conn.accountId}] dispatch skipped: business closed`);
6085
+ console.error(`${TAG14} [${conn.accountId}] dispatch skipped: business closed`);
5988
6086
  const afterHoursMessage = whatsAppConfig.accounts?.[conn.accountId]?.afterHoursMessage ?? whatsAppConfig.afterHoursMessage;
5989
6087
  if (afterHoursMessage) {
5990
6088
  try {
5991
6089
  await reply(afterHoursMessage);
5992
- console.error(`${TAG13} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
6090
+ console.error(`${TAG14} [${conn.accountId}] after-hours auto-reply sent to ${remoteJid}`);
5993
6091
  } catch (err) {
5994
6092
  console.error(
5995
- `${TAG13} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
6093
+ `${TAG14} [${conn.accountId}] after-hours auto-reply failed: ${err instanceof Error ? err.message : String(err)}`
5996
6094
  );
5997
6095
  }
5998
6096
  }
@@ -6437,13 +6535,13 @@ function audioExtension(mimeType) {
6437
6535
  }
6438
6536
 
6439
6537
  // app/lib/stt/voice-note.ts
6440
- var TAG14 = "[voice]";
6538
+ var TAG15 = "[voice]";
6441
6539
  async function transcribeVoiceNote(file, source) {
6442
6540
  const startMs = Date.now();
6443
6541
  const bytes = file.size;
6444
6542
  const mimeType = file.type;
6445
6543
  console.error(
6446
- `${TAG14} recording send source=${source} duration_ms=unknown bytes=${bytes} format=${mimeType}`
6544
+ `${TAG15} recording send source=${source} duration_ms=unknown bytes=${bytes} format=${mimeType}`
6447
6545
  );
6448
6546
  let tempDir;
6449
6547
  let tempPath;
@@ -6455,7 +6553,7 @@ async function transcribeVoiceNote(file, source) {
6455
6553
  await writeFile3(tempPath, buffer);
6456
6554
  } catch (err) {
6457
6555
  const reason = err instanceof Error ? err.message : String(err);
6458
- console.error(`${TAG14} failed source=${source} error=temp-file-write: ${reason}`);
6556
+ console.error(`${TAG15} failed source=${source} error=temp-file-write: ${reason}`);
6459
6557
  return { ok: false, error: "Could not process voice note" };
6460
6558
  }
6461
6559
  try {
@@ -6463,7 +6561,7 @@ async function transcribeVoiceNote(file, source) {
6463
6561
  if (!sttResult) {
6464
6562
  const elapsed2 = Date.now() - startMs;
6465
6563
  console.error(
6466
- `${TAG14} failed source=${source} error=transcription-failed duration_ms=${elapsed2}`
6564
+ `${TAG15} failed source=${source} error=transcription-failed duration_ms=${elapsed2}`
6467
6565
  );
6468
6566
  return { ok: false, error: "Could not transcribe voice note. Please try again or type your message." };
6469
6567
  }
@@ -6471,7 +6569,7 @@ async function transcribeVoiceNote(file, source) {
6471
6569
  const elapsed = Date.now() - startMs;
6472
6570
  const words = rawText.split(/\s+/).filter(Boolean).length;
6473
6571
  console.error(
6474
- `${TAG14} transcribed source=${source} duration_ms=${elapsed} stt_ms=${sttResult.durationMs} words=${words}`
6572
+ `${TAG15} transcribed source=${source} duration_ms=${elapsed} stt_ms=${sttResult.durationMs} words=${words}`
6475
6573
  );
6476
6574
  return {
6477
6575
  ok: true,
@@ -6481,7 +6579,7 @@ async function transcribeVoiceNote(file, source) {
6481
6579
  const elapsed = Date.now() - startMs;
6482
6580
  const reason = err instanceof Error ? err.message : String(err);
6483
6581
  console.error(
6484
- `${TAG14} failed source=${source} error=${reason} duration_ms=${elapsed}`
6582
+ `${TAG15} failed source=${source} error=${reason} duration_ms=${elapsed}`
6485
6583
  );
6486
6584
  return { ok: false, error: "Voice note transcription failed. Please try again or type your message." };
6487
6585
  } finally {
@@ -6528,7 +6626,7 @@ function composePublicSessionTitle(input) {
6528
6626
  }
6529
6627
 
6530
6628
  // app/lib/webchat/persist-customer-inbound.ts
6531
- var TAG15 = "[channel-house]";
6629
+ var TAG16 = "[channel-house]";
6532
6630
  async function persistCustomerWebchatInbound(input) {
6533
6631
  if (!input.text) return null;
6534
6632
  try {
@@ -6544,7 +6642,7 @@ async function persistCustomerWebchatInbound(input) {
6544
6642
  );
6545
6643
  if (!conv.sessionId) {
6546
6644
  console.error(
6547
- `${TAG15} op=persist-skip reason=conversation-merge-failed accountId=${input.houseAccountId} key=${input.sessionKey}`
6645
+ `${TAG16} op=persist-skip reason=conversation-merge-failed accountId=${input.houseAccountId} key=${input.sessionKey}`
6548
6646
  );
6549
6647
  return null;
6550
6648
  }
@@ -6556,17 +6654,17 @@ async function persistCustomerWebchatInbound(input) {
6556
6654
  );
6557
6655
  if (!messageId) {
6558
6656
  console.error(
6559
- `${TAG15} op=persist-skip reason=message-write-failed accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)}`
6657
+ `${TAG16} op=persist-skip reason=message-write-failed accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)}`
6560
6658
  );
6561
6659
  return null;
6562
6660
  }
6563
6661
  console.error(
6564
- `${TAG15} op=persist accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)} messageId=${messageId.slice(0, 8)} bytes=${Buffer.byteLength(input.text, "utf8")}`
6662
+ `${TAG16} op=persist accountId=${input.houseAccountId} sessionId=${conv.sessionId.slice(0, 8)} messageId=${messageId.slice(0, 8)} bytes=${Buffer.byteLength(input.text, "utf8")}`
6565
6663
  );
6566
6664
  return { sessionId: conv.sessionId, messageId };
6567
6665
  } catch (err) {
6568
6666
  console.error(
6569
- `${TAG15} op=persist-fail accountId=${input.houseAccountId} key=${input.sessionKey} reason=${err instanceof Error ? err.message : String(err)}`
6667
+ `${TAG16} op=persist-fail accountId=${input.houseAccountId} key=${input.sessionKey} reason=${err instanceof Error ? err.message : String(err)}`
6570
6668
  );
6571
6669
  return null;
6572
6670
  }
@@ -6874,7 +6972,7 @@ function parseScheduleProvenance(v) {
6874
6972
  }
6875
6973
 
6876
6974
  // server/routes/channel/schedule-inject.ts
6877
- var TAG16 = "[schedule-inject]";
6975
+ var TAG17 = "[schedule-inject]";
6878
6976
  function isLoopbackAddr(addr) {
6879
6977
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
6880
6978
  }
@@ -6884,7 +6982,7 @@ function createScheduleInjectRoutes(deps) {
6884
6982
  const env = c.env;
6885
6983
  const remoteAddr = env?.incoming?.socket?.remoteAddress ?? "";
6886
6984
  if (!isLoopbackAddr(remoteAddr)) {
6887
- console.error(`${TAG16} reject reason=non-loopback remoteAddr=${remoteAddr}`);
6985
+ console.error(`${TAG17} reject reason=non-loopback remoteAddr=${remoteAddr}`);
6888
6986
  return c.json({ ok: false, error: "schedule-inject-loopback-only" }, 403);
6889
6987
  }
6890
6988
  const xffHeader = env?.incoming?.headers?.["x-forwarded-for"];
@@ -6892,7 +6990,7 @@ function createScheduleInjectRoutes(deps) {
6892
6990
  if (xffRaw.length > 0) {
6893
6991
  const offender = xffRaw.split(",").map((t) => t.trim()).filter(Boolean).find((t) => !isLoopbackAddr(t));
6894
6992
  if (offender !== void 0) {
6895
- console.error(`${TAG16} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
6993
+ console.error(`${TAG17} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
6896
6994
  return c.json({ ok: false, error: "schedule-inject-loopback-only" }, 403);
6897
6995
  }
6898
6996
  }
@@ -6908,23 +7006,23 @@ function createScheduleInjectRoutes(deps) {
6908
7006
  const eventId = typeof body.eventId === "string" ? body.eventId : "";
6909
7007
  const scheduleProvenance = parseScheduleProvenance(body.scheduleProvenance);
6910
7008
  if (channel !== "whatsapp" && channel !== "telegram" || !destination || !prompt) {
6911
- console.error(`${TAG16} reject reason=bad-request channel=${String(channel)} hasDestination=${Boolean(destination)} hasPrompt=${Boolean(prompt)}`);
7009
+ console.error(`${TAG17} reject reason=bad-request channel=${String(channel)} hasDestination=${Boolean(destination)} hasPrompt=${Boolean(prompt)}`);
6912
7010
  return c.json({ ok: false, error: "schedule-inject-bad-request" }, 400);
6913
7011
  }
6914
7012
  const account = deps.resolveAccount();
6915
7013
  if (!account) {
6916
- console.error(`${TAG16} reject reason=no-account eventId=${eventId}`);
7014
+ console.error(`${TAG17} reject reason=no-account eventId=${eventId}`);
6917
7015
  return c.json({ ok: false, error: "no-account" }, 500);
6918
7016
  }
6919
7017
  const houseAccountId = account.accountId;
6920
- console.error(`${TAG16} op=received eventId=${eventId} channel=${channel} destination=${destination} account=${houseAccountId}`);
7018
+ console.error(`${TAG17} op=received eventId=${eventId} channel=${channel} destination=${destination} account=${houseAccountId}`);
6921
7019
  if (channel === "whatsapp") {
6922
7020
  const effectiveAccount2 = deps.effectiveAccountFor(houseAccountId, account.accountDir, destination);
6923
7021
  if (effectiveAccount2 === null) {
6924
- console.error(`${TAG16} op=schedule-account-manager-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
7022
+ console.error(`${TAG17} op=schedule-account-manager-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
6925
7023
  return c.json({ ok: false, error: "account-manager-unresolved" }, 403);
6926
7024
  }
6927
- console.error(`${TAG16} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount2}`);
7025
+ console.error(`${TAG17} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount2}`);
6928
7026
  const sessionId2 = adminSessionIdFor(effectiveAccount2, normalizeTarget(destination) ?? destination);
6929
7027
  const reply2 = (text) => deps.sendWhatsAppText(houseAccountId, destination, text);
6930
7028
  try {
@@ -6946,17 +7044,17 @@ function createScheduleInjectRoutes(deps) {
6946
7044
  reply: reply2
6947
7045
  });
6948
7046
  } catch (err) {
6949
- console.error(`${TAG16} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
7047
+ console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
6950
7048
  return c.json({ ok: false, error: "inject-error" }, 502);
6951
7049
  }
6952
- console.error(`${TAG16} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=ok status=-`);
7050
+ console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=ok status=-`);
6953
7051
  return c.json({ ok: true }, 200);
6954
7052
  }
6955
7053
  const effectiveAccount = houseAccountId;
6956
- console.error(`${TAG16} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount}`);
7054
+ console.error(`${TAG17} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount}`);
6957
7055
  const botToken = account.config.telegram?.adminBotToken;
6958
7056
  if (!botToken) {
6959
- console.error(`${TAG16} reject reason=no-admin-bot-token eventId=${eventId}`);
7057
+ console.error(`${TAG17} reject reason=no-admin-bot-token eventId=${eventId}`);
6960
7058
  return c.json({ ok: false, error: "no-admin-bot-token" }, 400);
6961
7059
  }
6962
7060
  const chatId = Number(destination);
@@ -6975,17 +7073,17 @@ function createScheduleInjectRoutes(deps) {
6975
7073
  reply
6976
7074
  });
6977
7075
  } catch (err) {
6978
- console.error(`${TAG16} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
7076
+ console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=inject-error status=${err instanceof Error ? err.message : String(err)}`);
6979
7077
  return c.json({ ok: false, error: "inject-error" }, 502);
6980
7078
  }
6981
- console.error(`${TAG16} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
7079
+ console.error(`${TAG17} op=inject-spawn eventId=${eventId} sessionId=${sessionId} result=ok status=-`);
6982
7080
  return c.json({ ok: true }, 200);
6983
7081
  });
6984
7082
  return app63;
6985
7083
  }
6986
7084
 
6987
7085
  // server/index.ts
6988
- var import_dist8 = __toESM(require_dist(), 1);
7086
+ var import_dist9 = __toESM(require_dist(), 1);
6989
7087
 
6990
7088
  // app/lib/telegram/outbound/send-text.ts
6991
7089
  async function sendTelegramText(botToken, chatId, text) {
@@ -7012,26 +7110,26 @@ import { randomUUID as randomUUID6 } from "crypto";
7012
7110
  // app/lib/whatsapp/config-persist.ts
7013
7111
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
7014
7112
  import { resolve as resolve8, join as join8, dirname as dirname2, basename as basename2 } from "path";
7015
- var TAG17 = "[whatsapp:config]";
7113
+ var TAG18 = "[whatsapp:config]";
7016
7114
  function configPath(accountDir) {
7017
7115
  return resolve8(accountDir, "account.json");
7018
7116
  }
7019
7117
  function readConfig(accountDir) {
7020
- const path2 = configPath(accountDir);
7021
- if (!existsSync6(path2)) throw new Error(`account.json not found at ${path2}`);
7022
- return JSON.parse(readFileSync11(path2, "utf-8"));
7118
+ const path3 = configPath(accountDir);
7119
+ if (!existsSync6(path3)) throw new Error(`account.json not found at ${path3}`);
7120
+ return JSON.parse(readFileSync11(path3, "utf-8"));
7023
7121
  }
7024
7122
  function writeConfig(accountDir, config) {
7025
- const path2 = configPath(accountDir);
7026
- writeFileSync4(path2, JSON.stringify(config, null, 2) + "\n", "utf-8");
7123
+ const path3 = configPath(accountDir);
7124
+ writeFileSync4(path3, JSON.stringify(config, null, 2) + "\n", "utf-8");
7027
7125
  }
7028
7126
  function reloadManagerConfig(accountDir) {
7029
7127
  try {
7030
7128
  const config = readConfig(accountDir);
7031
7129
  reloadConfig(config);
7032
- console.error(`${TAG17} reloaded manager config`);
7130
+ console.error(`${TAG18} reloaded manager config`);
7033
7131
  } catch (err) {
7034
- console.error(`${TAG17} manager config reload failed: ${String(err)}`);
7132
+ console.error(`${TAG18} manager config reload failed: ${String(err)}`);
7035
7133
  }
7036
7134
  }
7037
7135
  var E164_PATTERN = /^\+\d{7,15}$/;
@@ -7057,25 +7155,25 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
7057
7155
  const adminPhones = wa.adminPhones;
7058
7156
  if (!adminPhones.includes(normalized)) {
7059
7157
  adminPhones.push(normalized);
7060
- console.error(`${TAG17} added selfPhone=${normalized} to adminPhones`);
7158
+ console.error(`${TAG18} added selfPhone=${normalized} to adminPhones`);
7061
7159
  }
7062
7160
  } else {
7063
- console.error(`${TAG17} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
7161
+ console.error(`${TAG18} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
7064
7162
  }
7065
7163
  const parsed = WhatsAppConfigSchema.safeParse(wa);
7066
7164
  if (!parsed.success) {
7067
7165
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
7068
- console.error(`${TAG17} validation failed after pairing: ${msg}`);
7166
+ console.error(`${TAG18} validation failed after pairing: ${msg}`);
7069
7167
  return { ok: false, error: `Validation failed: ${msg}` };
7070
7168
  }
7071
7169
  config.whatsapp = parsed.data;
7072
7170
  writeConfig(accountDir, config);
7073
- console.error(`${TAG17} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
7171
+ console.error(`${TAG18} persisted after pairing account=${accountId} phone=${selfPhone ?? "null"}`);
7074
7172
  reloadManagerConfig(accountDir);
7075
7173
  return { ok: true };
7076
7174
  } catch (err) {
7077
7175
  const msg = err instanceof Error ? err.message : String(err);
7078
- console.error(`${TAG17} persist failed account=${accountId}: ${msg}`);
7176
+ console.error(`${TAG18} persist failed account=${accountId}: ${msg}`);
7079
7177
  return { ok: false, error: msg };
7080
7178
  }
7081
7179
  }
@@ -7089,7 +7187,7 @@ function addAdminPhone(accountDir, phone) {
7089
7187
  const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename2(resolve8(accountDir));
7090
7188
  const socketOwner = resolveHouseOrSoleAccountId(dirname2(resolve8(accountDir)));
7091
7189
  if (targetId !== socketOwner) {
7092
- console.error(`${TAG17} add-admin-phone rejected accountId=${targetId} reason=not-socket-account`);
7190
+ console.error(`${TAG18} add-admin-phone rejected accountId=${targetId} reason=not-socket-account`);
7093
7191
  return {
7094
7192
  ok: false,
7095
7193
  reason: "not-socket-account",
@@ -7118,12 +7216,12 @@ function addAdminPhone(accountDir, phone) {
7118
7216
  }
7119
7217
  config.whatsapp = parsed.data;
7120
7218
  writeConfig(accountDir, config);
7121
- console.error(`${TAG17} added admin phone=${normalized}`);
7219
+ console.error(`${TAG18} added admin phone=${normalized}`);
7122
7220
  reloadManagerConfig(accountDir);
7123
7221
  return { ok: true, message: `Added ${normalized} as admin phone. Messages from this number will route to the admin agent.` };
7124
7222
  } catch (err) {
7125
7223
  const msg = err instanceof Error ? err.message : String(err);
7126
- console.error(`${TAG17} addAdminPhone failed: ${msg}`);
7224
+ console.error(`${TAG18} addAdminPhone failed: ${msg}`);
7127
7225
  return { ok: false, error: msg };
7128
7226
  }
7129
7227
  }
@@ -7151,12 +7249,12 @@ function removeAdminPhone(accountDir, phone) {
7151
7249
  }
7152
7250
  config.whatsapp = parsed.data;
7153
7251
  writeConfig(accountDir, config);
7154
- console.error(`${TAG17} removed admin phone=${normalized}`);
7252
+ console.error(`${TAG18} removed admin phone=${normalized}`);
7155
7253
  reloadManagerConfig(accountDir);
7156
7254
  return { ok: true, message: `Removed ${normalized} from admin phones. Messages from this number will now route to the public agent.` };
7157
7255
  } catch (err) {
7158
7256
  const msg = err instanceof Error ? err.message : String(err);
7159
- console.error(`${TAG17} removeAdminPhone failed: ${msg}`);
7257
+ console.error(`${TAG18} removeAdminPhone failed: ${msg}`);
7160
7258
  return { ok: false, error: msg };
7161
7259
  }
7162
7260
  }
@@ -7251,13 +7349,13 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
7251
7349
  }
7252
7350
  config.whatsapp = parsed.data;
7253
7351
  writeConfig(accountDir, config);
7254
- console.error(`${TAG17} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
7352
+ console.error(`${TAG18} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
7255
7353
  reloadManagerConfig(accountDir);
7256
7354
  const behaviour = mode === "passive" ? `Their WhatsApp messages will each file one task scoped to that sub-account; the agent sends nothing back.` : `Their WhatsApp messages will reach that sub-account's admin agent; replies still go out over this account's WhatsApp device.`;
7257
7355
  return { ok: true, message: `Bound ${normalized} as ${mode} account manager for sub-account ${sub}. ${behaviour}` };
7258
7356
  } catch (err) {
7259
7357
  const msg = err instanceof Error ? err.message : String(err);
7260
- console.error(`${TAG17} setAccountManager failed: ${msg}`);
7358
+ console.error(`${TAG18} setAccountManager failed: ${msg}`);
7261
7359
  return { ok: false, error: msg };
7262
7360
  }
7263
7361
  }
@@ -7282,12 +7380,12 @@ function clearAccountManager(accountDir, phone) {
7282
7380
  }
7283
7381
  config.whatsapp = parsed.data;
7284
7382
  writeConfig(accountDir, config);
7285
- console.error(`${TAG17} cleared account manager phone=${normalized}`);
7383
+ console.error(`${TAG18} cleared account manager phone=${normalized}`);
7286
7384
  reloadManagerConfig(accountDir);
7287
7385
  return { ok: true, message: `Cleared the account-manager binding for ${normalized}.` };
7288
7386
  } catch (err) {
7289
7387
  const msg = err instanceof Error ? err.message : String(err);
7290
- console.error(`${TAG17} clearAccountManager failed: ${msg}`);
7388
+ console.error(`${TAG18} clearAccountManager failed: ${msg}`);
7291
7389
  return { ok: false, error: msg };
7292
7390
  }
7293
7391
  }
@@ -7334,12 +7432,12 @@ function setPublicAgent(accountDir, slug) {
7334
7432
  }
7335
7433
  config.whatsapp = parsed.data;
7336
7434
  writeConfig(accountDir, config);
7337
- console.error(`${TAG17} publicAgent set to ${trimmed}`);
7435
+ console.error(`${TAG18} publicAgent set to ${trimmed}`);
7338
7436
  reloadManagerConfig(accountDir);
7339
7437
  return { ok: true, message: `Public agent set to "${trimmed}". WhatsApp messages from non-admin phones will be handled by this agent.` };
7340
7438
  } catch (err) {
7341
7439
  const msg = err instanceof Error ? err.message : String(err);
7342
- console.error(`${TAG17} setPublicAgent failed: ${msg}`);
7440
+ console.error(`${TAG18} setPublicAgent failed: ${msg}`);
7343
7441
  return { ok: false, error: msg };
7344
7442
  }
7345
7443
  }
@@ -7380,17 +7478,17 @@ function updateConfig(accountDir, fields) {
7380
7478
  const parsed = WhatsAppConfigSchema.safeParse(wa);
7381
7479
  if (!parsed.success) {
7382
7480
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
7383
- console.error(`${TAG17} update validation failed: ${msg}`);
7481
+ console.error(`${TAG18} update validation failed: ${msg}`);
7384
7482
  return { ok: false, error: `Validation failed: ${msg}` };
7385
7483
  }
7386
7484
  config.whatsapp = parsed.data;
7387
7485
  writeConfig(accountDir, config);
7388
- console.error(`${TAG17} updated fields=[${fieldNames.join(",")}]`);
7486
+ console.error(`${TAG18} updated fields=[${fieldNames.join(",")}]`);
7389
7487
  reloadManagerConfig(accountDir);
7390
7488
  return { ok: true, message: `Updated WhatsApp config: ${fieldNames.join(", ")}.` };
7391
7489
  } catch (err) {
7392
7490
  const msg = err instanceof Error ? err.message : String(err);
7393
- console.error(`${TAG17} updateConfig failed: ${msg}`);
7491
+ console.error(`${TAG18} updateConfig failed: ${msg}`);
7394
7492
  return { ok: false, error: msg };
7395
7493
  }
7396
7494
  }
@@ -7427,17 +7525,17 @@ function migrateRemovedConfigKeys(accountDir) {
7427
7525
  }
7428
7526
  writeConfig(accountDir, config);
7429
7527
  console.error(
7430
- `${TAG17} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
7528
+ `${TAG18} migration: stripped unknown keys=[${result.droppedKeys.join(",")}] from account.json`
7431
7529
  );
7432
7530
  return { dropped: result.droppedKeys };
7433
7531
  } catch (err) {
7434
- console.error(`${TAG17} migration failed: ${String(err)}`);
7532
+ console.error(`${TAG18} migration failed: ${String(err)}`);
7435
7533
  return { dropped: [] };
7436
7534
  }
7437
7535
  }
7438
7536
 
7439
7537
  // app/lib/whatsapp/login.ts
7440
- var TAG18 = "[whatsapp:login]";
7538
+ var TAG19 = "[whatsapp:login]";
7441
7539
  function maskPhone(digits) {
7442
7540
  if (digits.length <= 4) return "*".repeat(digits.length);
7443
7541
  return digits.slice(0, 2) + "*".repeat(digits.length - 4) + digits.slice(-2);
@@ -7448,7 +7546,7 @@ function closeSocket(sock) {
7448
7546
  try {
7449
7547
  sock.ws?.close?.();
7450
7548
  } catch (err) {
7451
- console.warn(`${TAG18} socket close error during cleanup: ${String(err)}`);
7549
+ console.warn(`${TAG19} socket close error during cleanup: ${String(err)}`);
7452
7550
  }
7453
7551
  }
7454
7552
  function resetActiveLogin(accountId) {
@@ -7475,18 +7573,18 @@ async function runPostPairing(login) {
7475
7573
  const masked = selfPhone ? `${selfPhone.slice(0, 4)}***` : "null";
7476
7574
  console.error(`[whatsapp-persist] wa-persist-on-connect account=${login.accountId} phone=${masked}`);
7477
7575
  } else {
7478
- console.error(`${TAG18} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
7576
+ console.error(`${TAG19} wa-persist-on-connect FAILED account=${login.accountId}: ${result.error}`);
7479
7577
  }
7480
7578
  } catch (err) {
7481
- console.error(`${TAG18} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
7579
+ console.error(`${TAG19} wa-persist-on-connect error account=${login.accountId}: ${String(err)}`);
7482
7580
  }
7483
7581
  } else {
7484
- console.error(`${TAG18} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
7582
+ console.error(`${TAG19} wa-persist-on-connect skipped \u2014 no accountDir account=${login.accountId}`);
7485
7583
  }
7486
7584
  try {
7487
7585
  await registerLoginSocket(login.accountId, login.sock, login.authDir);
7488
7586
  } catch (err) {
7489
- console.error(`${TAG18} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
7587
+ console.error(`${TAG19} registerLoginSocket failed account=${login.accountId}: ${String(err)}`);
7490
7588
  }
7491
7589
  }
7492
7590
  async function loginConnectionLoop(accountId, login) {
@@ -7495,7 +7593,7 @@ async function loginConnectionLoop(accountId, login) {
7495
7593
  const logTerminal = (outcome, errMsg) => {
7496
7594
  const fc = credsCensus(login.authDir);
7497
7595
  console.error(
7498
- `${TAG18} op=terminal cid=${cidShort} outcome=${outcome} credsFinal={registered=${fc.registered},account=${fc.hasAccountSignature},me=${fc.e164 ?? "null"}}` + (errMsg ? ` error="${errMsg}"` : "")
7596
+ `${TAG19} op=terminal cid=${cidShort} outcome=${outcome} credsFinal={registered=${fc.registered},account=${fc.hasAccountSignature},me=${fc.e164 ?? "null"}}` + (errMsg ? ` error="${errMsg}"` : "")
7499
7597
  );
7500
7598
  };
7501
7599
  while (true) {
@@ -7505,7 +7603,7 @@ async function loginConnectionLoop(accountId, login) {
7505
7603
  if (current?.id === login.id) {
7506
7604
  await runPostPairing(current);
7507
7605
  current.connected = true;
7508
- console.error(`${TAG18} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
7606
+ console.error(`${TAG19} loginConnectionLoop: connected account=${accountId} attempt=${attempt} configPersisted=${current.configPersisted}`);
7509
7607
  logTerminal("connected");
7510
7608
  }
7511
7609
  return;
@@ -7517,7 +7615,7 @@ async function loginConnectionLoop(accountId, login) {
7517
7615
  if (!preRetryCensus.hasAccountSignature) {
7518
7616
  current.error = "WhatsApp accepted the code but did not finish linking. This usually means the number has reached its linked-device limit or has a stale linked device. On the phone, open WhatsApp, Linked Devices, remove existing devices, then ask for a new code.";
7519
7617
  console.error(
7520
- `${TAG18} op=pairing-incomplete cid=${cidShort} closeKind=${closeKind(getStatusCode(err), false)} reason=no-account-signature`
7618
+ `${TAG19} op=pairing-incomplete cid=${cidShort} closeKind=${closeKind(getStatusCode(err), false)} reason=no-account-signature`
7521
7619
  );
7522
7620
  return;
7523
7621
  }
@@ -7525,7 +7623,7 @@ async function loginConnectionLoop(accountId, login) {
7525
7623
  if (!classification.shouldRetry || attempt >= LOGIN_MAX_RECONNECTS) {
7526
7624
  if (attempt >= LOGIN_MAX_RECONNECTS) {
7527
7625
  console.error(
7528
- `${TAG18} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
7626
+ `${TAG19} login reconnect attempts exhausted (${attempt}/${LOGIN_MAX_RECONNECTS}) \u2014 surfacing error to agent`
7529
7627
  );
7530
7628
  current.error = `Login failed after ${attempt} reconnect attempts: ${formatError(err)}`;
7531
7629
  } else {
@@ -7538,7 +7636,7 @@ async function loginConnectionLoop(accountId, login) {
7538
7636
  attempt++;
7539
7637
  const delay = LOGIN_RECONNECT_DELAYS[attempt - 1] ?? 8e3;
7540
7638
  console.error(
7541
- `${TAG18} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
7639
+ `${TAG19} status=${classification.statusCode ?? "unknown"} restart required \u2014 reconnecting with saved creds (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}) delay=${delay}ms`
7542
7640
  );
7543
7641
  closeSocket(current.sock);
7544
7642
  await new Promise((r) => setTimeout(r, delay));
@@ -7546,14 +7644,14 @@ async function loginConnectionLoop(accountId, login) {
7546
7644
  if (afterDelay?.id !== login.id) return;
7547
7645
  const rc = credsCensus(login.authDir);
7548
7646
  console.error(
7549
- `${TAG18} op=reconnect cid=${cidShort} attempt=${attempt}/${LOGIN_MAX_RECONNECTS} withRegistered=${rc.registered} withAccount=${rc.hasAccountSignature}`
7647
+ `${TAG19} op=reconnect cid=${cidShort} attempt=${attempt}/${LOGIN_MAX_RECONNECTS} withRegistered=${rc.registered} withAccount=${rc.hasAccountSignature}`
7550
7648
  );
7551
7649
  try {
7552
7650
  const newSock = await createWaSocket({ authDir: login.authDir, correlationId: cidShort, account: accountId });
7553
7651
  current.sock = newSock;
7554
7652
  } catch (sockErr) {
7555
7653
  console.error(
7556
- `${TAG18} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
7654
+ `${TAG19} reconnect socket creation failed (attempt ${attempt}/${LOGIN_MAX_RECONNECTS}): ${String(sockErr)}`
7557
7655
  );
7558
7656
  current.error = `Reconnection failed: ${String(sockErr)}`;
7559
7657
  logTerminal("error", current.error);
@@ -7570,7 +7668,7 @@ async function startLogin(opts) {
7570
7668
  const hasAuth = await authExists(authDir);
7571
7669
  const selfId = readSelfId(authDir);
7572
7670
  console.error(
7573
- `${TAG18} op=start cid=${cid} 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")
7671
+ `${TAG19} op=start cid=${cid} 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")
7574
7672
  );
7575
7673
  if (hasAuth && !force) {
7576
7674
  const who = selfId.e164 ?? selfId.jid ?? "unknown";
@@ -7585,7 +7683,7 @@ async function startLogin(opts) {
7585
7683
  }
7586
7684
  const existing = activeLogins.get(accountId);
7587
7685
  if (existing && isLoginFresh(existing) && existing.pairingCode && !force) {
7588
- console.error(`${TAG18} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
7686
+ console.error(`${TAG19} startLogin account=${accountId} guard: returning existing pairing code (age=${Math.round((Date.now() - existing.startedAt) / 1e3)}s)`);
7589
7687
  return {
7590
7688
  pairingCode: existing.pairingCode,
7591
7689
  phone: existing.phone,
@@ -7593,7 +7691,7 @@ async function startLogin(opts) {
7593
7691
  };
7594
7692
  }
7595
7693
  if (existing) {
7596
- console.error(`${TAG18} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
7694
+ console.error(`${TAG19} startLogin account=${accountId} ${force ? "force override" : "stale/no-code"}, resetting active login`);
7597
7695
  }
7598
7696
  resetActiveLogin(accountId);
7599
7697
  await clearAuth(authDir);
@@ -7624,7 +7722,7 @@ async function startLogin(opts) {
7624
7722
  if (requested) return;
7625
7723
  requested = true;
7626
7724
  console.error(
7627
- `${TAG18} op=pairing-request cid=${cid} account=${accountId} qrRef=#1 sinceOpenMs=${socketOpenTs ? Date.now() - socketOpenTs : 0}`
7725
+ `${TAG19} op=pairing-request cid=${cid} account=${accountId} qrRef=#1 sinceOpenMs=${socketOpenTs ? Date.now() - socketOpenTs : 0}`
7628
7726
  );
7629
7727
  void sock.requestPairingCode(digits).then((code) => {
7630
7728
  clearTimeout(codeTimer);
@@ -7633,7 +7731,7 @@ async function startLogin(opts) {
7633
7731
  if (current?.id !== login.id) return;
7634
7732
  if (!current.pairingCode) current.pairingCode = code;
7635
7733
  console.error(
7636
- `${TAG18} op=code-issued cid=${cid} account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length} sinceOpenMs=${socketOpenTs ? codeIssuedTs - socketOpenTs : 0}`
7734
+ `${TAG19} op=code-issued cid=${cid} account=${accountId} phone=${maskPhone(digits)} codeLen=${code.length} sinceOpenMs=${socketOpenTs ? codeIssuedTs - socketOpenTs : 0}`
7637
7735
  );
7638
7736
  resolveCode?.(code);
7639
7737
  }).catch((err) => {
@@ -7664,7 +7762,7 @@ async function startLogin(opts) {
7664
7762
  const ttlTimer = setTimeout(() => {
7665
7763
  const current = activeLogins.get(accountId);
7666
7764
  if (current?.id === login.id && !current.connected) {
7667
- console.error(`${TAG18} pairing-window-elapsed account=${accountId} (ttl sweep)`);
7765
+ console.error(`${TAG19} pairing-window-elapsed account=${accountId} (ttl sweep)`);
7668
7766
  resetActiveLogin(accountId);
7669
7767
  }
7670
7768
  }, ACTIVE_LOGIN_TTL_MS);
@@ -7672,7 +7770,7 @@ async function startLogin(opts) {
7672
7770
  ttlTimer.unref();
7673
7771
  }
7674
7772
  loginConnectionLoop(accountId, login).catch((err) => {
7675
- console.error(`${TAG18} loginConnectionLoop unexpected error: ${String(err)}`);
7773
+ console.error(`${TAG19} loginConnectionLoop unexpected error: ${String(err)}`);
7676
7774
  const current = activeLogins.get(accountId);
7677
7775
  if (current?.id === login.id) {
7678
7776
  current.error = `Unexpected login error: ${String(err)}`;
@@ -7697,13 +7795,13 @@ async function waitForLogin(opts) {
7697
7795
  const { accountId, timeoutMs = 6e4 } = opts;
7698
7796
  const login = activeLogins.get(accountId);
7699
7797
  console.error(
7700
- `${TAG18} 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")
7798
+ `${TAG19} 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")
7701
7799
  );
7702
7800
  if (!login) {
7703
7801
  return { connected: false, message: "No active WhatsApp login in progress.", configPersisted: false };
7704
7802
  }
7705
7803
  if (!isLoginFresh(login)) {
7706
- console.error(`${TAG18} pairing-window-elapsed account=${accountId}`);
7804
+ console.error(`${TAG19} pairing-window-elapsed account=${accountId}`);
7707
7805
  resetActiveLogin(accountId);
7708
7806
  return { connected: false, message: "The pairing window expired. Ask me to generate a new code.", configPersisted: false };
7709
7807
  }
@@ -7711,8 +7809,8 @@ async function waitForLogin(opts) {
7711
7809
  while (Date.now() < deadline) {
7712
7810
  if (login.connected) {
7713
7811
  const selfId = readSelfId(login.authDir);
7714
- console.error(`${TAG18} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
7715
- console.error(`${TAG18} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
7812
+ console.error(`${TAG19} pairing-connected account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
7813
+ console.error(`${TAG19} login complete for account=${accountId} phone=${selfId.e164 ?? "unknown"} configPersisted=${login.configPersisted}`);
7716
7814
  const configPersisted = login.configPersisted;
7717
7815
  activeLogins.delete(accountId);
7718
7816
  return {
@@ -7730,7 +7828,7 @@ async function waitForLogin(opts) {
7730
7828
  await new Promise((r) => setTimeout(r, 1e3));
7731
7829
  }
7732
7830
  const elapsed = Math.round((Date.now() - (deadline - timeoutMs)) / 1e3);
7733
- console.error(`${TAG18} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
7831
+ console.error(`${TAG19} waitForLogin poll timeout account=${accountId} elapsed=${elapsed}s \u2014 login kept alive (pairing code still valid)`);
7734
7832
  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 };
7735
7833
  }
7736
7834
  function listActiveLoginAccountIds() {
@@ -7752,8 +7850,7 @@ function reconcileMessageStoreAccountKeys(input) {
7752
7850
  import { realpathSync as realpathSync3 } from "fs";
7753
7851
  import { readFile, stat as stat2 } from "fs/promises";
7754
7852
  import { resolve as resolve9, basename as basename3 } from "path";
7755
- var TAG19 = "[whatsapp:outbound]";
7756
- var WHATSAPP_DOCUMENT_MAX_BYTES = 100 * 1024 * 1024;
7853
+ var TAG20 = "[whatsapp:outbound]";
7757
7854
  var lastDocumentOutboundAt = /* @__PURE__ */ new Map();
7758
7855
  var lastRouteDocumentOutboundAt = /* @__PURE__ */ new Map();
7759
7856
  function normalizeJid2(to) {
@@ -7786,16 +7883,16 @@ async function sendWhatsAppDocument(input) {
7786
7883
  const accountResolved = realpathSync3(accountDir);
7787
7884
  if (!resolvedPath.startsWith(accountResolved + "/")) {
7788
7885
  const sanitised = filePath.replace(accountDir, "<account>/");
7789
- console.error(`${TAG19} document REJECTED path=${sanitised} reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
7886
+ console.error(`${TAG20} document REJECTED path=${sanitised} reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
7790
7887
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
7791
7888
  }
7792
7889
  } catch (err) {
7793
7890
  const code = err.code;
7794
7891
  if (code === "ENOENT") {
7795
- console.error(`${TAG19} document ENOENT path=${filePath}`);
7892
+ console.error(`${TAG20} document ENOENT path=${filePath}`);
7796
7893
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
7797
7894
  }
7798
- console.error(`${TAG19} document path error: ${String(err)}`);
7895
+ console.error(`${TAG20} document path error: ${String(err)}`);
7799
7896
  return { ok: false, status: 500, error: String(err) };
7800
7897
  }
7801
7898
  const fileStat = await stat2(resolvedPath);
@@ -7808,21 +7905,30 @@ async function sendWhatsAppDocument(input) {
7808
7905
  }
7809
7906
  const filename = basename3(resolvedPath);
7810
7907
  const jid = normalizeJid2(to);
7811
- const sock = getSocket(accountId);
7812
- if (!sock) {
7813
- console.error(`${TAG19} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=not-connected`);
7814
- return { ok: false, status: 503, error: `WhatsApp account "${accountId}" is not connected` };
7908
+ const res = resolveSocket(accountId);
7909
+ if (!res.ok) {
7910
+ const reason = res.reason === "key-miss" ? `key-miss presentKeys=${res.presentKeys.join(",") || "none"}` : "disconnected";
7911
+ console.error(`${TAG20} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=false reason=${reason}`);
7912
+ if (res.reason === "key-miss") {
7913
+ return {
7914
+ ok: false,
7915
+ status: 503,
7916
+ error: `WhatsApp socket key miss: no socket is registered for accountId=${accountId} (present keys: ${res.presentKeys.join(",") || "none"}). This is an account-keying fault, not a dropped link \u2014 do not advise re-pairing.`
7917
+ };
7918
+ }
7919
+ return { ok: false, status: 503, error: `WhatsApp account "${accountId}" is registered but its transport is down.` };
7815
7920
  }
7921
+ const sock = res.sock;
7816
7922
  const buffer = Buffer.from(await readFile(resolvedPath));
7817
7923
  const mimetype = detectMimeType(resolvedPath);
7818
7924
  const result = await sendMediaMessage(
7819
7925
  sock,
7820
7926
  to,
7821
7927
  { type: "document", buffer, mimetype, filename, caption },
7822
- { accountId }
7928
+ { accountId: res.accountId }
7823
7929
  );
7824
7930
  console.error(
7825
- `${TAG19} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
7931
+ `${TAG20} sent document to=${jid} file=${filename} bytes=${fileStat.size} ok=${result.success}` + (result.messageId ? ` id=${result.messageId}` : "")
7826
7932
  );
7827
7933
  if (result.success) {
7828
7934
  recordDocumentOutbound(to);
@@ -7942,7 +8048,7 @@ function serializeWhatsAppSchema() {
7942
8048
  // app/lib/whatsapp/status-reconcile.ts
7943
8049
  import { readdirSync as readdirSync4 } from "fs";
7944
8050
  import { join as join9 } from "path";
7945
- var TAG20 = "[whatsapp:reconcile]";
8051
+ var TAG21 = "[whatsapp:reconcile]";
7946
8052
  var HALF_REGISTERED_MIN_AGE_MS = 5 * 6e4;
7947
8053
  function listCredsAccountIds(credsRoot) {
7948
8054
  try {
@@ -7968,14 +8074,14 @@ function reconcileCredsOnDisk(opts) {
7968
8074
  const ageMs = getAuthAgeMs(authDir);
7969
8075
  const abandoned = !inFlight && ageMs !== null && ageMs >= HALF_REGISTERED_MIN_AGE_MS;
7970
8076
  if (!abandoned) {
7971
- console.error(`${TAG20} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight} ageMs=${ageMs ?? "null"}`);
8077
+ console.error(`${TAG21} op=half-registered account=${accountId} registered=true hasAccount=false inFlight=${inFlight} ageMs=${ageMs ?? "null"}`);
7972
8078
  entries.push({ accountId, connected: false, linkedUnconfigured: true });
7973
8079
  continue;
7974
8080
  }
7975
- console.error(`${TAG20} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs}`);
8081
+ console.error(`${TAG21} op=discard-half-registered account=${accountId} registered=true hasAccount=false ageMs=${ageMs}`);
7976
8082
  const removed = discardAuthDirSync(authDir);
7977
8083
  if (!removed) {
7978
- console.error(`${TAG20} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
8084
+ console.error(`${TAG21} op=discard-half-registered-failed account=${accountId} \u2014 residue persists`);
7979
8085
  entries.push({ accountId, connected: false, linkedUnconfigured: true });
7980
8086
  }
7981
8087
  continue;
@@ -7983,14 +8089,14 @@ function reconcileCredsOnDisk(opts) {
7983
8089
  const selfPhone = readSelfId(authDir).e164 ?? void 0;
7984
8090
  const configured = accountDir ? isAccountConfigured(accountDir, accountId) : false;
7985
8091
  console.error(
7986
- `${TAG20} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
8092
+ `${TAG21} wa-link-unconfigured account=${accountId} creds=present config=${configured ? "present" : "absent"}`
7987
8093
  );
7988
8094
  if (!configured && accountDir) {
7989
8095
  const result = persistAfterPairing(accountDir, accountId, selfPhone ?? null);
7990
8096
  if (result.ok) {
7991
- console.error(`${TAG20} self-healed account=${accountId}`);
8097
+ console.error(`${TAG21} self-healed account=${accountId}`);
7992
8098
  } else {
7993
- console.error(`${TAG20} self-heal FAILED account=${accountId}: ${result.error}`);
8099
+ console.error(`${TAG21} self-heal FAILED account=${accountId}: ${result.error}`);
7994
8100
  }
7995
8101
  }
7996
8102
  entries.push({ accountId, selfPhone, connected: false, linkedUnconfigured: !configured });
@@ -8091,7 +8197,7 @@ function authorizeRecallRead(input) {
8091
8197
  }
8092
8198
 
8093
8199
  // server/routes/whatsapp.ts
8094
- var TAG21 = "[whatsapp:api]";
8200
+ var TAG22 = "[whatsapp:api]";
8095
8201
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
8096
8202
  function recallAttachment(storeAccountId, attachmentId) {
8097
8203
  if (!attachmentId) return void 0;
@@ -8136,7 +8242,7 @@ function houseSocketScopeDenial(c, op) {
8136
8242
  const houseAccountId = resolveAccount()?.accountId ?? "";
8137
8243
  const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, houseAccountId);
8138
8244
  if (decision.authorized) return null;
8139
- console.error(`${TAG21} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
8245
+ console.error(`${TAG22} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
8140
8246
  return c.json({ ok: false, error: "Not authorized. The WhatsApp connection and its settings are house-scoped; this session may only act on its own account." }, 403);
8141
8247
  }
8142
8248
  var app2 = new Hono();
@@ -8152,7 +8258,7 @@ app2.get("/status", (c) => {
8152
8258
  const reconciled = reconcileCredsOnDisk({ credsRoot, accountDir, liveAccountIds: liveIds, activeLoginAccountIds: activeLoginIds });
8153
8259
  const accounts = [...live, ...reconciled];
8154
8260
  const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}`).join(", ");
8155
- console.error(`${TAG21} status accounts=${accounts.length} [${summary}]`);
8261
+ console.error(`${TAG22} status accounts=${accounts.length} [${summary}]`);
8156
8262
  const reachableAccountIds = [
8157
8263
  .../* @__PURE__ */ new Set([...listStoredAccountIds(), ...listConnectionPlatformAccountIds()])
8158
8264
  ];
@@ -8161,11 +8267,11 @@ app2.get("/status", (c) => {
8161
8267
  reachableAccountIds
8162
8268
  });
8163
8269
  if (!storeKeyReconcile.ok) {
8164
- console.warn(`${TAG21} store-key-drift orphanLiveKeys=[${storeKeyReconcile.orphanLiveKeys.join(",")}] \u2014 in-memory cache keyed by a value with no disk account; read tools will miss`);
8270
+ console.warn(`${TAG22} store-key-drift orphanLiveKeys=[${storeKeyReconcile.orphanLiveKeys.join(",")}] \u2014 in-memory cache keyed by a value with no disk account; read tools will miss`);
8165
8271
  }
8166
8272
  return c.json({ accounts, storeKeyReconcile });
8167
8273
  } catch (err) {
8168
- console.error(`${TAG21} status error: ${String(err)}`);
8274
+ console.error(`${TAG22} status error: ${String(err)}`);
8169
8275
  return c.json({ error: String(err) }, 500);
8170
8276
  }
8171
8277
  });
@@ -8183,10 +8289,10 @@ app2.post("/login/start", async (c) => {
8183
8289
  const authDir = join10(MAXY_DIR, "credentials", "whatsapp", accountId);
8184
8290
  const accountDir = resolveAccount()?.accountDir ?? null;
8185
8291
  const result = await startLogin({ accountId, authDir, phone, accountDir, force });
8186
- console.error(`${TAG21} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
8292
+ console.error(`${TAG22} login/start result account=${accountId} hasCode=${!!result.pairingCode}${result.selfPhone ? ` phone=${result.selfPhone}` : ""}`);
8187
8293
  return c.json(result);
8188
8294
  } catch (err) {
8189
- console.error(`${TAG21} login/start error: ${String(err)}`);
8295
+ console.error(`${TAG22} login/start error: ${String(err)}`);
8190
8296
  return c.json({ error: String(err) }, 500);
8191
8297
  }
8192
8298
  });
@@ -8198,7 +8304,7 @@ app2.post("/login/wait", async (c) => {
8198
8304
  const accountId = validateAccountId(body.accountId);
8199
8305
  const timeoutMs = body.timeoutMs ?? 6e4;
8200
8306
  const result = await waitForLogin({ accountId, timeoutMs });
8201
- console.error(`${TAG21} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
8307
+ console.error(`${TAG22} login/wait result account=${accountId} connected=${result.connected}${result.selfPhone ? ` phone=${result.selfPhone}` : ""} configPersisted=${result.configPersisted}`);
8202
8308
  return c.json({
8203
8309
  connected: result.connected,
8204
8310
  message: result.message,
@@ -8206,7 +8312,7 @@ app2.post("/login/wait", async (c) => {
8206
8312
  configPersisted: result.configPersisted
8207
8313
  });
8208
8314
  } catch (err) {
8209
- console.error(`${TAG21} login/wait error: ${String(err)}`);
8315
+ console.error(`${TAG22} login/wait error: ${String(err)}`);
8210
8316
  return c.json({ error: String(err) }, 500);
8211
8317
  }
8212
8318
  });
@@ -8219,7 +8325,7 @@ app2.post("/disconnect", async (c) => {
8219
8325
  await stopConnection(accountId);
8220
8326
  return c.json({ disconnected: true, accountId });
8221
8327
  } catch (err) {
8222
- console.error(`${TAG21} disconnect error: ${String(err)}`);
8328
+ console.error(`${TAG22} disconnect error: ${String(err)}`);
8223
8329
  return c.json({ error: String(err) }, 500);
8224
8330
  }
8225
8331
  });
@@ -8232,7 +8338,7 @@ app2.post("/reconnect", async (c) => {
8232
8338
  await startConnection(accountId);
8233
8339
  return c.json({ reconnecting: true, accountId });
8234
8340
  } catch (err) {
8235
- console.error(`${TAG21} reconnect error: ${String(err)}`);
8341
+ console.error(`${TAG22} reconnect error: ${String(err)}`);
8236
8342
  return c.json({ error: String(err) }, 500);
8237
8343
  }
8238
8344
  });
@@ -8256,7 +8362,7 @@ app2.post("/config", async (c) => {
8256
8362
  }
8257
8363
  const result = addAdminPhone(account.accountDir, phone);
8258
8364
  const reasonTail = !result.ok && result.reason ? ` reason=${result.reason}` : "";
8259
- console.error(`${TAG21} config action=add-admin-phone accountId=${account.accountId} phone=${phone} ok=${result.ok}${reasonTail}`);
8365
+ console.error(`${TAG22} config action=add-admin-phone accountId=${account.accountId} phone=${phone} ok=${result.ok}${reasonTail}`);
8260
8366
  return c.json(result, result.ok ? 200 : 400);
8261
8367
  }
8262
8368
  case "remove-admin-phone": {
@@ -8264,12 +8370,12 @@ app2.post("/config", async (c) => {
8264
8370
  return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
8265
8371
  }
8266
8372
  const result = removeAdminPhone(account.accountDir, phone);
8267
- console.error(`${TAG21} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
8373
+ console.error(`${TAG22} config action=remove-admin-phone phone=${phone} ok=${result.ok}`);
8268
8374
  return c.json(result, result.ok ? 200 : 400);
8269
8375
  }
8270
8376
  case "list-admin-phones": {
8271
8377
  const phones = readAdminPhones(account.accountDir);
8272
- console.error(`${TAG21} config action=list-admin-phones count=${phones.length}`);
8378
+ console.error(`${TAG22} config action=list-admin-phones count=${phones.length}`);
8273
8379
  return c.json({ ok: true, phones });
8274
8380
  }
8275
8381
  case "add-account-manager": {
@@ -8281,7 +8387,7 @@ app2.post("/config", async (c) => {
8281
8387
  }
8282
8388
  const managerMode = mode === "passive" ? "passive" : "active";
8283
8389
  const result = setAccountManager(account.accountDir, phone, managesAccount, managerMode);
8284
- console.error(`${TAG21} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
8390
+ console.error(`${TAG22} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
8285
8391
  return c.json(result, result.ok ? 200 : 400);
8286
8392
  }
8287
8393
  case "remove-account-manager": {
@@ -8289,12 +8395,12 @@ app2.post("/config", async (c) => {
8289
8395
  return c.json({ ok: false, error: 'Missing required field "phone".' }, 400);
8290
8396
  }
8291
8397
  const result = clearAccountManager(account.accountDir, phone);
8292
- console.error(`${TAG21} config action=remove-account-manager phone=${phone} ok=${result.ok}`);
8398
+ console.error(`${TAG22} config action=remove-account-manager phone=${phone} ok=${result.ok}`);
8293
8399
  return c.json(result, result.ok ? 200 : 400);
8294
8400
  }
8295
8401
  case "list-account-managers": {
8296
8402
  const accountManagers = readAccountManagers(account.accountDir);
8297
- console.error(`${TAG21} config action=list-account-managers count=${Object.keys(accountManagers).length}`);
8403
+ console.error(`${TAG22} config action=list-account-managers count=${Object.keys(accountManagers).length}`);
8298
8404
  return c.json({ ok: true, accountManagers });
8299
8405
  }
8300
8406
  case "set-public-agent": {
@@ -8302,13 +8408,13 @@ app2.post("/config", async (c) => {
8302
8408
  return c.json({ ok: false, error: 'Missing required field "slug" (the agent directory name, e.g. "my-agent").' }, 400);
8303
8409
  }
8304
8410
  const result = setPublicAgent(account.accountDir, slug);
8305
- console.error(`${TAG21} config action=set-public-agent slug=${slug} ok=${result.ok}`);
8411
+ console.error(`${TAG22} config action=set-public-agent slug=${slug} ok=${result.ok}`);
8306
8412
  return c.json(result, result.ok ? 200 : 400);
8307
8413
  }
8308
8414
  case "get-public-agent": {
8309
8415
  const targetAccount = typeof accountId === "string" && accountId.trim() ? accountId.trim() : "default";
8310
8416
  const resolved = resolvePublicAgent(account.accountDir, { accountId: targetAccount });
8311
- console.error(`${TAG21} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
8417
+ console.error(`${TAG22} config action=get-public-agent accountId=${targetAccount} slug=${resolved?.slug ?? "none"} source=${resolved?.source ?? "none"}`);
8312
8418
  return c.json({ ok: true, slug: resolved?.slug ?? null, source: resolved?.source ?? null });
8313
8419
  }
8314
8420
  case "list-public-agents": {
@@ -8325,26 +8431,26 @@ app2.post("/config", async (c) => {
8325
8431
  const config = JSON.parse(readFileSync12(configPath2, "utf-8"));
8326
8432
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
8327
8433
  } catch {
8328
- console.error(`${TAG21} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
8434
+ console.error(`${TAG22} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
8329
8435
  }
8330
8436
  }
8331
8437
  } catch (err) {
8332
- console.error(`${TAG21} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
8438
+ console.error(`${TAG22} config action=list-public-agents error="failed to scan agents directory: ${String(err)}"`);
8333
8439
  }
8334
8440
  }
8335
- console.error(`${TAG21} config action=list-public-agents count=${agents.length}`);
8441
+ console.error(`${TAG22} config action=list-public-agents count=${agents.length}`);
8336
8442
  return c.json({ ok: true, agents });
8337
8443
  }
8338
8444
  case "schema": {
8339
8445
  const text = serializeWhatsAppSchema();
8340
- console.error(`${TAG21} config action=schema`);
8446
+ console.error(`${TAG22} config action=schema`);
8341
8447
  return c.json({ ok: true, text });
8342
8448
  }
8343
8449
  case "list-groups": {
8344
8450
  const groupAccountId = accountId ?? "default";
8345
8451
  const sock = getSocket(groupAccountId);
8346
8452
  if (!sock) {
8347
- console.error(`${TAG21} config action=list-groups error="not connected" accountId=${groupAccountId}`);
8453
+ console.error(`${TAG22} config action=list-groups error="not connected" accountId=${groupAccountId}`);
8348
8454
  return c.json({ ok: false, error: `WhatsApp account "${groupAccountId}" is not connected. Connect first, then retry.` });
8349
8455
  }
8350
8456
  try {
@@ -8354,10 +8460,10 @@ app2.post("/config", async (c) => {
8354
8460
  name: g.subject ?? g.id,
8355
8461
  participantCount: Array.isArray(g.participants) ? g.participants.length : 0
8356
8462
  }));
8357
- console.error(`${TAG21} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
8463
+ console.error(`${TAG22} config action=list-groups count=${groups.length} accountId=${groupAccountId}`);
8358
8464
  return c.json({ ok: true, groups });
8359
8465
  } catch (err) {
8360
- console.error(`${TAG21} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
8466
+ console.error(`${TAG22} config action=list-groups error="${String(err)}" accountId=${groupAccountId}`);
8361
8467
  return c.json({ ok: false, error: `Failed to fetch groups: ${String(err)}` });
8362
8468
  }
8363
8469
  }
@@ -8367,12 +8473,12 @@ app2.post("/config", async (c) => {
8367
8473
  }
8368
8474
  const result = updateConfig(account.accountDir, fields);
8369
8475
  const fieldNames = Object.keys(fields);
8370
- console.error(`${TAG21} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
8476
+ console.error(`${TAG22} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
8371
8477
  return c.json(result, result.ok ? 200 : 400);
8372
8478
  }
8373
8479
  case "get-config": {
8374
8480
  const waConfig = getConfig(account.accountDir);
8375
- console.error(`${TAG21} config action=get-config`);
8481
+ console.error(`${TAG22} config action=get-config`);
8376
8482
  return c.json({ ok: true, config: waConfig });
8377
8483
  }
8378
8484
  default:
@@ -8382,7 +8488,7 @@ app2.post("/config", async (c) => {
8382
8488
  );
8383
8489
  }
8384
8490
  } catch (err) {
8385
- console.error(`${TAG21} config error: ${String(err)}`);
8491
+ console.error(`${TAG22} config error: ${String(err)}`);
8386
8492
  return c.json({ ok: false, error: String(err) }, 500);
8387
8493
  }
8388
8494
  });
@@ -8403,7 +8509,7 @@ app2.post("/send-document", async (c) => {
8403
8509
  recordRouteDocumentOutbound(to, filePath);
8404
8510
  return c.json({ success: true, messageId: result.messageId });
8405
8511
  } catch (err) {
8406
- console.error(`${TAG21} send-document error: ${String(err)}`);
8512
+ console.error(`${TAG22} send-document error: ${String(err)}`);
8407
8513
  return c.json({ error: String(err) }, 500);
8408
8514
  }
8409
8515
  });
@@ -8448,7 +8554,7 @@ app2.post("/send-admin", async (c) => {
8448
8554
  console.error(`[whatsapp:outbound] op=send-admin-ok via=${via} to=${phone}`);
8449
8555
  return c.json({ success: true, messageId: result.messageId });
8450
8556
  } catch (err) {
8451
- console.error(`${TAG21} send-admin error: ${String(err)}`);
8557
+ console.error(`${TAG22} send-admin error: ${String(err)}`);
8452
8558
  return c.json({ error: String(err) }, 500);
8453
8559
  }
8454
8560
  });
@@ -8458,11 +8564,11 @@ app2.get("/activity", (c) => {
8458
8564
  const result = getChannelActivity(accountId);
8459
8565
  const total = result.accounts.reduce((sum, a) => sum + a.total, 0);
8460
8566
  console.error(
8461
- `${TAG21} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
8567
+ `${TAG22} activity accounts=${result.accounts.length} total=${total} recentEvents=${result.recentEvents.length}` + (accountId ? ` filter=${accountId}` : "")
8462
8568
  );
8463
8569
  return c.json(result);
8464
8570
  } catch (err) {
8465
- console.error(`${TAG21} activity error: ${String(err)}`);
8571
+ console.error(`${TAG22} activity error: ${String(err)}`);
8466
8572
  return c.json({ error: String(err) }, 500);
8467
8573
  }
8468
8574
  });
@@ -8487,10 +8593,10 @@ app2.get("/conversations", (c) => {
8487
8593
  };
8488
8594
  });
8489
8595
  conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
8490
- console.error(`${TAG21} conversations requested=${requestedAccountId} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
8596
+ console.error(`${TAG22} conversations requested=${requestedAccountId} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
8491
8597
  return c.json({ conversations });
8492
8598
  } catch (err) {
8493
- console.error(`${TAG21} conversations error: ${String(err)}`);
8599
+ console.error(`${TAG22} conversations error: ${String(err)}`);
8494
8600
  return c.json({ error: String(err) }, 500);
8495
8601
  }
8496
8602
  });
@@ -8535,10 +8641,10 @@ app2.get("/messages", (c) => {
8535
8641
  ...reactions && reactions.length > 0 ? { reactions } : {}
8536
8642
  };
8537
8643
  });
8538
- console.error(`${TAG21} messages requested=${requestedAccountId} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
8644
+ console.error(`${TAG22} messages requested=${requestedAccountId} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
8539
8645
  return c.json({ messages });
8540
8646
  } catch (err) {
8541
- console.error(`${TAG21} messages error: ${String(err)}`);
8647
+ console.error(`${TAG22} messages error: ${String(err)}`);
8542
8648
  return c.json({ error: String(err) }, 500);
8543
8649
  }
8544
8650
  });
@@ -8640,7 +8746,7 @@ app2.get("/conversation-graph-state", async (c) => {
8640
8746
  ms
8641
8747
  });
8642
8748
  } catch (err) {
8643
- console.error(`${TAG21} conversation-graph-state error: ${String(err)}`);
8749
+ console.error(`${TAG22} conversation-graph-state error: ${String(err)}`);
8644
8750
  return c.json({ error: String(err) }, 500);
8645
8751
  }
8646
8752
  });
@@ -8652,12 +8758,12 @@ app2.get("/group-info", async (c) => {
8652
8758
  return c.json({ error: "Missing required parameter: jid" }, 400);
8653
8759
  }
8654
8760
  if (!isGroupJid(jid)) {
8655
- console.error(`${TAG21} group-info error="not a group JID" jid=${jid} account=${accountId}`);
8761
+ console.error(`${TAG22} group-info error="not a group JID" jid=${jid} account=${accountId}`);
8656
8762
  return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
8657
8763
  }
8658
8764
  const sock = getSocket(accountId);
8659
8765
  if (!sock) {
8660
- console.error(`${TAG21} group-info error="not connected" account=${accountId}`);
8766
+ console.error(`${TAG22} group-info error="not connected" account=${accountId}`);
8661
8767
  return c.json({ error: `WhatsApp account "${accountId}" is not connected. Connect first, then retry.` }, 400);
8662
8768
  }
8663
8769
  const meta = await sock.groupMetadata(jid);
@@ -8670,10 +8776,10 @@ app2.get("/group-info", async (c) => {
8670
8776
  participantCount: meta.participants.length,
8671
8777
  participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
8672
8778
  };
8673
- console.error(`${TAG21} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
8779
+ console.error(`${TAG22} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} account=${accountId}`);
8674
8780
  return c.json(result);
8675
8781
  } catch (err) {
8676
- console.error(`${TAG21} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
8782
+ console.error(`${TAG22} group-info error="${String(err)}" jid=${jid} account=${accountId}`);
8677
8783
  return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
8678
8784
  }
8679
8785
  });
@@ -9480,10 +9586,10 @@ async function fetchLiveSessions() {
9480
9586
  function loadUserTitles(accountDir) {
9481
9587
  const out = /* @__PURE__ */ new Map();
9482
9588
  if (!accountDir) return out;
9483
- const path2 = join14(accountDir, "session-titles.json");
9589
+ const path3 = join14(accountDir, "session-titles.json");
9484
9590
  let raw;
9485
9591
  try {
9486
- raw = readFileSync15(path2, "utf8");
9592
+ raw = readFileSync15(path3, "utf8");
9487
9593
  } catch {
9488
9594
  return out;
9489
9595
  }
@@ -9638,21 +9744,21 @@ app4.get("/", requireAdminSession, async (c) => {
9638
9744
  const adminUserIdBySession = /* @__PURE__ */ new Map();
9639
9745
  const channelSenderBySession = /* @__PURE__ */ new Map();
9640
9746
  const channelMissNoSender = [];
9641
- for (const { path: path2, isSubagent, archived } of jsonls) {
9642
- const lastSlash = path2.lastIndexOf("/");
9643
- const base = path2.slice(lastSlash + 1);
9644
- const projectDir = path2.slice(0, lastSlash);
9747
+ for (const { path: path3, isSubagent, archived } of jsonls) {
9748
+ const lastSlash = path3.lastIndexOf("/");
9749
+ const base = path3.slice(lastSlash + 1);
9750
+ const projectDir = path3.slice(0, lastSlash);
9645
9751
  const sessionId = base.slice(0, -".jsonl".length);
9646
9752
  if (seenIds.has(sessionId)) {
9647
- console.error(`[admin-sessions-list] duplicate-sessionId sessionId=${sessionId} path=${path2}`);
9753
+ console.error(`[admin-sessions-list] duplicate-sessionId sessionId=${sessionId} path=${path3}`);
9648
9754
  continue;
9649
9755
  }
9650
9756
  seenIds.add(sessionId);
9651
9757
  let body;
9652
9758
  let mtimeMs;
9653
9759
  try {
9654
- body = readFileSync15(path2, "utf8");
9655
- mtimeMs = statSync4(path2).mtimeMs;
9760
+ body = readFileSync15(path3, "utf8");
9761
+ mtimeMs = statSync4(path3).mtimeMs;
9656
9762
  } catch {
9657
9763
  continue;
9658
9764
  }
@@ -10271,9 +10377,9 @@ app5.get("/conversations", requireAdminSession, async (c) => {
10271
10377
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
10272
10378
  const rows = [];
10273
10379
  let sessionRowsExcludedUntagged = 0;
10274
- for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
10275
- const sessionId = basename4(path2).replace(/\.jsonl$/, "");
10276
- const projectDir = dirname5(path2);
10380
+ for (const { path: path3 } of enumerateJsonls(projectsRoot)) {
10381
+ const sessionId = basename4(path3).replace(/\.jsonl$/, "");
10382
+ const projectDir = dirname5(path3);
10277
10383
  const meta = readSidecarMeta(join16(projectDir, `${sessionId}.meta.json`));
10278
10384
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
10279
10385
  if (multiAccount) {
@@ -10282,7 +10388,7 @@ app5.get("/conversations", requireAdminSession, async (c) => {
10282
10388
  }
10283
10389
  let body = "";
10284
10390
  try {
10285
- body = readFileSync17(path2, "utf8");
10391
+ body = readFileSync17(path3, "utf8");
10286
10392
  } catch {
10287
10393
  }
10288
10394
  const { title } = resolveTitle(body, sessionId, userTitles);
@@ -10365,10 +10471,10 @@ app5.get("/conversations", requireAdminSession, async (c) => {
10365
10471
  );
10366
10472
  return c.json({ conversations: [...sessionConversations, ...allStoreRows] });
10367
10473
  });
10368
- function readFrom(path2, from) {
10369
- const end = statSync5(path2).size;
10474
+ function readFrom(path3, from) {
10475
+ const end = statSync5(path3).size;
10370
10476
  if (end <= from) return { buf: Buffer.alloc(0), end: from };
10371
- const fd = openSync(path2, "r");
10477
+ const fd = openSync(path3, "r");
10372
10478
  try {
10373
10479
  const buf = Buffer.alloc(end - from);
10374
10480
  readSync(fd, buf, 0, buf.length, from);
@@ -10703,11 +10809,11 @@ app5.get("/directive", requireAdminSession, (c) => {
10703
10809
  }
10704
10810
  const dir = directiveStoreDir(sessionId);
10705
10811
  if (!dir) return c.json({ error: "no account" }, 404);
10706
- const path2 = resolve13(join16(dir, name));
10707
- if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
10812
+ const path3 = resolve13(join16(dir, name));
10813
+ if (!path3.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
10708
10814
  let body;
10709
10815
  try {
10710
- body = readFileSync17(path2, "utf8");
10816
+ body = readFileSync17(path3, "utf8");
10711
10817
  } catch {
10712
10818
  return c.json({ error: "not found" }, 404);
10713
10819
  }
@@ -11184,7 +11290,7 @@ function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
11184
11290
 
11185
11291
  // server/routes/public-reader.ts
11186
11292
  var app6 = new Hono();
11187
- var TAG22 = "[public-webchat]";
11293
+ var TAG23 = "[public-webchat]";
11188
11294
  function parseAccessSessionId(cookieHeader) {
11189
11295
  if (!cookieHeader) return null;
11190
11296
  const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
@@ -11211,9 +11317,9 @@ function enumeratePublicRows() {
11211
11317
  const cfg = claudeConfigDir();
11212
11318
  if (!cfg) return [];
11213
11319
  const rows = [];
11214
- for (const { path: path2 } of enumerateJsonls(join17(cfg, "projects"))) {
11215
- const sessionId = basename5(path2).replace(/\.jsonl$/, "");
11216
- const projectDir = dirname6(path2);
11320
+ for (const { path: path3 } of enumerateJsonls(join17(cfg, "projects"))) {
11321
+ const sessionId = basename5(path3).replace(/\.jsonl$/, "");
11322
+ const projectDir = dirname6(path3);
11217
11323
  const meta = readSidecarMeta(join17(projectDir, `${sessionId}.meta.json`));
11218
11324
  rows.push({
11219
11325
  sessionId,
@@ -11232,7 +11338,7 @@ function enumeratePublicRows() {
11232
11338
  app6.get("/session", (c) => {
11233
11339
  const visitor = resolveVisitor(c);
11234
11340
  if (!visitor) {
11235
- console.error(`${TAG22} op=reader-refused reason=no-anchor path=/session`);
11341
+ console.error(`${TAG23} op=reader-refused reason=no-anchor path=/session`);
11236
11342
  return c.json({ error: "gate-required" }, 401);
11237
11343
  }
11238
11344
  const rows = enumeratePublicRows();
@@ -11251,11 +11357,11 @@ app6.get("/session", (c) => {
11251
11357
  found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
11252
11358
  }
11253
11359
  if (found) {
11254
- console.log(`${TAG22} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
11360
+ console.log(`${TAG23} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
11255
11361
  return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
11256
11362
  }
11257
11363
  const sessionKey = crypto.randomUUID();
11258
- console.log(`${TAG22} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
11364
+ console.log(`${TAG23} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
11259
11365
  return c.json({ sessionId: null, projectDir: null, sessionKey });
11260
11366
  });
11261
11367
  function publicUploadsDir(accountId, sessionId) {
@@ -11269,10 +11375,10 @@ function enrichPublicAttachments(turns, sessionAttachments, cursor) {
11269
11375
  if (slice.length > 0) turn.attachments = slice;
11270
11376
  }
11271
11377
  }
11272
- function readFrom2(path2, from) {
11273
- const end = statSync6(path2).size;
11378
+ function readFrom2(path3, from) {
11379
+ const end = statSync6(path3).size;
11274
11380
  if (end <= from) return { buf: Buffer.alloc(0), end: from };
11275
- const fd = openSync2(path2, "r");
11381
+ const fd = openSync2(path3, "r");
11276
11382
  try {
11277
11383
  const buf = Buffer.alloc(end - from);
11278
11384
  readSync2(fd, buf, 0, buf.length, from);
@@ -11284,7 +11390,7 @@ function readFrom2(path2, from) {
11284
11390
  app6.get("/stream", (c) => {
11285
11391
  const visitor = resolveVisitor(c);
11286
11392
  if (!visitor) {
11287
- console.error(`${TAG22} op=reader-refused reason=no-anchor path=/stream`);
11393
+ console.error(`${TAG23} op=reader-refused reason=no-anchor path=/stream`);
11288
11394
  return c.json({ error: "gate-required" }, 401);
11289
11395
  }
11290
11396
  const sessionId = c.req.query("sessionId") ?? "";
@@ -11302,12 +11408,12 @@ app6.get("/stream", (c) => {
11302
11408
  const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
11303
11409
  const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
11304
11410
  if (!(isPublicWebchat && owns)) {
11305
- console.error(`${TAG22} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
11411
+ console.error(`${TAG23} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
11306
11412
  return c.json({ error: "forbidden" }, 403);
11307
11413
  }
11308
11414
  const senderShort = (meta.senderId ?? "").slice(0, 8);
11309
11415
  const encoder = new TextEncoder();
11310
- console.log(`${TAG22} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
11416
+ console.log(`${TAG23} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
11311
11417
  const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
11312
11418
  data: ${JSON.stringify(turn)}
11313
11419
 
@@ -11322,7 +11428,7 @@ data: ${JSON.stringify(turn)}
11322
11428
  const { turns, dropped } = filterDeliveredFrames(lines);
11323
11429
  if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
11324
11430
  for (const turn of turns) send(controller, turn, offset);
11325
- if (dropped > 0) console.log(`${TAG22} op=reader-filtered key=${senderShort} dropped=${dropped}`);
11431
+ if (dropped > 0) console.log(`${TAG23} op=reader-filtered key=${senderShort} dropped=${dropped}`);
11326
11432
  };
11327
11433
  try {
11328
11434
  const { buf, end } = readFrom2(jsonlPath, 0);
@@ -11357,7 +11463,7 @@ data: ${JSON.stringify(turn)}
11357
11463
  watcher?.close();
11358
11464
  clearInterval(poll);
11359
11465
  clearInterval(heartbeat);
11360
- console.log(`${TAG22} op=reader-close key=${senderShort}`);
11466
+ console.log(`${TAG23} op=reader-close key=${senderShort}`);
11361
11467
  try {
11362
11468
  controller.close();
11363
11469
  } catch {
@@ -11474,10 +11580,10 @@ function writeCanonicalOverrideId(accountDir, userId, sessionId) {
11474
11580
  }
11475
11581
  }
11476
11582
  byAdmin[userId] = sessionId;
11477
- const path2 = canonicalOverridePath(accountDir);
11478
- const tmp = `${path2}.${process.pid}.tmp`;
11583
+ const path3 = canonicalOverridePath(accountDir);
11584
+ const tmp = `${path3}.${process.pid}.tmp`;
11479
11585
  writeFileSync5(tmp, JSON.stringify({ byAdmin }, null, 2), "utf8");
11480
- renameSync2(tmp, path2);
11586
+ renameSync2(tmp, path3);
11481
11587
  }
11482
11588
 
11483
11589
  // ../lib/models/src/index.ts
@@ -11486,7 +11592,7 @@ var SONNET_MODEL = "claude-sonnet-5";
11486
11592
  var HAIKU_MODEL = "claude-haiku-4-5";
11487
11593
  var MODEL_CONTEXT_WINDOW = {
11488
11594
  [OPUS_MODEL]: 1e6,
11489
- [SONNET_MODEL]: 2e5,
11595
+ [SONNET_MODEL]: 1e6,
11490
11596
  [HAIKU_MODEL]: 2e5
11491
11597
  };
11492
11598
  function contextWindow(model) {
@@ -11536,11 +11642,11 @@ function createSidecarStore(config) {
11536
11642
  }
11537
11643
  function write(sessionsDir, id, record) {
11538
11644
  mkdirSync3(sessionsDir, { recursive: true });
11539
- const path2 = pathFor(sessionsDir, id);
11540
- const tmp = `${path2}.tmp.${process.pid}`;
11645
+ const path3 = pathFor(sessionsDir, id);
11646
+ const tmp = `${path3}.tmp.${process.pid}`;
11541
11647
  try {
11542
11648
  writeFileSync6(tmp, JSON.stringify(record), "utf8");
11543
- renameSync3(tmp, path2);
11649
+ renameSync3(tmp, path3);
11544
11650
  return { ok: true };
11545
11651
  } catch (error) {
11546
11652
  try {
@@ -11550,10 +11656,10 @@ function createSidecarStore(config) {
11550
11656
  return { ok: false, error };
11551
11657
  }
11552
11658
  }
11553
- function readFileAt(path2) {
11659
+ function readFileAt(path3) {
11554
11660
  let raw;
11555
11661
  try {
11556
- raw = readFileSync20(path2, "utf8");
11662
+ raw = readFileSync20(path3, "utf8");
11557
11663
  } catch {
11558
11664
  return null;
11559
11665
  }
@@ -11576,10 +11682,10 @@ function createSidecarStore(config) {
11576
11682
  const out = [];
11577
11683
  for (const name of names) {
11578
11684
  if (!suffixRe.test(name)) continue;
11579
- const path2 = join19(sessionsDir, name);
11685
+ const path3 = join19(sessionsDir, name);
11580
11686
  let raw;
11581
11687
  try {
11582
- raw = readFileSync20(path2, "utf8");
11688
+ raw = readFileSync20(path3, "utf8");
11583
11689
  } catch {
11584
11690
  onSkip?.(name, "unreadable");
11585
11691
  continue;
@@ -11598,10 +11704,10 @@ function createSidecarStore(config) {
11598
11704
  return out;
11599
11705
  }
11600
11706
  function clear(sessionsDir, id) {
11601
- const path2 = pathFor(sessionsDir, id);
11707
+ const path3 = pathFor(sessionsDir, id);
11602
11708
  try {
11603
- if (existsSync12(path2)) {
11604
- unlinkSync2(path2);
11709
+ if (existsSync12(path3)) {
11710
+ unlinkSync2(path3);
11605
11711
  return { ok: true, removed: true };
11606
11712
  }
11607
11713
  return { ok: true, removed: false };
@@ -11830,15 +11936,15 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
11830
11936
  const cfg = claudeConfigDir();
11831
11937
  if (!cfg) return null;
11832
11938
  let best = null;
11833
- for (const { path: path2, isSubagent, archived } of enumerateJsonls(join20(cfg, "projects"))) {
11939
+ for (const { path: path3, isSubagent, archived } of enumerateJsonls(join20(cfg, "projects"))) {
11834
11940
  if (isSubagent || archived) continue;
11835
- const id = basename6(path2).slice(0, -".jsonl".length);
11836
- const meta = readSidecarMeta(join20(dirname7(path2), `${id}.meta.json`));
11941
+ const id = basename6(path3).slice(0, -".jsonl".length);
11942
+ const meta = readSidecarMeta(join20(dirname7(path3), `${id}.meta.json`));
11837
11943
  if (meta.role !== "admin" || meta.channel !== "webchat") continue;
11838
11944
  if (multiAccount && meta.accountId !== scopeAccountId) continue;
11839
11945
  let mtimeMs;
11840
11946
  try {
11841
- mtimeMs = statSync7(path2).mtimeMs;
11947
+ mtimeMs = statSync7(path3).mtimeMs;
11842
11948
  } catch {
11843
11949
  continue;
11844
11950
  }
@@ -12235,9 +12341,9 @@ ${note}` : note;
12235
12341
  const cfg = claudeConfigDir();
12236
12342
  let projectDir = null;
12237
12343
  if (cfg) {
12238
- for (const { path: path2 } of enumerateJsonls(join20(cfg, "projects"))) {
12239
- if (basename6(path2) === `${sessionId}.jsonl`) {
12240
- projectDir = dirname7(path2);
12344
+ for (const { path: path3 } of enumerateJsonls(join20(cfg, "projects"))) {
12345
+ if (basename6(path3) === `${sessionId}.jsonl`) {
12346
+ projectDir = dirname7(path3);
12241
12347
  break;
12242
12348
  }
12243
12349
  }
@@ -12469,7 +12575,7 @@ function routeTelegramUpdate(input) {
12469
12575
  }
12470
12576
 
12471
12577
  // server/routes/telegram.ts
12472
- var TAG23 = "[telegram-inbound]";
12578
+ var TAG24 = "[telegram-inbound]";
12473
12579
  function configDirName() {
12474
12580
  const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
12475
12581
  const brandPath = join22(platformRoot5, "config", "brand.json");
@@ -12490,18 +12596,18 @@ app8.post("/", async (c) => {
12490
12596
  const botParam = c.req.query("bot");
12491
12597
  const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
12492
12598
  if (!botType) {
12493
- console.error(`${TAG23} op=reject reason=missing-bot-param`);
12599
+ console.error(`${TAG24} op=reject reason=missing-bot-param`);
12494
12600
  return c.json({ ok: false }, 400);
12495
12601
  }
12496
12602
  const sp = secretPath(botType);
12497
12603
  if (!existsSync15(sp)) {
12498
- console.error(`${TAG23} op=reject reason=no-secret-file botType=${botType}`);
12604
+ console.error(`${TAG24} op=reject reason=no-secret-file botType=${botType}`);
12499
12605
  return c.json({ ok: false }, 401);
12500
12606
  }
12501
12607
  const expected = readFileSync23(sp, "utf-8").trim();
12502
12608
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
12503
12609
  if (got !== expected) {
12504
- console.error(`${TAG23} op=reject reason=bad-secret botType=${botType}`);
12610
+ console.error(`${TAG24} op=reject reason=bad-secret botType=${botType}`);
12505
12611
  return c.json({ ok: false }, 401);
12506
12612
  }
12507
12613
  let update;
@@ -12512,40 +12618,40 @@ app8.post("/", async (c) => {
12512
12618
  }
12513
12619
  const account = resolveAccount();
12514
12620
  if (!account) {
12515
- console.error(`${TAG23} op=reject reason=no-account`);
12621
+ console.error(`${TAG24} op=reject reason=no-account`);
12516
12622
  return c.json({ ok: true }, 200);
12517
12623
  }
12518
12624
  const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
12519
12625
  if (decision.kind === "ignore") {
12520
- console.error(`${TAG23} op=ignore reason=${decision.reason}`);
12626
+ console.error(`${TAG24} op=ignore reason=${decision.reason}`);
12521
12627
  return c.json({ ok: true }, 200);
12522
12628
  }
12523
12629
  if (decision.kind === "denied") {
12524
- console.error(`${TAG23} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
12630
+ console.error(`${TAG24} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
12525
12631
  return c.json({ ok: true }, 200);
12526
12632
  }
12527
12633
  const role = decision.agentType === "admin" ? "admin" : "public";
12528
12634
  const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
12529
12635
  if (!agentSlug) {
12530
- console.error(`${TAG23} op=reject reason=no-default-agent`);
12636
+ console.error(`${TAG24} op=reject reason=no-default-agent`);
12531
12637
  return c.json({ ok: true }, 200);
12532
12638
  }
12533
- console.error(`${TAG23} op=update accountId=${account.accountId} from=${decision.senderId}`);
12639
+ console.error(`${TAG24} op=update accountId=${account.accountId} from=${decision.senderId}`);
12534
12640
  const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
12535
12641
  const { senderId, chatId, text } = decision;
12536
12642
  const gateway = getTelegramGateway();
12537
12643
  if (!gateway) {
12538
- console.error(`${TAG23} op=reject reason=gateway-not-ready from=${senderId}`);
12644
+ console.error(`${TAG24} op=reject reason=gateway-not-ready from=${senderId}`);
12539
12645
  return c.json({ ok: true }, 200);
12540
12646
  }
12541
12647
  const reply = async (replyText) => {
12542
12648
  if (!botToken) {
12543
12649
  const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
12544
- console.error(`${TAG23} op=reply-dropped reason=${reason} botType=${botType} from=${senderId}`);
12650
+ console.error(`${TAG24} op=reply-dropped reason=${reason} botType=${botType} from=${senderId}`);
12545
12651
  return;
12546
12652
  }
12547
12653
  const sent = await sendTelegramText(botToken, chatId, replyText);
12548
- console.error(`${TAG23} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
12654
+ console.error(`${TAG24} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
12549
12655
  };
12550
12656
  void gateway.handleInbound({
12551
12657
  accountId: account.accountId,
@@ -12563,7 +12669,7 @@ var telegram_default = app8;
12563
12669
  // server/routes/quickbooks.ts
12564
12670
  import { join as join23 } from "path";
12565
12671
  import { existsSync as existsSync16, readFileSync as readFileSync24, writeFileSync as writeFileSync8, mkdirSync as mkdirSync4, rmSync, renameSync as renameSync5 } from "fs";
12566
- var TAG24 = "[quickbooks]";
12672
+ var TAG25 = "[quickbooks]";
12567
12673
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
12568
12674
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
12569
12675
  function pendingPath(accountDir, state) {
@@ -12576,14 +12682,14 @@ async function completeConsent(args) {
12576
12682
  const { accountDir, code, realmId } = args;
12577
12683
  const state = args.state;
12578
12684
  if (!state || !STATE_RE.test(state) || !existsSync16(pendingPath(accountDir, state))) {
12579
- console.error(`${TAG24} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
12685
+ console.error(`${TAG25} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
12580
12686
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
12581
12687
  }
12582
12688
  const claimFile = `${pendingPath(accountDir, state)}.claimed`;
12583
12689
  try {
12584
12690
  renameSync5(pendingPath(accountDir, state), claimFile);
12585
12691
  } catch {
12586
- console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
12692
+ console.error(`${TAG25} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
12587
12693
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
12588
12694
  }
12589
12695
  try {
@@ -12591,14 +12697,14 @@ async function completeConsent(args) {
12591
12697
  try {
12592
12698
  pending = JSON.parse(readFileSync24(claimFile, "utf-8"));
12593
12699
  } catch {
12594
- console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
12700
+ console.error(`${TAG25} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
12595
12701
  return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
12596
12702
  }
12597
12703
  if (Date.now() > pending.expiresAt) {
12598
- console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
12704
+ console.error(`${TAG25} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
12599
12705
  return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
12600
12706
  }
12601
- console.error(`${TAG24} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
12707
+ console.error(`${TAG25} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
12602
12708
  if (!code || !realmId) {
12603
12709
  return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
12604
12710
  }
@@ -12622,12 +12728,12 @@ async function completeConsent(args) {
12622
12728
  });
12623
12729
  const text = await res.text();
12624
12730
  if (!res.ok) {
12625
- console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=false`);
12731
+ console.error(`${TAG25} op=token-exchange realmId=${realmId} persisted=false`);
12626
12732
  return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
12627
12733
  }
12628
12734
  const body = JSON.parse(text);
12629
12735
  if (!body.refresh_token) {
12630
- console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=false`);
12736
+ console.error(`${TAG25} op=token-exchange realmId=${realmId} persisted=false`);
12631
12737
  return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
12632
12738
  }
12633
12739
  const now = Date.now();
@@ -12640,7 +12746,7 @@ async function completeConsent(args) {
12640
12746
  mkdirSync4(join23(accountDir, "secrets"), { recursive: true });
12641
12747
  writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
12642
12748
  const persisted = JSON.parse(readFileSync24(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
12643
- console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=${persisted}`);
12749
+ console.error(`${TAG25} op=token-exchange realmId=${realmId} persisted=${persisted}`);
12644
12750
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
12645
12751
  } finally {
12646
12752
  rmSync(claimFile, { force: true });
@@ -12653,7 +12759,7 @@ var app9 = new Hono();
12653
12759
  app9.get("/callback", async (c) => {
12654
12760
  const account = resolveAccount();
12655
12761
  if (!account) {
12656
- console.error(`${TAG24} op=callback stateValid=false realmId= reason=no-account`);
12762
+ console.error(`${TAG25} op=callback stateValid=false realmId= reason=no-account`);
12657
12763
  return c.html(page("QuickBooks", "This install has no account configured."), 500);
12658
12764
  }
12659
12765
  const result = await completeConsent({
@@ -14845,10 +14951,10 @@ function extractPluginToolDescriptions(pluginDir) {
14845
14951
  join28(pluginDir, "mcp/src/index.ts")
14846
14952
  ];
14847
14953
  let raw = null;
14848
- for (const path2 of candidates) {
14849
- if (!existsSync25(path2)) continue;
14954
+ for (const path3 of candidates) {
14955
+ if (!existsSync25(path3)) continue;
14850
14956
  try {
14851
- raw = readFileSync29(path2, "utf-8");
14957
+ raw = readFileSync29(path3, "utf-8");
14852
14958
  break;
14853
14959
  } catch {
14854
14960
  }
@@ -14938,11 +15044,11 @@ function readEnabledPlugins(accountId) {
14938
15044
  return /* @__PURE__ */ new Set();
14939
15045
  }
14940
15046
  }
14941
- function readFrontmatter(path2) {
14942
- if (!existsSync25(path2)) return null;
15047
+ function readFrontmatter(path3) {
15048
+ if (!existsSync25(path3)) return null;
14943
15049
  let raw;
14944
15050
  try {
14945
- raw = readFileSync29(path2, "utf-8");
15051
+ raw = readFileSync29(path3, "utf-8");
14946
15052
  } catch {
14947
15053
  return null;
14948
15054
  }
@@ -15121,7 +15227,7 @@ function resolveTunnelUrl() {
15121
15227
  if (!state) return null;
15122
15228
  return `https://${hostname2}.${state.domain}`;
15123
15229
  }
15124
- var TAG25 = "[claude-session-manager:wrapper]";
15230
+ var TAG26 = "[claude-session-manager:wrapper]";
15125
15231
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
15126
15232
  const auth = await ensureAuth();
15127
15233
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -15139,12 +15245,12 @@ async function performSpawnWithInitialMessage(args) {
15139
15245
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
15140
15246
  const ownerMs = Date.now() - ownerStart;
15141
15247
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
15142
- console.log(`${TAG25} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
15248
+ console.log(`${TAG26} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
15143
15249
  const dormantPlugins = computeDormantPlugins(args.senderId);
15144
15250
  const activePlugins = computeActivePlugins(args.senderId);
15145
15251
  const specialistDomains = computeSpecialistDomains(args.senderId);
15146
15252
  const tunnelUrl = resolveTunnelUrl();
15147
- console.log(`${TAG25} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
15253
+ console.log(`${TAG26} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
15148
15254
  const upstreamPayload = JSON.stringify({
15149
15255
  senderId: args.senderId,
15150
15256
  // Task 205 — pass userId through to the manager so it lands as
@@ -15171,24 +15277,24 @@ async function performSpawnWithInitialMessage(args) {
15171
15277
  // unshapely values.
15172
15278
  conversationNodeId: args.conversationNodeId
15173
15279
  });
15174
- console.log(`${TAG25} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
15280
+ console.log(`${TAG26} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
15175
15281
  const forwardStart = Date.now();
15176
15282
  const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
15177
15283
  method: "POST",
15178
15284
  headers: { "content-type": "application/json" },
15179
15285
  body: upstreamPayload
15180
15286
  }).catch((err) => {
15181
- console.error(`${TAG25} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
15287
+ console.error(`${TAG26} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
15182
15288
  return null;
15183
15289
  });
15184
15290
  if (!upstream) return {
15185
15291
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
15186
15292
  claudeSessionId: null
15187
15293
  };
15188
- console.log(`${TAG25} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
15294
+ console.log(`${TAG26} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
15189
15295
  if (args.initialMessage) {
15190
15296
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
15191
- console.log(`${TAG25} initial-message-inlined bytes=${inputBytes}`);
15297
+ console.log(`${TAG26} initial-message-inlined bytes=${inputBytes}`);
15192
15298
  }
15193
15299
  const bodyText = await upstream.text().catch(() => "");
15194
15300
  let claudeSessionId = null;
@@ -15223,7 +15329,7 @@ app19.post("/", async (c) => {
15223
15329
  if (refusal) return refusal;
15224
15330
  const senderId = getAccountIdForSession(cacheKey) ?? "";
15225
15331
  if (!senderId) {
15226
- console.error(`${TAG25} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
15332
+ console.error(`${TAG26} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
15227
15333
  return c.json({ error: "admin-account-not-resolved" }, 500);
15228
15334
  }
15229
15335
  const userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -15232,7 +15338,7 @@ app19.post("/", async (c) => {
15232
15338
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
15233
15339
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
15234
15340
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
15235
- console.log(`${TAG25} 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"}`);
15341
+ console.log(`${TAG26} 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"}`);
15236
15342
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
15237
15343
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
15238
15344
  senderId,
@@ -15251,13 +15357,13 @@ app19.post("/", async (c) => {
15251
15357
  claudeSessionId,
15252
15358
  senderId
15253
15359
  );
15254
- console.log(`${TAG25} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
15360
+ console.log(`${TAG26} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
15255
15361
  return response;
15256
15362
  });
15257
15363
  var claude_sessions_default = app19;
15258
15364
 
15259
15365
  // server/routes/admin/log-ingest.ts
15260
- var TAG26 = "[log-ingest]";
15366
+ var TAG27 = "[log-ingest]";
15261
15367
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
15262
15368
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
15263
15369
  var MAX_LINE_BYTES = 4096;
@@ -15268,7 +15374,7 @@ function isLoopbackAddr2(addr) {
15268
15374
  app20.post("/", async (c) => {
15269
15375
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
15270
15376
  if (!isLoopbackAddr2(remoteAddr)) {
15271
- console.error(`${TAG26} reject reason=non-loopback remoteAddr=${remoteAddr}`);
15377
+ console.error(`${TAG27} reject reason=non-loopback remoteAddr=${remoteAddr}`);
15272
15378
  return c.json({ error: "log-ingest-loopback-only" }, 403);
15273
15379
  }
15274
15380
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -15277,7 +15383,7 @@ app20.post("/", async (c) => {
15277
15383
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
15278
15384
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
15279
15385
  if (offender !== void 0) {
15280
- console.error(`${TAG26} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
15386
+ console.error(`${TAG27} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
15281
15387
  return c.json({ error: "log-ingest-loopback-only" }, 403);
15282
15388
  }
15283
15389
  }
@@ -15319,18 +15425,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
15319
15425
  ]);
15320
15426
  var app21 = new Hono();
15321
15427
  app21.post("/", async (c) => {
15322
- const TAG46 = "[admin:events]";
15428
+ const TAG47 = "[admin:events]";
15323
15429
  let body;
15324
15430
  try {
15325
15431
  body = await c.req.json();
15326
15432
  } catch (err) {
15327
15433
  const detail = err instanceof Error ? err.message : String(err);
15328
- console.error(`${TAG46} reject reason=body-not-json detail=${detail}`);
15434
+ console.error(`${TAG47} reject reason=body-not-json detail=${detail}`);
15329
15435
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
15330
15436
  }
15331
15437
  const event = typeof body.event === "string" ? body.event : "";
15332
15438
  if (!ALLOWED_EVENTS.has(event)) {
15333
- console.error(`${TAG46} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15439
+ console.error(`${TAG47} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
15334
15440
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
15335
15441
  }
15336
15442
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -20382,7 +20488,7 @@ function managerLogFollowUrl(sessionId, opts) {
20382
20488
  }
20383
20489
 
20384
20490
  // server/routes/admin/linkedin-ingest.ts
20385
- var TAG27 = "[linkedin-ingest-route]";
20491
+ var TAG28 = "[linkedin-ingest-route]";
20386
20492
  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}$/;
20387
20493
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
20388
20494
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -20486,29 +20592,29 @@ app39.post("/", requireAdminSession, async (c) => {
20486
20592
  try {
20487
20593
  body = await c.req.json();
20488
20594
  } catch {
20489
- console.error(TAG27 + " rejected status=400 reason=schema:body-not-json");
20595
+ console.error(TAG28 + " rejected status=400 reason=schema:body-not-json");
20490
20596
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
20491
20597
  }
20492
20598
  const v = validate(body);
20493
20599
  if (!v.ok) {
20494
- console.error(TAG27 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
20600
+ console.error(TAG28 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
20495
20601
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
20496
20602
  }
20497
20603
  const envelope = v.envelope;
20498
20604
  const cacheKey = c.var.cacheKey ?? "";
20499
20605
  const senderId = getAccountIdForSession(cacheKey) ?? "";
20500
20606
  if (!senderId) {
20501
- console.error(TAG27 + " rejected status=500 reason=admin-account-not-resolved");
20607
+ console.error(TAG28 + " rejected status=500 reason=admin-account-not-resolved");
20502
20608
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
20503
20609
  }
20504
20610
  const payloadBytes = JSON.stringify(envelope).length;
20505
20611
  console.log(
20506
- TAG27 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
20612
+ TAG28 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
20507
20613
  );
20508
20614
  const initialMessage = buildInitialMessage(envelope);
20509
20615
  const spawnStart = Date.now();
20510
20616
  const sessionId = randomUUID13();
20511
- console.log(TAG27 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
20617
+ console.log(TAG28 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
20512
20618
  const spawned = await managerRcSpawn({
20513
20619
  sessionId,
20514
20620
  initialMessage,
@@ -20517,12 +20623,12 @@ app39.post("/", requireAdminSession, async (c) => {
20517
20623
  });
20518
20624
  if ("error" in spawned) {
20519
20625
  console.error(
20520
- TAG27 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
20626
+ TAG28 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
20521
20627
  );
20522
20628
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
20523
20629
  }
20524
20630
  console.log(
20525
- TAG27 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
20631
+ TAG28 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
20526
20632
  );
20527
20633
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
20528
20634
  });
@@ -20530,7 +20636,7 @@ var linkedin_ingest_default = app39;
20530
20636
 
20531
20637
  // server/routes/admin/post-turn-context.ts
20532
20638
  import neo4j3 from "neo4j-driver";
20533
- var TAG28 = "[post-turn-context]";
20639
+ var TAG29 = "[post-turn-context]";
20534
20640
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
20535
20641
  "embedding",
20536
20642
  "passwordHash",
@@ -20572,7 +20678,7 @@ var app40 = new Hono();
20572
20678
  app40.get("/", async (c) => {
20573
20679
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
20574
20680
  if (!isLoopbackAddr3(remoteAddr)) {
20575
- console.error(`${TAG28} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20681
+ console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20576
20682
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
20577
20683
  }
20578
20684
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -20581,7 +20687,7 @@ app40.get("/", async (c) => {
20581
20687
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
20582
20688
  const offender = tokens.find((t) => !isLoopbackAddr3(t));
20583
20689
  if (offender !== void 0) {
20584
- console.error(`${TAG28} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20690
+ console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20585
20691
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
20586
20692
  }
20587
20693
  }
@@ -20613,7 +20719,7 @@ app40.get("/", async (c) => {
20613
20719
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
20614
20720
  const total = Date.now() - started;
20615
20721
  console.log(
20616
- `${TAG28} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
20722
+ `${TAG29} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
20617
20723
  );
20618
20724
  return c.json({
20619
20725
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -20622,7 +20728,7 @@ app40.get("/", async (c) => {
20622
20728
  const elapsed = Date.now() - started;
20623
20729
  const message = err instanceof Error ? err.message : String(err);
20624
20730
  console.error(
20625
- `${TAG28} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
20731
+ `${TAG29} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
20626
20732
  );
20627
20733
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
20628
20734
  } finally {
@@ -20735,7 +20841,7 @@ function formatPreviousContext(writes) {
20735
20841
  }
20736
20842
 
20737
20843
  // server/routes/admin/public-session-context.ts
20738
- var TAG29 = "[public-session-context]";
20844
+ var TAG30 = "[public-session-context]";
20739
20845
  function isLoopbackAddr4(addr) {
20740
20846
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
20741
20847
  }
@@ -20743,7 +20849,7 @@ var app41 = new Hono();
20743
20849
  app41.get("/", async (c) => {
20744
20850
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
20745
20851
  if (!isLoopbackAddr4(remoteAddr)) {
20746
- console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20852
+ console.error(`${TAG30} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20747
20853
  return c.json({ error: "public-session-context-loopback-only" }, 403);
20748
20854
  }
20749
20855
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -20752,7 +20858,7 @@ app41.get("/", async (c) => {
20752
20858
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
20753
20859
  const offender = tokens.find((t) => !isLoopbackAddr4(t));
20754
20860
  if (offender !== void 0) {
20755
- console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20861
+ console.error(`${TAG30} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20756
20862
  return c.json({ error: "public-session-context-loopback-only" }, 403);
20757
20863
  }
20758
20864
  }
@@ -20766,14 +20872,14 @@ app41.get("/", async (c) => {
20766
20872
  const writes = await fetchSliceWrites(session, sliceToken, accountId);
20767
20873
  const total = Date.now() - started;
20768
20874
  console.log(
20769
- `${TAG29} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
20875
+ `${TAG30} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
20770
20876
  );
20771
20877
  return c.json({ writes });
20772
20878
  } catch (err) {
20773
20879
  const elapsed = Date.now() - started;
20774
20880
  const message = err instanceof Error ? err.message : String(err);
20775
20881
  console.error(
20776
- `${TAG29} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
20882
+ `${TAG30} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
20777
20883
  );
20778
20884
  return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
20779
20885
  } finally {
@@ -20792,7 +20898,7 @@ function getWebchatGateway() {
20792
20898
  }
20793
20899
 
20794
20900
  // server/routes/admin/public-session-exit.ts
20795
- var TAG30 = "[public-session-exit-route]";
20901
+ var TAG31 = "[public-session-exit-route]";
20796
20902
  function isLoopbackAddr5(addr) {
20797
20903
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
20798
20904
  }
@@ -20800,7 +20906,7 @@ var app42 = new Hono();
20800
20906
  app42.post("/", async (c) => {
20801
20907
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
20802
20908
  if (!isLoopbackAddr5(remoteAddr)) {
20803
- console.error(`${TAG30} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20909
+ console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20804
20910
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
20805
20911
  }
20806
20912
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -20809,7 +20915,7 @@ app42.post("/", async (c) => {
20809
20915
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
20810
20916
  const offender = tokens.find((t) => !isLoopbackAddr5(t));
20811
20917
  if (offender !== void 0) {
20812
- console.error(`${TAG30} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20918
+ console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20813
20919
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
20814
20920
  }
20815
20921
  }
@@ -20823,7 +20929,7 @@ app42.post("/", async (c) => {
20823
20929
  if (!sessionId) return c.json({ error: "sessionId required" }, 400);
20824
20930
  const gateway = getWebchatGateway();
20825
20931
  if (!gateway) {
20826
- console.error(`${TAG30} reject reason=gateway-unset sessionId=${sessionId.slice(0, 8)}`);
20932
+ console.error(`${TAG31} reject reason=gateway-unset sessionId=${sessionId.slice(0, 8)}`);
20827
20933
  return c.json({ error: "webchat-gateway-unset" }, 503);
20828
20934
  }
20829
20935
  gateway.handlePublicSessionExit(sessionId);
@@ -20832,7 +20938,7 @@ app42.post("/", async (c) => {
20832
20938
  var public_session_exit_default = app42;
20833
20939
 
20834
20940
  // server/routes/admin/access-session-evict.ts
20835
- var TAG31 = "[access-session-evict]";
20941
+ var TAG32 = "[access-session-evict]";
20836
20942
  function isLoopbackAddr6(addr) {
20837
20943
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
20838
20944
  }
@@ -20840,7 +20946,7 @@ var app43 = new Hono();
20840
20946
  app43.post("/", async (c) => {
20841
20947
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
20842
20948
  if (!isLoopbackAddr6(remoteAddr)) {
20843
- console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20949
+ console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20844
20950
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
20845
20951
  }
20846
20952
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -20849,7 +20955,7 @@ app43.post("/", async (c) => {
20849
20955
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
20850
20956
  const offender = tokens.find((t) => !isLoopbackAddr6(t));
20851
20957
  if (offender !== void 0) {
20852
- console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20958
+ console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20853
20959
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
20854
20960
  }
20855
20961
  }
@@ -20862,13 +20968,13 @@ app43.post("/", async (c) => {
20862
20968
  const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
20863
20969
  if (!grantId) return c.json({ error: "grantId required" }, 400);
20864
20970
  const dropped = evictAccessSessionsByGrant(grantId);
20865
- console.log(`${TAG31} grantId=${grantId} dropped=${dropped}`);
20971
+ console.log(`${TAG32} grantId=${grantId} dropped=${dropped}`);
20866
20972
  return c.json({ ok: true, dropped });
20867
20973
  });
20868
20974
  var access_session_evict_default = app43;
20869
20975
 
20870
20976
  // server/routes/admin/enrol-person.ts
20871
- var TAG32 = "[enrol]";
20977
+ var TAG33 = "[enrol]";
20872
20978
  function isLoopbackAddr7(addr) {
20873
20979
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
20874
20980
  }
@@ -20877,7 +20983,7 @@ var app44 = new Hono();
20877
20983
  app44.post("/", async (c) => {
20878
20984
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
20879
20985
  if (!isLoopbackAddr7(remoteAddr)) {
20880
- console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20986
+ console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
20881
20987
  return c.json({ error: "enrol-person-loopback-only" }, 403);
20882
20988
  }
20883
20989
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -20886,7 +20992,7 @@ app44.post("/", async (c) => {
20886
20992
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
20887
20993
  const offender = tokens.find((t) => !isLoopbackAddr7(t));
20888
20994
  if (offender !== void 0) {
20889
- console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20995
+ console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
20890
20996
  return c.json({ error: "enrol-person-loopback-only" }, 403);
20891
20997
  }
20892
20998
  }
@@ -20911,7 +21017,7 @@ app44.post("/", async (c) => {
20911
21017
  }
20912
21018
  const accountId = process.env.ACCOUNT_ID ?? "";
20913
21019
  if (!accountId) {
20914
- console.error(`${TAG32} op=person result=no-account-id`);
21020
+ console.error(`${TAG33} op=person result=no-account-id`);
20915
21021
  return c.json({ error: "no-account-id" }, 500);
20916
21022
  }
20917
21023
  let personId;
@@ -20919,7 +21025,7 @@ app44.post("/", async (c) => {
20919
21025
  personId = await enrolPerson({ accountId, phone, email, agentSlug });
20920
21026
  } catch (err) {
20921
21027
  console.error(
20922
- `${TAG32} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
21028
+ `${TAG33} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
20923
21029
  );
20924
21030
  return c.json({ error: "enrol-failed" }, 500);
20925
21031
  }
@@ -20929,11 +21035,11 @@ app44.post("/", async (c) => {
20929
21035
  if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
20930
21036
  } catch (err) {
20931
21037
  console.error(
20932
- `${TAG32} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
21038
+ `${TAG33} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
20933
21039
  );
20934
21040
  }
20935
21041
  console.log(
20936
- `${TAG32} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
21042
+ `${TAG33} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
20937
21043
  );
20938
21044
  return c.json({ personId, grant }, 200);
20939
21045
  });
@@ -21089,33 +21195,33 @@ var REQUIRED_KEYS = [
21089
21195
  "weekly"
21090
21196
  ];
21091
21197
  function readAvailabilityConfig(accountDir) {
21092
- const path2 = join33(accountDir, AVAILABILITY_FILENAME);
21198
+ const path3 = join33(accountDir, AVAILABILITY_FILENAME);
21093
21199
  let raw;
21094
21200
  try {
21095
- raw = readFileSync33(path2, "utf-8");
21201
+ raw = readFileSync33(path3, "utf-8");
21096
21202
  } catch {
21097
- throw new Error(`availability not configured: ${path2} not found`);
21203
+ throw new Error(`availability not configured: ${path3} not found`);
21098
21204
  }
21099
21205
  let parsed;
21100
21206
  try {
21101
21207
  parsed = JSON.parse(raw);
21102
21208
  } catch (err) {
21103
- throw new Error(`invalid JSON in ${path2}: ${err.message}`);
21209
+ throw new Error(`invalid JSON in ${path3}: ${err.message}`);
21104
21210
  }
21105
21211
  const cfg = parsed;
21106
21212
  for (const key of REQUIRED_KEYS) {
21107
21213
  if (cfg[key] === void 0 || cfg[key] === null) {
21108
- throw new Error(`availability config ${path2} missing required key: ${key}`);
21214
+ throw new Error(`availability config ${path3} missing required key: ${key}`);
21109
21215
  }
21110
21216
  }
21111
21217
  if (cfg.daysAhead !== void 0 && cfg.daysAhead !== null) {
21112
21218
  if (typeof cfg.daysAhead !== "number" || !Number.isInteger(cfg.daysAhead) || cfg.daysAhead < 1) {
21113
- throw new Error(`availability config ${path2} invalid daysAhead: must be a positive integer`);
21219
+ throw new Error(`availability config ${path3} invalid daysAhead: must be a positive integer`);
21114
21220
  }
21115
21221
  }
21116
21222
  if (cfg.allowMultiSlot !== void 0 && cfg.allowMultiSlot !== null) {
21117
21223
  if (typeof cfg.allowMultiSlot !== "boolean") {
21118
- throw new Error(`availability config ${path2} invalid allowMultiSlot: must be a boolean`);
21224
+ throw new Error(`availability config ${path3} invalid allowMultiSlot: must be a boolean`);
21119
21225
  }
21120
21226
  }
21121
21227
  return cfg;
@@ -21487,7 +21593,7 @@ function toNum(v) {
21487
21593
  }
21488
21594
 
21489
21595
  // server/routes/admin/tasks-list.ts
21490
- var TAG33 = "[task-timer]";
21596
+ var TAG34 = "[task-timer]";
21491
21597
  var COMPLETED = /* @__PURE__ */ new Set(["completed"]);
21492
21598
  var HIDDEN_FROM_OPEN = /* @__PURE__ */ new Set(["completed", "cancelled"]);
21493
21599
  var app47 = new Hono();
@@ -21495,7 +21601,7 @@ app47.get("/", requireAdminSession, async (c) => {
21495
21601
  const cacheKey = c.var.cacheKey;
21496
21602
  const accountId = getAccountIdForSession(cacheKey);
21497
21603
  if (!accountId) {
21498
- console.error(`${TAG33} op=list auth-rejected reason="no account for session"`);
21604
+ console.error(`${TAG34} op=list auth-rejected reason="no account for session"`);
21499
21605
  return c.json({ error: "Account not found for session" }, 401);
21500
21606
  }
21501
21607
  const session = getSession();
@@ -21533,10 +21639,10 @@ app47.get("/", requireAdminSession, async (c) => {
21533
21639
  if (COMPLETED.has(status)) completed.push(row);
21534
21640
  else if (!HIDDEN_FROM_OPEN.has(status)) open.push(row);
21535
21641
  }
21536
- console.log(`${TAG33} op=list account=${accountId} open=${open.length} completed=${completed.length}`);
21642
+ console.log(`${TAG34} op=list account=${accountId} open=${open.length} completed=${completed.length}`);
21537
21643
  return c.json({ open, completed });
21538
21644
  } catch (err) {
21539
- console.error(`${TAG33} op=list error="${err instanceof Error ? err.message : String(err)}"`);
21645
+ console.error(`${TAG34} op=list error="${err instanceof Error ? err.message : String(err)}"`);
21540
21646
  return c.json({ error: "tasks-list-failed" }, 500);
21541
21647
  } finally {
21542
21648
  await session.close();
@@ -21546,7 +21652,7 @@ var tasks_list_default = app47;
21546
21652
 
21547
21653
  // server/routes/admin/task-timer-start.ts
21548
21654
  import { randomUUID as randomUUID14 } from "crypto";
21549
- var TAG34 = "[task-timer]";
21655
+ var TAG35 = "[task-timer]";
21550
21656
  var app48 = new Hono();
21551
21657
  app48.post("/", requireAdminSession, async (c) => {
21552
21658
  const cacheKey = c.var.cacheKey;
@@ -21568,7 +21674,7 @@ app48.post("/", requireAdminSession, async (c) => {
21568
21674
  { taskId, accountId }
21569
21675
  );
21570
21676
  if (target.records.length === 0) {
21571
- console.error(`${TAG34} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
21677
+ console.error(`${TAG35} op=start taskId=${taskId} accountId=${accountId} result=task-not-found`);
21572
21678
  return c.json({ error: "task-not-found" }, 404);
21573
21679
  }
21574
21680
  const open = await session.run(
@@ -21580,7 +21686,7 @@ app48.post("/", requireAdminSession, async (c) => {
21580
21686
  const priorEntryId = open.records[0]?.get("entryId");
21581
21687
  const priorStartedAt = open.records[0]?.get("startedAt");
21582
21688
  if (priorEntryId) {
21583
- console.log(`${TAG34} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
21689
+ console.log(`${TAG35} op=start taskId=${taskId} accountId=${accountId} result=already-running`);
21584
21690
  return c.json({ ok: true, running: true, entryId: priorEntryId, startedAt: priorStartedAt });
21585
21691
  }
21586
21692
  const entryId = randomUUID14();
@@ -21590,10 +21696,10 @@ app48.post("/", requireAdminSession, async (c) => {
21590
21696
  CREATE (te)-[:LOGGED_AGAINST]->(t)`,
21591
21697
  { taskId, accountId, entryId, now }
21592
21698
  );
21593
- console.log(`${TAG34} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
21699
+ console.log(`${TAG35} op=opened taskId=${taskId} entryId=${entryId} startedAt=${now}`);
21594
21700
  return c.json({ ok: true, running: true, entryId, startedAt: now });
21595
21701
  } catch (err) {
21596
- console.error(`${TAG34} op=start error="${err instanceof Error ? err.message : String(err)}"`);
21702
+ console.error(`${TAG35} op=start error="${err instanceof Error ? err.message : String(err)}"`);
21597
21703
  return c.json({ error: "task-timer-start-failed" }, 500);
21598
21704
  } finally {
21599
21705
  await session.close();
@@ -21603,7 +21709,7 @@ var task_timer_start_default = app48;
21603
21709
 
21604
21710
  // server/routes/admin/task-timer-stop.ts
21605
21711
  import neo4j5 from "neo4j-driver";
21606
- var TAG35 = "[task-timer]";
21712
+ var TAG36 = "[task-timer]";
21607
21713
  var app49 = new Hono();
21608
21714
  app49.post("/", requireAdminSession, async (c) => {
21609
21715
  const cacheKey = c.var.cacheKey;
@@ -21635,7 +21741,7 @@ app49.post("/", requireAdminSession, async (c) => {
21635
21741
  { taskId, accountId }
21636
21742
  );
21637
21743
  const secondsLogged2 = toNum(cur.records[0]?.get("secondsLogged"));
21638
- console.log(`${TAG35} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
21744
+ console.log(`${TAG36} op=stop taskId=${taskId} entryId=none seconds=0 totalAfter=${secondsLogged2}`);
21639
21745
  return c.json({ ok: true, running: false, seconds: 0, secondsLogged: secondsLogged2 });
21640
21746
  }
21641
21747
  const seconds = secondsBetween(startedAt, now);
@@ -21658,11 +21764,11 @@ app49.post("/", requireAdminSession, async (c) => {
21658
21764
  const sumEntries = toNum(verify.records[0]?.get("sumEntries"));
21659
21765
  const sumAdjustments = toNum(verify.records[0]?.get("sumAdjustments"));
21660
21766
  const secondsLogged = toNum(verify.records[0]?.get("secondsLogged"));
21661
- console.log(`${TAG35} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
21662
- console.log(`${TAG35} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
21767
+ console.log(`${TAG36} op=stop taskId=${taskId} entryId=${entryId} seconds=${seconds} totalAfter=${secondsLogged}`);
21768
+ console.log(`${TAG36} op=persisted taskId=${taskId} entryId=${entryId} sumEntries=${sumEntries} sumAdjustments=${sumAdjustments} secondsLogged=${secondsLogged}`);
21663
21769
  return c.json({ ok: true, running: false, seconds, secondsLogged });
21664
21770
  } catch (err) {
21665
- console.error(`${TAG35} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
21771
+ console.error(`${TAG36} op=stop error="${err instanceof Error ? err.message : String(err)}"`);
21666
21772
  return c.json({ error: "task-timer-stop-failed" }, 500);
21667
21773
  } finally {
21668
21774
  await session.close();
@@ -21671,7 +21777,7 @@ app49.post("/", requireAdminSession, async (c) => {
21671
21777
  var task_timer_stop_default = app49;
21672
21778
 
21673
21779
  // server/routes/admin/task-complete.ts
21674
- var TAG36 = "[task-timer]";
21780
+ var TAG37 = "[task-timer]";
21675
21781
  var app50 = new Hono();
21676
21782
  app50.post("/", requireAdminSession, async (c) => {
21677
21783
  const cacheKey = c.var.cacheKey;
@@ -21698,10 +21804,10 @@ app50.post("/", requireAdminSession, async (c) => {
21698
21804
  return c.json({ error: "task-not-found" }, 404);
21699
21805
  }
21700
21806
  const secondsLogged = toNum(result.records[0].get("secondsLogged"));
21701
- console.log(`${TAG36} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
21807
+ console.log(`${TAG37} op=complete taskId=${taskId} status=completed secondsLogged=${secondsLogged}`);
21702
21808
  return c.json({ ok: true, status: "completed", secondsLogged });
21703
21809
  } catch (err) {
21704
- console.error(`${TAG36} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
21810
+ console.error(`${TAG37} op=complete error="${err instanceof Error ? err.message : String(err)}"`);
21705
21811
  return c.json({ error: "task-complete-failed" }, 500);
21706
21812
  } finally {
21707
21813
  await session.close();
@@ -21712,7 +21818,7 @@ var task_complete_default = app50;
21712
21818
  // server/routes/admin/task-time-adjust.ts
21713
21819
  import { randomUUID as randomUUID15 } from "crypto";
21714
21820
  import neo4j6 from "neo4j-driver";
21715
- var TAG37 = "[task-time-adjust]";
21821
+ var TAG38 = "[task-time-adjust]";
21716
21822
  var app51 = new Hono();
21717
21823
  app51.post("/", requireAdminSession, async (c) => {
21718
21824
  const cacheKey = c.var.cacheKey;
@@ -21722,22 +21828,22 @@ app51.post("/", requireAdminSession, async (c) => {
21722
21828
  try {
21723
21829
  body = await c.req.json();
21724
21830
  } catch {
21725
- console.error(`${TAG37} op=reject reason=bad-json`);
21831
+ console.error(`${TAG38} op=reject reason=bad-json`);
21726
21832
  return c.json({ error: "invalid-json" }, 400);
21727
21833
  }
21728
21834
  const taskId = typeof body.taskId === "string" ? body.taskId : "";
21729
21835
  if (!taskId) {
21730
- console.error(`${TAG37} op=reject reason=no-task`);
21836
+ console.error(`${TAG38} op=reject reason=no-task`);
21731
21837
  return c.json({ error: "taskId required" }, 400);
21732
21838
  }
21733
21839
  const newSeconds = body.newSeconds;
21734
21840
  if (typeof newSeconds !== "number" || !Number.isInteger(newSeconds) || newSeconds < 0) {
21735
- console.error(`${TAG37} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
21841
+ console.error(`${TAG38} op=reject reason=invalid-seconds taskId=${taskId} accountId=${accountId}`);
21736
21842
  return c.json({ error: "newSeconds must be a non-negative integer" }, 400);
21737
21843
  }
21738
21844
  const adjustmentId = randomUUID15();
21739
21845
  const now = (/* @__PURE__ */ new Date()).toISOString();
21740
- console.log(`${TAG37} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
21846
+ console.log(`${TAG38} op=request adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId} newSeconds=${newSeconds}`);
21741
21847
  const session = getSession();
21742
21848
  try {
21743
21849
  const guard = await session.run(
@@ -21747,12 +21853,12 @@ app51.post("/", requireAdminSession, async (c) => {
21747
21853
  { taskId, accountId }
21748
21854
  );
21749
21855
  if (guard.records.length === 0) {
21750
- console.error(`${TAG37} op=reject reason=no-task adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
21856
+ console.error(`${TAG38} op=reject reason=no-task adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
21751
21857
  return c.json({ error: "task-not-found" }, 404);
21752
21858
  }
21753
21859
  const openCount = toNum(guard.records[0].get("openCount"));
21754
21860
  if (openCount > 0) {
21755
- console.error(`${TAG37} op=reject reason=running adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
21861
+ console.error(`${TAG38} op=reject reason=running adjustmentId=${adjustmentId} taskId=${taskId} accountId=${accountId}`);
21756
21862
  return c.json({ error: "task-timer-running" }, 409);
21757
21863
  }
21758
21864
  const previousSeconds = toNum(guard.records[0].get("previousSeconds"));
@@ -21778,10 +21884,10 @@ app51.post("/", requireAdminSession, async (c) => {
21778
21884
  }
21779
21885
  );
21780
21886
  const secondsLoggedAfter = toNum(write.records[0]?.get("secondsLoggedAfter"));
21781
- console.log(`${TAG37} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
21887
+ console.log(`${TAG38} op=persisted adjustmentId=${adjustmentId} previousSeconds=${previousSeconds} newSeconds=${newSeconds} delta=${delta} secondsLoggedAfter=${secondsLoggedAfter}`);
21782
21888
  return c.json({ ok: true, secondsLogged: secondsLoggedAfter });
21783
21889
  } catch (err) {
21784
- console.error(`${TAG37} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
21890
+ console.error(`${TAG38} op=error adjustmentId=${adjustmentId} taskId=${taskId} error="${err instanceof Error ? err.message : String(err)}"`);
21785
21891
  return c.json({ error: "task-time-adjust-failed" }, 500);
21786
21892
  } finally {
21787
21893
  await session.close();
@@ -21835,7 +21941,7 @@ app52.route("/task-time-adjust", task_time_adjust_default);
21835
21941
  var admin_default = app52;
21836
21942
 
21837
21943
  // server/routes/access/verify-token.ts
21838
- var TAG38 = "[access-verify]";
21944
+ var TAG39 = "[access-verify]";
21839
21945
  var MINT_TAG = "[access-session-mint]";
21840
21946
  var COOKIE_NAME = "__access_session";
21841
21947
  var app53 = new Hono();
@@ -21854,39 +21960,39 @@ app53.post("/", async (c) => {
21854
21960
  }
21855
21961
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
21856
21962
  if (rateMsg) {
21857
- console.error(`${TAG38} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
21963
+ console.error(`${TAG39} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
21858
21964
  return c.json({ error: rateMsg }, 429);
21859
21965
  }
21860
21966
  const grant = await findGrantByMagicToken(token);
21861
21967
  if (!grant) {
21862
21968
  recordAccessFailedAttempt(ip, agentSlug);
21863
- console.error(`${TAG38} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
21969
+ console.error(`${TAG39} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
21864
21970
  return c.json({ error: "invalid-or-expired-link" }, 401);
21865
21971
  }
21866
21972
  if (grant.agentSlug !== agentSlug) {
21867
21973
  recordAccessFailedAttempt(ip, agentSlug);
21868
21974
  console.error(
21869
- `${TAG38} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
21975
+ `${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
21870
21976
  );
21871
21977
  return c.json({ error: "invalid-or-expired-link" }, 401);
21872
21978
  }
21873
21979
  if (grant.status === "expired" || grant.status === "revoked") {
21874
21980
  recordAccessFailedAttempt(ip, agentSlug);
21875
21981
  console.error(
21876
- `${TAG38} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
21982
+ `${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
21877
21983
  );
21878
21984
  return c.json({ error: "access-no-longer-valid" }, 401);
21879
21985
  }
21880
21986
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
21881
21987
  recordAccessFailedAttempt(ip, agentSlug);
21882
21988
  console.error(
21883
- `${TAG38} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
21989
+ `${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
21884
21990
  );
21885
21991
  return c.json({ error: "access-no-longer-valid" }, 401);
21886
21992
  }
21887
21993
  if (!grant.sliceToken) {
21888
21994
  console.error(
21889
- `${TAG38} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
21995
+ `${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
21890
21996
  );
21891
21997
  return c.json({ error: "grant-misconfigured" }, 500);
21892
21998
  }
@@ -21902,12 +22008,12 @@ app53.post("/", async (c) => {
21902
22008
  await consumeMagicTokenAndActivate(grant.grantId);
21903
22009
  } catch (err) {
21904
22010
  console.error(
21905
- `${TAG38} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
22011
+ `${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
21906
22012
  );
21907
22013
  return c.json({ error: "verification-failed" }, 500);
21908
22014
  }
21909
22015
  clearAccessRateLimit(ip, agentSlug);
21910
- console.log(`${TAG38} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
22016
+ console.log(`${TAG39} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
21911
22017
  console.log(
21912
22018
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
21913
22019
  );
@@ -21983,7 +22089,7 @@ async function sendMagicLinkEmail(payload) {
21983
22089
  }
21984
22090
 
21985
22091
  // server/routes/access/request-magic-link.ts
21986
- var TAG39 = "[access-request-link]";
22092
+ var TAG40 = "[access-request-link]";
21987
22093
  var app54 = new Hono();
21988
22094
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
21989
22095
  app54.post("/", async (c) => {
@@ -22000,18 +22106,18 @@ app54.post("/", async (c) => {
22000
22106
  }
22001
22107
  const rateMsg = checkRequestLinkRateLimit(contactValue);
22002
22108
  if (rateMsg) {
22003
- console.error(`${TAG39} contactValue=${maskContact(contactValue)} result=rate-limited`);
22109
+ console.error(`${TAG40} contactValue=${maskContact(contactValue)} result=rate-limited`);
22004
22110
  return c.json({ error: rateMsg }, 429);
22005
22111
  }
22006
22112
  recordRequestLinkAttempt(contactValue);
22007
22113
  const accountId = process.env.ACCOUNT_ID ?? "";
22008
22114
  if (!accountId) {
22009
- console.error(`${TAG39} contactValue=${maskContact(contactValue)} result=no-account-id`);
22115
+ console.error(`${TAG40} contactValue=${maskContact(contactValue)} result=no-account-id`);
22010
22116
  return c.json({ message: VISITOR_MESSAGE }, 200);
22011
22117
  }
22012
22118
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
22013
22119
  if (!grant) {
22014
- console.log(`${TAG39} contactValue=${maskContact(contactValue)} result=notfound`);
22120
+ console.log(`${TAG40} contactValue=${maskContact(contactValue)} result=notfound`);
22015
22121
  return c.json({ message: VISITOR_MESSAGE }, 200);
22016
22122
  }
22017
22123
  let token;
@@ -22019,7 +22125,7 @@ app54.post("/", async (c) => {
22019
22125
  token = await generateNewMagicToken(grant.grantId);
22020
22126
  } catch (err) {
22021
22127
  console.error(
22022
- `${TAG39} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
22128
+ `${TAG40} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
22023
22129
  );
22024
22130
  return c.json({ message: VISITOR_MESSAGE }, 200);
22025
22131
  }
@@ -22051,12 +22157,12 @@ app54.post("/", async (c) => {
22051
22157
  });
22052
22158
  if (!sendResult.ok) {
22053
22159
  console.error(
22054
- `${TAG39} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
22160
+ `${TAG40} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
22055
22161
  );
22056
22162
  return c.json({ message: VISITOR_MESSAGE }, 200);
22057
22163
  }
22058
22164
  console.log(
22059
- `${TAG39} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
22165
+ `${TAG40} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
22060
22166
  );
22061
22167
  return c.json({ message: VISITOR_MESSAGE }, 200);
22062
22168
  });
@@ -23118,7 +23224,7 @@ app61.get("/free-busy", async (c) => {
23118
23224
  var calendar_public_default = app61;
23119
23225
 
23120
23226
  // app/lib/timeentry-census.ts
23121
- var TAG40 = "[timeentry-census]";
23227
+ var TAG41 = "[timeentry-census]";
23122
23228
  function reconcileTimeEntries(rows, now, horizonMs) {
23123
23229
  const openEntries = rows.openEntries.length;
23124
23230
  let oldestOpenAgeMs = 0;
@@ -23158,12 +23264,12 @@ async function runTimeEntryCensusOnce(session, now, horizonMs) {
23158
23264
  sumAdjustments: toNum(r.get("sumAdjustments"))
23159
23265
  }));
23160
23266
  const finding = reconcileTimeEntries({ openEntries, taskSums }, now, horizonMs);
23161
- const line = `${TAG40} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
23267
+ const line = `${TAG41} openEntries=${finding.openEntries} oldestOpenAgeMin=${finding.oldestOpenAgeMin} maxOpenPerAccount=${finding.maxOpenPerAccount} driftTasks=${finding.driftTasks}`;
23162
23268
  if (finding.alarm) console.error(`${line} alarm=true`);
23163
23269
  else console.log(line);
23164
23270
  return finding;
23165
23271
  } catch (err) {
23166
- console.error(`${TAG40} error="${err instanceof Error ? err.message : String(err)}"`);
23272
+ console.error(`${TAG41} error="${err instanceof Error ? err.message : String(err)}"`);
23167
23273
  return null;
23168
23274
  }
23169
23275
  }
@@ -23177,13 +23283,13 @@ function startTimeEntryCensus(openSession, opts = {}) {
23177
23283
  try {
23178
23284
  session = openSession();
23179
23285
  } catch (err) {
23180
- console.error(`${TAG40} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
23286
+ console.error(`${TAG41} open-session-failed error="${err instanceof Error ? err.message : String(err)}"`);
23181
23287
  return;
23182
23288
  }
23183
23289
  try {
23184
23290
  await runTimeEntryCensusOnce(session, Date.now(), horizonMs);
23185
23291
  } catch (err) {
23186
- console.error(`${TAG40} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
23292
+ console.error(`${TAG41} tick-failed error="${err instanceof Error ? err.message : String(err)}"`);
23187
23293
  } finally {
23188
23294
  try {
23189
23295
  await session.close();
@@ -23555,10 +23661,10 @@ var defaultResolveName = async (_accountId, userId) => {
23555
23661
  const res = await loadAdminUserName(userId);
23556
23662
  return res.source === "neo4j" && res.joined.length > 0 ? res.joined : null;
23557
23663
  };
23558
- async function readRaw2(path2) {
23664
+ async function readRaw2(path3) {
23559
23665
  let raw;
23560
23666
  try {
23561
- raw = await readFile6(path2, "utf8");
23667
+ raw = await readFile6(path3, "utf8");
23562
23668
  } catch {
23563
23669
  return null;
23564
23670
  }
@@ -23569,11 +23675,11 @@ async function readRaw2(path2) {
23569
23675
  return null;
23570
23676
  }
23571
23677
  }
23572
- async function writeRaw(path2, obj) {
23573
- const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
23678
+ async function writeRaw(path3, obj) {
23679
+ const tmp = `${path3}.tmp.${process.pid}.${Date.now()}`;
23574
23680
  try {
23575
23681
  await writeFile4(tmp, JSON.stringify(obj, null, 2), "utf8");
23576
- await rename3(tmp, path2);
23682
+ await rename3(tmp, path3);
23577
23683
  } catch (err) {
23578
23684
  try {
23579
23685
  if (existsSync34(tmp)) await unlink3(tmp);
@@ -23618,8 +23724,8 @@ async function collectLiveSidecars(projectsRoot) {
23618
23724
  }
23619
23725
  return out;
23620
23726
  }
23621
- function shortId(path2) {
23622
- const base = path2.slice(path2.lastIndexOf("/") + 1).replace(/\.meta\.json$/, "");
23727
+ function shortId(path3) {
23728
+ const base = path3.slice(path3.lastIndexOf("/") + 1).replace(/\.meta\.json$/, "");
23623
23729
  return base.slice(0, 8);
23624
23730
  }
23625
23731
  async function migrateAdminWebchatSidecars(opts = {}) {
@@ -23662,8 +23768,8 @@ async function migrateAdminWebchatSidecars(opts = {}) {
23662
23768
  }
23663
23769
  };
23664
23770
  const sidecars = await collectLiveSidecars(projectsRoot);
23665
- for (const path2 of sidecars) {
23666
- const raw = await readRaw2(path2);
23771
+ for (const path3 of sidecars) {
23772
+ const raw = await readRaw2(path3);
23667
23773
  if (!raw) continue;
23668
23774
  if (raw.role !== ADMIN_ROLE2 || raw.channel !== WEBCHAT_CHANNEL2) continue;
23669
23775
  result.scanned++;
@@ -23674,21 +23780,21 @@ async function migrateAdminWebchatSidecars(opts = {}) {
23674
23780
  }
23675
23781
  if (!singleAdmin) {
23676
23782
  result.skippedAmbiguous++;
23677
- console.error(`[sidecar-backfill] skip sessionId=${shortId(path2)} reason=admin-count-not-one count=${adminCount < 0 ? "unparseable" : adminCount}`);
23783
+ console.error(`[sidecar-backfill] skip sessionId=${shortId(path3)} reason=admin-count-not-one count=${adminCount < 0 ? "unparseable" : adminCount}`);
23678
23784
  continue;
23679
23785
  }
23680
23786
  raw.adminUserId = soleAdminUserId;
23681
- await writeRaw(path2, raw);
23787
+ await writeRaw(path3, raw);
23682
23788
  result.backfilled++;
23683
23789
  await resolveAdminName();
23684
- const back = await readRaw2(path2);
23790
+ const back = await readRaw2(path3);
23685
23791
  const wrote = back && back.adminUserId === soleAdminUserId;
23686
23792
  if (wrote && resolvedNameForAdmin) {
23687
23793
  result.resolvedName++;
23688
23794
  } else if (wrote) {
23689
- console.error(`[sidecar-backfill] backfilled-no-name sessionId=${shortId(path2)} userId=${soleAdminUserId.slice(0, 8)}`);
23795
+ console.error(`[sidecar-backfill] backfilled-no-name sessionId=${shortId(path3)} userId=${soleAdminUserId.slice(0, 8)}`);
23690
23796
  } else {
23691
- console.error(`[sidecar-backfill] post-write-verify-failed sessionId=${shortId(path2)}`);
23797
+ console.error(`[sidecar-backfill] post-write-verify-failed sessionId=${shortId(path3)}`);
23692
23798
  }
23693
23799
  }
23694
23800
  console.error(
@@ -25291,14 +25397,14 @@ function makeFileDelivery(opts) {
25291
25397
  }
25292
25398
 
25293
25399
  // app/lib/webchat/file-delivery.ts
25294
- var TAG41 = "[webchat-adaptor]";
25400
+ var TAG42 = "[webchat-adaptor]";
25295
25401
  function platformRoot2() {
25296
25402
  return process.env.MAXY_PLATFORM_ROOT || "";
25297
25403
  }
25298
25404
  function makeWebchatSendFile(entry) {
25299
25405
  return async (filePath) => {
25300
25406
  if (!entry.accountId) {
25301
- console.error(`${TAG41} file-delivery reject reason=no-account sender=${entry.senderId}`);
25407
+ console.error(`${TAG42} file-delivery reject reason=no-account sender=${entry.senderId}`);
25302
25408
  return { ok: false, error: "no-account" };
25303
25409
  }
25304
25410
  const accountDir = resolve32(platformRoot2(), "..", "data/accounts", entry.accountId);
@@ -25306,14 +25412,14 @@ function makeWebchatSendFile(entry) {
25306
25412
  const resolved = realpathSync8(filePath);
25307
25413
  const accountResolved = realpathSync8(accountDir);
25308
25414
  if (!resolved.startsWith(accountResolved + "/")) {
25309
- console.error(`${TAG41} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
25415
+ console.error(`${TAG42} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
25310
25416
  return { ok: false, error: "outside-account" };
25311
25417
  }
25312
25418
  return { ok: true };
25313
25419
  } catch (err) {
25314
25420
  const code = err.code;
25315
25421
  console.error(
25316
- `${TAG41} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
25422
+ `${TAG42} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
25317
25423
  );
25318
25424
  return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
25319
25425
  }
@@ -25322,7 +25428,7 @@ function makeWebchatSendFile(entry) {
25322
25428
  function makeWebchatFileDelivery(entry) {
25323
25429
  return makeFileDelivery({
25324
25430
  entry,
25325
- tag: TAG41,
25431
+ tag: TAG42,
25326
25432
  channel: "webchat",
25327
25433
  sendFile: makeWebchatSendFile(entry),
25328
25434
  deferUntilVerdict: true
@@ -25384,7 +25490,7 @@ function buildPublicWebchatSpawnRequest(input) {
25384
25490
  }
25385
25491
 
25386
25492
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
25387
- var TAG42 = "[whatsapp-adaptor]";
25493
+ var TAG43 = "[whatsapp-adaptor]";
25388
25494
  var SEND_USER_FILE2 = "SendUserFile";
25389
25495
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
25390
25496
  function platformRoot3() {
@@ -25402,7 +25508,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25402
25508
  });
25403
25509
  if (result.ok) return { ok: true };
25404
25510
  console.error(
25405
- `${TAG42} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25511
+ `${TAG43} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
25406
25512
  );
25407
25513
  return { ok: false, error: result.error };
25408
25514
  };
@@ -25410,7 +25516,7 @@ function makeWhatsAppSendFile(entry, maxyAccountId) {
25410
25516
  function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25411
25517
  const shared = makeFileDelivery({
25412
25518
  entry,
25413
- tag: TAG42,
25519
+ tag: TAG43,
25414
25520
  channel: "whatsapp",
25415
25521
  sendFile: makeWhatsAppSendFile(entry, maxyAccountId)
25416
25522
  });
@@ -25445,7 +25551,7 @@ function makeWhatsAppFileDelivery(entry, maxyAccountId) {
25445
25551
  if (!delivered) {
25446
25552
  const fileField = call.filePath !== void 0 ? ` file=${call.filePath}` : "";
25447
25553
  console.error(
25448
- `${TAG42} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25554
+ `${TAG43} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
25449
25555
  );
25450
25556
  }
25451
25557
  }
@@ -25840,7 +25946,7 @@ function buildTelegramSpawnRequest(input) {
25840
25946
  import { realpathSync as realpathSync9 } from "fs";
25841
25947
  import { readFile as readFile7, stat as stat7 } from "fs/promises";
25842
25948
  import { resolve as resolve33, basename as basename11 } from "path";
25843
- var TAG43 = "[telegram:outbound]";
25949
+ var TAG44 = "[telegram:outbound]";
25844
25950
  var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
25845
25951
  async function sendTelegramDocument(input) {
25846
25952
  const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot5 } = input;
@@ -25856,16 +25962,16 @@ async function sendTelegramDocument(input) {
25856
25962
  resolvedPath = realpathSync9(filePath);
25857
25963
  const accountResolved = realpathSync9(accountDir);
25858
25964
  if (!resolvedPath.startsWith(accountResolved + "/")) {
25859
- console.error(`${TAG43} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25965
+ console.error(`${TAG44} document REJECTED reason=outside_account_directory maxyAccountId=${maxyAccountId}`);
25860
25966
  return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
25861
25967
  }
25862
25968
  } catch (err) {
25863
25969
  const code = err.code;
25864
25970
  if (code === "ENOENT") {
25865
- console.error(`${TAG43} document ENOENT path=${filePath}`);
25971
+ console.error(`${TAG44} document ENOENT path=${filePath}`);
25866
25972
  return { ok: false, status: 404, error: `File not found: ${filePath}` };
25867
25973
  }
25868
- console.error(`${TAG43} document path error: ${String(err)}`);
25974
+ console.error(`${TAG44} document path error: ${String(err)}`);
25869
25975
  return { ok: false, status: 500, error: String(err) };
25870
25976
  }
25871
25977
  const fileStat = await stat7(resolvedPath);
@@ -25898,13 +26004,13 @@ async function sendTelegramDocument(input) {
25898
26004
  error = e instanceof Error ? e.message : String(e);
25899
26005
  }
25900
26006
  console.error(
25901
- `${TAG43} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
26007
+ `${TAG44} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
25902
26008
  );
25903
26009
  return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
25904
26010
  }
25905
26011
 
25906
26012
  // app/lib/telegram/outbound/file-delivery.ts
25907
- var TAG44 = "[telegram:outbound]";
26013
+ var TAG45 = "[telegram:outbound]";
25908
26014
  function platformRoot4() {
25909
26015
  return process.env.MAXY_PLATFORM_ROOT || "";
25910
26016
  }
@@ -25916,11 +26022,11 @@ function makeTelegramSendFile(entry) {
25916
26022
  botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
25917
26023
  }
25918
26024
  if (!botToken) {
25919
- console.error(`${TAG44} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
26025
+ console.error(`${TAG45} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
25920
26026
  return { ok: false, error: "no-bot-token" };
25921
26027
  }
25922
26028
  if (entry.replyTarget == null) {
25923
- console.error(`${TAG44} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
26029
+ console.error(`${TAG45} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
25924
26030
  return { ok: false, error: "no-reply-target" };
25925
26031
  }
25926
26032
  const result = await sendTelegramDocument({
@@ -25937,7 +26043,7 @@ function makeTelegramSendFile(entry) {
25937
26043
  function makeTelegramFileDelivery(entry) {
25938
26044
  return makeFileDelivery({
25939
26045
  entry,
25940
- tag: TAG44,
26046
+ tag: TAG45,
25941
26047
  channel: "telegram",
25942
26048
  sendFile: makeTelegramSendFile(entry)
25943
26049
  });
@@ -25987,7 +26093,7 @@ function telegramServerPath() {
25987
26093
 
25988
26094
  // app/lib/channel-pty-bridge/public-session-end-review.ts
25989
26095
  import { randomUUID as randomUUID16 } from "crypto";
25990
- var TAG45 = "[public-session-review]";
26096
+ var TAG46 = "[public-session-review]";
25991
26097
  var CONSUMED_CAP = 500;
25992
26098
  var consumed = /* @__PURE__ */ new Set();
25993
26099
  function consumeOnce(sessionId) {
@@ -26014,7 +26120,7 @@ async function fetchJsonl(sessionId) {
26014
26120
  return await res.text();
26015
26121
  } catch (err) {
26016
26122
  console.error(
26017
- `${TAG45} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
26123
+ `${TAG46} jsonl-fetch-failed sessionId=${sessionId} err="${err instanceof Error ? err.message : String(err)}"`
26018
26124
  );
26019
26125
  return null;
26020
26126
  }
@@ -26038,7 +26144,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
26038
26144
  const res = await fetch(url);
26039
26145
  if (!res.ok) {
26040
26146
  console.error(
26041
- `${TAG45} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
26147
+ `${TAG46} prior-writes-fetch-failed sliceToken=${sliceToken.slice(0, 8)} status=${res.status}`
26042
26148
  );
26043
26149
  return [];
26044
26150
  }
@@ -26046,7 +26152,7 @@ async function fetchPriorWrites(sliceToken, accountId) {
26046
26152
  return Array.isArray(body.writes) ? body.writes : [];
26047
26153
  } catch (err) {
26048
26154
  console.error(
26049
- `${TAG45} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
26155
+ `${TAG46} prior-writes-fetch-threw sliceToken=${sliceToken.slice(0, 8)} err="${err instanceof Error ? err.message : String(err)}"`
26050
26156
  );
26051
26157
  return [];
26052
26158
  }
@@ -26075,7 +26181,7 @@ function composeInitialMessage(input) {
26075
26181
  }
26076
26182
  async function dispatchReviewer(input, initialMessage) {
26077
26183
  const sessionId = randomUUID16();
26078
- console.log(`${TAG45} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
26184
+ console.log(`${TAG46} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
26079
26185
  const spawned = await managerRcSpawn({
26080
26186
  sessionId,
26081
26187
  initialMessage,
@@ -26095,21 +26201,21 @@ async function firePublicSessionEndReview(input) {
26095
26201
  const dispatchedAt = Date.now();
26096
26202
  if (consumed.has(input.sessionId)) {
26097
26203
  console.log(
26098
- `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26204
+ `${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26099
26205
  );
26100
26206
  return;
26101
26207
  }
26102
26208
  const jsonl = await fetchJsonl(input.sessionId);
26103
26209
  if (!jsonl) {
26104
26210
  console.log(
26105
- `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
26211
+ `${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-jsonl-missing`
26106
26212
  );
26107
26213
  return;
26108
26214
  }
26109
26215
  const operatorTurns = countOperatorTurns(jsonl);
26110
26216
  if (operatorTurns === 0) {
26111
26217
  console.log(
26112
- `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
26218
+ `${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-no-turns`
26113
26219
  );
26114
26220
  return;
26115
26221
  }
@@ -26123,22 +26229,47 @@ async function firePublicSessionEndReview(input) {
26123
26229
  });
26124
26230
  if (!consumeOnce(input.sessionId)) {
26125
26231
  console.log(
26126
- `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26232
+ `${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-already-reviewed`
26127
26233
  );
26128
26234
  return;
26129
26235
  }
26130
26236
  const dispatched = await dispatchReviewer(input, initialMessage);
26131
26237
  if (!dispatched.ok) {
26132
26238
  console.error(
26133
- `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
26239
+ `${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=skipped-spawn-failed reason=${dispatched.reason}`
26134
26240
  );
26135
26241
  return;
26136
26242
  }
26137
26243
  console.log(
26138
- `${TAG45} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
26244
+ `${TAG46} dispatchFor=${input.dispatchFor} sessionId=${input.sessionId} sliceToken=${sliceShort} personId=${personField} result=dispatched managerSessionId=${dispatched.managerSessionId} operatorTurns=${operatorTurns} priorWrites=${priorWrites.length} ms=${Date.now() - dispatchedAt}`
26139
26245
  );
26140
26246
  }
26141
26247
 
26248
+ // app/lib/whatsapp/inbound/resolve-client-graph-owner.ts
26249
+ var import_dist8 = __toESM(require_dist(), 1);
26250
+ async function resolveClientOwnerUserId(accountId, deps = {}) {
26251
+ const listAccounts = deps.listAccounts ?? listValidAccounts;
26252
+ const getSession4 = deps.getSession ?? getSession;
26253
+ const resolveOwner2 = deps.resolveOwner ?? import_dist8.resolveOwnerUserId;
26254
+ const role = listAccounts().find((a) => a.accountId === accountId)?.config.role;
26255
+ if (role !== "client") return null;
26256
+ const session = getSession4();
26257
+ try {
26258
+ return await resolveOwner2(session, accountId);
26259
+ } catch (err) {
26260
+ console.error(
26261
+ `[whatsapp-native] op=graph-owner-error account=${accountId} error=${err instanceof Error ? err.message : String(err)}`
26262
+ );
26263
+ return null;
26264
+ } finally {
26265
+ await session.close().catch((err) => {
26266
+ console.error(
26267
+ `[whatsapp-native] op=graph-owner-close-error account=${accountId} error=${err instanceof Error ? err.message : String(err)}`
26268
+ );
26269
+ });
26270
+ }
26271
+ }
26272
+
26142
26273
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
26143
26274
  function findAccountManagerDrift(accountManagers, adminPhones, validAccountIds) {
26144
26275
  const drift = [];
@@ -26556,8 +26687,8 @@ var webchatFileFollowers = /* @__PURE__ */ new Map();
26556
26687
  async function fetchAccountStandingRules(accountId) {
26557
26688
  const session = getSession();
26558
26689
  try {
26559
- const res = await (0, import_dist8.resolveActiveRules)(session, accountId);
26560
- return { block: (0, import_dist8.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
26690
+ const res = await (0, import_dist9.resolveActiveRules)(session, accountId);
26691
+ return { block: (0, import_dist9.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
26561
26692
  } catch (err) {
26562
26693
  console.error(
26563
26694
  `[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
@@ -26658,10 +26789,19 @@ var waGateway = new WaGateway({
26658
26789
  console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
26659
26790
  }
26660
26791
  const req = buildWaSpawnRequest({ accountId: effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath });
26661
- const userId = role === "admin" ? resolveAdminUserId({ accountId: effectiveAccountId, senderPhone: senderId }) ?? (ownerSelfPhone ? resolveOwnerUserId(effectiveAccountId) ?? void 0 : void 0) : void 0;
26792
+ let userId = role === "admin" ? resolveAdminUserId({ accountId: effectiveAccountId, senderPhone: senderId }) ?? (ownerSelfPhone ? resolveOwnerUserId(effectiveAccountId) ?? void 0 : void 0) : void 0;
26793
+ let graphOwnerResolved = false;
26794
+ if (role === "admin" && userId === void 0) {
26795
+ const graphOwner = await resolveClientOwnerUserId(effectiveAccountId);
26796
+ if (graphOwner) {
26797
+ userId = graphOwner;
26798
+ graphOwnerResolved = true;
26799
+ }
26800
+ }
26662
26801
  if (role === "admin") {
26663
26802
  const attribution = userId ?? (ownerSelfPhone ? "owner-self-unresolved" : "unbound-empty");
26664
- console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${attribution}`);
26803
+ const sourceSuffix = graphOwnerResolved ? " source=graph-owner" : "";
26804
+ console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${attribution}${sourceSuffix}`);
26665
26805
  }
26666
26806
  const result = await managerRcSpawn({ ...req, userId });
26667
26807
  if ("error" in result) {
@@ -26874,8 +27014,8 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
26874
27014
  });
26875
27015
  app62.route("/api/channel/schedule-inject", scheduleInjectRoutes);
26876
27016
  app62.use("*", clientIpMiddleware);
26877
- function allowsSameOriginFraming(path2) {
26878
- return path2 === "/vnc-viewer.html" || path2.startsWith("/api/admin/attachment/") || path2.startsWith("/api/public-reader/attachment/") || path2 === "/api/admin/files/download";
27017
+ function allowsSameOriginFraming(path3) {
27018
+ return path3 === "/vnc-viewer.html" || path3.startsWith("/api/admin/attachment/") || path3.startsWith("/api/public-reader/attachment/") || path3 === "/api/admin/files/download";
26879
27019
  }
26880
27020
  app62.use("*", async (c, next) => {
26881
27021
  await next();
@@ -26908,12 +27048,12 @@ app62.use("*", async (c, next) => {
26908
27048
  await next();
26909
27049
  return;
26910
27050
  }
26911
- const path2 = c.req.path;
26912
- if (isPublicPathAllowed(path2)) {
27051
+ const path3 = c.req.path;
27052
+ if (isPublicPathAllowed(path3)) {
26913
27053
  await next();
26914
27054
  return;
26915
27055
  }
26916
- console.error(`[public-host] blocked path=${path2}`);
27056
+ console.error(`[public-host] blocked path=${path3}`);
26917
27057
  return c.text("Not found", 404);
26918
27058
  });
26919
27059
  var lastRemoteAuthDisplayName = "";
@@ -27148,12 +27288,12 @@ console.log("[client-error-route] mounted");
27148
27288
  var PWA_PUBLIC_PATHS = /* @__PURE__ */ new Set(["/sw.js", ...PWA_SURFACES.map((s) => s.manifestPath)]);
27149
27289
  app62.use("*", async (c, next) => {
27150
27290
  const host = (c.req.header("host") ?? "").split(":")[0];
27151
- const path2 = c.req.path;
27152
- if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
27291
+ const path3 = c.req.path;
27292
+ if (path3 === "/favicon.ico" || path3.startsWith("/assets/") || path3.startsWith("/brand/") || // Public free/busy is read by the booking page (a separate Cloudflare Pages
27153
27293
  // origin) against the brand host with no admin cookie. It is public by
27154
27294
  // design — open slots only, no :Meeting detail — so it bypasses the gate
27155
27295
  // exactly like /brand/. The detail-leak guard lives in the route + its test.
27156
- path2 === "/api/calendar/free-busy" || PWA_PUBLIC_PATHS.has(path2)) {
27296
+ path3 === "/api/calendar/free-busy" || PWA_PUBLIC_PATHS.has(path3)) {
27157
27297
  await next();
27158
27298
  return;
27159
27299
  }
@@ -27180,15 +27320,15 @@ app62.use("*", async (c, next) => {
27180
27320
  }
27181
27321
  const disambig = `remoteAddress="${rawRemote ?? ""}" xff="${rawXff ?? ""}" resolvedKind=${decision.client.kind}`;
27182
27322
  if (decision.reason === "remote-unconfigured") {
27183
- console.error(`[remote-auth] not configured, redirecting to setup ip=${clientIp} path=${path2} ${disambig}`);
27323
+ console.error(`[remote-auth] not configured, redirecting to setup ip=${clientIp} path=${path3} ${disambig}`);
27184
27324
  return c.redirect("/__remote-auth/setup");
27185
27325
  }
27186
- const respond = isApiRequestPath(path2) ? "401" : "html";
27187
- console.error(`[remote-auth] login required ip=${clientIp} path=${path2} respond=${respond} ${disambig}`);
27326
+ const respond = isApiRequestPath(path3) ? "401" : "html";
27327
+ console.error(`[remote-auth] login required ip=${clientIp} path=${path3} respond=${respond} ${disambig}`);
27188
27328
  if (respond === "401") {
27189
27329
  return c.json({ code: "remote-auth-required" }, 401);
27190
27330
  }
27191
- return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path2 }), 200);
27331
+ return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(c), redirect: path3 }), 200);
27192
27332
  });
27193
27333
  app62.route("/api/health", health_default);
27194
27334
  app62.route("/api/chat", chatRoutes);
@@ -27613,11 +27753,11 @@ if (brandFaviconPath !== "/favicon.ico") {
27613
27753
  app62.use("/*", serveStatic({ root: "./public" }));
27614
27754
  app62.all("*", (c) => {
27615
27755
  const host = (c.req.header("host") ?? "").split(":")[0];
27616
- const path2 = c.req.path;
27756
+ const path3 = c.req.path;
27617
27757
  if (isPublicHost(host)) {
27618
- const inAllowedList = PUBLIC_ALLOWED_EXACT.includes(path2) || PUBLIC_ALLOWED_PREFIXES.some((prefix) => path2.startsWith(prefix));
27758
+ const inAllowedList = PUBLIC_ALLOWED_EXACT.includes(path3) || PUBLIC_ALLOWED_PREFIXES.some((prefix) => path3.startsWith(prefix));
27619
27759
  if (inAllowedList) {
27620
- console.error(`[public-host] unmatched-allowed path=${path2}`);
27760
+ console.error(`[public-host] unmatched-allowed path=${path3}`);
27621
27761
  return c.json({ error: "not-found" }, 404);
27622
27762
  }
27623
27763
  }