@rubytech/create-maxy-code 0.1.313 → 0.1.314

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 (103) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-style/dist/index.d.ts.map +1 -1
  3. package/payload/platform/lib/graph-style/dist/index.js +5 -3
  4. package/payload/platform/lib/graph-style/dist/index.js.map +1 -1
  5. package/payload/platform/lib/graph-style/src/index.ts +5 -3
  6. package/payload/platform/neo4j/schema.cypher +7 -17
  7. package/payload/platform/plugins/admin/skills/file-presentation/SKILL.md +1 -1
  8. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +4 -4
  9. package/payload/platform/plugins/admin/skills/professional-document/SKILL.md +1 -1
  10. package/payload/platform/plugins/docs/references/admin-session.md +2 -0
  11. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  12. package/payload/platform/plugins/docs/references/memory-guide.md +1 -1
  13. package/payload/platform/plugins/graph-viewer/mcp/dist/index.js +1 -1
  14. package/payload/platform/plugins/graph-viewer/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.d.ts.map +1 -1
  16. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js +8 -7
  17. package/payload/platform/plugins/graph-viewer/mcp/dist/tools/graph-render.js.map +1 -1
  18. package/payload/platform/plugins/graph-viewer/skills/render-graph/SKILL.md +8 -5
  19. package/payload/platform/plugins/memory/mcp/dist/index.js +2 -0
  20. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  21. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.d.ts +2 -0
  22. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.d.ts.map +1 -0
  23. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.js +146 -0
  24. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/relationship-patterns.test.js.map +1 -0
  25. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js +4 -0
  26. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-cypher-drift.test.js.map +1 -1
  27. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +1 -0
  28. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -1
  29. package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.d.ts +66 -0
  30. package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.d.ts.map +1 -0
  31. package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.js +102 -0
  32. package/payload/platform/plugins/memory/mcp/dist/lib/relationship-patterns.js.map +1 -0
  33. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +9 -0
  34. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
  35. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +6 -1
  36. package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js +1 -0
  38. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-personfields-open.test.js.map +1 -1
  39. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.d.ts +9 -0
  40. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.d.ts.map +1 -1
  41. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.js +25 -1
  42. package/payload/platform/plugins/memory/mcp/dist/tools/memory-edge.js.map +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
  44. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +21 -0
  45. package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
  46. package/payload/platform/plugins/memory/mcp/vitest.config.ts +2 -0
  47. package/payload/platform/plugins/memory/references/schema-construction.md +37 -15
  48. package/payload/platform/scripts/check-no-esm-require.mjs +4 -0
  49. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  50. package/payload/platform/services/claude-session-manager/dist/http-server.js +53 -3
  51. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  52. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +4 -0
  53. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  54. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +6 -0
  55. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  56. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +10 -0
  57. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
  58. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +15 -0
  59. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
  60. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  61. package/payload/platform/services/webchat-channel/dist/instructions.js +8 -1
  62. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  63. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  64. package/payload/platform/services/whatsapp-channel/dist/notification.js +8 -1
  65. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  66. package/payload/platform/templates/specialists/agents/content-producer.md +1 -1
  67. package/payload/server/public/assets/AccessGate-B_sL5-0I.js +1 -0
  68. package/payload/server/public/assets/{AdminLoginScreens-DgwQi3HN.js → AdminLoginScreens-BgicJfUu.js} +1 -1
  69. package/payload/server/public/assets/AdminShell-C0gZ5JKc.js +1 -0
  70. package/payload/server/public/assets/{Checkbox-C54JSreC.js → Checkbox-BJdwS4v2.js} +1 -1
  71. package/payload/server/public/assets/OperatorConversations-DPlZIELy.js +1 -0
  72. package/payload/server/public/assets/{admin-B25rkI9Q.js → admin-DAFDivnO.js} +1 -1
  73. package/payload/server/public/assets/audio-attachment-mime-h53tpH_4.js +30 -0
  74. package/payload/server/public/assets/{brand-TWflEl22.css → brand-bye0l_rp.css} +1 -1
  75. package/payload/server/public/assets/{browser-C9Z5dLlN.js → browser-CmFjVZYo.js} +1 -1
  76. package/payload/server/public/assets/chat-CSw2u48Q.js +1 -0
  77. package/payload/server/public/assets/{data-DCQDh0-Y.js → data-C4v6AdQ-.js} +1 -1
  78. package/payload/server/public/assets/graph-BJyMQdRx.js +51 -0
  79. package/payload/server/public/assets/graph-labels-BTpv_U3g.js +1 -0
  80. package/payload/server/public/assets/operator-Bj88rdnI.js +1 -0
  81. package/payload/server/public/assets/page-hmlVCBRQ.js +1 -0
  82. package/payload/server/public/assets/{public-Cs5N4-Az.js → public-IRKxc9hu.js} +3 -3
  83. package/payload/server/public/assets/{public-next-DoGPYX6T.js → public-next-DqQ4PZLk.js} +1 -1
  84. package/payload/server/public/assets/{useSelectionMode-Cs-vtuKI.js → useSelectionMode-CTJhWnNp.js} +1 -1
  85. package/payload/server/public/browser.html +6 -6
  86. package/payload/server/public/chat.html +8 -8
  87. package/payload/server/public/data.html +5 -5
  88. package/payload/server/public/graph.html +8 -8
  89. package/payload/server/public/index.html +9 -9
  90. package/payload/server/public/operator.html +10 -10
  91. package/payload/server/public/public-next.html +9 -9
  92. package/payload/server/public/public.html +7 -7
  93. package/payload/server/server.js +967 -513
  94. package/payload/server/public/assets/AccessGate-Byskkf47.js +0 -1
  95. package/payload/server/public/assets/AdminShell-DQfL2c_L.js +0 -1
  96. package/payload/server/public/assets/OperatorConversations-DulQyF8c.js +0 -1
  97. package/payload/server/public/assets/audio-attachment-mime-BlwrgPQa.js +0 -30
  98. package/payload/server/public/assets/chat-CGNWDmid.js +0 -1
  99. package/payload/server/public/assets/graph-labels-BBtL--_M.js +0 -1
  100. package/payload/server/public/assets/graph-sMzOI71g.js +0 -51
  101. package/payload/server/public/assets/operator-hI6kJi0I.js +0 -1
  102. package/payload/server/public/assets/page-DbsqlpuX.js +0 -1
  103. /package/payload/server/public/assets/{brand-CYjs10m-.js → brand-DqQPSWa7.js} +0 -0
@@ -429,12 +429,14 @@ var require_dist2 = __commonJS({
429
429
  // apart from people-terracotta, viewing-amber, and task-rose bands so
430
430
  // siteoffice legends read as their own group. Top-level (operator-entry)
431
431
  // labels only; child labels (LineItem, Milestone, Valuation,
432
- // VariationNote, TimeLog) intentionally stay fallback-grey.
432
+ // VariationNote, CheckIn) intentionally stay fallback-grey. The
433
+ // field-worker Worker label is an additional label on Person and takes
434
+ // the Person colour. A subcontractor company's received invoices use the
435
+ // supplier model (Organization + InboundInvoice), so they carry no
436
+ // construction-specific colour.
433
437
  Job: "#8C3B2E",
434
438
  QuoteDocument: "#A65A4A",
435
439
  InboundInvoice: "#6B2A20",
436
- SubContractor: "#BF7E6E",
437
- SubInvoice: "#52201A",
438
440
  WhatsAppGroup: "#D4A096",
439
441
  // External business entity — schema:Organization, written by memory-write
440
442
  // as the Supplier label. Warm tobacco — distinct hue from internal-business
@@ -1271,8 +1273,8 @@ var serveStatic = (options = { root: "" }) => {
1271
1273
  };
1272
1274
 
1273
1275
  // server/index.ts
1274
- import { readFileSync as readFileSync29, existsSync as existsSync31, watchFile } from "fs";
1275
- import { resolve as resolve31, join as join27, basename as basename9 } from "path";
1276
+ import { readFileSync as readFileSync31, existsSync as existsSync31, watchFile } from "fs";
1277
+ import { resolve as resolve33, join as join28, basename as basename10 } from "path";
1276
1278
  import { homedir as homedir3 } from "os";
1277
1279
  import { monitorEventLoopDelay } from "perf_hooks";
1278
1280
 
@@ -1920,7 +1922,7 @@ var credsSaveQueue = Promise.resolve();
1920
1922
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
1921
1923
  console.error(`${TAG2} draining credential save queue\u2026`);
1922
1924
  const timer2 = new Promise(
1923
- (resolve32) => setTimeout(() => resolve32("timeout"), timeoutMs)
1925
+ (resolve34) => setTimeout(() => resolve34("timeout"), timeoutMs)
1924
1926
  );
1925
1927
  const result = await Promise.race([
1926
1928
  credsSaveQueue.then(() => "drained"),
@@ -2048,11 +2050,11 @@ async function createWaSocket(opts) {
2048
2050
  return sock;
2049
2051
  }
2050
2052
  async function waitForConnection(sock) {
2051
- return new Promise((resolve32, reject) => {
2053
+ return new Promise((resolve34, reject) => {
2052
2054
  const handler = (update) => {
2053
2055
  if (update.connection === "open") {
2054
2056
  sock.ev.off("connection.update", handler);
2055
- resolve32();
2057
+ resolve34();
2056
2058
  }
2057
2059
  if (update.connection === "close") {
2058
2060
  sock.ev.off("connection.update", handler);
@@ -2166,14 +2168,14 @@ ${inspected}`;
2166
2168
  return inspect2(err, INSPECT_OPTS2);
2167
2169
  }
2168
2170
  function withTimeout(label, promise, timeoutMs) {
2169
- return new Promise((resolve32, reject) => {
2171
+ return new Promise((resolve34, reject) => {
2170
2172
  const timer2 = setTimeout(() => {
2171
2173
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
2172
2174
  }, timeoutMs);
2173
2175
  promise.then(
2174
2176
  (value) => {
2175
2177
  clearTimeout(timer2);
2176
- resolve32(value);
2178
+ resolve34(value);
2177
2179
  },
2178
2180
  (err) => {
2179
2181
  clearTimeout(timer2);
@@ -3057,8 +3059,8 @@ async function persistWhatsAppMessage(input) {
3057
3059
  const { givenName, familyName } = splitName(input.pushName);
3058
3060
  const prev = sessionWriteLocks.get(input.cacheKey);
3059
3061
  let release;
3060
- const mine = new Promise((resolve32) => {
3061
- release = resolve32;
3062
+ const mine = new Promise((resolve34) => {
3063
+ release = resolve34;
3062
3064
  });
3063
3065
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3064
3066
  sessionWriteLocks.set(input.cacheKey, chained);
@@ -4138,11 +4140,11 @@ async function connectWithReconnect(conn) {
4138
4140
  console.error(
4139
4141
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
4140
4142
  );
4141
- await new Promise((resolve32) => {
4142
- const timer2 = setTimeout(resolve32, delay);
4143
+ await new Promise((resolve34) => {
4144
+ const timer2 = setTimeout(resolve34, delay);
4143
4145
  conn.abortController.signal.addEventListener("abort", () => {
4144
4146
  clearTimeout(timer2);
4145
- resolve32();
4147
+ resolve34();
4146
4148
  }, { once: true });
4147
4149
  });
4148
4150
  }
@@ -4150,16 +4152,16 @@ async function connectWithReconnect(conn) {
4150
4152
  }
4151
4153
  }
4152
4154
  function waitForDisconnectEvent(conn) {
4153
- return new Promise((resolve32) => {
4155
+ return new Promise((resolve34) => {
4154
4156
  if (!conn.sock) {
4155
- resolve32();
4157
+ resolve34();
4156
4158
  return;
4157
4159
  }
4158
4160
  const sock = conn.sock;
4159
4161
  const handler = (update) => {
4160
4162
  if (update.connection === "close") {
4161
4163
  sock.ev.off("connection.update", handler);
4162
- resolve32();
4164
+ resolve34();
4163
4165
  }
4164
4166
  };
4165
4167
  sock.ev.on("connection.update", handler);
@@ -4419,8 +4421,8 @@ async function handleInboundMessage(conn, msg) {
4419
4421
  const conversationKey = isGroup ? remoteJid : senderPhone;
4420
4422
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
4421
4423
  let resolvePending;
4422
- const sttPending = new Promise((resolve32) => {
4423
- resolvePending = resolve32;
4424
+ const sttPending = new Promise((resolve34) => {
4425
+ resolvePending = resolve34;
4424
4426
  });
4425
4427
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
4426
4428
  try {
@@ -4814,20 +4816,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
4814
4816
 
4815
4817
  // server/routes/health.ts
4816
4818
  function checkPort(port2, timeoutMs = 500) {
4817
- return new Promise((resolve32) => {
4819
+ return new Promise((resolve34) => {
4818
4820
  const socket = createConnection2(port2, "127.0.0.1");
4819
4821
  socket.setTimeout(timeoutMs);
4820
4822
  socket.once("connect", () => {
4821
4823
  socket.destroy();
4822
- resolve32(true);
4824
+ resolve34(true);
4823
4825
  });
4824
4826
  socket.once("error", () => {
4825
4827
  socket.destroy();
4826
- resolve32(false);
4828
+ resolve34(false);
4827
4829
  });
4828
4830
  socket.once("timeout", () => {
4829
4831
  socket.destroy();
4830
- resolve32(false);
4832
+ resolve34(false);
4831
4833
  });
4832
4834
  });
4833
4835
  }
@@ -5383,6 +5385,13 @@ ${result.result.text}` : result.result.text;
5383
5385
  sliceToken = session.sliceToken;
5384
5386
  personId = session.personId;
5385
5387
  }
5388
+ let visitorId;
5389
+ if (accessMode === "open") {
5390
+ const cookieHeader = c.req.header("cookie") ?? "";
5391
+ const m = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
5392
+ const v = m ? decodeURIComponent(m[1]).trim() : "";
5393
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v)) visitorId = v;
5394
+ }
5386
5395
  const sk = session_key.slice(0, 8);
5387
5396
  console.error(
5388
5397
  `${WEBCHAT_TAG} inbound from=${sk} bytes=${Buffer.byteLength(fullMessage, "utf8")} attachments=${attachmentCount} accountId=${account.accountId}`
@@ -5405,6 +5414,7 @@ ${result.result.text}` : result.result.text;
5405
5414
  agentSlug,
5406
5415
  ...sliceToken ? { sliceToken } : {},
5407
5416
  ...personId ? { personId } : {},
5417
+ ...visitorId ? { visitorId } : {},
5408
5418
  name: publicTitle
5409
5419
  }
5410
5420
  });
@@ -5871,8 +5881,8 @@ async function startLogin(opts) {
5871
5881
  await clearAuth(authDir);
5872
5882
  let resolveCode = null;
5873
5883
  let rejectCode = null;
5874
- const codePromise = new Promise((resolve32, reject) => {
5875
- resolveCode = resolve32;
5884
+ const codePromise = new Promise((resolve34, reject) => {
5885
+ resolveCode = resolve34;
5876
5886
  rejectCode = reject;
5877
5887
  });
5878
5888
  const codeTimer = setTimeout(
@@ -6008,9 +6018,6 @@ function normalizeJid2(to) {
6008
6018
  function routeKey(to, filePath) {
6009
6019
  return `${normalizeJid2(to)}\0${filePath}`;
6010
6020
  }
6011
- function documentOutboundAt(to) {
6012
- return lastDocumentOutboundAt.get(normalizeJid2(to));
6013
- }
6014
6021
  function recordDocumentOutbound(to) {
6015
6022
  lastDocumentOutboundAt.set(normalizeJid2(to), Date.now());
6016
6023
  }
@@ -6021,14 +6028,14 @@ function routeDocumentOutboundAt(to, filePath) {
6021
6028
  return lastRouteDocumentOutboundAt.get(routeKey(to, filePath));
6022
6029
  }
6023
6030
  async function sendWhatsAppDocument(input) {
6024
- const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot2 } = input;
6031
+ const { to, filePath, caption, accountId, maxyAccountId, platformRoot: platformRoot4 } = input;
6025
6032
  if (!to || !filePath) {
6026
6033
  return { ok: false, status: 400, error: "Missing required fields: to, filePath" };
6027
6034
  }
6028
- if (!maxyAccountId || !platformRoot2) {
6035
+ if (!maxyAccountId || !platformRoot4) {
6029
6036
  return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
6030
6037
  }
6031
- const accountDir = resolve8(platformRoot2, "..", "data/accounts", maxyAccountId);
6038
+ const accountDir = resolve8(platformRoot4, "..", "data/accounts", maxyAccountId);
6032
6039
  let resolvedPath;
6033
6040
  try {
6034
6041
  resolvedPath = realpathSync3(filePath);
@@ -6634,8 +6641,8 @@ app2.get("/group-info", async (c) => {
6634
6641
  var whatsapp_default = app2;
6635
6642
 
6636
6643
  // server/routes/whatsapp-reader.ts
6637
- import { readFileSync as readFileSync11, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync6 } from "fs";
6638
- import { basename as basename2, dirname, join as join10, resolve as resolve11, sep as sep3 } from "path";
6644
+ import { readFileSync as readFileSync12, watch, statSync as statSync4, openSync, readSync, closeSync, existsSync as existsSync7, readdirSync as readdirSync7 } from "fs";
6645
+ import { basename as basename2, dirname, isAbsolute, join as join11, relative as relative2, resolve as resolve11, sep as sep3 } from "path";
6639
6646
 
6640
6647
  // server/routes/admin/sidebar-sessions.ts
6641
6648
  import { readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
@@ -6657,12 +6664,30 @@ function selectReaderChannelSessions(rows) {
6657
6664
  operatorName: null,
6658
6665
  whatsappName: null,
6659
6666
  lastMessageAt: r.lastMessageAt,
6660
- modelGated: r.modelGated
6667
+ modelGated: r.modelGated,
6668
+ model: r.model
6661
6669
  }));
6662
6670
  }
6663
6671
 
6664
6672
  // server/routes/admin/sidebar-sessions.ts
6665
6673
  var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
6674
+ function lastAssistantModel(body) {
6675
+ const lines = body.split("\n");
6676
+ for (let i = lines.length - 1; i >= 0; i--) {
6677
+ const line = lines[i];
6678
+ if (!line || line[0] !== "{") continue;
6679
+ let rec;
6680
+ try {
6681
+ rec = JSON.parse(line);
6682
+ } catch {
6683
+ continue;
6684
+ }
6685
+ if (rec.type !== "assistant") continue;
6686
+ const model = rec.message?.model;
6687
+ if (typeof model === "string" && model !== "<synthetic>") return model;
6688
+ }
6689
+ return null;
6690
+ }
6666
6691
  var CLI_MARKER_PREFIXES = ["<local-command-", "<command-name>", "<command-message>"];
6667
6692
  var ADMIN_ROLE = "admin";
6668
6693
  var WEBCHAT_CHANNEL = "webchat";
@@ -6778,7 +6803,7 @@ function loadUserTitles(accountDir) {
6778
6803
  return out;
6779
6804
  }
6780
6805
  function readSidecarMeta(metaPath) {
6781
- const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null };
6806
+ const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null };
6782
6807
  let raw;
6783
6808
  try {
6784
6809
  raw = readFileSync9(metaPath, "utf8");
@@ -6801,7 +6826,9 @@ function readSidecarMeta(metaPath) {
6801
6826
  senderId: typeof r.senderId === "string" ? r.senderId : null,
6802
6827
  startedAt: typeof r.startedAt === "string" ? r.startedAt : null,
6803
6828
  personId: typeof r.personId === "string" ? r.personId : null,
6804
- adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null
6829
+ adminUserId: typeof r.adminUserId === "string" ? r.adminUserId : null,
6830
+ visitorId: typeof r.visitorId === "string" ? r.visitorId : null,
6831
+ agentSlug: typeof r.agentSlug === "string" ? r.agentSlug : null
6805
6832
  };
6806
6833
  }
6807
6834
  function startsWithCliMarker(s) {
@@ -6932,7 +6959,8 @@ app3.get("/", requireAdminSession, async (c) => {
6932
6959
  bridgeIds,
6933
6960
  archived,
6934
6961
  channel: resolved.channel,
6935
- personName: null
6962
+ personName: null,
6963
+ model: lastAssistantModel(body)
6936
6964
  });
6937
6965
  if (live) liveCount += 1;
6938
6966
  if (isSubagent) subagentCount += 1;
@@ -7086,6 +7114,7 @@ var WA_REPLY_DOCUMENT_TOOL = "mcp__whatsapp-channel__reply-document";
7086
7114
  var WEBCHAT_REPLY_TOOL = "mcp__webchat-channel__reply";
7087
7115
  var CHANNEL_REPLY_TOOL = "mcp__channel__reply";
7088
7116
  var CHANNEL_REPLY_DOCUMENT_TOOL = "mcp__channel__reply-document";
7117
+ var SEND_USER_FILE_TOOL = "SendUserFile";
7089
7118
  var CLI_MARKER_PREFIXES2 = ["<local-command-", "<command-name>", "<command-message>"];
7090
7119
  var CHANNEL_WRAPPER2 = /^<channel\b([^>]*)>\n?([\s\S]*?)\n?<\/channel>$/;
7091
7120
  var SOURCE_ATTR2 = /\bsource="([^"]+)"/;
@@ -7111,17 +7140,29 @@ function unwrapChannel(text) {
7111
7140
  return source ? { text: inner, source } : { text: inner };
7112
7141
  }
7113
7142
  var COMPOSED_ADMIN_FRAME = /^## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
7143
+ var UPLOAD_ANNOTATION = /(?:\n\n)?\[The operator uploaded \d+ file\(s\): [\s\S]*\]$/;
7114
7144
  function rawFromComposedAdminFrame(text) {
7115
- return text.match(COMPOSED_ADMIN_FRAME)?.[1];
7145
+ const context = text.match(COMPOSED_ADMIN_FRAME)?.[1];
7146
+ if (context === void 0) return void 0;
7147
+ return context.replace(UPLOAD_ANNOTATION, "");
7148
+ }
7149
+ var PUBLIC_UPLOAD_ANNOTATION = /(?:\n\n)?\[The user uploaded \d+ file\(s\): [\s\S]*\]$/;
7150
+ var UPLOAD_COUNT = /\[The (?:operator|user) uploaded (\d+) file\(s\):/;
7151
+ function uploadCountOf(text) {
7152
+ const n = Number(text.match(UPLOAD_COUNT)?.[1]);
7153
+ return Number.isInteger(n) && n > 0 ? n : void 0;
7116
7154
  }
7117
7155
  function operatorInboundTurn(u, ts) {
7118
7156
  const rawBody = rawFromComposedAdminFrame(u.text);
7157
+ const uploadCount = uploadCountOf(u.text);
7158
+ const text = rawBody === void 0 ? u.text.replace(PUBLIC_UPLOAD_ANNOTATION, "") : u.text;
7119
7159
  return {
7120
7160
  kind: "operator-inbound",
7121
- text: u.text,
7161
+ text,
7122
7162
  ts,
7123
7163
  ...u.source ? { source: u.source } : {},
7124
- ...rawBody !== void 0 ? { rawBody } : {}
7164
+ ...rawBody !== void 0 ? { rawBody } : {},
7165
+ ...uploadCount !== void 0 ? { uploadCount } : {}
7125
7166
  };
7126
7167
  }
7127
7168
  function asString(content) {
@@ -7157,6 +7198,11 @@ function assistantTurns(content, ts) {
7157
7198
  const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
7158
7199
  const caption = typeof input.caption === "string" ? input.caption : null;
7159
7200
  out.push({ kind: "agent-reply-document", files, caption, ts });
7201
+ } else if (b.type === "tool_use" && b.name === SEND_USER_FILE_TOOL) {
7202
+ const input = b.input ?? {};
7203
+ const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
7204
+ const caption = typeof input.caption === "string" ? input.caption : null;
7205
+ out.push({ kind: "agent-file", files, caption, ts });
7160
7206
  } else if (b.type === "tool_use" && typeof b.name === "string") {
7161
7207
  out.push({ kind: "tool-call", name: b.name, input: b.input ?? null, ts });
7162
7208
  }
@@ -7194,9 +7240,8 @@ function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress) {
7194
7240
  out.push(operatorInboundTurn(u, ts));
7195
7241
  }
7196
7242
  }
7197
- function parseTranscript(lines) {
7243
+ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map()) {
7198
7244
  const out = [];
7199
- const queuedPendingSuppress = /* @__PURE__ */ new Map();
7200
7245
  for (const line of lines) {
7201
7246
  if (!line || line[0] !== "{") continue;
7202
7247
  let row;
@@ -7256,6 +7301,65 @@ function parseTranscript(lines) {
7256
7301
  return out;
7257
7302
  }
7258
7303
 
7304
+ // app/lib/whatsapp-reader/enrich-attachments.ts
7305
+ import { readFileSync as readFileSync11, readdirSync as readdirSync6 } from "fs";
7306
+ import { join as join10 } from "path";
7307
+ var ADMIN_UPLOAD_PATH = /\/uploads\/(?!public\/)([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\//gi;
7308
+ function adminAttachmentIds(text) {
7309
+ const ids = [];
7310
+ for (const m of text.matchAll(ADMIN_UPLOAD_PATH)) {
7311
+ const id = m[1].toLowerCase();
7312
+ if (!ids.includes(id)) ids.push(id);
7313
+ }
7314
+ return ids;
7315
+ }
7316
+ function adminAttachmentsFromText(text, uploadsBase) {
7317
+ const out = [];
7318
+ for (const id of adminAttachmentIds(text)) {
7319
+ const meta = readAttachmentMeta(join10(uploadsBase, id));
7320
+ if (meta) out.push(meta);
7321
+ }
7322
+ return out;
7323
+ }
7324
+ function readSidecar(dir, attachmentId) {
7325
+ let parsed;
7326
+ try {
7327
+ parsed = JSON.parse(readFileSync11(join10(dir, `${attachmentId}.meta.json`), "utf8"));
7328
+ } catch {
7329
+ return null;
7330
+ }
7331
+ const filename = parsed.filename;
7332
+ const mimeType = parsed.mimeType;
7333
+ const sizeBytes = parsed.sizeBytes;
7334
+ const storedAt = parsed.storedAt;
7335
+ if (typeof filename !== "string" || typeof mimeType !== "string" || typeof sizeBytes !== "number" || typeof storedAt !== "string") {
7336
+ return null;
7337
+ }
7338
+ return { attachmentId, filename, mimeType, sizeBytes, storedAt };
7339
+ }
7340
+ function readAttachmentMeta(dir) {
7341
+ const attachmentId = dir.split("/").filter(Boolean).pop() ?? "";
7342
+ const sidecar = readSidecar(dir, attachmentId);
7343
+ if (!sidecar) return null;
7344
+ const { storedAt: _storedAt, ...render } = sidecar;
7345
+ return render;
7346
+ }
7347
+ function listSessionAttachmentsInDir(dir) {
7348
+ let entries;
7349
+ try {
7350
+ entries = readdirSync6(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
7351
+ } catch {
7352
+ return [];
7353
+ }
7354
+ const sidecars = [];
7355
+ for (const name of entries) {
7356
+ const sidecar = readSidecar(join10(dir, name), name);
7357
+ if (sidecar) sidecars.push(sidecar);
7358
+ }
7359
+ sidecars.sort((a, b) => a.storedAt.localeCompare(b.storedAt));
7360
+ return sidecars.map(({ storedAt: _storedAt, ...render }) => render);
7361
+ }
7362
+
7259
7363
  // app/lib/whatsapp-reader/tail.ts
7260
7364
  function splitNewLines(buf, baseOffset) {
7261
7365
  const text = buf.toString("utf8");
@@ -7284,17 +7388,17 @@ var app4 = new Hono();
7284
7388
  app4.get("/conversations", requireAdminSession, async (c) => {
7285
7389
  const cfg = claudeConfigDir();
7286
7390
  if (!cfg) return c.json({ conversations: [] });
7287
- const projectsRoot = join10(cfg, "projects");
7391
+ const projectsRoot = join11(cfg, "projects");
7288
7392
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
7289
7393
  const rows = [];
7290
7394
  for (const { path: path2 } of enumerateJsonls(projectsRoot)) {
7291
7395
  const sessionId = basename2(path2).replace(/\.jsonl$/, "");
7292
7396
  const projectDir = dirname(path2);
7293
- const meta = readSidecarMeta(join10(projectDir, `${sessionId}.meta.json`));
7397
+ const meta = readSidecarMeta(join11(projectDir, `${sessionId}.meta.json`));
7294
7398
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
7295
7399
  let body = "";
7296
7400
  try {
7297
- body = readFileSync11(path2, "utf8");
7401
+ body = readFileSync12(path2, "utf8");
7298
7402
  } catch {
7299
7403
  }
7300
7404
  const { title } = resolveTitle(body, sessionId, userTitles);
@@ -7311,7 +7415,8 @@ app4.get("/conversations", requireAdminSession, async (c) => {
7311
7415
  channel: meta.channel,
7312
7416
  senderId: meta.senderId,
7313
7417
  lastMessageAt,
7314
- modelGated
7418
+ modelGated,
7419
+ model: lastAssistantModel(body)
7315
7420
  });
7316
7421
  }
7317
7422
  const selected = selectReaderChannelSessions(rows);
@@ -7377,15 +7482,31 @@ function resumeOffset(lastEventId, fileEnd) {
7377
7482
  if (!Number.isFinite(n) || n < 0) return 0;
7378
7483
  return Math.min(n, fileEnd);
7379
7484
  }
7485
+ function enrichDownloads(turn) {
7486
+ if (turn.kind !== "agent-file") return turn;
7487
+ const downloads = turn.files.map((f) => {
7488
+ const rel = relative2(DATA_ROOT, f);
7489
+ return !rel || rel.startsWith("..") || isAbsolute(rel) ? null : rel;
7490
+ });
7491
+ return { ...turn, downloads };
7492
+ }
7493
+ function enrichAdminAttachments(turn) {
7494
+ if (turn.kind !== "operator-inbound") return turn;
7495
+ if (adminAttachmentIds(turn.text).length === 0) return turn;
7496
+ const account = resolveAccount();
7497
+ if (!account) return turn;
7498
+ const attachments = adminAttachmentsFromText(turn.text, resolve11(DATA_ROOT, "accounts", account.accountId, "uploads"));
7499
+ return attachments.length > 0 ? { ...turn, attachments } : turn;
7500
+ }
7380
7501
  app4.get("/stream", requireAdminSession, (c) => {
7381
7502
  const sessionId = c.req.query("sessionId") ?? "";
7382
7503
  const projectDir = c.req.query("projectDir") ?? "";
7383
7504
  const cfg = claudeConfigDir();
7384
- const projectsRoot = cfg ? resolve11(join10(cfg, "projects")) : null;
7505
+ const projectsRoot = cfg ? resolve11(join11(cfg, "projects")) : null;
7385
7506
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
7386
7507
  return c.json({ error: "bad session reference" }, 400);
7387
7508
  }
7388
- const jsonlPath = resolve11(join10(projectDir, `${sessionId}.jsonl`));
7509
+ const jsonlPath = resolve11(join11(projectDir, `${sessionId}.jsonl`));
7389
7510
  if (!jsonlPath.startsWith(projectsRoot + sep3)) {
7390
7511
  return c.json({ error: "bad session reference" }, 400);
7391
7512
  }
@@ -7408,6 +7529,7 @@ data: ${JSON.stringify(turn)}
7408
7529
  })();
7409
7530
  let offset = resumeOffset(lastEventId, fileEnd0);
7410
7531
  console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
7532
+ const queuedPendingSuppress = /* @__PURE__ */ new Map();
7411
7533
  const drain = () => {
7412
7534
  try {
7413
7535
  const { buf, end } = readFrom(jsonlPath, offset);
@@ -7415,8 +7537,8 @@ data: ${JSON.stringify(turn)}
7415
7537
  const { lines, nextOffset } = splitNewLines(buf, offset);
7416
7538
  offset = nextOffset;
7417
7539
  let n = 0;
7418
- for (const turn of parseTranscript(lines)) {
7419
- send(controller, turn, offset);
7540
+ for (const turn of parseTranscript(lines, queuedPendingSuppress)) {
7541
+ send(controller, enrichAdminAttachments(enrichDownloads(turn)), offset);
7420
7542
  if (turn.kind === "agent-error") {
7421
7543
  console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
7422
7544
  }
@@ -7431,8 +7553,8 @@ data: ${JSON.stringify(turn)}
7431
7553
  const { buf, end } = readFrom(jsonlPath, 0);
7432
7554
  offset = end;
7433
7555
  let n = 0;
7434
- for (const turn of parseTranscript(buf.toString("utf8").split("\n"))) {
7435
- send(controller, turn, offset);
7556
+ for (const turn of parseTranscript(buf.toString("utf8").split("\n"), queuedPendingSuppress)) {
7557
+ send(controller, enrichAdminAttachments(enrichDownloads(turn)), offset);
7436
7558
  if (turn.kind === "agent-error") {
7437
7559
  console.log(`[wa-stream] op=agent-error conn=${connId} sessionId=${sessionId} code=${turn.code} source=jsonl`);
7438
7560
  }
@@ -7487,7 +7609,7 @@ app4.get("/directives", requireAdminSession, (c) => {
7487
7609
  if (!dir || !existsSync7(dir)) return c.json({ entries: [] });
7488
7610
  let names;
7489
7611
  try {
7490
- names = readdirSync6(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
7612
+ names = readdirSync7(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
7491
7613
  } catch {
7492
7614
  return c.json({ entries: [] });
7493
7615
  }
@@ -7497,7 +7619,7 @@ app4.get("/directives", requireAdminSession, (c) => {
7497
7619
  const pid = Number(pidPart);
7498
7620
  let len = 0;
7499
7621
  try {
7500
- len = statSync4(join10(dir, name)).size;
7622
+ len = statSync4(join11(dir, name)).size;
7501
7623
  } catch {
7502
7624
  }
7503
7625
  return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
@@ -7512,11 +7634,11 @@ app4.get("/directive", requireAdminSession, (c) => {
7512
7634
  }
7513
7635
  const dir = directiveStoreDir(sessionId);
7514
7636
  if (!dir) return c.json({ error: "no account" }, 404);
7515
- const path2 = resolve11(join10(dir, name));
7637
+ const path2 = resolve11(join11(dir, name));
7516
7638
  if (!path2.startsWith(dir + sep3)) return c.json({ error: "bad reference" }, 400);
7517
7639
  let body;
7518
7640
  try {
7519
- body = readFileSync11(path2, "utf8");
7641
+ body = readFileSync12(path2, "utf8");
7520
7642
  } catch {
7521
7643
  return c.json({ error: "not found" }, 404);
7522
7644
  }
@@ -7525,8 +7647,8 @@ app4.get("/directive", requireAdminSession, (c) => {
7525
7647
  var whatsapp_reader_default = app4;
7526
7648
 
7527
7649
  // server/routes/public-reader.ts
7528
- import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2 } from "fs";
7529
- import { basename as basename3, dirname as dirname2, join as join11, resolve as resolve12, sep as sep4 } from "path";
7650
+ import { statSync as statSync5, openSync as openSync2, readSync as readSync2, closeSync as closeSync2, watch as watch2, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
7651
+ import { basename as basename3, dirname as dirname2, join as join12, resolve as resolve12, sep as sep4 } from "path";
7530
7652
 
7531
7653
  // app/lib/whatsapp-reader/delivered-kinds.ts
7532
7654
  var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
@@ -7563,6 +7685,13 @@ function resolveVisitorSession(rows, visitorPersonId) {
7563
7685
  (best, r) => (r.startedAt ?? "") > (best.startedAt ?? "") ? r : best
7564
7686
  );
7565
7687
  }
7688
+ function resolveOpenVisitorSession(rows, visitorId, agentSlug) {
7689
+ const matches = rows.filter(
7690
+ (r) => r.role === "public" && r.channel === "webchat" && r.personId === null && r.visitorId === visitorId && r.agentSlug === agentSlug
7691
+ );
7692
+ if (matches.length === 0) return null;
7693
+ return matches.reduce((best, r) => (r.startedAt ?? "") > (best.startedAt ?? "") ? r : best);
7694
+ }
7566
7695
 
7567
7696
  // server/routes/public-reader.ts
7568
7697
  var app5 = new Hono();
@@ -7572,19 +7701,31 @@ function parseAccessSessionId(cookieHeader) {
7572
7701
  const part = cookieHeader.split(";").map((p) => p.trim()).find((p) => p.startsWith("__access_session="));
7573
7702
  return part ? part.slice("__access_session=".length) : null;
7574
7703
  }
7704
+ var VISITOR_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7705
+ function parseVisitorCookie(cookieHeader) {
7706
+ if (!cookieHeader) return null;
7707
+ const m = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
7708
+ if (!m) return null;
7709
+ const v = decodeURIComponent(m[1]).trim();
7710
+ return VISITOR_UUID_RE.test(v) ? v : null;
7711
+ }
7575
7712
  function resolveVisitor(c) {
7576
- const id = parseAccessSessionId(c.req.header("cookie"));
7577
- const session = id ? getAccessSession(id) : null;
7578
- return session && session.personId ? { personId: session.personId } : null;
7713
+ const cookie = c.req.header("cookie");
7714
+ const grantId = parseAccessSessionId(cookie);
7715
+ const session = grantId ? getAccessSession(grantId) : null;
7716
+ if (session && session.personId) return { kind: "person", personId: session.personId };
7717
+ const visitorId = parseVisitorCookie(cookie);
7718
+ if (visitorId) return { kind: "visitor", visitorId };
7719
+ return null;
7579
7720
  }
7580
7721
  function enumeratePublicRows() {
7581
7722
  const cfg = claudeConfigDir();
7582
7723
  if (!cfg) return [];
7583
7724
  const rows = [];
7584
- for (const { path: path2 } of enumerateJsonls(join11(cfg, "projects"))) {
7725
+ for (const { path: path2 } of enumerateJsonls(join12(cfg, "projects"))) {
7585
7726
  const sessionId = basename3(path2).replace(/\.jsonl$/, "");
7586
7727
  const projectDir = dirname2(path2);
7587
- const meta = readSidecarMeta(join11(projectDir, `${sessionId}.meta.json`));
7728
+ const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
7588
7729
  rows.push({
7589
7730
  sessionId,
7590
7731
  projectDir,
@@ -7592,7 +7733,9 @@ function enumeratePublicRows() {
7592
7733
  channel: meta.channel,
7593
7734
  personId: meta.personId,
7594
7735
  senderId: meta.senderId,
7595
- startedAt: meta.startedAt
7736
+ startedAt: meta.startedAt,
7737
+ visitorId: meta.visitorId,
7738
+ agentSlug: meta.agentSlug
7596
7739
  });
7597
7740
  }
7598
7741
  return rows;
@@ -7600,18 +7743,43 @@ function enumeratePublicRows() {
7600
7743
  app5.get("/session", (c) => {
7601
7744
  const visitor = resolveVisitor(c);
7602
7745
  if (!visitor) {
7603
- console.error(`${TAG20} op=reader-refused reason=no-grant path=/session`);
7746
+ console.error(`${TAG20} op=reader-refused reason=no-anchor path=/session`);
7604
7747
  return c.json({ error: "gate-required" }, 401);
7605
7748
  }
7606
- const found = resolveVisitorSession(enumeratePublicRows(), visitor.personId);
7749
+ const rows = enumeratePublicRows();
7750
+ let found = null;
7751
+ if (visitor.kind === "person") {
7752
+ found = resolveVisitorSession(rows, visitor.personId);
7753
+ } else {
7754
+ const account = resolveAccount();
7755
+ const agentParam = c.req.query("agent");
7756
+ let effectiveSlug;
7757
+ if (agentParam) {
7758
+ effectiveSlug = account && validateAgentSlug(agentParam) && isActiveAgentSlug(account.accountDir, agentParam) ? agentParam : null;
7759
+ } else {
7760
+ effectiveSlug = account ? resolveDefaultAgentSlug(account.accountDir) : null;
7761
+ }
7762
+ found = effectiveSlug ? resolveOpenVisitorSession(rows, visitor.visitorId, effectiveSlug) : null;
7763
+ }
7607
7764
  if (found) {
7608
- console.log(`${TAG20} op=session-resume key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
7765
+ console.log(`${TAG20} op=session-resume anchor=${visitor.kind} key=${(found.senderId ?? "").slice(0, 8)} sessionId=${found.sessionId.slice(0, 8)}`);
7609
7766
  return c.json({ sessionId: found.sessionId, projectDir: found.projectDir, sessionKey: found.senderId });
7610
7767
  }
7611
7768
  const sessionKey = crypto.randomUUID();
7612
- console.log(`${TAG20} op=session-new key=${sessionKey.slice(0, 8)}`);
7769
+ console.log(`${TAG20} op=session-new anchor=${visitor.kind} key=${sessionKey.slice(0, 8)}`);
7613
7770
  return c.json({ sessionId: null, projectDir: null, sessionKey });
7614
7771
  });
7772
+ function publicUploadsDir(accountId, sessionId) {
7773
+ return resolve12(DATA_ROOT, "accounts", accountId, "uploads", "public", sessionId);
7774
+ }
7775
+ function enrichPublicAttachments(turns, sessionAttachments, cursor) {
7776
+ for (const turn of turns) {
7777
+ if (turn.kind !== "operator-inbound" || !turn.uploadCount) continue;
7778
+ const slice = sessionAttachments.slice(cursor.consumed, cursor.consumed + turn.uploadCount);
7779
+ cursor.consumed += turn.uploadCount;
7780
+ if (slice.length > 0) turn.attachments = slice;
7781
+ }
7782
+ }
7615
7783
  function readFrom2(path2, from) {
7616
7784
  const end = statSync5(path2).size;
7617
7785
  if (end <= from) return { buf: Buffer.alloc(0), end: from };
@@ -7627,37 +7795,43 @@ function readFrom2(path2, from) {
7627
7795
  app5.get("/stream", (c) => {
7628
7796
  const visitor = resolveVisitor(c);
7629
7797
  if (!visitor) {
7630
- console.error(`${TAG20} op=reader-refused reason=no-grant path=/stream`);
7798
+ console.error(`${TAG20} op=reader-refused reason=no-anchor path=/stream`);
7631
7799
  return c.json({ error: "gate-required" }, 401);
7632
7800
  }
7633
7801
  const sessionId = c.req.query("sessionId") ?? "";
7634
7802
  const projectDir = c.req.query("projectDir") ?? "";
7635
7803
  const cfg = claudeConfigDir();
7636
- const projectsRoot = cfg ? resolve12(join11(cfg, "projects")) : null;
7804
+ const projectsRoot = cfg ? resolve12(join12(cfg, "projects")) : null;
7637
7805
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
7638
7806
  return c.json({ error: "bad session reference" }, 400);
7639
7807
  }
7640
- const jsonlPath = resolve12(join11(projectDir, `${sessionId}.jsonl`));
7808
+ const jsonlPath = resolve12(join12(projectDir, `${sessionId}.jsonl`));
7641
7809
  if (!jsonlPath.startsWith(projectsRoot + sep4)) {
7642
7810
  return c.json({ error: "bad session reference" }, 400);
7643
7811
  }
7644
- const meta = readSidecarMeta(join11(projectDir, `${sessionId}.meta.json`));
7645
- if (!(meta.role === "public" && meta.channel === "webchat" && meta.personId === visitor.personId)) {
7646
- console.error(`${TAG20} op=reader-refused reason=not-owner sessionId=${sessionId.slice(0, 8)}`);
7812
+ const meta = readSidecarMeta(join12(projectDir, `${sessionId}.meta.json`));
7813
+ const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
7814
+ const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
7815
+ if (!(isPublicWebchat && owns)) {
7816
+ console.error(`${TAG20} op=reader-refused reason=not-owner anchor=${visitor.kind} sessionId=${sessionId.slice(0, 8)}`);
7647
7817
  return c.json({ error: "forbidden" }, 403);
7648
7818
  }
7649
7819
  const senderShort = (meta.senderId ?? "").slice(0, 8);
7650
7820
  const encoder = new TextEncoder();
7651
- console.log(`${TAG20} op=reader-open key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
7821
+ console.log(`${TAG20} op=reader-open anchor=${visitor.kind} key=${senderShort} mode=delivered-only sessionId=${sessionId.slice(0, 8)}`);
7652
7822
  const send = (controller, turn, id) => controller.enqueue(encoder.encode(`id: ${id}
7653
7823
  data: ${JSON.stringify(turn)}
7654
7824
 
7655
7825
  `));
7826
+ const account = resolveAccount();
7827
+ const uploadsDir = account ? publicUploadsDir(account.accountId, sessionId) : null;
7828
+ const attachmentCursor = { consumed: 0 };
7656
7829
  const readable = new ReadableStream({
7657
7830
  start(controller) {
7658
7831
  let offset = 0;
7659
7832
  const emit = (lines) => {
7660
7833
  const { turns, dropped } = filterDeliveredFrames(lines);
7834
+ if (uploadsDir) enrichPublicAttachments(turns, listSessionAttachmentsInDir(uploadsDir), attachmentCursor);
7661
7835
  for (const turn of turns) send(controller, turn, offset);
7662
7836
  if (dropped > 0) console.log(`${TAG20} op=reader-filtered key=${senderShort} dropped=${dropped}`);
7663
7837
  };
@@ -7706,24 +7880,83 @@ data: ${JSON.stringify(turn)}
7706
7880
  headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" }
7707
7881
  });
7708
7882
  });
7883
+ var ATT_TAG = "[public-attachment]";
7884
+ var ATTACHMENT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7885
+ app5.get("/attachment/:attachmentId", (c) => {
7886
+ const visitor = resolveVisitor(c);
7887
+ const attachmentId = c.req.param("attachmentId");
7888
+ const sessionId = c.req.query("sessionId") ?? "";
7889
+ const ses = sessionId.slice(0, 8);
7890
+ const at = attachmentId.slice(0, 8);
7891
+ if (!visitor) {
7892
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=no-anchor`);
7893
+ return new Response("Unauthorized", { status: 401 });
7894
+ }
7895
+ console.log(`${ATT_TAG} op=request session=${ses} att=${at}`);
7896
+ if (!ATTACHMENT_ID_RE.test(attachmentId) || !SESSION_ID_RE2.test(sessionId)) {
7897
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=bad-id`);
7898
+ return new Response("Not found", { status: 404 });
7899
+ }
7900
+ const row = enumeratePublicRows().find((r) => r.sessionId === sessionId);
7901
+ const owns = row != null && row.role === "public" && row.channel === "webchat" && (visitor.kind === "person" ? row.personId === visitor.personId : row.personId === null && row.visitorId === visitor.visitorId);
7902
+ if (!owns) {
7903
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=foreign-session`);
7904
+ return new Response("Not found", { status: 404 });
7905
+ }
7906
+ const account = resolveAccount();
7907
+ if (!account) {
7908
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
7909
+ return new Response("Not found", { status: 404 });
7910
+ }
7911
+ const uploadsRoot = publicUploadsDir(account.accountId, sessionId);
7912
+ const dir = resolve12(uploadsRoot, attachmentId);
7913
+ if (!dir.startsWith(uploadsRoot + sep4)) {
7914
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=bad-id`);
7915
+ return new Response("Not found", { status: 404 });
7916
+ }
7917
+ const meta = readAttachmentMeta(dir);
7918
+ if (!meta) {
7919
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
7920
+ return new Response("Not found", { status: 404 });
7921
+ }
7922
+ let dataFile;
7923
+ let buffer;
7924
+ try {
7925
+ dataFile = readdirSync8(dir).find((f) => !f.endsWith(".meta.json"));
7926
+ if (!dataFile) throw new Error("no data file");
7927
+ buffer = readFileSync13(resolve12(dir, dataFile));
7928
+ } catch {
7929
+ console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
7930
+ return new Response("Not found", { status: 404 });
7931
+ }
7932
+ console.log(`${ATT_TAG} op=served session=${ses} att=${at} bytes=${buffer.length} mime=${meta.mimeType}`);
7933
+ const safeName = meta.filename.replace(/["\\\r\n]/g, "_");
7934
+ return new Response(new Uint8Array(buffer), {
7935
+ headers: {
7936
+ "Content-Type": meta.mimeType,
7937
+ "Content-Disposition": `inline; filename="${safeName}"`,
7938
+ "Cache-Control": "private, max-age=3600"
7939
+ }
7940
+ });
7941
+ });
7709
7942
  var public_reader_default = app5;
7710
7943
 
7711
7944
  // server/routes/webchat.ts
7712
7945
  import { basename as basename4, dirname as dirname3 } from "path";
7713
- import { join as join14 } from "path";
7714
- import { existsSync as existsSync9, readdirSync as readdirSync8, readFileSync as readFileSync14, renameSync as renameSync3, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
7946
+ import { join as join15 } from "path";
7947
+ import { existsSync as existsSync9, readdirSync as readdirSync10, readFileSync as readFileSync16, renameSync as renameSync3, statSync as statSync6, writeFileSync as writeFileSync7 } from "fs";
7715
7948
 
7716
7949
  // server/canonical-webchat-override.ts
7717
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync5, renameSync } from "fs";
7718
- import { join as join12 } from "path";
7950
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync5, renameSync } from "fs";
7951
+ import { join as join13 } from "path";
7719
7952
  var FILE = "canonical-webchat-session.json";
7720
7953
  var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
7721
7954
  function canonicalOverridePath(accountDir) {
7722
- return join12(accountDir, FILE);
7955
+ return join13(accountDir, FILE);
7723
7956
  }
7724
7957
  function readCanonicalOverrideId(accountDir) {
7725
7958
  try {
7726
- const raw = JSON.parse(readFileSync12(canonicalOverridePath(accountDir), "utf8"));
7959
+ const raw = JSON.parse(readFileSync14(canonicalOverridePath(accountDir), "utf8"));
7727
7960
  return typeof raw.sessionId === "string" && UUID.test(raw.sessionId) ? raw.sessionId : null;
7728
7961
  } catch {
7729
7962
  return null;
@@ -7749,8 +7982,8 @@ function adminSessionIdFor(accountId, senderId, personId) {
7749
7982
  }
7750
7983
 
7751
7984
  // ../services/claude-session-manager/src/sidecar-store.ts
7752
- import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync7, readFileSync as readFileSync13, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
7753
- import { join as join13 } from "path";
7985
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync as readdirSync9, readFileSync as readFileSync15, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync6 } from "fs";
7986
+ import { join as join14 } from "path";
7754
7987
  function escapeRegExp(s) {
7755
7988
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7756
7989
  }
@@ -7758,7 +7991,7 @@ function createSidecarStore(config) {
7758
7991
  const { suffix, validate: validate2 } = config;
7759
7992
  const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
7760
7993
  function pathFor(sessionsDir, id) {
7761
- return join13(sessionsDir, `${id}${suffix}`);
7994
+ return join14(sessionsDir, `${id}${suffix}`);
7762
7995
  }
7763
7996
  function write(sessionsDir, id, record) {
7764
7997
  mkdirSync2(sessionsDir, { recursive: true });
@@ -7779,7 +8012,7 @@ function createSidecarStore(config) {
7779
8012
  function readFileAt(path2) {
7780
8013
  let raw;
7781
8014
  try {
7782
- raw = readFileSync13(path2, "utf8");
8015
+ raw = readFileSync15(path2, "utf8");
7783
8016
  } catch {
7784
8017
  return null;
7785
8018
  }
@@ -7795,17 +8028,17 @@ function createSidecarStore(config) {
7795
8028
  function readAll(sessionsDir, onSkip) {
7796
8029
  let names;
7797
8030
  try {
7798
- names = readdirSync7(sessionsDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
8031
+ names = readdirSync9(sessionsDir, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => entry.name);
7799
8032
  } catch {
7800
8033
  return [];
7801
8034
  }
7802
8035
  const out = [];
7803
8036
  for (const name of names) {
7804
8037
  if (!suffixRe.test(name)) continue;
7805
- const path2 = join13(sessionsDir, name);
8038
+ const path2 = join14(sessionsDir, name);
7806
8039
  let raw;
7807
8040
  try {
7808
- raw = readFileSync13(path2, "utf8");
8041
+ raw = readFileSync15(path2, "utf8");
7809
8042
  } catch {
7810
8043
  onSkip?.(name, "unreadable");
7811
8044
  continue;
@@ -7879,10 +8112,10 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
7879
8112
  function locateSession(sessionId) {
7880
8113
  const cfg = claudeConfigDir();
7881
8114
  if (!cfg) return { projectDir: null, channel: null };
7882
- for (const { path: path2 } of enumerateJsonls(join14(cfg, "projects"))) {
8115
+ for (const { path: path2 } of enumerateJsonls(join15(cfg, "projects"))) {
7883
8116
  if (basename4(path2) === `${sessionId}.jsonl`) {
7884
8117
  const dir = dirname3(path2);
7885
- const meta = readSidecarMeta(join14(dir, `${sessionId}.meta.json`));
8118
+ const meta = readSidecarMeta(join15(dir, `${sessionId}.meta.json`));
7886
8119
  return { projectDir: dir, channel: meta.channel };
7887
8120
  }
7888
8121
  }
@@ -7891,15 +8124,15 @@ function locateSession(sessionId) {
7891
8124
  function locateSidecar(sessionId) {
7892
8125
  const cfg = claudeConfigDir();
7893
8126
  if (!cfg) return null;
7894
- const projectsRoot = join14(cfg, "projects");
8127
+ const projectsRoot = join15(cfg, "projects");
7895
8128
  let slugs;
7896
8129
  try {
7897
- slugs = readdirSync8(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
8130
+ slugs = readdirSync10(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
7898
8131
  } catch {
7899
8132
  return null;
7900
8133
  }
7901
8134
  for (const slug of slugs) {
7902
- const metaPath = join14(projectsRoot, slug, `${sessionId}.meta.json`);
8135
+ const metaPath = join15(projectsRoot, slug, `${sessionId}.meta.json`);
7903
8136
  if (existsSync9(metaPath)) {
7904
8137
  return { channel: readSidecarMeta(metaPath).channel };
7905
8138
  }
@@ -7912,7 +8145,7 @@ function sessionSidecarExists(sessionId) {
7912
8145
  function jsonlSizeBytes(projectDir, sessionId) {
7913
8146
  if (projectDir === null) return null;
7914
8147
  try {
7915
- return statSync6(join14(projectDir, `${sessionId}.jsonl`)).size;
8148
+ return statSync6(join15(projectDir, `${sessionId}.jsonl`)).size;
7916
8149
  } catch {
7917
8150
  return null;
7918
8151
  }
@@ -7981,10 +8214,10 @@ function latestAdminWebchatSessionId() {
7981
8214
  const cfg = claudeConfigDir();
7982
8215
  if (!cfg) return null;
7983
8216
  let best = null;
7984
- for (const { path: path2, isSubagent, archived } of enumerateJsonls(join14(cfg, "projects"))) {
8217
+ for (const { path: path2, isSubagent, archived } of enumerateJsonls(join15(cfg, "projects"))) {
7985
8218
  if (isSubagent || archived) continue;
7986
8219
  const id = basename4(path2).slice(0, -".jsonl".length);
7987
- const meta = readSidecarMeta(join14(dirname3(path2), `${id}.meta.json`));
8220
+ const meta = readSidecarMeta(join15(dirname3(path2), `${id}.meta.json`));
7988
8221
  if (meta.role !== "admin" || meta.channel !== "webchat") continue;
7989
8222
  let mtimeMs;
7990
8223
  try {
@@ -8213,9 +8446,9 @@ ${note}` : note;
8213
8446
  console.error(`[webchat:settings] op=${op} outcome=refused value=${value} reason=account-unresolved`);
8214
8447
  return c.json({ error: "account unresolved" }, 503);
8215
8448
  }
8216
- const accountJsonPath = join14(account.accountDir, "account.json");
8449
+ const accountJsonPath = join15(account.accountDir, "account.json");
8217
8450
  try {
8218
- const parsed = JSON.parse(readFileSync14(accountJsonPath, "utf8"));
8451
+ const parsed = JSON.parse(readFileSync16(accountJsonPath, "utf8"));
8219
8452
  if (op === "model") parsed.adminModel = value;
8220
8453
  else if (op === "effort") parsed.effort = value;
8221
8454
  else parsed.adminPermissionMode = value;
@@ -8252,7 +8485,7 @@ ${note}` : note;
8252
8485
  const cfg2 = claudeConfigDir();
8253
8486
  if (cfg2) {
8254
8487
  const source2 = resolveBridgeSource(
8255
- join14(cfg2, "sessions"),
8488
+ join15(cfg2, "sessions"),
8256
8489
  target,
8257
8490
  (id) => locateSession(id).projectDir !== null
8258
8491
  );
@@ -8281,7 +8514,7 @@ ${note}` : note;
8281
8514
  const cfg = claudeConfigDir();
8282
8515
  let projectDir = null;
8283
8516
  if (cfg) {
8284
- for (const { path: path2 } of enumerateJsonls(join14(cfg, "projects"))) {
8517
+ for (const { path: path2 } of enumerateJsonls(join15(cfg, "projects"))) {
8285
8518
  if (basename4(path2) === `${sessionId}.jsonl`) {
8286
8519
  projectDir = dirname3(path2);
8287
8520
  break;
@@ -8298,8 +8531,8 @@ ${note}` : note;
8298
8531
  import { resolve as resolve13 } from "path";
8299
8532
 
8300
8533
  // app/lib/claude-agent/specialist-roster.ts
8301
- import { existsSync as existsSync10, readFileSync as readFileSync15, readdirSync as readdirSync9 } from "fs";
8302
- import { join as join15 } from "path";
8534
+ import { existsSync as existsSync10, readFileSync as readFileSync17, readdirSync as readdirSync11 } from "fs";
8535
+ import { join as join16 } from "path";
8303
8536
  function field(fm, name) {
8304
8537
  const m = fm.match(new RegExp(`^${name}:\\s*(.*?)\\r?$`, "m"));
8305
8538
  if (!m) return null;
@@ -8311,14 +8544,14 @@ function field(fm, name) {
8311
8544
  }
8312
8545
  function readSpecialistRoster(specialistsDir) {
8313
8546
  if (!existsSync10(specialistsDir)) return { specialists: [], skipped: [] };
8314
- const entries = readdirSync9(specialistsDir);
8547
+ const entries = readdirSync11(specialistsDir);
8315
8548
  const specialists = [];
8316
8549
  const skipped = [];
8317
8550
  for (const file of entries.sort()) {
8318
8551
  if (!file.endsWith(".md")) continue;
8319
8552
  let raw;
8320
8553
  try {
8321
- raw = readFileSync15(join15(specialistsDir, file), "utf-8");
8554
+ raw = readFileSync17(join16(specialistsDir, file), "utf-8");
8322
8555
  } catch (err) {
8323
8556
  skipped.push({ file, reason: err instanceof Error ? err.message : String(err) });
8324
8557
  continue;
@@ -8435,11 +8668,11 @@ var webchat_greeting_default = app6;
8435
8668
 
8436
8669
  // server/routes/telegram.ts
8437
8670
  import { homedir } from "os";
8438
- import { resolve as resolve15, join as join16 } from "path";
8439
- import { existsSync as existsSync11, readFileSync as readFileSync16 } from "fs";
8671
+ import { resolve as resolve17, join as join17 } from "path";
8672
+ import { existsSync as existsSync11, readFileSync as readFileSync18 } from "fs";
8440
8673
 
8441
8674
  // app/lib/channel-pty-bridge/bridge.ts
8442
- import { resolve as resolve14 } from "path";
8675
+ import { resolve as resolve16 } from "path";
8443
8676
 
8444
8677
  // app/lib/channel-pty-bridge/manager-client.ts
8445
8678
  function managerBase2() {
@@ -8470,6 +8703,7 @@ async function managerSpawn(opts) {
8470
8703
  specialistDomains: opts.specialistDomains,
8471
8704
  sliceToken: opts.sliceToken,
8472
8705
  personId: opts.personId,
8706
+ visitorId: opts.visitorId,
8473
8707
  name: opts.name,
8474
8708
  webchatChannel: opts.webchatChannel
8475
8709
  })
@@ -8878,81 +9112,114 @@ async function fanOut(subscribers, text, onError, tag) {
8878
9112
  );
8879
9113
  }
8880
9114
 
9115
+ // app/lib/channel-pty-bridge/file-delivery.ts
9116
+ var SEND_USER_FILE = "SendUserFile";
9117
+ function makeFileDelivery(opts) {
9118
+ const { entry, tag, channel, sendFile } = opts;
9119
+ let failedFiles = [];
9120
+ let attempts = 0;
9121
+ return {
9122
+ isFileDeliveryTool(toolName) {
9123
+ return toolName === SEND_USER_FILE;
9124
+ },
9125
+ async onFileToolUse(use) {
9126
+ if (use.toolName !== SEND_USER_FILE) return;
9127
+ const input = use.input ?? {};
9128
+ const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
9129
+ const caption = typeof input.caption === "string" ? input.caption : void 0;
9130
+ for (let i = 0; i < files.length; i++) {
9131
+ attempts++;
9132
+ const result = await sendFile(files[i], i === 0 ? caption : void 0);
9133
+ const name = files[i].split("/").pop() ?? files[i];
9134
+ console.error(`${tag} op=file-forward channel=${channel} file=${name} outcome=${result.ok ? "ok" : "fail"}`);
9135
+ if (!result.ok) failedFiles.push(files[i]);
9136
+ }
9137
+ },
9138
+ onTurnEnd(firedTools) {
9139
+ const failed = failedFiles;
9140
+ const tried = attempts;
9141
+ failedFiles = [];
9142
+ attempts = 0;
9143
+ const sid = entry.sessionId.slice(0, 8);
9144
+ for (const file of failed) {
9145
+ console.error(
9146
+ `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
9147
+ );
9148
+ }
9149
+ if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
9150
+ console.error(
9151
+ `${tag} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
9152
+ );
9153
+ }
9154
+ }
9155
+ };
9156
+ }
9157
+
8881
9158
  // app/lib/whatsapp/inbound/file-delivery-bridge.ts
8882
9159
  var TAG22 = "[whatsapp-adaptor]";
8883
- var SEND_USER_FILE = "SendUserFile";
9160
+ var SEND_USER_FILE2 = "SendUserFile";
8884
9161
  var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
8885
9162
  function platformRoot() {
8886
9163
  return process.env.MAXY_PLATFORM_ROOT || "";
8887
9164
  }
9165
+ function makeWhatsAppSendFile(entry) {
9166
+ return async (filePath, caption) => {
9167
+ let maxyAccountId;
9168
+ try {
9169
+ maxyAccountId = resolvePlatformAccountId();
9170
+ } catch (err) {
9171
+ console.error(
9172
+ `${TAG22} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9173
+ );
9174
+ return { ok: false, error: "account-unresolved" };
9175
+ }
9176
+ const result = await sendWhatsAppDocument({
9177
+ to: entry.senderId,
9178
+ filePath,
9179
+ caption,
9180
+ accountId: entry.accountId,
9181
+ maxyAccountId,
9182
+ platformRoot: platformRoot()
9183
+ });
9184
+ if (result.ok) return { ok: true };
9185
+ console.error(
9186
+ `${TAG22} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
9187
+ );
9188
+ return { ok: false, error: result.error };
9189
+ };
9190
+ }
8888
9191
  function makeWhatsAppFileDelivery(entry) {
9192
+ const shared = makeFileDelivery({
9193
+ entry,
9194
+ tag: TAG22,
9195
+ channel: "whatsapp",
9196
+ sendFile: makeWhatsAppSendFile(entry)
9197
+ });
8889
9198
  let turnStartedAt = null;
8890
- let failedFiles = [];
8891
- let sendUserFileAttempts = 0;
8892
9199
  let routeCalls = [];
8893
9200
  return {
8894
9201
  isFileDeliveryTool(toolName) {
8895
- return toolName === SEND_USER_FILE || toolName === WHATSAPP_SEND_DOCUMENT;
9202
+ return toolName === SEND_USER_FILE2 || toolName === WHATSAPP_SEND_DOCUMENT;
8896
9203
  },
8897
9204
  async onFileToolUse(use) {
8898
9205
  if (turnStartedAt === null) turnStartedAt = Date.now();
8899
9206
  if (use.toolName === WHATSAPP_SEND_DOCUMENT) {
8900
- const input2 = use.input ?? {};
9207
+ const input = use.input ?? {};
8901
9208
  routeCalls.push({
8902
- to: typeof input2.to === "string" ? input2.to : void 0,
8903
- filePath: typeof input2.filePath === "string" ? input2.filePath : void 0
9209
+ to: typeof input.to === "string" ? input.to : void 0,
9210
+ filePath: typeof input.filePath === "string" ? input.filePath : void 0
8904
9211
  });
8905
9212
  return;
8906
9213
  }
8907
- if (use.toolName !== SEND_USER_FILE) return;
8908
- const input = use.input ?? {};
8909
- const files = Array.isArray(input.files) ? input.files.filter((f) => typeof f === "string") : [];
8910
- const caption = typeof input.caption === "string" ? input.caption : void 0;
8911
- if (files.length === 0) return;
8912
- let maxyAccountId;
8913
- try {
8914
- maxyAccountId = resolvePlatformAccountId();
8915
- } catch (err) {
8916
- console.error(
8917
- `${TAG22} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
8918
- );
8919
- return;
8920
- }
8921
- for (let i = 0; i < files.length; i++) {
8922
- sendUserFileAttempts++;
8923
- const result = await sendWhatsAppDocument({
8924
- to: entry.senderId,
8925
- filePath: files[i],
8926
- caption: i === 0 ? caption : void 0,
8927
- accountId: entry.accountId,
8928
- maxyAccountId,
8929
- platformRoot: platformRoot()
8930
- });
8931
- if (!result.ok) {
8932
- failedFiles.push(files[i]);
8933
- console.error(
8934
- `${TAG22} file-delivery reject reason=send-failed sender=${entry.senderId} status=${result.status} message=${result.error}`
8935
- );
8936
- }
8937
- }
9214
+ await shared.onFileToolUse(use);
8938
9215
  },
8939
9216
  onTurnEnd(firedTools) {
8940
9217
  const startedAt = turnStartedAt ?? 0;
8941
- const failed = failedFiles;
8942
- const attempts = sendUserFileAttempts;
8943
9218
  const routes = routeCalls;
8944
9219
  turnStartedAt = null;
8945
- failedFiles = [];
8946
- sendUserFileAttempts = 0;
8947
9220
  routeCalls = [];
8948
9221
  const sid = entry.sessionId.slice(0, 8);
8949
- for (const file of failed) {
8950
- console.error(
8951
- `${TAG22} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
8952
- );
8953
- }
8954
- const okAt = documentOutboundAt(entry.senderId);
8955
- const documentWentOut = okAt !== void 0 && okAt >= startedAt;
9222
+ shared.onTurnEnd(firedTools);
8956
9223
  for (const call of routes) {
8957
9224
  const routeAt = call.to !== void 0 && call.filePath !== void 0 ? routeDocumentOutboundAt(call.to, call.filePath) : void 0;
8958
9225
  const delivered = routeAt !== void 0 && routeAt >= startedAt;
@@ -8963,21 +9230,186 @@ function makeWhatsAppFileDelivery(entry) {
8963
9230
  );
8964
9231
  }
8965
9232
  }
8966
- if (firedTools.includes(SEND_USER_FILE) && attempts === 0 && !documentWentOut) {
8967
- console.error(
8968
- `${TAG22} file-delivery-unreconciled sender=${entry.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
8969
- );
9233
+ }
9234
+ };
9235
+ }
9236
+
9237
+ // app/lib/telegram/outbound/send-document.ts
9238
+ import { realpathSync as realpathSync4 } from "fs";
9239
+ import { readFile as readFile2, stat as stat3 } from "fs/promises";
9240
+ import { resolve as resolve14, basename as basename5 } from "path";
9241
+ var TAG23 = "[telegram:outbound]";
9242
+ var TELEGRAM_DOCUMENT_MAX_BYTES = 50 * 1024 * 1024;
9243
+ async function sendTelegramDocument(input) {
9244
+ const { botToken, chatId, filePath, caption, maxyAccountId, platformRoot: platformRoot4 } = input;
9245
+ if (!botToken || !filePath) {
9246
+ return { ok: false, status: 400, error: "Missing required fields: botToken, filePath" };
9247
+ }
9248
+ if (!maxyAccountId || !platformRoot4) {
9249
+ return { ok: false, status: 400, error: "Cannot validate file path: missing account or platform context" };
9250
+ }
9251
+ const accountDir = resolve14(platformRoot4, "..", "data/accounts", maxyAccountId);
9252
+ let resolvedPath;
9253
+ try {
9254
+ resolvedPath = realpathSync4(filePath);
9255
+ const accountResolved = realpathSync4(accountDir);
9256
+ if (!resolvedPath.startsWith(accountResolved + "/")) {
9257
+ console.error(`${TAG23} document REJECTED reason=outside_account_directory`);
9258
+ return { ok: false, status: 403, error: "Access denied: file is outside the account directory" };
9259
+ }
9260
+ } catch (err) {
9261
+ const code = err.code;
9262
+ if (code === "ENOENT") {
9263
+ console.error(`${TAG23} document ENOENT path=${filePath}`);
9264
+ return { ok: false, status: 404, error: `File not found: ${filePath}` };
9265
+ }
9266
+ console.error(`${TAG23} document path error: ${String(err)}`);
9267
+ return { ok: false, status: 500, error: String(err) };
9268
+ }
9269
+ const fileStat = await stat3(resolvedPath);
9270
+ if (fileStat.size > TELEGRAM_DOCUMENT_MAX_BYTES) {
9271
+ return {
9272
+ ok: false,
9273
+ status: 400,
9274
+ error: `File exceeds 50 MB limit (${(fileStat.size / 1024 / 1024).toFixed(1)} MB)`
9275
+ };
9276
+ }
9277
+ const filename = basename5(resolvedPath);
9278
+ const buffer = Buffer.from(await readFile2(resolvedPath));
9279
+ const form = new FormData();
9280
+ form.append("chat_id", String(chatId));
9281
+ if (caption) form.append("caption", caption);
9282
+ form.append("document", new File([buffer], filename, { type: detectMimeType(resolvedPath) }));
9283
+ let ok = false;
9284
+ let messageId;
9285
+ let error = "send failed";
9286
+ try {
9287
+ const res = await fetch(`https://api.telegram.org/bot${botToken}/sendDocument`, {
9288
+ method: "POST",
9289
+ body: form
9290
+ });
9291
+ const data = await res.json();
9292
+ ok = data.ok;
9293
+ messageId = data.result?.message_id;
9294
+ if (!data.ok) error = data.description ?? "Unknown Telegram error";
9295
+ } catch (e) {
9296
+ error = e instanceof Error ? e.message : String(e);
9297
+ }
9298
+ console.error(
9299
+ `${TAG23} sent document to=${chatId} file=${filename} bytes=${fileStat.size} ok=${ok}` + (messageId !== void 0 ? ` id=${messageId}` : "")
9300
+ );
9301
+ return ok ? { ok: true, messageId } : { ok: false, status: 500, error };
9302
+ }
9303
+
9304
+ // app/lib/telegram/outbound/file-delivery.ts
9305
+ var TAG24 = "[telegram:outbound]";
9306
+ function platformRoot2() {
9307
+ return process.env.MAXY_PLATFORM_ROOT || "";
9308
+ }
9309
+ function makeTelegramSendFile(entry) {
9310
+ let botToken;
9311
+ return async (filePath, caption) => {
9312
+ if (botToken === void 0) {
9313
+ const tg = resolveAccount()?.config.telegram;
9314
+ botToken = (entry.role === "admin" ? tg?.adminBotToken : tg?.publicBotToken) ?? null;
9315
+ }
9316
+ if (!botToken) {
9317
+ console.error(`${TAG24} file-delivery reject reason=no-bot-token sender=${entry.senderId} role=${entry.role}`);
9318
+ return { ok: false, error: "no-bot-token" };
9319
+ }
9320
+ if (entry.replyTarget == null) {
9321
+ console.error(`${TAG24} file-delivery reject reason=no-reply-target sender=${entry.senderId} role=${entry.role}`);
9322
+ return { ok: false, error: "no-reply-target" };
9323
+ }
9324
+ let maxyAccountId;
9325
+ try {
9326
+ maxyAccountId = resolvePlatformAccountId();
9327
+ } catch (err) {
9328
+ console.error(
9329
+ `${TAG24} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9330
+ );
9331
+ return { ok: false, error: "account-unresolved" };
9332
+ }
9333
+ const result = await sendTelegramDocument({
9334
+ botToken,
9335
+ chatId: Number(entry.replyTarget),
9336
+ filePath,
9337
+ caption,
9338
+ maxyAccountId,
9339
+ platformRoot: platformRoot2()
9340
+ });
9341
+ return result.ok ? { ok: true } : { ok: false, error: result.error };
9342
+ };
9343
+ }
9344
+ function makeTelegramFileDelivery(entry) {
9345
+ return makeFileDelivery({
9346
+ entry,
9347
+ tag: TAG24,
9348
+ channel: "telegram",
9349
+ sendFile: makeTelegramSendFile(entry)
9350
+ });
9351
+ }
9352
+
9353
+ // app/lib/webchat/file-delivery.ts
9354
+ import { realpathSync as realpathSync5 } from "fs";
9355
+ import { resolve as resolve15 } from "path";
9356
+ var TAG25 = "[webchat-adaptor]";
9357
+ function platformRoot3() {
9358
+ return process.env.MAXY_PLATFORM_ROOT || "";
9359
+ }
9360
+ function makeWebchatSendFile(entry) {
9361
+ return async (filePath) => {
9362
+ let maxyAccountId;
9363
+ try {
9364
+ maxyAccountId = resolvePlatformAccountId();
9365
+ } catch (err) {
9366
+ console.error(
9367
+ `${TAG25} file-delivery reject reason=account-unresolved sender=${entry.senderId} message=${err instanceof Error ? err.message : String(err)}`
9368
+ );
9369
+ return { ok: false, error: "account-unresolved" };
9370
+ }
9371
+ const accountDir = resolve15(platformRoot3(), "..", "data/accounts", maxyAccountId);
9372
+ try {
9373
+ const resolved = realpathSync5(filePath);
9374
+ const accountResolved = realpathSync5(accountDir);
9375
+ if (!resolved.startsWith(accountResolved + "/")) {
9376
+ console.error(`${TAG25} file-delivery reject reason=outside_account_directory sender=${entry.senderId}`);
9377
+ return { ok: false, error: "outside-account" };
8970
9378
  }
9379
+ return { ok: true };
9380
+ } catch (err) {
9381
+ const code = err.code;
9382
+ console.error(
9383
+ `${TAG25} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry.senderId}`
9384
+ );
9385
+ return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
8971
9386
  }
8972
9387
  };
8973
9388
  }
9389
+ function makeWebchatFileDelivery(entry) {
9390
+ return makeFileDelivery({
9391
+ entry,
9392
+ tag: TAG25,
9393
+ channel: "webchat",
9394
+ sendFile: makeWebchatSendFile(entry)
9395
+ });
9396
+ }
8974
9397
 
8975
9398
  // app/lib/channel-pty-bridge/bridge.ts
8976
9399
  function tagFor(channel) {
8977
9400
  return `[${channel}-adaptor]`;
8978
9401
  }
8979
9402
  function fileDeliveryFor(entry) {
8980
- return entry.channel === "whatsapp" ? makeWhatsAppFileDelivery(entry) : null;
9403
+ switch (entry.channel) {
9404
+ case "whatsapp":
9405
+ return makeWhatsAppFileDelivery(entry);
9406
+ case "telegram":
9407
+ return makeTelegramFileDelivery(entry);
9408
+ case "webchat":
9409
+ return makeWebchatFileDelivery(entry);
9410
+ case "email":
9411
+ return null;
9412
+ }
8981
9413
  }
8982
9414
  function publicIdleMs() {
8983
9415
  return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(5 * 6e4));
@@ -8995,6 +9427,7 @@ async function ensureEntry(input) {
8995
9427
  const sliceTokenValue = input.sliceToken ?? "";
8996
9428
  const personIdValue = input.personId ?? null;
8997
9429
  const nameValue = input.name ?? null;
9430
+ const replyTargetValue = input.replyTarget ?? null;
8998
9431
  const key = indexKey(input.accountId, input.agentSlug, input.senderId, sliceTokenValue, personIdValue);
8999
9432
  const existing = index.get(key);
9000
9433
  const tag = tagFor(input.channel);
@@ -9016,6 +9449,7 @@ async function ensureEntry(input) {
9016
9449
  });
9017
9450
  existing.followerRunning = true;
9018
9451
  }
9452
+ existing.replyTarget = replyTargetValue;
9019
9453
  return existing;
9020
9454
  }
9021
9455
  let spawned;
@@ -9036,7 +9470,7 @@ async function ensureEntry(input) {
9036
9470
  });
9037
9471
  } else {
9038
9472
  console.error(`${tag} route role=${input.role} target=public-spawn senderId=${input.senderId}`);
9039
- const attachmentDir = resolve14(DATA_ROOT, "accounts", input.accountId, "uploads", "public", input.senderId);
9473
+ const attachmentDir = resolve16(DATA_ROOT, "accounts", input.accountId, "uploads", "public", input.senderId);
9040
9474
  spawned = await managerSpawn({
9041
9475
  senderId: input.senderId,
9042
9476
  role: input.role,
@@ -9072,7 +9506,8 @@ async function ensureEntry(input) {
9072
9506
  followerRunning: false,
9073
9507
  sliceToken: sliceTokenValue,
9074
9508
  personId: personIdValue,
9075
- name: nameValue
9509
+ name: nameValue,
9510
+ replyTarget: replyTargetValue
9076
9511
  };
9077
9512
  entry.followerAbort = startFollower({
9078
9513
  entry,
@@ -9124,7 +9559,8 @@ async function writeInput(entry, text) {
9124
9559
  agentSlug: entry.agentSlug,
9125
9560
  sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
9126
9561
  personId: entry.personId,
9127
- name: entry.name
9562
+ name: entry.name,
9563
+ replyTarget: entry.replyTarget ?? void 0
9128
9564
  });
9129
9565
  if (!respawned) return false;
9130
9566
  for (const cb of entry.subscribers) respawned.subscribers.add(cb);
@@ -9164,16 +9600,17 @@ async function dispatchOnce(input) {
9164
9600
  agentSlug: input.agentSlug,
9165
9601
  sliceToken: input.sliceToken,
9166
9602
  personId: input.personId,
9167
- name: input.name
9603
+ name: input.name,
9604
+ replyTarget: input.replyTarget
9168
9605
  });
9169
9606
  if (!entry) return { error: "spawn-failed" };
9170
9607
  entry.lastInboundAt = Date.now();
9171
- let resolve32;
9608
+ let resolve34;
9172
9609
  const turnPromise = new Promise((r) => {
9173
- resolve32 = r;
9610
+ resolve34 = r;
9174
9611
  });
9175
9612
  const listener = (text) => {
9176
- resolve32(text);
9613
+ resolve34(text);
9177
9614
  };
9178
9615
  entry.subscribers.add(listener);
9179
9616
  const writeAtMs = Date.now();
@@ -9320,14 +9757,14 @@ function routeTelegramUpdate(input) {
9320
9757
  }
9321
9758
 
9322
9759
  // server/routes/telegram.ts
9323
- var TAG23 = "[telegram-inbound]";
9760
+ var TAG26 = "[telegram-inbound]";
9324
9761
  var DISPATCH_TIMEOUT_MS = 12e4;
9325
9762
  function configDirName() {
9326
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve15(process.cwd(), "..");
9327
- const brandPath = join16(platformRoot2, "config", "brand.json");
9763
+ const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
9764
+ const brandPath = join17(platformRoot4, "config", "brand.json");
9328
9765
  if (existsSync11(brandPath)) {
9329
9766
  try {
9330
- return JSON.parse(readFileSync16(brandPath, "utf-8")).configDir ?? ".maxy";
9767
+ return JSON.parse(readFileSync18(brandPath, "utf-8")).configDir ?? ".maxy";
9331
9768
  } catch {
9332
9769
  }
9333
9770
  }
@@ -9335,7 +9772,7 @@ function configDirName() {
9335
9772
  }
9336
9773
  function secretPath(botType) {
9337
9774
  const filename = botType === "admin" ? ".telegram-admin-webhook-secret" : ".telegram-webhook-secret";
9338
- return resolve15(homedir(), configDirName(), filename);
9775
+ return resolve17(homedir(), configDirName(), filename);
9339
9776
  }
9340
9777
  async function sendTelegram(botToken, chatId, text) {
9341
9778
  try {
@@ -9355,18 +9792,18 @@ app7.post("/", async (c) => {
9355
9792
  const botParam = c.req.query("bot");
9356
9793
  const botType = botParam === "admin" ? "admin" : botParam === "public" ? "public" : null;
9357
9794
  if (!botType) {
9358
- console.error(`${TAG23} op=reject reason=missing-bot-param`);
9795
+ console.error(`${TAG26} op=reject reason=missing-bot-param`);
9359
9796
  return c.json({ ok: false }, 400);
9360
9797
  }
9361
9798
  const sp = secretPath(botType);
9362
9799
  if (!existsSync11(sp)) {
9363
- console.error(`${TAG23} op=reject reason=no-secret-file botType=${botType}`);
9800
+ console.error(`${TAG26} op=reject reason=no-secret-file botType=${botType}`);
9364
9801
  return c.json({ ok: false }, 401);
9365
9802
  }
9366
- const expected = readFileSync16(sp, "utf-8").trim();
9803
+ const expected = readFileSync18(sp, "utf-8").trim();
9367
9804
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
9368
9805
  if (got !== expected) {
9369
- console.error(`${TAG23} op=reject reason=bad-secret botType=${botType}`);
9806
+ console.error(`${TAG26} op=reject reason=bad-secret botType=${botType}`);
9370
9807
  return c.json({ ok: false }, 401);
9371
9808
  }
9372
9809
  let update;
@@ -9377,25 +9814,25 @@ app7.post("/", async (c) => {
9377
9814
  }
9378
9815
  const account = resolveAccount();
9379
9816
  if (!account) {
9380
- console.error(`${TAG23} op=reject reason=no-account`);
9817
+ console.error(`${TAG26} op=reject reason=no-account`);
9381
9818
  return c.json({ ok: true }, 200);
9382
9819
  }
9383
9820
  const decision = routeTelegramUpdate({ update, botType, config: account.config.telegram ?? {} });
9384
9821
  if (decision.kind === "ignore") {
9385
- console.error(`${TAG23} op=ignore reason=${decision.reason}`);
9822
+ console.error(`${TAG26} op=ignore reason=${decision.reason}`);
9386
9823
  return c.json({ ok: true }, 200);
9387
9824
  }
9388
9825
  if (decision.kind === "denied") {
9389
- console.error(`${TAG23} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
9826
+ console.error(`${TAG26} op=denied reason=${decision.reason} agentType=${decision.agentType}`);
9390
9827
  return c.json({ ok: true }, 200);
9391
9828
  }
9392
9829
  const role = decision.agentType === "admin" ? "admin" : "public";
9393
9830
  const agentSlug = role === "admin" ? "admin" : resolveDefaultAgentSlug(account.accountDir);
9394
9831
  if (!agentSlug) {
9395
- console.error(`${TAG23} op=reject reason=no-default-agent`);
9832
+ console.error(`${TAG26} op=reject reason=no-default-agent`);
9396
9833
  return c.json({ ok: true }, 200);
9397
9834
  }
9398
- console.error(`${TAG23} op=update accountId=${account.accountId} from=${decision.senderId}`);
9835
+ console.error(`${TAG26} op=update accountId=${account.accountId} from=${decision.senderId}`);
9399
9836
  const botToken = botType === "admin" ? account.config.telegram?.adminBotToken : account.config.telegram?.publicBotToken;
9400
9837
  const { senderId, chatId, text } = decision;
9401
9838
  void (async () => {
@@ -9406,70 +9843,73 @@ app7.post("/", async (c) => {
9406
9843
  channel: "telegram",
9407
9844
  agentSlug,
9408
9845
  text,
9846
+ // Task 924 — the file adapter delivers SendUserFile to this chat id, so a
9847
+ // group turn's document reaches the group rather than the sender's DM.
9848
+ replyTarget: String(chatId),
9409
9849
  timeoutMs: DISPATCH_TIMEOUT_MS
9410
9850
  });
9411
9851
  if ("error" in result) {
9412
- console.error(`${TAG23} op=dispatch-failed from=${senderId} error=${result.error}`);
9852
+ console.error(`${TAG26} op=dispatch-failed from=${senderId} error=${result.error}`);
9413
9853
  return;
9414
9854
  }
9415
9855
  if (!botToken) {
9416
9856
  const reason = account.config.telegram ? "no-bot-token" : "no-telegram-config";
9417
- console.error(`${TAG23} op=reply-dropped reason=${reason} botType=${botType}`);
9857
+ console.error(`${TAG26} op=reply-dropped reason=${reason} botType=${botType}`);
9418
9858
  return;
9419
9859
  }
9420
9860
  const sent = await sendTelegram(botToken, chatId, result.turnText);
9421
- console.error(`${TAG23} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
9861
+ console.error(`${TAG26} op=reply-sent from=${senderId} ok=${sent.ok}${sent.ok ? "" : ` error=${sent.error}`}`);
9422
9862
  })();
9423
9863
  return c.json({ ok: true }, 200);
9424
9864
  });
9425
9865
  var telegram_default = app7;
9426
9866
 
9427
9867
  // server/routes/quickbooks.ts
9428
- import { join as join17 } from "path";
9429
- import { existsSync as existsSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync, renameSync as renameSync4 } from "fs";
9430
- var TAG24 = "[quickbooks]";
9868
+ import { join as join18 } from "path";
9869
+ import { existsSync as existsSync12, readFileSync as readFileSync19, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, rmSync, renameSync as renameSync4 } from "fs";
9870
+ var TAG27 = "[quickbooks]";
9431
9871
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
9432
9872
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
9433
9873
  function pendingPath(accountDir, state) {
9434
- return join17(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
9874
+ return join18(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
9435
9875
  }
9436
9876
  function storePath(accountDir) {
9437
- return join17(accountDir, "secrets", "quickbooks.json");
9877
+ return join18(accountDir, "secrets", "quickbooks.json");
9438
9878
  }
9439
9879
  async function completeConsent(args) {
9440
9880
  const { accountDir, code, realmId } = args;
9441
9881
  const state = args.state;
9442
9882
  if (!state || !STATE_RE.test(state) || !existsSync12(pendingPath(accountDir, state))) {
9443
- console.error(`${TAG24} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
9883
+ console.error(`${TAG27} op=callback state=${state ?? ""} stateValid=false realmId=${realmId ?? ""}`);
9444
9884
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
9445
9885
  }
9446
9886
  const claimFile = `${pendingPath(accountDir, state)}.claimed`;
9447
9887
  try {
9448
9888
  renameSync4(pendingPath(accountDir, state), claimFile);
9449
9889
  } catch {
9450
- console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
9890
+ console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
9451
9891
  return { ok: false, status: 400, message: "Invalid or unknown authorization state.", stateValid: false };
9452
9892
  }
9453
9893
  try {
9454
9894
  let pending;
9455
9895
  try {
9456
- pending = JSON.parse(readFileSync17(claimFile, "utf-8"));
9896
+ pending = JSON.parse(readFileSync19(claimFile, "utf-8"));
9457
9897
  } catch {
9458
- console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
9898
+ console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
9459
9899
  return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
9460
9900
  }
9461
9901
  if (Date.now() > pending.expiresAt) {
9462
- console.error(`${TAG24} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
9902
+ console.error(`${TAG27} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
9463
9903
  return { ok: false, status: 400, message: "Authorization request expired. Generate a new consent link.", stateValid: false };
9464
9904
  }
9465
- console.error(`${TAG24} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
9905
+ console.error(`${TAG27} op=callback state=${state} stateValid=true realmId=${realmId ?? ""}`);
9466
9906
  if (!code || !realmId) {
9467
9907
  return { ok: false, status: 400, message: "Missing code or realmId in the callback.", stateValid: true };
9468
9908
  }
9469
9909
  if (!existsSync12(storePath(accountDir))) {
9470
9910
  return { ok: false, status: 500, message: "No QuickBooks credentials are stored for this account.", stateValid: true };
9471
9911
  }
9472
- const store2 = JSON.parse(readFileSync17(storePath(accountDir), "utf-8"));
9912
+ const store2 = JSON.parse(readFileSync19(storePath(accountDir), "utf-8"));
9473
9913
  const form = new URLSearchParams({
9474
9914
  grant_type: "authorization_code",
9475
9915
  code,
@@ -9486,12 +9926,12 @@ async function completeConsent(args) {
9486
9926
  });
9487
9927
  const text = await res.text();
9488
9928
  if (!res.ok) {
9489
- console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=false`);
9929
+ console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=false`);
9490
9930
  return { ok: false, status: 502, message: `Token exchange failed (${res.status}). Generate a new consent link to retry.`, stateValid: true };
9491
9931
  }
9492
9932
  const body = JSON.parse(text);
9493
9933
  if (!body.refresh_token) {
9494
- console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=false`);
9934
+ console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=false`);
9495
9935
  return { ok: false, status: 502, message: "Token exchange returned no refresh token.", stateValid: true };
9496
9936
  }
9497
9937
  const now = Date.now();
@@ -9501,10 +9941,10 @@ async function completeConsent(args) {
9501
9941
  refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
9502
9942
  lastRefresh: now
9503
9943
  };
9504
- mkdirSync3(join17(accountDir, "secrets"), { recursive: true });
9944
+ mkdirSync3(join18(accountDir, "secrets"), { recursive: true });
9505
9945
  writeFileSync8(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
9506
- const persisted = JSON.parse(readFileSync17(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
9507
- console.error(`${TAG24} op=token-exchange realmId=${realmId} persisted=${persisted}`);
9946
+ const persisted = JSON.parse(readFileSync19(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
9947
+ console.error(`${TAG27} op=token-exchange realmId=${realmId} persisted=${persisted}`);
9508
9948
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
9509
9949
  } finally {
9510
9950
  rmSync(claimFile, { force: true });
@@ -9517,7 +9957,7 @@ var app8 = new Hono();
9517
9957
  app8.get("/callback", async (c) => {
9518
9958
  const account = resolveAccount();
9519
9959
  if (!account) {
9520
- console.error(`${TAG24} op=callback stateValid=false realmId= reason=no-account`);
9960
+ console.error(`${TAG27} op=callback stateValid=false realmId= reason=no-account`);
9521
9961
  return c.html(page("QuickBooks", "This install has no account configured."), 500);
9522
9962
  }
9523
9963
  const result = await completeConsent({
@@ -9534,12 +9974,12 @@ var quickbooks_default = app8;
9534
9974
 
9535
9975
  // server/routes/onboarding.ts
9536
9976
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
9537
- import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as readFileSync19, unlinkSync as unlinkSync2 } from "fs";
9977
+ import { openSync as openSync3, closeSync as closeSync3, writeFileSync as writeFileSync10, writeSync, existsSync as existsSync14, readFileSync as readFileSync21, unlinkSync as unlinkSync2 } from "fs";
9538
9978
  import { createHash as createHash3, randomUUID as randomUUID8 } from "crypto";
9539
9979
 
9540
9980
  // ../lib/admins-write/src/index.ts
9541
- import { existsSync as existsSync13, readFileSync as readFileSync18, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync10, statSync as statSync7 } from "fs";
9542
- import { dirname as dirname4, join as join18 } from "path";
9981
+ import { existsSync as existsSync13, readFileSync as readFileSync20, writeFileSync as writeFileSync9, renameSync as renameSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync12, statSync as statSync7 } from "fs";
9982
+ import { dirname as dirname4, join as join19 } from "path";
9543
9983
  function logLine(input, result) {
9544
9984
  const userIdShort = input.userId.slice(0, 8);
9545
9985
  console.error(
@@ -9557,7 +9997,7 @@ function writeAdminEntry(input) {
9557
9997
  try {
9558
9998
  let users = [];
9559
9999
  if (existsSync13(input.usersFile)) {
9560
- const raw = readFileSync18(input.usersFile, "utf-8").trim();
10000
+ const raw = readFileSync20(input.usersFile, "utf-8").trim();
9561
10001
  if (raw) {
9562
10002
  const parsed = JSON.parse(raw);
9563
10003
  if (!Array.isArray(parsed)) {
@@ -9581,11 +10021,11 @@ function writeAdminEntry(input) {
9581
10021
  return result;
9582
10022
  }
9583
10023
  try {
9584
- const accountJsonPath = join18(input.accountDir, "account.json");
10024
+ const accountJsonPath = join19(input.accountDir, "account.json");
9585
10025
  if (!existsSync13(accountJsonPath)) {
9586
10026
  throw new Error(`account.json not found at ${accountJsonPath}`);
9587
10027
  }
9588
- const config = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
10028
+ const config = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
9589
10029
  const admins = config.admins ?? [];
9590
10030
  if (admins.some((a) => a.userId === input.userId)) {
9591
10031
  result.accountJsonResult = "noop";
@@ -9611,7 +10051,7 @@ function checkAdminAuthInvariant(input) {
9611
10051
  const usersUserIds = /* @__PURE__ */ new Set();
9612
10052
  if (existsSync13(input.usersFile)) {
9613
10053
  try {
9614
- const raw = readFileSync18(input.usersFile, "utf-8").trim();
10054
+ const raw = readFileSync20(input.usersFile, "utf-8").trim();
9615
10055
  if (raw) {
9616
10056
  const users = JSON.parse(raw);
9617
10057
  for (const u of users) {
@@ -9627,7 +10067,7 @@ function checkAdminAuthInvariant(input) {
9627
10067
  if (existsSync13(input.accountsDir)) {
9628
10068
  let entries;
9629
10069
  try {
9630
- entries = readdirSync10(input.accountsDir);
10070
+ entries = readdirSync12(input.accountsDir);
9631
10071
  } catch (err) {
9632
10072
  const msg = err instanceof Error ? err.message : String(err);
9633
10073
  console.error(`[${input.tag}] accounts-dir unreadable accountsDir=${input.accountsDir} error=${msg}`);
@@ -9636,17 +10076,17 @@ function checkAdminAuthInvariant(input) {
9636
10076
  }
9637
10077
  for (const entry of entries) {
9638
10078
  if (entry.startsWith(".")) continue;
9639
- const accountDir = join18(input.accountsDir, entry);
10079
+ const accountDir = join19(input.accountsDir, entry);
9640
10080
  try {
9641
10081
  if (!statSync7(accountDir).isDirectory()) continue;
9642
10082
  } catch {
9643
10083
  continue;
9644
10084
  }
9645
- const accountJsonPath = join18(accountDir, "account.json");
10085
+ const accountJsonPath = join19(accountDir, "account.json");
9646
10086
  if (!existsSync13(accountJsonPath)) continue;
9647
10087
  let admins = [];
9648
10088
  try {
9649
- const config = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
10089
+ const config = JSON.parse(readFileSync20(accountJsonPath, "utf-8"));
9650
10090
  admins = config.admins ?? [];
9651
10091
  } catch (err) {
9652
10092
  const msg = err instanceof Error ? err.message : String(err);
@@ -9687,7 +10127,7 @@ function hashPin2(pin) {
9687
10127
  }
9688
10128
  function readUsersFile2() {
9689
10129
  if (!existsSync14(USERS_FILE)) return null;
9690
- const raw = readFileSync19(USERS_FILE, "utf-8").trim();
10130
+ const raw = readFileSync21(USERS_FILE, "utf-8").trim();
9691
10131
  if (!raw) return [];
9692
10132
  return JSON.parse(raw);
9693
10133
  }
@@ -9849,7 +10289,7 @@ app9.post("/set-pin", async (c) => {
9849
10289
  let installOwner = null;
9850
10290
  if (existsSync14(INSTALL_OWNER_FILE)) {
9851
10291
  try {
9852
- const raw = readFileSync19(INSTALL_OWNER_FILE, "utf-8").trim();
10292
+ const raw = readFileSync21(INSTALL_OWNER_FILE, "utf-8").trim();
9853
10293
  if (raw.length > 0) installOwner = raw;
9854
10294
  } catch (err) {
9855
10295
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -9931,8 +10371,8 @@ var onboarding_default = app9;
9931
10371
 
9932
10372
  // server/routes/client-error.ts
9933
10373
  import { appendFileSync, existsSync as existsSync15, renameSync as renameSync6, statSync as statSync8 } from "fs";
9934
- import { join as join19 } from "path";
9935
- var CLIENT_ERRORS_LOG = join19(LOG_DIR, "client-errors.log");
10374
+ import { join as join20 } from "path";
10375
+ var CLIENT_ERRORS_LOG = join20(LOG_DIR, "client-errors.log");
9936
10376
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
9937
10377
  var MAX_BODY_SIZE = 8 * 1024;
9938
10378
  var MAX_STACK_LEN = 2e3;
@@ -10257,17 +10697,17 @@ app11.post("/", async (c) => {
10257
10697
  var session_default = app11;
10258
10698
 
10259
10699
  // server/routes/admin/logs.ts
10260
- import { existsSync as existsSync17, readdirSync as readdirSync11, readFileSync as readFileSync20, statSync as statSync9 } from "fs";
10261
- import { resolve as resolve16, basename as basename5 } from "path";
10700
+ import { existsSync as existsSync17, readdirSync as readdirSync13, readFileSync as readFileSync22, statSync as statSync9 } from "fs";
10701
+ import { resolve as resolve18, basename as basename6 } from "path";
10262
10702
 
10263
10703
  // app/lib/logs-read-resolve.ts
10264
10704
  import { existsSync as existsSync16 } from "fs";
10265
- import { join as join20 } from "path";
10705
+ import { join as join21 } from "path";
10266
10706
  function resolveSessionLogPaths(filename, logDirs) {
10267
10707
  const tried = [filename];
10268
10708
  const hits = [];
10269
10709
  for (const dir of logDirs) {
10270
- const fullPath = join20(dir, filename);
10710
+ const fullPath = join21(dir, filename);
10271
10711
  if (existsSync16(fullPath)) {
10272
10712
  hits.push({ path: fullPath, dir });
10273
10713
  }
@@ -10285,18 +10725,18 @@ app12.get("/", async (c) => {
10285
10725
  const cacheKeyParam = c.req.query("cacheKey");
10286
10726
  const download = c.req.query("download") === "1";
10287
10727
  const account = resolveAccount();
10288
- const accountLogDir = account ? resolve16(account.accountDir, "logs") : null;
10728
+ const accountLogDir = account ? resolve18(account.accountDir, "logs") : null;
10289
10729
  const logDirs = [];
10290
10730
  if (accountLogDir) logDirs.push(accountLogDir);
10291
10731
  logDirs.push(LOG_DIR);
10292
10732
  if (fileParam) {
10293
- const safe = basename5(fileParam);
10733
+ const safe = basename6(fileParam);
10294
10734
  const searched = [];
10295
10735
  for (const dir of logDirs) {
10296
- const filePath = resolve16(dir, safe);
10736
+ const filePath = resolve18(dir, safe);
10297
10737
  searched.push(filePath);
10298
10738
  try {
10299
- const buffer = readFileSync20(filePath);
10739
+ const buffer = readFileSync22(filePath);
10300
10740
  const onDiskBytes = statSync9(filePath).size;
10301
10741
  const headers = {
10302
10742
  "Content-Type": "text/plain; charset=utf-8",
@@ -10360,8 +10800,8 @@ app12.get("/", async (c) => {
10360
10800
  if (hit) {
10361
10801
  console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
10362
10802
  try {
10363
- const filename = basename5(hit.path);
10364
- const buffer = readFileSync20(hit.path);
10803
+ const filename = basename6(hit.path);
10804
+ const buffer = readFileSync22(hit.path);
10365
10805
  const onDiskBytes = statSync9(hit.path).size;
10366
10806
  const headers = {
10367
10807
  "Content-Type": "text/plain; charset=utf-8",
@@ -10399,16 +10839,16 @@ app12.get("/", async (c) => {
10399
10839
  if (!existsSync17(dir)) continue;
10400
10840
  let files;
10401
10841
  try {
10402
- files = readdirSync11(dir).filter((f) => f.endsWith(".log"));
10842
+ files = readdirSync13(dir).filter((f) => f.endsWith(".log"));
10403
10843
  } catch (err) {
10404
10844
  const reason = err instanceof Error ? err.message : String(err);
10405
10845
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
10406
10846
  continue;
10407
10847
  }
10408
- files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve16(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
10848
+ files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
10409
10849
  seen.add(name);
10410
10850
  try {
10411
- const content = readFileSync20(resolve16(dir, name));
10851
+ const content = readFileSync22(resolve18(dir, name));
10412
10852
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
10413
10853
  logs[name] = tail.trim() || "(empty)";
10414
10854
  } catch (err) {
@@ -10446,9 +10886,9 @@ app13.get("/", (c) => {
10446
10886
  var claude_info_default = app13;
10447
10887
 
10448
10888
  // server/routes/admin/attachment.ts
10449
- import { readFile as readFile2, readdir } from "fs/promises";
10889
+ import { readFile as readFile3, readdir } from "fs/promises";
10450
10890
  import { existsSync as existsSync18 } from "fs";
10451
- import { resolve as resolve17 } from "path";
10891
+ import { resolve as resolve19 } from "path";
10452
10892
  var app14 = new Hono();
10453
10893
  app14.get("/:attachmentId", requireAdminSession, async (c) => {
10454
10894
  const attachmentId = c.req.param("attachmentId");
@@ -10460,17 +10900,17 @@ app14.get("/:attachmentId", requireAdminSession, async (c) => {
10460
10900
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
10461
10901
  return new Response("Not found", { status: 404 });
10462
10902
  }
10463
- const dir = resolve17(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
10903
+ const dir = resolve19(DATA_ROOT, "accounts", accountId, "uploads", attachmentId);
10464
10904
  if (!existsSync18(dir)) {
10465
10905
  return new Response("Not found", { status: 404 });
10466
10906
  }
10467
- const metaPath = resolve17(dir, `${attachmentId}.meta.json`);
10907
+ const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
10468
10908
  if (!existsSync18(metaPath)) {
10469
10909
  return new Response("Not found", { status: 404 });
10470
10910
  }
10471
10911
  let meta;
10472
10912
  try {
10473
- meta = JSON.parse(await readFile2(metaPath, "utf-8"));
10913
+ meta = JSON.parse(await readFile3(metaPath, "utf-8"));
10474
10914
  } catch {
10475
10915
  return new Response("Not found", { status: 404 });
10476
10916
  }
@@ -10479,8 +10919,8 @@ app14.get("/:attachmentId", requireAdminSession, async (c) => {
10479
10919
  if (!dataFile) {
10480
10920
  return new Response("Not found", { status: 404 });
10481
10921
  }
10482
- const filePath = resolve17(dir, dataFile);
10483
- const buffer = await readFile2(filePath);
10922
+ const filePath = resolve19(dir, dataFile);
10923
+ const buffer = await readFile3(filePath);
10484
10924
  return new Response(new Uint8Array(buffer), {
10485
10925
  headers: {
10486
10926
  "Content-Type": meta.mimeType,
@@ -10492,24 +10932,24 @@ app14.get("/:attachmentId", requireAdminSession, async (c) => {
10492
10932
  var attachment_default = app14;
10493
10933
 
10494
10934
  // server/routes/admin/agents.ts
10495
- import { resolve as resolve18 } from "path";
10496
- import { readdirSync as readdirSync12, readFileSync as readFileSync21, existsSync as existsSync19, rmSync as rmSync2 } from "fs";
10935
+ import { resolve as resolve20 } from "path";
10936
+ import { readdirSync as readdirSync14, readFileSync as readFileSync23, existsSync as existsSync19, rmSync as rmSync2 } from "fs";
10497
10937
  var app15 = new Hono();
10498
10938
  app15.get("/", (c) => {
10499
10939
  const account = resolveAccount();
10500
10940
  if (!account) return c.json({ agents: [] });
10501
- const agentsDir = resolve18(account.accountDir, "agents");
10941
+ const agentsDir = resolve20(account.accountDir, "agents");
10502
10942
  if (!existsSync19(agentsDir)) return c.json({ agents: [] });
10503
10943
  const agents = [];
10504
10944
  try {
10505
- const entries = readdirSync12(agentsDir, { withFileTypes: true });
10945
+ const entries = readdirSync14(agentsDir, { withFileTypes: true });
10506
10946
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
10507
10947
  if (!entry.isDirectory()) continue;
10508
10948
  if (entry.name === "admin") continue;
10509
- const configPath2 = resolve18(agentsDir, entry.name, "config.json");
10949
+ const configPath2 = resolve20(agentsDir, entry.name, "config.json");
10510
10950
  if (!existsSync19(configPath2)) continue;
10511
10951
  try {
10512
- const config = JSON.parse(readFileSync21(configPath2, "utf-8"));
10952
+ const config = JSON.parse(readFileSync23(configPath2, "utf-8"));
10513
10953
  agents.push({
10514
10954
  slug: entry.name,
10515
10955
  displayName: config.displayName ?? entry.name,
@@ -10535,7 +10975,7 @@ app15.delete("/:slug", async (c) => {
10535
10975
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
10536
10976
  return c.json({ error: "Invalid agent slug" }, 400);
10537
10977
  }
10538
- const agentDir = resolve18(account.accountDir, "agents", slug);
10978
+ const agentDir = resolve20(account.accountDir, "agents", slug);
10539
10979
  if (!existsSync19(agentDir)) {
10540
10980
  return c.json({ error: "Agent not found" }, 404);
10541
10981
  }
@@ -10565,7 +11005,7 @@ app15.post("/:slug/project", async (c) => {
10565
11005
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
10566
11006
  return c.json({ error: "Invalid agent slug" }, 400);
10567
11007
  }
10568
- const agentDir = resolve18(account.accountDir, "agents", slug);
11008
+ const agentDir = resolve20(account.accountDir, "agents", slug);
10569
11009
  if (!existsSync19(agentDir)) {
10570
11010
  return c.json({ error: "Agent not found on disk" }, 404);
10571
11011
  }
@@ -11268,8 +11708,8 @@ app16.put("/:id/label", requireAdminSession, async (c) => {
11268
11708
  var sessions_default = app16;
11269
11709
 
11270
11710
  // app/lib/claude-agent/spawn-context.ts
11271
- import { existsSync as existsSync21, readFileSync as readFileSync22, readdirSync as readdirSync13, statSync as statSync10 } from "fs";
11272
- import { dirname as dirname5, resolve as resolve19, join as join21 } from "path";
11711
+ import { existsSync as existsSync21, readFileSync as readFileSync24, readdirSync as readdirSync15, statSync as statSync10 } from "fs";
11712
+ import { dirname as dirname5, resolve as resolve21, join as join22 } from "path";
11273
11713
  async function resolveOwnerProfileBlock(accountId, userId) {
11274
11714
  if (!userId) return { ok: false, reason: "missing-user-id" };
11275
11715
  try {
@@ -11310,14 +11750,14 @@ function frontmatterField(manifest, field2) {
11310
11750
  function extractPluginToolDescriptions(pluginDir) {
11311
11751
  const out = /* @__PURE__ */ new Map();
11312
11752
  const candidates = [
11313
- join21(pluginDir, "mcp/dist/index.js"),
11314
- join21(pluginDir, "mcp/src/index.ts")
11753
+ join22(pluginDir, "mcp/dist/index.js"),
11754
+ join22(pluginDir, "mcp/src/index.ts")
11315
11755
  ];
11316
11756
  let raw = null;
11317
11757
  for (const path2 of candidates) {
11318
11758
  if (!existsSync21(path2)) continue;
11319
11759
  try {
11320
- raw = readFileSync22(path2, "utf-8");
11760
+ raw = readFileSync24(path2, "utf-8");
11321
11761
  break;
11322
11762
  } catch {
11323
11763
  }
@@ -11366,24 +11806,24 @@ function parseSkillPathsFromFrontmatter(fm) {
11366
11806
  }
11367
11807
  function listPluginDirs() {
11368
11808
  const out = /* @__PURE__ */ new Map();
11369
- const platformPlugins = resolve19(PLATFORM_ROOT, "plugins");
11809
+ const platformPlugins = resolve21(PLATFORM_ROOT, "plugins");
11370
11810
  if (existsSync21(platformPlugins)) {
11371
- for (const name of readdirSync13(platformPlugins)) {
11372
- const dir = join21(platformPlugins, name);
11811
+ for (const name of readdirSync15(platformPlugins)) {
11812
+ const dir = join22(platformPlugins, name);
11373
11813
  try {
11374
11814
  if (statSync10(dir).isDirectory()) out.set(name, dir);
11375
11815
  } catch {
11376
11816
  }
11377
11817
  }
11378
11818
  }
11379
- const premiumRoot = resolve19(dirname5(PLATFORM_ROOT), "premium-plugins");
11819
+ const premiumRoot = resolve21(dirname5(PLATFORM_ROOT), "premium-plugins");
11380
11820
  if (existsSync21(premiumRoot)) {
11381
- for (const bundle of readdirSync13(premiumRoot)) {
11382
- const bundlePlugins = join21(premiumRoot, bundle, "plugins");
11821
+ for (const bundle of readdirSync15(premiumRoot)) {
11822
+ const bundlePlugins = join22(premiumRoot, bundle, "plugins");
11383
11823
  if (!existsSync21(bundlePlugins)) continue;
11384
11824
  try {
11385
- for (const name of readdirSync13(bundlePlugins)) {
11386
- const dir = join21(bundlePlugins, name);
11825
+ for (const name of readdirSync15(bundlePlugins)) {
11826
+ const dir = join22(bundlePlugins, name);
11387
11827
  try {
11388
11828
  if (statSync10(dir).isDirectory()) out.set(name, dir);
11389
11829
  } catch {
@@ -11396,10 +11836,10 @@ function listPluginDirs() {
11396
11836
  return out;
11397
11837
  }
11398
11838
  function readEnabledPlugins(accountId) {
11399
- const accountFile = resolve19(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
11839
+ const accountFile = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
11400
11840
  if (!existsSync21(accountFile)) return /* @__PURE__ */ new Set();
11401
11841
  try {
11402
- const parsed = JSON.parse(readFileSync22(accountFile, "utf-8"));
11842
+ const parsed = JSON.parse(readFileSync24(accountFile, "utf-8"));
11403
11843
  return new Set(
11404
11844
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
11405
11845
  );
@@ -11411,7 +11851,7 @@ function readFrontmatter(path2) {
11411
11851
  if (!existsSync21(path2)) return null;
11412
11852
  let raw;
11413
11853
  try {
11414
- raw = readFileSync22(path2, "utf-8");
11854
+ raw = readFileSync24(path2, "utf-8");
11415
11855
  } catch {
11416
11856
  return null;
11417
11857
  }
@@ -11426,7 +11866,7 @@ function computeActivePlugins(accountId) {
11426
11866
  for (const name of Array.from(enabled).sort()) {
11427
11867
  const dir = pluginDirs.get(name);
11428
11868
  if (!dir) continue;
11429
- const fm = readFrontmatter(join21(dir, "PLUGIN.md"));
11869
+ const fm = readFrontmatter(join22(dir, "PLUGIN.md"));
11430
11870
  if (!fm) continue;
11431
11871
  const description = frontmatterField(`---
11432
11872
  ${fm}
@@ -11443,7 +11883,7 @@ ${fm}
11443
11883
  `plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
11444
11884
  );
11445
11885
  if (toolNames.length > 0 && descMap.size === 0) {
11446
- const hasSource = existsSync21(join21(dir, "mcp/dist/index.js")) || existsSync21(join21(dir, "mcp/src/index.ts"));
11886
+ const hasSource = existsSync21(join22(dir, "mcp/dist/index.js")) || existsSync21(join22(dir, "mcp/src/index.ts"));
11447
11887
  if (hasSource) {
11448
11888
  console.warn(
11449
11889
  `[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
@@ -11453,7 +11893,7 @@ ${fm}
11453
11893
  const skillPaths = parseSkillPathsFromFrontmatter(fm);
11454
11894
  const skills = [];
11455
11895
  for (const relPath of skillPaths) {
11456
- const skillPath = join21(dir, relPath);
11896
+ const skillPath = join22(dir, relPath);
11457
11897
  const skillFm = readFrontmatter(skillPath);
11458
11898
  if (!skillFm) continue;
11459
11899
  const skillName = frontmatterField(`---
@@ -11469,11 +11909,11 @@ ${skillFm}
11469
11909
  return out;
11470
11910
  }
11471
11911
  function computeSpecialistDomains(accountId) {
11472
- const specialistsDir = resolve19(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
11912
+ const specialistsDir = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "specialists", "agents");
11473
11913
  if (!existsSync21(specialistsDir)) return [];
11474
11914
  let entries;
11475
11915
  try {
11476
- entries = readdirSync13(specialistsDir);
11916
+ entries = readdirSync15(specialistsDir);
11477
11917
  } catch {
11478
11918
  return [];
11479
11919
  }
@@ -11497,7 +11937,7 @@ function computeSpecialistDomains(accountId) {
11497
11937
  const out = [];
11498
11938
  for (const file of entries.sort()) {
11499
11939
  if (!file.endsWith(".md")) continue;
11500
- const fm = readFrontmatter(join21(specialistsDir, file));
11940
+ const fm = readFrontmatter(join22(specialistsDir, file));
11501
11941
  if (!fm) continue;
11502
11942
  const wrapped = `---
11503
11943
  ${fm}
@@ -11515,11 +11955,11 @@ ${fm}
11515
11955
  return out;
11516
11956
  }
11517
11957
  function computeDormantPlugins(accountId) {
11518
- const accountFile = resolve19(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
11958
+ const accountFile = resolve21(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
11519
11959
  if (!existsSync21(accountFile)) return [];
11520
11960
  let enabled;
11521
11961
  try {
11522
- const raw = readFileSync22(accountFile, "utf-8");
11962
+ const raw = readFileSync24(accountFile, "utf-8");
11523
11963
  const parsed = JSON.parse(raw);
11524
11964
  enabled = new Set(
11525
11965
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
@@ -11530,22 +11970,22 @@ function computeDormantPlugins(accountId) {
11530
11970
  );
11531
11971
  return [];
11532
11972
  }
11533
- const pluginsDir = resolve19(PLATFORM_ROOT, "plugins");
11973
+ const pluginsDir = resolve21(PLATFORM_ROOT, "plugins");
11534
11974
  if (!existsSync21(pluginsDir)) return [];
11535
11975
  let entries;
11536
11976
  try {
11537
- entries = readdirSync13(pluginsDir);
11977
+ entries = readdirSync15(pluginsDir);
11538
11978
  } catch {
11539
11979
  return [];
11540
11980
  }
11541
11981
  const out = [];
11542
11982
  for (const name of entries) {
11543
11983
  if (enabled.has(name)) continue;
11544
- const manifestPath = resolve19(pluginsDir, name, "PLUGIN.md");
11984
+ const manifestPath = resolve21(pluginsDir, name, "PLUGIN.md");
11545
11985
  if (!existsSync21(manifestPath)) continue;
11546
11986
  let manifest;
11547
11987
  try {
11548
- manifest = readFileSync22(manifestPath, "utf-8");
11988
+ manifest = readFileSync24(manifestPath, "utf-8");
11549
11989
  } catch {
11550
11990
  continue;
11551
11991
  }
@@ -11559,13 +11999,13 @@ function computeDormantPlugins(accountId) {
11559
11999
  }
11560
12000
 
11561
12001
  // server/routes/admin/claude-sessions.ts
11562
- import { existsSync as existsSync22, readFileSync as readFileSync23 } from "fs";
11563
- import { resolve as resolve20 } from "path";
12002
+ import { existsSync as existsSync22, readFileSync as readFileSync25 } from "fs";
12003
+ import { resolve as resolve22 } from "path";
11564
12004
  function readTunnelState(brandConfigDir) {
11565
12005
  const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
11566
12006
  if (!existsSync22(statePath)) return null;
11567
12007
  try {
11568
- const parsed = JSON.parse(readFileSync23(statePath, "utf-8"));
12008
+ const parsed = JSON.parse(readFileSync25(statePath, "utf-8"));
11569
12009
  const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
11570
12010
  const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
11571
12011
  const domain = typeof parsed.domain === "string" ? parsed.domain : null;
@@ -11576,10 +12016,10 @@ function readTunnelState(brandConfigDir) {
11576
12016
  }
11577
12017
  }
11578
12018
  function resolveTunnelUrl() {
11579
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve20(process.cwd(), "..");
12019
+ const platformRoot4 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
11580
12020
  let brand;
11581
12021
  try {
11582
- brand = JSON.parse(readFileSync23(resolve20(platformRoot2, "config", "brand.json"), "utf-8"));
12022
+ brand = JSON.parse(readFileSync25(resolve22(platformRoot4, "config", "brand.json"), "utf-8"));
11583
12023
  } catch {
11584
12024
  return null;
11585
12025
  }
@@ -11590,7 +12030,7 @@ function resolveTunnelUrl() {
11590
12030
  if (!state) return null;
11591
12031
  return `https://${hostname2}.${state.domain}`;
11592
12032
  }
11593
- var TAG25 = "[claude-session-manager:wrapper]";
12033
+ var TAG28 = "[claude-session-manager:wrapper]";
11594
12034
  async function refuseIfClaudeAuthDead(c, route, sessionId) {
11595
12035
  const auth = await ensureAuth();
11596
12036
  if (auth.status !== "dead" && auth.status !== "missing") return null;
@@ -11608,12 +12048,12 @@ async function performSpawnWithInitialMessage(args) {
11608
12048
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
11609
12049
  const ownerMs = Date.now() - ownerStart;
11610
12050
  const aboutOwnerStatus = aboutOwner == null ? "absent" : "ok" in aboutOwner && aboutOwner.ok === false ? `unresolved:${aboutOwner.reason}` : "ok";
11611
- console.log(`${TAG25} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
12051
+ console.log(`${TAG28} about-owner-resolved status=${aboutOwnerStatus} ms=${ownerMs}`);
11612
12052
  const dormantPlugins = computeDormantPlugins(args.senderId);
11613
12053
  const activePlugins = computeActivePlugins(args.senderId);
11614
12054
  const specialistDomains = computeSpecialistDomains(args.senderId);
11615
12055
  const tunnelUrl = resolveTunnelUrl();
11616
- console.log(`${TAG25} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
12056
+ console.log(`${TAG28} tunnel-url-resolved value=${tunnelUrl ?? "null"}`);
11617
12057
  const upstreamPayload = JSON.stringify({
11618
12058
  senderId: args.senderId,
11619
12059
  // Task 205 — pass userId through to the manager so it lands as
@@ -11640,24 +12080,24 @@ async function performSpawnWithInitialMessage(args) {
11640
12080
  // unshapely values.
11641
12081
  conversationNodeId: args.conversationNodeId
11642
12082
  });
11643
- console.log(`${TAG25} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
12083
+ console.log(`${TAG28} forward-spawn-start managerBase=${managerBase("claude-session-manager:wrapper")} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
11644
12084
  const forwardStart = Date.now();
11645
12085
  const upstream = await fetch(`${managerBase("claude-session-manager:wrapper")}/public-spawn`, {
11646
12086
  method: "POST",
11647
12087
  headers: { "content-type": "application/json" },
11648
12088
  body: upstreamPayload
11649
12089
  }).catch((err) => {
11650
- console.error(`${TAG25} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
12090
+ console.error(`${TAG28} fetch-failed op=spawn message=${err instanceof Error ? err.message : String(err)} ms=${Date.now() - forwardStart}`);
11651
12091
  return null;
11652
12092
  });
11653
12093
  if (!upstream) return {
11654
12094
  response: new Response(JSON.stringify({ error: "manager-unreachable" }), { status: 503, headers: { "content-type": "application/json" } }),
11655
12095
  claudeSessionId: null
11656
12096
  };
11657
- console.log(`${TAG25} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
12097
+ console.log(`${TAG28} forward-spawn-done status=${upstream.status} ms=${Date.now() - forwardStart}`);
11658
12098
  if (args.initialMessage) {
11659
12099
  const inputBytes = Buffer.byteLength(args.initialMessage, "utf8");
11660
- console.log(`${TAG25} initial-message-inlined bytes=${inputBytes}`);
12100
+ console.log(`${TAG28} initial-message-inlined bytes=${inputBytes}`);
11661
12101
  }
11662
12102
  const bodyText = await upstream.text().catch(() => "");
11663
12103
  let claudeSessionId = null;
@@ -11692,7 +12132,7 @@ app17.post("/", async (c) => {
11692
12132
  if (refusal) return refusal;
11693
12133
  const senderId = getAccountIdForSession(cacheKey) ?? "";
11694
12134
  if (!senderId) {
11695
- console.error(`${TAG25} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
12135
+ console.error(`${TAG28} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
11696
12136
  return c.json({ error: "admin-account-not-resolved" }, 500);
11697
12137
  }
11698
12138
  const userId = getUserIdForSession(cacheKey) ?? void 0;
@@ -11701,7 +12141,7 @@ app17.post("/", async (c) => {
11701
12141
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
11702
12142
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
11703
12143
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
11704
- 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"}`);
12144
+ console.log(`${TAG28} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
11705
12145
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
11706
12146
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
11707
12147
  senderId,
@@ -11720,13 +12160,13 @@ app17.post("/", async (c) => {
11720
12160
  claudeSessionId,
11721
12161
  senderId
11722
12162
  );
11723
- console.log(`${TAG25} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
12163
+ console.log(`${TAG28} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
11724
12164
  return response;
11725
12165
  });
11726
12166
  var claude_sessions_default = app17;
11727
12167
 
11728
12168
  // server/routes/admin/log-ingest.ts
11729
- var TAG26 = "[log-ingest]";
12169
+ var TAG29 = "[log-ingest]";
11730
12170
  var TAG_PATTERN = /^[A-Za-z0-9_:-]{1,32}$/;
11731
12171
  var LEVELS = /* @__PURE__ */ new Set(["debug", "info", "warn", "error"]);
11732
12172
  var MAX_LINE_BYTES = 4096;
@@ -11737,7 +12177,7 @@ function isLoopbackAddr(addr) {
11737
12177
  app18.post("/", async (c) => {
11738
12178
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
11739
12179
  if (!isLoopbackAddr(remoteAddr)) {
11740
- console.error(`${TAG26} reject reason=non-loopback remoteAddr=${remoteAddr}`);
12180
+ console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
11741
12181
  return c.json({ error: "log-ingest-loopback-only" }, 403);
11742
12182
  }
11743
12183
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -11746,7 +12186,7 @@ app18.post("/", async (c) => {
11746
12186
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
11747
12187
  const offender = tokens.find((t) => !isLoopbackAddr(t));
11748
12188
  if (offender !== void 0) {
11749
- console.error(`${TAG26} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
12189
+ console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
11750
12190
  return c.json({ error: "log-ingest-loopback-only" }, 403);
11751
12191
  }
11752
12192
  }
@@ -11788,18 +12228,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
11788
12228
  ]);
11789
12229
  var app19 = new Hono();
11790
12230
  app19.post("/", async (c) => {
11791
- const TAG35 = "[admin:events]";
12231
+ const TAG38 = "[admin:events]";
11792
12232
  let body;
11793
12233
  try {
11794
12234
  body = await c.req.json();
11795
12235
  } catch (err) {
11796
12236
  const detail = err instanceof Error ? err.message : String(err);
11797
- console.error(`${TAG35} reject reason=body-not-json detail=${detail}`);
12237
+ console.error(`${TAG38} reject reason=body-not-json detail=${detail}`);
11798
12238
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
11799
12239
  }
11800
12240
  const event = typeof body.event === "string" ? body.event : "";
11801
12241
  if (!ALLOWED_EVENTS.has(event)) {
11802
- console.error(`${TAG35} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
12242
+ console.error(`${TAG38} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
11803
12243
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
11804
12244
  }
11805
12245
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -11822,10 +12262,10 @@ var events_default = app19;
11822
12262
 
11823
12263
  // server/routes/admin/files.ts
11824
12264
  import { createReadStream as createReadStream2, createWriteStream as createWriteStream2, existsSync as existsSync23 } from "fs";
11825
- import { readdir as readdir3, readFile as readFile4, stat as stat4, mkdir as mkdir3, unlink as unlink2, rename } from "fs/promises";
11826
- import { realpathSync as realpathSync4 } from "fs";
12265
+ import { readdir as readdir3, readFile as readFile5, stat as stat5, mkdir as mkdir3, unlink as unlink2, rename } from "fs/promises";
12266
+ import { realpathSync as realpathSync6 } from "fs";
11827
12267
  import { randomUUID as randomUUID10 } from "crypto";
11828
- import { basename as basename7, dirname as dirname6, join as join23, resolve as resolve22, sep as sep6 } from "path";
12268
+ import { basename as basename8, dirname as dirname6, join as join24, resolve as resolve24, sep as sep6 } from "path";
11829
12269
  import { Readable as Readable2 } from "stream";
11830
12270
 
11831
12271
  // ../lib/graph-trash/src/index.ts
@@ -12143,7 +12583,7 @@ async function cascadeDeleteDocument(params) {
12143
12583
 
12144
12584
  // app/lib/file-index.ts
12145
12585
  import * as fsp from "fs/promises";
12146
- import { resolve as resolve21, relative as relative2, join as join22, basename as basename6, extname as extname2, sep as sep5 } from "path";
12586
+ import { resolve as resolve23, relative as relative3, join as join23, basename as basename7, extname as extname2, sep as sep5 } from "path";
12147
12587
  import { tmpdir as tmpdir2 } from "os";
12148
12588
  import { execFile as execFile2 } from "child_process";
12149
12589
  import { promisify as promisify2 } from "util";
@@ -12225,7 +12665,7 @@ async function extractPdf(absolute) {
12225
12665
  return stdout.trim();
12226
12666
  }
12227
12667
  async function ocrPdf(absolute) {
12228
- const outPdf = join22(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
12668
+ const outPdf = join23(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
12229
12669
  try {
12230
12670
  await execFileAsync2(
12231
12671
  "ocrmypdf",
@@ -12276,18 +12716,18 @@ async function extractFileContent(absolute) {
12276
12716
  return { content: "", route: "name-only" };
12277
12717
  } catch (err) {
12278
12718
  const msg = err instanceof Error ? err.message : String(err);
12279
- console.error(`[file-index] extract-failed path="${relative2(DATA_ROOT, absolute)}" err="${msg}"`);
12719
+ console.error(`[file-index] extract-failed path="${relative3(DATA_ROOT, absolute)}" err="${msg}"`);
12280
12720
  return { content: "", route: "name-only" };
12281
12721
  }
12282
12722
  }
12283
12723
  async function readDisplayName(absolute) {
12284
- const dir = absolute.slice(0, absolute.length - basename6(absolute).length);
12285
- const base = basename6(absolute);
12724
+ const dir = absolute.slice(0, absolute.length - basename7(absolute).length);
12725
+ const base = basename7(absolute);
12286
12726
  const dot = base.lastIndexOf(".");
12287
12727
  const stem = dot === -1 ? base : base.slice(0, dot);
12288
12728
  if (base === `${stem}.meta.json`) return null;
12289
12729
  try {
12290
- const raw = await fsp.readFile(join22(dir, `${stem}.meta.json`), "utf-8");
12730
+ const raw = await fsp.readFile(join23(dir, `${stem}.meta.json`), "utf-8");
12291
12731
  const meta = JSON.parse(raw);
12292
12732
  const dn = meta.displayName ?? meta.filename;
12293
12733
  return typeof dn === "string" && dn.length > 0 ? dn : null;
@@ -12301,14 +12741,14 @@ async function walkSubtree(root, dataRoot, out) {
12301
12741
  entries = await fsp.readdir(root, { withFileTypes: true });
12302
12742
  } catch (err) {
12303
12743
  if (err.code === "ENOENT") return { clean: true };
12304
- console.error(`[file-index] walk-error dir="${relative2(dataRoot, root)}" err="${err.message}"`);
12744
+ console.error(`[file-index] walk-error dir="${relative3(dataRoot, root)}" err="${err.message}"`);
12305
12745
  return { clean: false };
12306
12746
  }
12307
12747
  let clean = true;
12308
12748
  for (const entry of entries) {
12309
12749
  if (entry.isSymbolicLink()) continue;
12310
12750
  if (entry.isDirectory() && entry.name === ".uploads-tmp") continue;
12311
- const abs = join22(root, entry.name);
12751
+ const abs = join23(root, entry.name);
12312
12752
  if (entry.isDirectory()) {
12313
12753
  const sub = await walkSubtree(abs, dataRoot, out);
12314
12754
  if (!sub.clean) clean = false;
@@ -12317,12 +12757,12 @@ async function walkSubtree(root, dataRoot, out) {
12317
12757
  const st = await fsp.stat(abs);
12318
12758
  out.push({
12319
12759
  absolute: abs,
12320
- relativePath: relative2(dataRoot, abs).split(sep5).join("/"),
12760
+ relativePath: relative3(dataRoot, abs).split(sep5).join("/"),
12321
12761
  sizeBytes: st.size,
12322
12762
  modifiedAt: st.mtime.toISOString()
12323
12763
  });
12324
12764
  } catch (err) {
12325
- console.error(`[file-index] stat-error path="${relative2(dataRoot, abs)}" err="${err.message}"`);
12765
+ console.error(`[file-index] stat-error path="${relative3(dataRoot, abs)}" err="${err.message}"`);
12326
12766
  clean = false;
12327
12767
  }
12328
12768
  }
@@ -12420,7 +12860,7 @@ async function cascadeTrashLinkedKnowledgeDocs(session, accountId, relativePaths
12420
12860
  return { kds, sections, chunks };
12421
12861
  }
12422
12862
  async function buildArtifact(accountId, wf, embed2) {
12423
- const name = basename6(wf.absolute);
12863
+ const name = basename7(wf.absolute);
12424
12864
  const { content, route } = await extractFileContent(wf.absolute);
12425
12865
  const displayName = await readDisplayName(wf.absolute);
12426
12866
  const embedInput = `${name}
@@ -12463,7 +12903,7 @@ async function reconcileFileIndex(accountId, opts) {
12463
12903
  return withSession(opts, async (session) => {
12464
12904
  const walked = [];
12465
12905
  const subtreeRoots = [
12466
- resolve21(dataRoot, "accounts", accountId)
12906
+ resolve23(dataRoot, "accounts", accountId)
12467
12907
  ];
12468
12908
  let clean = true;
12469
12909
  for (const root of subtreeRoots) {
@@ -12546,7 +12986,7 @@ async function reconcileFileIndexDebounced(accountId, opts) {
12546
12986
  async function indexFile(accountId, relativePath, opts) {
12547
12987
  const dataRoot = opts?.dataRoot ?? DATA_ROOT;
12548
12988
  const embed2 = opts?.embed ?? embed;
12549
- const absolute = resolve21(dataRoot, relativePath);
12989
+ const absolute = resolve23(dataRoot, relativePath);
12550
12990
  await withSession(opts, async (session) => {
12551
12991
  const st = await fsp.stat(absolute);
12552
12992
  const wf = {
@@ -12581,7 +13021,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
12581
13021
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12582
13022
  async function readMeta(absDir, baseName) {
12583
13023
  try {
12584
- const raw = await readFile4(join23(absDir, `${baseName}.meta.json`), "utf8");
13024
+ const raw = await readFile5(join24(absDir, `${baseName}.meta.json`), "utf8");
12585
13025
  const parsed = JSON.parse(raw);
12586
13026
  if (typeof parsed?.filename === "string") {
12587
13027
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -12592,7 +13032,7 @@ async function readMeta(absDir, baseName) {
12592
13032
  }
12593
13033
  async function readAccountNames() {
12594
13034
  const map = /* @__PURE__ */ new Map();
12595
- const accountsDir = resolve22(DATA_ROOT, "accounts");
13035
+ const accountsDir = resolve24(DATA_ROOT, "accounts");
12596
13036
  let names;
12597
13037
  try {
12598
13038
  names = await readdir3(accountsDir);
@@ -12601,9 +13041,9 @@ async function readAccountNames() {
12601
13041
  }
12602
13042
  for (const name of names) {
12603
13043
  if (!UUID_RE3.test(name)) continue;
12604
- const configPath2 = resolve22(accountsDir, name, "account.json");
13044
+ const configPath2 = resolve24(accountsDir, name, "account.json");
12605
13045
  try {
12606
- const raw = await readFile4(configPath2, "utf8");
13046
+ const raw = await readFile5(configPath2, "utf8");
12607
13047
  const parsed = JSON.parse(raw);
12608
13048
  if (typeof parsed?.name === "string" && parsed.name.length > 0) {
12609
13049
  map.set(name, parsed.name);
@@ -12619,7 +13059,7 @@ async function readAccountNames() {
12619
13059
  }
12620
13060
  async function enrich(absolute, entry, accountNames) {
12621
13061
  if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
12622
- const meta = await readMeta(join23(absolute, entry.name), entry.name);
13062
+ const meta = await readMeta(join24(absolute, entry.name), entry.name);
12623
13063
  if (meta?.filename) {
12624
13064
  entry.displayName = meta.filename;
12625
13065
  entry.mimeType = meta.mimeType;
@@ -12765,7 +13205,7 @@ app20.get("/", requireAdminSession, async (c) => {
12765
13205
  return c.json({ error: "Not found" }, 404);
12766
13206
  }
12767
13207
  try {
12768
- const info = await stat4(absolute);
13208
+ const info = await stat5(absolute);
12769
13209
  if (!info.isDirectory()) {
12770
13210
  return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
12771
13211
  }
@@ -12783,8 +13223,8 @@ app20.get("/", requireAdminSession, async (c) => {
12783
13223
  const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
12784
13224
  const protectedEntry = isProtectedFromDeletion(childRel).protected;
12785
13225
  try {
12786
- const entryPath = join23(absolute, name);
12787
- const s = await stat4(entryPath);
13226
+ const entryPath = join24(absolute, name);
13227
+ const s = await stat5(entryPath);
12788
13228
  entries.push({
12789
13229
  name,
12790
13230
  kind: s.isDirectory() ? "directory" : s.isFile() ? "file" : "other",
@@ -12846,11 +13286,11 @@ app20.get("/download", requireAdminSession, async (c) => {
12846
13286
  return c.json({ error: "Not found" }, 404);
12847
13287
  }
12848
13288
  try {
12849
- const info = await stat4(absolute);
13289
+ const info = await stat5(absolute);
12850
13290
  if (!info.isFile()) {
12851
13291
  return c.json({ error: "Path is not a file" }, 400);
12852
13292
  }
12853
- const filename = basename7(absolute);
13293
+ const filename = basename8(absolute);
12854
13294
  const mimeType = detectMimeType(absolute);
12855
13295
  const nodeStream = createReadStream2(absolute);
12856
13296
  const webStream = Readable2.toWeb(nodeStream);
@@ -12889,14 +13329,14 @@ app20.post("/upload", requireAdminSession, async (c) => {
12889
13329
  return c.json({ error: "Account not found for session" }, 401);
12890
13330
  }
12891
13331
  const rawName = c.req.query("filename") ?? "";
12892
- const safeName = basename7(rawName).replace(/[\0/\\]/g, "_");
13332
+ const safeName = basename8(rawName).replace(/[\0/\\]/g, "_");
12893
13333
  if (!safeName) return c.json({ error: "filename query param required" }, 400);
12894
13334
  const uid = `${Date.now()}-${randomUUID10().slice(0, 8)}`;
12895
13335
  const declaredBytes = c.req.header("content-length") ?? "unknown";
12896
13336
  const rawRelpath = c.req.query("relpath") ?? "";
12897
13337
  const relpath = rawRelpath !== "" ? rawRelpath : null;
12898
13338
  const token = c.req.query("token") ?? "";
12899
- const finalName = relpath ? basename7(relpath).replace(/[\0/\\]/g, "_") : `${Date.now()}-${safeName}`;
13339
+ const finalName = relpath ? basename8(relpath).replace(/[\0/\\]/g, "_") : `${Date.now()}-${safeName}`;
12900
13340
  const mimeType = (c.req.header("content-type") ?? "").split(";")[0].trim();
12901
13341
  if (!SUPPORTED_MIME_TYPES.has(mimeType)) {
12902
13342
  console.error(`[data-upload] op=reject-mime uid=${uid} mime="${mimeType}" token=${token} rel="${relpath ?? ""}"`);
@@ -12908,7 +13348,7 @@ app20.post("/upload", requireAdminSession, async (c) => {
12908
13348
  const base = resolveOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
12909
13349
  if (!base.ok) return c.json({ error: base.error }, base.status);
12910
13350
  const relDir = relpath ? dirname6(relpath) : ".";
12911
- const destRel = relDir === "." ? base.relative : join23(base.relative, relDir);
13351
+ const destRel = relDir === "." ? base.relative : join24(base.relative, relDir);
12912
13352
  if (crossesForeignAccountPartition(destRel, accountId)) {
12913
13353
  console.error(`[data] account-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
12914
13354
  return c.json({ error: "Not found" }, 404);
@@ -12917,17 +13357,17 @@ app20.post("/upload", requireAdminSession, async (c) => {
12917
13357
  console.error(`[data] write-scope-blocked endpoint="POST /api/admin/files/upload" path="${destRel}" account=${accountId.slice(0, 8)}\u2026`);
12918
13358
  return c.json({ error: "Path is outside your account folder" }, 403);
12919
13359
  }
12920
- const destDir = relDir === "." ? base.absolute : resolve22(base.absolute, relDir);
12921
- const destPath = resolve22(destDir, finalName);
13360
+ const destDir = relDir === "." ? base.absolute : resolve24(base.absolute, relDir);
13361
+ const destPath = resolve24(destDir, finalName);
12922
13362
  if (relpath) {
12923
13363
  const firstCreated = await mkdir3(destDir, { recursive: true });
12924
13364
  if (firstCreated) console.error(`[data-upload] op=mkdir uid=${uid} token=${token} dir="${destRel}"`);
12925
13365
  } else {
12926
- const info = await stat4(destDir).catch(() => null);
13366
+ const info = await stat5(destDir).catch(() => null);
12927
13367
  if (!info || !info.isDirectory()) return c.json({ error: "Upload destination is not a directory" }, 400);
12928
13368
  }
12929
- const dataRootReal = realpathSync4(DATA_ROOT);
12930
- const destDirReal = realpathSync4(destDir);
13369
+ const dataRootReal = realpathSync6(DATA_ROOT);
13370
+ const destDirReal = realpathSync6(destDir);
12931
13371
  if (destDirReal !== dataRootReal && !destDirReal.startsWith(dataRootReal + sep6)) {
12932
13372
  console.error(`[data-upload] op=path-traversal-blocked uid=${uid} requested="${destRel}" resolved="${destDirReal}"`);
12933
13373
  return c.json({ error: "Upload destination escapes DATA_ROOT" }, 403);
@@ -12936,9 +13376,9 @@ app20.post("/upload", requireAdminSession, async (c) => {
12936
13376
  if (!body) return c.json({ error: "Empty request body" }, 400);
12937
13377
  console.error(`[data-upload] op=request uid=${uid} account=${accountId} dir="${destRel}" token=${token} rel="${relpath ?? ""}" declaredBytes=${declaredBytes}`);
12938
13378
  const ceiling = dataUploadCeiling();
12939
- const tmpDir = resolve22(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
13379
+ const tmpDir = resolve24(DATA_ROOT, "accounts", accountId, UPLOAD_TMP_DIR);
12940
13380
  await mkdir3(tmpDir, { recursive: true });
12941
- const tmpPath = resolve22(tmpDir, `${uid}.part`);
13381
+ const tmpPath = resolve24(tmpDir, `${uid}.part`);
12942
13382
  const out = createWriteStream2(tmpPath);
12943
13383
  const t0 = Date.now();
12944
13384
  let received = 0;
@@ -13020,7 +13460,7 @@ app20.post("/upload", requireAdminSession, async (c) => {
13020
13460
  });
13021
13461
  });
13022
13462
  async function sweepStaleUploadTemps() {
13023
- const accountsRoot = resolve22(DATA_ROOT, "accounts");
13463
+ const accountsRoot = resolve24(DATA_ROOT, "accounts");
13024
13464
  let accounts;
13025
13465
  try {
13026
13466
  accounts = await readdir3(accountsRoot, { withFileTypes: true });
@@ -13030,7 +13470,7 @@ async function sweepStaleUploadTemps() {
13030
13470
  let removed = 0;
13031
13471
  for (const acc of accounts) {
13032
13472
  if (!acc.isDirectory()) continue;
13033
- const tmpDir = resolve22(accountsRoot, acc.name, UPLOAD_TMP_DIR);
13473
+ const tmpDir = resolve24(accountsRoot, acc.name, UPLOAD_TMP_DIR);
13034
13474
  let entries;
13035
13475
  try {
13036
13476
  entries = await readdir3(tmpDir);
@@ -13039,7 +13479,7 @@ async function sweepStaleUploadTemps() {
13039
13479
  }
13040
13480
  for (const name of entries) {
13041
13481
  try {
13042
- await unlink2(resolve22(tmpDir, name));
13482
+ await unlink2(resolve24(tmpDir, name));
13043
13483
  removed++;
13044
13484
  } catch {
13045
13485
  }
@@ -13069,14 +13509,14 @@ app20.delete("/", requireAdminSession, async (c) => {
13069
13509
  console.error(`[data] account-scope-blocked endpoint="DELETE /api/admin/files" path="${relPath}" account=${accountId.slice(0, 8)}\u2026`);
13070
13510
  return c.json({ error: "Not found" }, 404);
13071
13511
  }
13072
- const base = basename7(absolute);
13512
+ const base = basename8(absolute);
13073
13513
  const protection = isProtectedFromDeletion(relPath);
13074
13514
  if (protection.protected) {
13075
13515
  console.error(`[data] file-delete blocked path="${relPath}" reason="protected" rule="${protection.rule}"`);
13076
13516
  return c.json({ error: "Protected file \u2014 refusing to delete" }, 403);
13077
13517
  }
13078
13518
  try {
13079
- const info = await stat4(absolute);
13519
+ const info = await stat5(absolute);
13080
13520
  if (info.isDirectory()) {
13081
13521
  return c.json({ error: "Directory deletion not supported" }, 400);
13082
13522
  }
@@ -13085,7 +13525,7 @@ app20.delete("/", requireAdminSession, async (c) => {
13085
13525
  }
13086
13526
  const dot = base.lastIndexOf(".");
13087
13527
  const stem = dot === -1 ? base : base.slice(0, dot);
13088
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join23(dirname6(absolute), `${stem}.meta.json`) : null;
13528
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join24(dirname6(absolute), `${stem}.meta.json`) : null;
13089
13529
  await unlink2(absolute);
13090
13530
  if (sidecarPath) {
13091
13531
  try {
@@ -13142,10 +13582,10 @@ app20.post("/folder", requireAdminSession, async (c) => {
13142
13582
  if (!isValidEntryName(name)) return c.json({ error: "Invalid folder name" }, 400);
13143
13583
  const parent = resolveOwnAccountWrite(rawPath, accountId, "POST /api/admin/files/folder");
13144
13584
  if (!parent.ok) return c.json({ error: parent.error }, parent.status);
13145
- const newAbs = resolve22(parent.absolute, name);
13585
+ const newAbs = resolve24(parent.absolute, name);
13146
13586
  const newRel = `${parent.relative}/${name}`;
13147
13587
  try {
13148
- const info = await stat4(parent.absolute);
13588
+ const info = await stat5(parent.absolute);
13149
13589
  if (!info.isDirectory()) return c.json({ error: "Parent is not a directory" }, 400);
13150
13590
  await mkdir3(newAbs, { recursive: false });
13151
13591
  } catch (err) {
@@ -13181,7 +13621,7 @@ app20.post("/rename", requireAdminSession, async (c) => {
13181
13621
  console.error(`[data] file-rename blocked path="${src.relative}" reason="protected"`);
13182
13622
  return c.json({ error: "Protected entry \u2014 refusing to rename" }, 403);
13183
13623
  }
13184
- const destAbs = resolve22(dirname6(src.absolute), newName);
13624
+ const destAbs = resolve24(dirname6(src.absolute), newName);
13185
13625
  const newRel = `${dirname6(src.relative)}/${newName}`.replace(/^\.\//, "");
13186
13626
  if (isProtectedFromRename(newRel)) {
13187
13627
  console.error(`[data] file-rename blocked path="${newRel}" reason="protected-target"`);
@@ -13190,7 +13630,7 @@ app20.post("/rename", requireAdminSession, async (c) => {
13190
13630
  try {
13191
13631
  let collision = true;
13192
13632
  try {
13193
- await stat4(destAbs);
13633
+ await stat5(destAbs);
13194
13634
  } catch (e) {
13195
13635
  if (e.code === "ENOENT") collision = false;
13196
13636
  }
@@ -14149,8 +14589,6 @@ var FILTER_TOP_LEVEL_LABELS = Object.freeze(
14149
14589
  "Job",
14150
14590
  "QuoteDocument",
14151
14591
  "InboundInvoice",
14152
- "SubContractor",
14153
- "SubInvoice",
14154
14592
  "WhatsAppGroup",
14155
14593
  "AdminConversation",
14156
14594
  "PublicConversation",
@@ -15218,14 +15656,14 @@ var WRITE_CYPHER = `
15218
15656
  var graph_default_view_default = app26;
15219
15657
 
15220
15658
  // server/routes/admin/sidebar-artefacts.ts
15221
- import { readdir as readdir4, stat as stat5 } from "fs/promises";
15222
- import { resolve as resolve23, relative as relative3, isAbsolute, sep as sep7, basename as basename8 } from "path";
15659
+ import { readdir as readdir4, stat as stat6 } from "fs/promises";
15660
+ import { resolve as resolve25, relative as relative4, isAbsolute as isAbsolute2, sep as sep7, basename as basename9 } from "path";
15223
15661
  import { existsSync as existsSync24 } from "fs";
15224
15662
  var LIMIT = 50;
15225
15663
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
15226
15664
  function toDataRootRelPath(absPath) {
15227
15665
  if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep7)) return null;
15228
- return relative3(DATA_ROOT, absPath);
15666
+ return relative4(DATA_ROOT, absPath);
15229
15667
  }
15230
15668
  var app27 = new Hono();
15231
15669
  app27.get("/", requireAdminSession, async (c) => {
@@ -15239,7 +15677,7 @@ app27.get("/", requireAdminSession, async (c) => {
15239
15677
  if (accountFiles === null) {
15240
15678
  return c.json({ error: "Failed to load artefacts" }, 500);
15241
15679
  }
15242
- const accountDir = resolve23(ACCOUNTS_DIR, accountId);
15680
+ const accountDir = resolve25(ACCOUNTS_DIR, accountId);
15243
15681
  const agents = await fetchAgentTemplateRows(accountDir);
15244
15682
  const artefacts = [...accountFiles, ...agents].sort(
15245
15683
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
@@ -15272,7 +15710,7 @@ async function fetchAccountFileArtefacts(accountId) {
15272
15710
  const modifiedAt = r.get("modifiedAt") ?? "";
15273
15711
  return {
15274
15712
  id: `account-file:${relativePath}`,
15275
- name: displayName ?? basename8(relativePath),
15713
+ name: displayName ?? basename9(relativePath),
15276
15714
  kind: "account-file",
15277
15715
  updatedAt: modifiedAt,
15278
15716
  mimeType,
@@ -15291,8 +15729,8 @@ async function fetchAccountFileArtefacts(accountId) {
15291
15729
  async function fetchAgentTemplateRows(accountDir) {
15292
15730
  const rows = [];
15293
15731
  for (const filename of ADMIN_AGENT_FILES) {
15294
- const overridePath = resolve23(accountDir, "agents", "admin", filename);
15295
- const bundledPath = resolve23(PLATFORM_ROOT, "templates", "agents", "admin", filename);
15732
+ const overridePath = resolve25(accountDir, "agents", "admin", filename);
15733
+ const bundledPath = resolve25(PLATFORM_ROOT, "templates", "agents", "admin", filename);
15296
15734
  const labelStem = filename.replace(/\.md$/, "");
15297
15735
  const row = await readAgentTemplateRow({
15298
15736
  id: `agent-template:admin:${filename}`,
@@ -15305,12 +15743,12 @@ async function fetchAgentTemplateRows(accountDir) {
15305
15743
  });
15306
15744
  if (row) rows.push(row);
15307
15745
  }
15308
- const overrideDir = resolve23(accountDir, "specialists", "agents");
15309
- const bundledDir = resolve23(PLATFORM_ROOT, "templates", "specialists", "agents");
15746
+ const overrideDir = resolve25(accountDir, "specialists", "agents");
15747
+ const bundledDir = resolve25(PLATFORM_ROOT, "templates", "specialists", "agents");
15310
15748
  const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
15311
15749
  for (const filename of specialistNames) {
15312
- const overridePath = resolve23(overrideDir, filename);
15313
- const bundledPath = resolve23(bundledDir, filename);
15750
+ const overridePath = resolve25(overrideDir, filename);
15751
+ const bundledPath = resolve25(bundledDir, filename);
15314
15752
  const row = await readAgentTemplateRow({
15315
15753
  id: `agent-template:specialist:${filename}`,
15316
15754
  displayName: filename.replace(/\.md$/, ""),
@@ -15364,7 +15802,7 @@ async function readAgentTemplateRow(inp) {
15364
15802
  }
15365
15803
  if (!chosenPath) return null;
15366
15804
  try {
15367
- const st = await stat5(chosenPath);
15805
+ const st = await stat6(chosenPath);
15368
15806
  const dlPath = toDataRootRelPath(chosenPath);
15369
15807
  return {
15370
15808
  id: inp.id,
@@ -15389,8 +15827,8 @@ async function readAgentTemplateRow(inp) {
15389
15827
  }
15390
15828
  }
15391
15829
  function isWithin(target, root) {
15392
- const rel = relative3(root, target);
15393
- return !rel.startsWith("..") && !isAbsolute(rel);
15830
+ const rel = relative4(root, target);
15831
+ return !rel.startsWith("..") && !isAbsolute2(rel);
15394
15832
  }
15395
15833
  var sidebar_artefacts_default = app27;
15396
15834
 
@@ -15676,6 +16114,8 @@ function isSelectableModel(id) {
15676
16114
  }
15677
16115
 
15678
16116
  // server/routes/admin/session-reseat.ts
16117
+ var RESEAT_PERMISSION_MODES = /* @__PURE__ */ new Set(["default", "acceptEdits", "plan", "auto", "bypassPermissions"]);
16118
+ var RESEAT_EFFORT_LEVELS = /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh"]);
15679
16119
  function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticatedName) {
15680
16120
  const sessionId = mintId();
15681
16121
  const key = `session:${sessionId}`;
@@ -15689,6 +16129,8 @@ function resolveReseatPlan(body, mintId = randomUUID12, adminUserId, authenticat
15689
16129
  webchatChannel: webchatChannelDescriptor(key)
15690
16130
  };
15691
16131
  if (body.name) payload.name = body.name;
16132
+ if (body.permissionMode) payload.permissionMode = body.permissionMode;
16133
+ if (body.effort) payload.effort = body.effort;
15692
16134
  if (adminUserId) payload.adminUserId = adminUserId;
15693
16135
  if (authenticatedName) payload.authenticatedName = authenticatedName;
15694
16136
  return { sessionId, payload };
@@ -15706,6 +16148,10 @@ app33.post("/", requireAdminSession, async (c) => {
15706
16148
  const model = typeof body.model === "string" && isSelectableModel(body.model) ? body.model : null;
15707
16149
  if (fromSessionId === null) return c.json({ error: "fromSessionId must be a valid session id" }, 400);
15708
16150
  if (model === null) return c.json({ error: "model must be a selectable model" }, 400);
16151
+ const permissionMode = typeof body.permissionMode === "string" ? RESEAT_PERMISSION_MODES.has(body.permissionMode) ? body.permissionMode : null : void 0;
16152
+ const effort = typeof body.effort === "string" ? RESEAT_EFFORT_LEVELS.has(body.effort) ? body.effort : null : void 0;
16153
+ if (permissionMode === null) return c.json({ error: "permissionMode must be a composer-writable mode" }, 400);
16154
+ if (effort === null) return c.json({ error: "effort must be an accepted level" }, 400);
15709
16155
  if (reseatsInFlight.has(fromSessionId)) {
15710
16156
  return c.json({ error: "re-seat already in progress for this session" }, 409);
15711
16157
  }
@@ -15715,9 +16161,14 @@ app33.post("/", requireAdminSession, async (c) => {
15715
16161
  const adminUserId = cacheKey ? getUserIdForSession(cacheKey) : void 0;
15716
16162
  const accountId = cacheKey ? getAccountIdForSession(cacheKey) : void 0;
15717
16163
  const authenticatedName = accountId ? await resolveAuthenticatedName(accountId, adminUserId) : void 0;
15718
- const plan = resolveReseatPlan({ fromSessionId, model }, randomUUID12, adminUserId, authenticatedName);
16164
+ const plan = resolveReseatPlan(
16165
+ { fromSessionId, model, ...permissionMode ? { permissionMode } : {}, ...effort ? { effort } : {} },
16166
+ randomUUID12,
16167
+ adminUserId,
16168
+ authenticatedName
16169
+ );
15719
16170
  const reseatReqId = randomUUID12();
15720
- console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} new=${plan.sessionId}`);
16171
+ console.log(`[chat-reseat] op=request reseatReqId=${reseatReqId} from=${fromSessionId} to-model=${model} to-mode=${permissionMode ?? "none"} to-effort=${effort ?? "none"} new=${plan.sessionId}`);
15721
16172
  let res;
15722
16173
  try {
15723
16174
  res = await fetch(`${managerBase("session-reseat:wrapper")}/rc-spawn`, {
@@ -15769,7 +16220,7 @@ app33.post("/", requireAdminSession, async (c) => {
15769
16220
  var session_reseat_default = app33;
15770
16221
 
15771
16222
  // server/routes/admin/system-stats.ts
15772
- import { readFile as readFile5 } from "fs/promises";
16223
+ import { readFile as readFile6 } from "fs/promises";
15773
16224
  import { cpus, loadavg, totalmem, freemem } from "os";
15774
16225
  function parseCpuStat(blob) {
15775
16226
  const line = blob.split("\n", 1)[0] ?? "";
@@ -15813,11 +16264,11 @@ function parseMeminfo(blob) {
15813
16264
  }
15814
16265
  var SAMPLE_DELAY_MS = 100;
15815
16266
  async function sampleLinux() {
15816
- const a = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
16267
+ const a = parseCpuStat(await readFile6("/proc/stat", "utf-8"));
15817
16268
  await new Promise((r) => setTimeout(r, SAMPLE_DELAY_MS));
15818
- const b = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
15819
- const meminfo = parseMeminfo(await readFile5("/proc/meminfo", "utf-8"));
15820
- const loadavgRaw = (await readFile5("/proc/loadavg", "utf-8")).trim();
16269
+ const b = parseCpuStat(await readFile6("/proc/stat", "utf-8"));
16270
+ const meminfo = parseMeminfo(await readFile6("/proc/meminfo", "utf-8"));
16271
+ const loadavgRaw = (await readFile6("/proc/loadavg", "utf-8")).trim();
15821
16272
  const [l1, l5, l15] = loadavgRaw.split(/\s+/, 3).map(Number);
15822
16273
  if (![l1, l5, l15].every(Number.isFinite)) {
15823
16274
  throw new Error(`malformed /proc/loadavg: ${loadavgRaw}`);
@@ -15890,24 +16341,24 @@ app34.get("/", async (c) => {
15890
16341
  var system_stats_default = app34;
15891
16342
 
15892
16343
  // server/routes/admin/health.ts
15893
- import { existsSync as existsSync25, readFileSync as readFileSync24 } from "fs";
15894
- import { resolve as resolve24, join as join24 } from "path";
15895
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
16344
+ import { existsSync as existsSync25, readFileSync as readFileSync26 } from "fs";
16345
+ import { resolve as resolve26, join as join25 } from "path";
16346
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "..");
15896
16347
  var brandHostname = "maxy";
15897
- var brandJsonPath = join24(PLATFORM_ROOT6, "config", "brand.json");
16348
+ var brandJsonPath = join25(PLATFORM_ROOT6, "config", "brand.json");
15898
16349
  if (existsSync25(brandJsonPath)) {
15899
16350
  try {
15900
- const brand = JSON.parse(readFileSync24(brandJsonPath, "utf-8"));
16351
+ const brand = JSON.parse(readFileSync26(brandJsonPath, "utf-8"));
15901
16352
  if (brand.hostname) brandHostname = brand.hostname;
15902
16353
  } catch {
15903
16354
  }
15904
16355
  }
15905
- var VERSION_FILE = resolve24(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
16356
+ var VERSION_FILE = resolve26(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
15906
16357
  var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
15907
16358
  var PROBE_TIMEOUT_MS = 1e3;
15908
16359
  function readVersion() {
15909
16360
  if (!existsSync25(VERSION_FILE)) return "unknown";
15910
- return readFileSync24(VERSION_FILE, "utf-8").trim() || "unknown";
16361
+ return readFileSync26(VERSION_FILE, "utf-8").trim() || "unknown";
15911
16362
  }
15912
16363
  async function probeConversationDb() {
15913
16364
  let session;
@@ -15959,7 +16410,7 @@ var health_default2 = app35;
15959
16410
 
15960
16411
  // server/routes/admin/linkedin-ingest.ts
15961
16412
  import { randomUUID as randomUUID13 } from "crypto";
15962
- var TAG27 = "[linkedin-ingest-route]";
16413
+ var TAG30 = "[linkedin-ingest-route]";
15963
16414
  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}$/;
15964
16415
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
15965
16416
  var LINKEDIN_URL = /^https:\/\/www\.linkedin\.com\//;
@@ -16063,29 +16514,29 @@ app36.post("/", requireAdminSession, async (c) => {
16063
16514
  try {
16064
16515
  body = await c.req.json();
16065
16516
  } catch {
16066
- console.error(TAG27 + " rejected status=400 reason=schema:body-not-json");
16517
+ console.error(TAG30 + " rejected status=400 reason=schema:body-not-json");
16067
16518
  return c.json({ ok: false, error: "schema", reason: "body-not-json" }, 400);
16068
16519
  }
16069
16520
  const v = validate(body);
16070
16521
  if (!v.ok) {
16071
- console.error(TAG27 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
16522
+ console.error(TAG30 + " rejected status=" + v.status + " reason=" + v.reason + " missing=" + v.missing.join(","));
16072
16523
  return c.json({ ok: false, error: v.error, reason: v.reason, missing: v.missing }, v.status);
16073
16524
  }
16074
16525
  const envelope = v.envelope;
16075
16526
  const cacheKey = c.var.cacheKey ?? "";
16076
16527
  const senderId = getAccountIdForSession(cacheKey) ?? "";
16077
16528
  if (!senderId) {
16078
- console.error(TAG27 + " rejected status=500 reason=admin-account-not-resolved");
16529
+ console.error(TAG30 + " rejected status=500 reason=admin-account-not-resolved");
16079
16530
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
16080
16531
  }
16081
16532
  const payloadBytes = JSON.stringify(envelope).length;
16082
16533
  console.log(
16083
- TAG27 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
16534
+ TAG30 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
16084
16535
  );
16085
16536
  const initialMessage = buildInitialMessage(envelope);
16086
16537
  const spawnStart = Date.now();
16087
16538
  const sessionId = randomUUID13();
16088
- console.log(TAG27 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
16539
+ console.log(TAG30 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
16089
16540
  const spawned = await managerRcSpawn({
16090
16541
  sessionId,
16091
16542
  initialMessage,
@@ -16094,12 +16545,12 @@ app36.post("/", requireAdminSession, async (c) => {
16094
16545
  });
16095
16546
  if ("error" in spawned) {
16096
16547
  console.error(
16097
- TAG27 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
16548
+ TAG30 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
16098
16549
  );
16099
16550
  return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
16100
16551
  }
16101
16552
  console.log(
16102
- TAG27 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
16553
+ TAG30 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
16103
16554
  );
16104
16555
  return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
16105
16556
  });
@@ -16107,7 +16558,7 @@ var linkedin_ingest_default = app36;
16107
16558
 
16108
16559
  // server/routes/admin/post-turn-context.ts
16109
16560
  import neo4j3 from "neo4j-driver";
16110
- var TAG28 = "[post-turn-context]";
16561
+ var TAG31 = "[post-turn-context]";
16111
16562
  var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
16112
16563
  "embedding",
16113
16564
  "passwordHash",
@@ -16149,7 +16600,7 @@ var app37 = new Hono();
16149
16600
  app37.get("/", async (c) => {
16150
16601
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16151
16602
  if (!isLoopbackAddr2(remoteAddr)) {
16152
- console.error(`${TAG28} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16603
+ console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16153
16604
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
16154
16605
  }
16155
16606
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16158,7 +16609,7 @@ app37.get("/", async (c) => {
16158
16609
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16159
16610
  const offender = tokens.find((t) => !isLoopbackAddr2(t));
16160
16611
  if (offender !== void 0) {
16161
- console.error(`${TAG28} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16612
+ console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16162
16613
  return c.json({ error: "post-turn-context-loopback-only" }, 403);
16163
16614
  }
16164
16615
  }
@@ -16190,7 +16641,7 @@ app37.get("/", async (c) => {
16190
16641
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
16191
16642
  const total = Date.now() - started;
16192
16643
  console.log(
16193
- `${TAG28} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
16644
+ `${TAG31} sessionId=${sessionId} accountId=${accountId} writes=${writes.length} ms=${total}`
16194
16645
  );
16195
16646
  return c.json({
16196
16647
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -16199,7 +16650,7 @@ app37.get("/", async (c) => {
16199
16650
  const elapsed = Date.now() - started;
16200
16651
  const message = err instanceof Error ? err.message : String(err);
16201
16652
  console.error(
16202
- `${TAG28} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
16653
+ `${TAG31} neo4j-unreachable sessionId=${sessionId} ms=${elapsed} err="${message}"`
16203
16654
  );
16204
16655
  return c.json({ error: `post-turn-context unavailable: ${message}` }, 503);
16205
16656
  } finally {
@@ -16210,7 +16661,7 @@ var post_turn_context_default = app37;
16210
16661
 
16211
16662
  // server/routes/admin/public-session-context.ts
16212
16663
  import neo4j4 from "neo4j-driver";
16213
- var TAG29 = "[public-session-context]";
16664
+ var TAG32 = "[public-session-context]";
16214
16665
  var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
16215
16666
  "embedding",
16216
16667
  "passwordHash",
@@ -16252,7 +16703,7 @@ var app38 = new Hono();
16252
16703
  app38.get("/", async (c) => {
16253
16704
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16254
16705
  if (!isLoopbackAddr3(remoteAddr)) {
16255
- console.error(`${TAG29} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16706
+ console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16256
16707
  return c.json({ error: "public-session-context-loopback-only" }, 403);
16257
16708
  }
16258
16709
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16261,7 +16712,7 @@ app38.get("/", async (c) => {
16261
16712
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16262
16713
  const offender = tokens.find((t) => !isLoopbackAddr3(t));
16263
16714
  if (offender !== void 0) {
16264
- console.error(`${TAG29} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16715
+ console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16265
16716
  return c.json({ error: "public-session-context-loopback-only" }, 403);
16266
16717
  }
16267
16718
  }
@@ -16292,7 +16743,7 @@ app38.get("/", async (c) => {
16292
16743
  writes.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
16293
16744
  const total = Date.now() - started;
16294
16745
  console.log(
16295
- `${TAG29} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
16746
+ `${TAG32} sliceToken=${sliceToken.slice(0, 8)} writes=${writes.length} ms=${total}`
16296
16747
  );
16297
16748
  return c.json({
16298
16749
  writes: writes.map(({ elementId, labels, properties }) => ({ elementId, labels, properties }))
@@ -16301,7 +16752,7 @@ app38.get("/", async (c) => {
16301
16752
  const elapsed = Date.now() - started;
16302
16753
  const message = err instanceof Error ? err.message : String(err);
16303
16754
  console.error(
16304
- `${TAG29} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
16755
+ `${TAG32} neo4j-unreachable sliceToken=${sliceToken.slice(0, 8)} ms=${elapsed} err="${message}"`
16305
16756
  );
16306
16757
  return c.json({ error: `public-session-context unavailable: ${message}` }, 503);
16307
16758
  } finally {
@@ -16311,7 +16762,7 @@ app38.get("/", async (c) => {
16311
16762
  var public_session_context_default = app38;
16312
16763
 
16313
16764
  // server/routes/admin/public-session-exit.ts
16314
- var TAG30 = "[public-session-exit-route]";
16765
+ var TAG33 = "[public-session-exit-route]";
16315
16766
  function isLoopbackAddr4(addr) {
16316
16767
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
16317
16768
  }
@@ -16319,7 +16770,7 @@ var app39 = new Hono();
16319
16770
  app39.post("/", async (c) => {
16320
16771
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16321
16772
  if (!isLoopbackAddr4(remoteAddr)) {
16322
- console.error(`${TAG30} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16773
+ console.error(`${TAG33} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16323
16774
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
16324
16775
  }
16325
16776
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16328,7 +16779,7 @@ app39.post("/", async (c) => {
16328
16779
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16329
16780
  const offender = tokens.find((t) => !isLoopbackAddr4(t));
16330
16781
  if (offender !== void 0) {
16331
- console.error(`${TAG30} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16782
+ console.error(`${TAG33} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16332
16783
  return c.json({ error: "public-session-exit-loopback-only" }, 403);
16333
16784
  }
16334
16785
  }
@@ -16346,7 +16797,7 @@ app39.post("/", async (c) => {
16346
16797
  var public_session_exit_default = app39;
16347
16798
 
16348
16799
  // server/routes/admin/access-session-evict.ts
16349
- var TAG31 = "[access-session-evict]";
16800
+ var TAG34 = "[access-session-evict]";
16350
16801
  function isLoopbackAddr5(addr) {
16351
16802
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
16352
16803
  }
@@ -16354,7 +16805,7 @@ var app40 = new Hono();
16354
16805
  app40.post("/", async (c) => {
16355
16806
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16356
16807
  if (!isLoopbackAddr5(remoteAddr)) {
16357
- console.error(`${TAG31} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16808
+ console.error(`${TAG34} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16358
16809
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
16359
16810
  }
16360
16811
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16363,7 +16814,7 @@ app40.post("/", async (c) => {
16363
16814
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16364
16815
  const offender = tokens.find((t) => !isLoopbackAddr5(t));
16365
16816
  if (offender !== void 0) {
16366
- console.error(`${TAG31} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16817
+ console.error(`${TAG34} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16367
16818
  return c.json({ error: "access-session-evict-loopback-only" }, 403);
16368
16819
  }
16369
16820
  }
@@ -16376,13 +16827,13 @@ app40.post("/", async (c) => {
16376
16827
  const grantId = typeof body.grantId === "string" ? body.grantId.trim() : "";
16377
16828
  if (!grantId) return c.json({ error: "grantId required" }, 400);
16378
16829
  const dropped = evictAccessSessionsByGrant(grantId);
16379
- console.log(`${TAG31} grantId=${grantId} dropped=${dropped}`);
16830
+ console.log(`${TAG34} grantId=${grantId} dropped=${dropped}`);
16380
16831
  return c.json({ ok: true, dropped });
16381
16832
  });
16382
16833
  var access_session_evict_default = app40;
16383
16834
 
16384
16835
  // server/routes/admin/enrol-person.ts
16385
- var TAG32 = "[enrol]";
16836
+ var TAG35 = "[enrol]";
16386
16837
  function isLoopbackAddr6(addr) {
16387
16838
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
16388
16839
  }
@@ -16391,7 +16842,7 @@ var app41 = new Hono();
16391
16842
  app41.post("/", async (c) => {
16392
16843
  const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
16393
16844
  if (!isLoopbackAddr6(remoteAddr)) {
16394
- console.error(`${TAG32} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16845
+ console.error(`${TAG35} reject reason=non-loopback remoteAddr=${remoteAddr}`);
16395
16846
  return c.json({ error: "enrol-person-loopback-only" }, 403);
16396
16847
  }
16397
16848
  const xffHeader = c.env?.incoming?.headers?.["x-forwarded-for"];
@@ -16400,7 +16851,7 @@ app41.post("/", async (c) => {
16400
16851
  const tokens = xffRaw.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
16401
16852
  const offender = tokens.find((t) => !isLoopbackAddr6(t));
16402
16853
  if (offender !== void 0) {
16403
- console.error(`${TAG32} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16854
+ console.error(`${TAG35} reject reason=xff-non-loopback xff=${JSON.stringify(xffRaw)}`);
16404
16855
  return c.json({ error: "enrol-person-loopback-only" }, 403);
16405
16856
  }
16406
16857
  }
@@ -16425,7 +16876,7 @@ app41.post("/", async (c) => {
16425
16876
  }
16426
16877
  const accountId = process.env.ACCOUNT_ID ?? "";
16427
16878
  if (!accountId) {
16428
- console.error(`${TAG32} op=person result=no-account-id`);
16879
+ console.error(`${TAG35} op=person result=no-account-id`);
16429
16880
  return c.json({ error: "no-account-id" }, 500);
16430
16881
  }
16431
16882
  let personId;
@@ -16433,7 +16884,7 @@ app41.post("/", async (c) => {
16433
16884
  personId = await enrolPerson({ accountId, phone, email, agentSlug });
16434
16885
  } catch (err) {
16435
16886
  console.error(
16436
- `${TAG32} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
16887
+ `${TAG35} op=person result=write-failed agentSlug=${agentSlug} contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
16437
16888
  );
16438
16889
  return c.json({ error: "enrol-failed" }, 500);
16439
16890
  }
@@ -16443,11 +16894,11 @@ app41.post("/", async (c) => {
16443
16894
  if (g) grant = { grantId: g.grantId, status: g.status, contactValue: g.contactValue };
16444
16895
  } catch (err) {
16445
16896
  console.error(
16446
- `${TAG32} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
16897
+ `${TAG35} op=person result=grant-lookup-failed contact=${maskContact(email)} err="${err instanceof Error ? err.message : String(err)}"`
16447
16898
  );
16448
16899
  }
16449
16900
  console.log(
16450
- `${TAG32} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
16901
+ `${TAG35} op=person personId=${personId} account=${accountId} agentSlug=${agentSlug} contact=${maskContact(email)}`
16451
16902
  );
16452
16903
  return c.json({ personId, grant }, 200);
16453
16904
  });
@@ -16523,7 +16974,7 @@ app43.route("/enrol-person", enrol_person_default);
16523
16974
  var admin_default = app43;
16524
16975
 
16525
16976
  // server/routes/access/verify-token.ts
16526
- var TAG33 = "[access-verify]";
16977
+ var TAG36 = "[access-verify]";
16527
16978
  var MINT_TAG = "[access-session-mint]";
16528
16979
  var COOKIE_NAME = "__access_session";
16529
16980
  var app44 = new Hono();
@@ -16542,39 +16993,39 @@ app44.post("/", async (c) => {
16542
16993
  }
16543
16994
  const rateMsg = checkAccessRateLimit(ip, agentSlug);
16544
16995
  if (rateMsg) {
16545
- console.error(`${TAG33} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
16996
+ console.error(`${TAG36} grantId=- agentSlug=${agentSlug} result=rate-limited ip=${ip}`);
16546
16997
  return c.json({ error: rateMsg }, 429);
16547
16998
  }
16548
16999
  const grant = await findGrantByMagicToken(token);
16549
17000
  if (!grant) {
16550
17001
  recordAccessFailedAttempt(ip, agentSlug);
16551
- console.error(`${TAG33} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
17002
+ console.error(`${TAG36} grantId=- agentSlug=${agentSlug} result=notfound ip=${ip}`);
16552
17003
  return c.json({ error: "invalid-or-expired-link" }, 401);
16553
17004
  }
16554
17005
  if (grant.agentSlug !== agentSlug) {
16555
17006
  recordAccessFailedAttempt(ip, agentSlug);
16556
17007
  console.error(
16557
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
17008
+ `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=agent-mismatch grantAgent=${grant.agentSlug} ip=${ip}`
16558
17009
  );
16559
17010
  return c.json({ error: "invalid-or-expired-link" }, 401);
16560
17011
  }
16561
17012
  if (grant.status === "expired" || grant.status === "revoked") {
16562
17013
  recordAccessFailedAttempt(ip, agentSlug);
16563
17014
  console.error(
16564
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
17015
+ `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired status=${grant.status} ip=${ip}`
16565
17016
  );
16566
17017
  return c.json({ error: "access-no-longer-valid" }, 401);
16567
17018
  }
16568
17019
  if (grant.expiresAt !== null && grant.expiresAt < Date.now()) {
16569
17020
  recordAccessFailedAttempt(ip, agentSlug);
16570
17021
  console.error(
16571
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
17022
+ `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=expired reason=expiresAt-past ip=${ip}`
16572
17023
  );
16573
17024
  return c.json({ error: "access-no-longer-valid" }, 401);
16574
17025
  }
16575
17026
  if (!grant.sliceToken) {
16576
17027
  console.error(
16577
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
17028
+ `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=no-slice-token reason=schema-violation`
16578
17029
  );
16579
17030
  return c.json({ error: "grant-misconfigured" }, 500);
16580
17031
  }
@@ -16590,12 +17041,12 @@ app44.post("/", async (c) => {
16590
17041
  await consumeMagicTokenAndActivate(grant.grantId);
16591
17042
  } catch (err) {
16592
17043
  console.error(
16593
- `${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
17044
+ `${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=consume-failed err="${err instanceof Error ? err.message : String(err)}"`
16594
17045
  );
16595
17046
  return c.json({ error: "verification-failed" }, 500);
16596
17047
  }
16597
17048
  clearAccessRateLimit(ip, agentSlug);
16598
- console.log(`${TAG33} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
17049
+ console.log(`${TAG36} grantId=${grant.grantId} agentSlug=${agentSlug} result=ok ip=${ip}`);
16599
17050
  console.log(
16600
17051
  `${MINT_TAG} grantId=${grant.grantId} sliceToken=${grant.sliceToken.slice(0, 8)} agentSlug=${agentSlug} personId=${grant.personId ?? "none"}`
16601
17052
  );
@@ -16613,9 +17064,9 @@ var verify_token_default = app44;
16613
17064
 
16614
17065
  // app/lib/access-email.ts
16615
17066
  import { spawn as spawn2 } from "child_process";
16616
- import { resolve as resolve25 } from "path";
16617
- var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
16618
- var SEND_SCRIPT = resolve25(
17067
+ import { resolve as resolve27 } from "path";
17068
+ var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve27(process.cwd(), "..");
17069
+ var SEND_SCRIPT = resolve27(
16619
17070
  PLATFORM_ROOT7,
16620
17071
  "plugins",
16621
17072
  "email",
@@ -16671,7 +17122,7 @@ async function sendMagicLinkEmail(payload) {
16671
17122
  }
16672
17123
 
16673
17124
  // server/routes/access/request-magic-link.ts
16674
- var TAG34 = "[access-request-link]";
17125
+ var TAG37 = "[access-request-link]";
16675
17126
  var app45 = new Hono();
16676
17127
  var VISITOR_MESSAGE = "If that email is on the invite list, a fresh link is on the way.";
16677
17128
  app45.post("/", async (c) => {
@@ -16688,18 +17139,18 @@ app45.post("/", async (c) => {
16688
17139
  }
16689
17140
  const rateMsg = checkRequestLinkRateLimit(contactValue);
16690
17141
  if (rateMsg) {
16691
- console.error(`${TAG34} contactValue=${maskContact(contactValue)} result=rate-limited`);
17142
+ console.error(`${TAG37} contactValue=${maskContact(contactValue)} result=rate-limited`);
16692
17143
  return c.json({ error: rateMsg }, 429);
16693
17144
  }
16694
17145
  recordRequestLinkAttempt(contactValue);
16695
17146
  const accountId = process.env.ACCOUNT_ID ?? "";
16696
17147
  if (!accountId) {
16697
- console.error(`${TAG34} contactValue=${maskContact(contactValue)} result=no-account-id`);
17148
+ console.error(`${TAG37} contactValue=${maskContact(contactValue)} result=no-account-id`);
16698
17149
  return c.json({ message: VISITOR_MESSAGE }, 200);
16699
17150
  }
16700
17151
  const grant = await findActiveGrantByContact(contactValue, agentSlug, accountId);
16701
17152
  if (!grant) {
16702
- console.log(`${TAG34} contactValue=${maskContact(contactValue)} result=notfound`);
17153
+ console.log(`${TAG37} contactValue=${maskContact(contactValue)} result=notfound`);
16703
17154
  return c.json({ message: VISITOR_MESSAGE }, 200);
16704
17155
  }
16705
17156
  let token;
@@ -16707,7 +17158,7 @@ app45.post("/", async (c) => {
16707
17158
  token = await generateNewMagicToken(grant.grantId);
16708
17159
  } catch (err) {
16709
17160
  console.error(
16710
- `${TAG34} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
17161
+ `${TAG37} contactValue=${maskContact(contactValue)} result=mint-failed err="${err instanceof Error ? err.message : String(err)}"`
16711
17162
  );
16712
17163
  return c.json({ message: VISITOR_MESSAGE }, 200);
16713
17164
  }
@@ -16739,12 +17190,12 @@ app45.post("/", async (c) => {
16739
17190
  });
16740
17191
  if (!sendResult.ok) {
16741
17192
  console.error(
16742
- `${TAG34} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
17193
+ `${TAG37} contactValue=${maskContact(contactValue)} result=send-failed err="${sendResult.error}"`
16743
17194
  );
16744
17195
  return c.json({ message: VISITOR_MESSAGE }, 200);
16745
17196
  }
16746
17197
  console.log(
16747
- `${TAG34} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
17198
+ `${TAG37} contactValue=${maskContact(contactValue)} result=ok messageId=${sendResult.messageId}`
16748
17199
  );
16749
17200
  return c.json({ message: VISITOR_MESSAGE }, 200);
16750
17201
  });
@@ -16757,8 +17208,8 @@ app46.route("/request-magic-link", request_magic_link_default);
16757
17208
  var access_default = app46;
16758
17209
 
16759
17210
  // server/routes/sites.ts
16760
- import { existsSync as existsSync26, readFileSync as readFileSync25, realpathSync as realpathSync5, statSync as statSync11 } from "fs";
16761
- import { resolve as resolve26 } from "path";
17211
+ import { existsSync as existsSync26, readFileSync as readFileSync27, realpathSync as realpathSync7, statSync as statSync11 } from "fs";
17212
+ import { resolve as resolve28 } from "path";
16762
17213
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
16763
17214
  var MIME = {
16764
17215
  ".html": "text/html; charset=utf-8",
@@ -16815,19 +17266,19 @@ app47.get("/:rel{.*}", (c) => {
16815
17266
  }
16816
17267
  segments.push(seg);
16817
17268
  }
16818
- const rootDir = resolve26(account.accountDir, "sites");
16819
- let filePath = segments.length === 0 ? rootDir : resolve26(rootDir, ...segments);
17269
+ const rootDir = resolve28(account.accountDir, "sites");
17270
+ let filePath = segments.length === 0 ? rootDir : resolve28(rootDir, ...segments);
16820
17271
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
16821
17272
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
16822
17273
  return c.text("Forbidden", 403);
16823
17274
  }
16824
- let stat7;
17275
+ let stat8;
16825
17276
  try {
16826
- stat7 = existsSync26(filePath) ? statSync11(filePath) : null;
17277
+ stat8 = existsSync26(filePath) ? statSync11(filePath) : null;
16827
17278
  } catch {
16828
- stat7 = null;
17279
+ stat8 = null;
16829
17280
  }
16830
- if (stat7?.isDirectory() && !reqPath.endsWith("/")) {
17281
+ if (stat8?.isDirectory() && !reqPath.endsWith("/")) {
16831
17282
  const search = new URL(c.req.url).search;
16832
17283
  const target = `${reqPath}/${search}`;
16833
17284
  console.log(
@@ -16835,8 +17286,8 @@ app47.get("/:rel{.*}", (c) => {
16835
17286
  );
16836
17287
  return c.redirect(target, 301);
16837
17288
  }
16838
- if (stat7?.isDirectory()) {
16839
- filePath = resolve26(filePath, "index.html");
17289
+ if (stat8?.isDirectory()) {
17290
+ filePath = resolve28(filePath, "index.html");
16840
17291
  }
16841
17292
  if (!filePath.startsWith(rootDir + "/")) {
16842
17293
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
@@ -16849,8 +17300,8 @@ app47.get("/:rel{.*}", (c) => {
16849
17300
  let realPath;
16850
17301
  let realRoot;
16851
17302
  try {
16852
- realPath = realpathSync5(filePath);
16853
- realRoot = realpathSync5(rootDir);
17303
+ realPath = realpathSync7(filePath);
17304
+ realRoot = realpathSync7(rootDir);
16854
17305
  } catch {
16855
17306
  console.error(`[sites] not-found path=${reqPath} status=404`);
16856
17307
  return c.text("Not found", 404);
@@ -16861,7 +17312,7 @@ app47.get("/:rel{.*}", (c) => {
16861
17312
  }
16862
17313
  let body;
16863
17314
  try {
16864
- body = readFileSync25(realPath);
17315
+ body = readFileSync27(realPath);
16865
17316
  } catch (err) {
16866
17317
  const code = err?.code;
16867
17318
  if (code === "EISDIR") {
@@ -16897,7 +17348,7 @@ var sites_default = app47;
16897
17348
 
16898
17349
  // app/lib/visitor-token.ts
16899
17350
  import { createHmac, randomBytes, timingSafeEqual } from "crypto";
16900
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync26, writeFileSync as writeFileSync11 } from "fs";
17351
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync28, writeFileSync as writeFileSync11 } from "fs";
16901
17352
  import { dirname as dirname7 } from "path";
16902
17353
  var TOKEN_PREFIX = "v1.";
16903
17354
  var SECRET_BYTES = 32;
@@ -16906,7 +17357,7 @@ var cachedSecret = null;
16906
17357
  function getSecret() {
16907
17358
  if (cachedSecret) return cachedSecret;
16908
17359
  try {
16909
- const hex2 = readFileSync26(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
17360
+ const hex2 = readFileSync28(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
16910
17361
  if (hex2.length === SECRET_BYTES * 2) {
16911
17362
  cachedSecret = Buffer.from(hex2, "hex");
16912
17363
  return cachedSecret;
@@ -16920,7 +17371,7 @@ function getSecret() {
16920
17371
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
16921
17372
  } catch {
16922
17373
  }
16923
- const hex = readFileSync26(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
17374
+ const hex = readFileSync28(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
16924
17375
  cachedSecret = Buffer.from(hex, "hex");
16925
17376
  return cachedSecret;
16926
17377
  }
@@ -16973,19 +17424,19 @@ var VISITOR_COOKIE_NAME = "mxy_v";
16973
17424
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
16974
17425
 
16975
17426
  // app/lib/brand-config.ts
16976
- import { existsSync as existsSync27, readFileSync as readFileSync27 } from "fs";
16977
- import { join as join25 } from "path";
17427
+ import { existsSync as existsSync27, readFileSync as readFileSync29 } from "fs";
17428
+ import { join as join26 } from "path";
16978
17429
  var cached2 = null;
16979
17430
  var cachedAttempted = false;
16980
17431
  function readBrandConfig() {
16981
17432
  if (cachedAttempted) return cached2;
16982
17433
  cachedAttempted = true;
16983
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT;
16984
- if (!platformRoot2) return null;
16985
- const brandPath = join25(platformRoot2, "config", "brand.json");
17434
+ const platformRoot4 = process.env.MAXY_PLATFORM_ROOT;
17435
+ if (!platformRoot4) return null;
17436
+ const brandPath = join26(platformRoot4, "config", "brand.json");
16986
17437
  if (!existsSync27(brandPath)) return null;
16987
17438
  try {
16988
- cached2 = JSON.parse(readFileSync27(brandPath, "utf-8"));
17439
+ cached2 = JSON.parse(readFileSync29(brandPath, "utf-8"));
16989
17440
  return cached2;
16990
17441
  } catch {
16991
17442
  return null;
@@ -17473,19 +17924,19 @@ async function writeEvent(opts) {
17473
17924
  var visitor_event_default = app50;
17474
17925
 
17475
17926
  // server/routes/session.ts
17476
- import { resolve as resolve27 } from "path";
17927
+ import { resolve as resolve29 } from "path";
17477
17928
  import { existsSync as existsSync28, writeFileSync as writeFileSync12, mkdirSync as mkdirSync7 } from "fs";
17478
17929
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17479
17930
  function writeBrandingCache(accountId, agentSlug, branding) {
17480
17931
  try {
17481
- const cacheDir = resolve27(MAXY_DIR, "branding-cache", accountId);
17932
+ const cacheDir = resolve29(MAXY_DIR, "branding-cache", accountId);
17482
17933
  mkdirSync7(cacheDir, { recursive: true });
17483
- writeFileSync12(resolve27(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
17934
+ writeFileSync12(resolve29(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
17484
17935
  } catch (err) {
17485
17936
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
17486
17937
  }
17487
17938
  }
17488
- function parseVisitorCookie(cookieHeader) {
17939
+ function parseVisitorCookie2(cookieHeader) {
17489
17940
  if (!cookieHeader) return null;
17490
17941
  const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
17491
17942
  if (!match) return null;
@@ -17534,7 +17985,7 @@ app51.post("/", async (c) => {
17534
17985
  }
17535
17986
  const account = resolveAccount();
17536
17987
  const cookieHeader = c.req.header("cookie");
17537
- const visitorId = parseVisitorCookie(cookieHeader);
17988
+ const visitorId = parseVisitorCookie2(cookieHeader);
17538
17989
  const groupSlug = body.group_slug && typeof body.group_slug === "string" ? body.group_slug : null;
17539
17990
  let groupInfo = null;
17540
17991
  if (groupSlug) {
@@ -17568,8 +18019,8 @@ app51.post("/", async (c) => {
17568
18019
  }
17569
18020
  let agentConfig = null;
17570
18021
  if (account) {
17571
- const agentDir = resolve27(account.accountDir, "agents", agentSlug);
17572
- if (!existsSync28(agentDir) || !existsSync28(resolve27(agentDir, "config.json"))) {
18022
+ const agentDir = resolve29(account.accountDir, "agents", agentSlug);
18023
+ if (!existsSync28(agentDir) || !existsSync28(resolve29(agentDir, "config.json"))) {
17573
18024
  return c.json({ error: "Agent not found" }, 404);
17574
18025
  }
17575
18026
  agentConfig = resolveAgentConfig(account.accountDir, agentSlug);
@@ -17849,7 +18300,7 @@ function startGraphHealthTimer() {
17849
18300
 
17850
18301
  // app/lib/file-watcher.ts
17851
18302
  import * as fsp2 from "fs/promises";
17852
- import { resolve as resolve28, sep as sep8 } from "path";
18303
+ import { resolve as resolve30, sep as sep8 } from "path";
17853
18304
  var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17854
18305
  var DEFAULT_COALESCE_MS = 500;
17855
18306
  var ROOTS = ["accounts"];
@@ -17868,7 +18319,7 @@ async function startFileWatcher(opts = {}) {
17868
18319
  const dropFn = opts.drop ?? dropFileIndex;
17869
18320
  for (const r of ROOTS) {
17870
18321
  try {
17871
- await fsp2.mkdir(resolve28(dataRoot, r), { recursive: true });
18322
+ await fsp2.mkdir(resolve30(dataRoot, r), { recursive: true });
17872
18323
  } catch (err) {
17873
18324
  console.error(
17874
18325
  `[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
@@ -17889,7 +18340,7 @@ async function startFileWatcher(opts = {}) {
17889
18340
  timers.set(relativePath, t);
17890
18341
  }
17891
18342
  async function runHook(relativePath, accountId) {
17892
- const absolute = resolve28(dataRoot, relativePath);
18343
+ const absolute = resolve30(dataRoot, relativePath);
17893
18344
  let exists = false;
17894
18345
  try {
17895
18346
  const st = await fsp2.stat(absolute);
@@ -17913,7 +18364,7 @@ async function startFileWatcher(opts = {}) {
17913
18364
  }
17914
18365
  }
17915
18366
  async function watchRoot(rootName) {
17916
- const absRoot = resolve28(dataRoot, rootName);
18367
+ const absRoot = resolve30(dataRoot, rootName);
17917
18368
  try {
17918
18369
  const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
17919
18370
  for await (const event of iter) {
@@ -17952,7 +18403,7 @@ async function startFileWatcher(opts = {}) {
17952
18403
 
17953
18404
  // app/lib/migrate-uploads.ts
17954
18405
  import { mkdir as mkdir5, readdir as readdir5, rename as rename2, rm as rm3 } from "fs/promises";
17955
- import { dirname as dirname8, relative as relative4, resolve as resolve29 } from "path";
18406
+ import { dirname as dirname8, relative as relative5, resolve as resolve31 } from "path";
17956
18407
  var ACCOUNT_UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17957
18408
  async function walkFiles(dir) {
17958
18409
  const out = [];
@@ -17963,7 +18414,7 @@ async function walkFiles(dir) {
17963
18414
  return out;
17964
18415
  }
17965
18416
  for (const e of entries) {
17966
- const abs = resolve29(dir, e.name);
18417
+ const abs = resolve31(dir, e.name);
17967
18418
  if (e.isDirectory()) {
17968
18419
  out.push(...await walkFiles(abs));
17969
18420
  } else if (e.isFile()) {
@@ -17982,13 +18433,13 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
17982
18433
  const files = await walkFiles(srcDir);
17983
18434
  let moved = 0;
17984
18435
  for (const oldAbs of files) {
17985
- const suffix = relative4(srcDir, oldAbs);
17986
- const newAbs = resolve29(destDir, suffix);
18436
+ const suffix = relative5(srcDir, oldAbs);
18437
+ const newAbs = resolve31(destDir, suffix);
17987
18438
  await mkdir5(dirname8(newAbs), { recursive: true });
17988
18439
  await rename2(oldAbs, newAbs);
17989
18440
  moved++;
17990
- const oldRel = relative4(dataRoot, oldAbs);
17991
- const newRel = relative4(dataRoot, newAbs);
18441
+ const oldRel = relative5(dataRoot, oldAbs);
18442
+ const newRel = relative5(dataRoot, newAbs);
17992
18443
  let linked = false;
17993
18444
  if (accountId && session) {
17994
18445
  const fa = await session.run(
@@ -18010,7 +18461,7 @@ async function relocateTree(srcDir, destDir, dataRoot, accountId, session) {
18010
18461
  }
18011
18462
  async function migrateUploads(opts = {}) {
18012
18463
  const dataRoot = opts.dataRoot ?? DATA_ROOT;
18013
- const oldRoot = resolve29(dataRoot, "uploads");
18464
+ const oldRoot = resolve31(dataRoot, "uploads");
18014
18465
  let topEntries;
18015
18466
  try {
18016
18467
  topEntries = await readdir5(oldRoot, { withFileTypes: true });
@@ -18031,8 +18482,8 @@ async function migrateUploads(opts = {}) {
18031
18482
  const name = entry.name;
18032
18483
  if (ACCOUNT_UUID_RE3.test(name)) {
18033
18484
  moved += await relocateTree(
18034
- resolve29(oldRoot, name),
18035
- resolve29(dataRoot, "accounts", name, "uploads"),
18485
+ resolve31(oldRoot, name),
18486
+ resolve31(dataRoot, "accounts", name, "uploads"),
18036
18487
  dataRoot,
18037
18488
  name,
18038
18489
  session
@@ -18044,8 +18495,8 @@ async function migrateUploads(opts = {}) {
18044
18495
  continue;
18045
18496
  }
18046
18497
  moved += await relocateTree(
18047
- resolve29(oldRoot, "public"),
18048
- resolve29(dataRoot, "accounts", installAccountId, "uploads", "public"),
18498
+ resolve31(oldRoot, "public"),
18499
+ resolve31(dataRoot, "accounts", installAccountId, "uploads", "public"),
18049
18500
  dataRoot,
18050
18501
  null,
18051
18502
  // public uploads carry no graph nodes
@@ -18074,9 +18525,9 @@ async function migrateUploads(opts = {}) {
18074
18525
  }
18075
18526
 
18076
18527
  // app/lib/migrate-admin-webchat-sidecars.ts
18077
- import { readdir as readdir6, readFile as readFile6, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
18528
+ import { readdir as readdir6, readFile as readFile7, rename as rename3, writeFile as writeFile4, unlink as unlink3 } from "fs/promises";
18078
18529
  import { existsSync as existsSync29 } from "fs";
18079
- import { join as join26 } from "path";
18530
+ import { join as join27 } from "path";
18080
18531
  var ADMIN_ROLE2 = "admin";
18081
18532
  var WEBCHAT_CHANNEL2 = "webchat";
18082
18533
  var SESSION_META_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
@@ -18087,7 +18538,7 @@ var defaultResolveName = async (accountId, userId) => {
18087
18538
  async function readRaw(path2) {
18088
18539
  let raw;
18089
18540
  try {
18090
- raw = await readFile6(path2, "utf8");
18541
+ raw = await readFile7(path2, "utf8");
18091
18542
  } catch {
18092
18543
  return null;
18093
18544
  }
@@ -18121,7 +18572,7 @@ async function collectLiveSidecars(projectsRoot) {
18121
18572
  }
18122
18573
  for (const slug of slugs) {
18123
18574
  if (!slug.isDirectory()) continue;
18124
- const slugDir = join26(projectsRoot, slug.name);
18575
+ const slugDir = join27(projectsRoot, slug.name);
18125
18576
  let entries;
18126
18577
  try {
18127
18578
  entries = await readdir6(slugDir, { withFileTypes: true });
@@ -18130,9 +18581,9 @@ async function collectLiveSidecars(projectsRoot) {
18130
18581
  }
18131
18582
  for (const entry of entries) {
18132
18583
  if (entry.isFile() && SESSION_META_RE.test(entry.name)) {
18133
- out.push(join26(slugDir, entry.name));
18584
+ out.push(join27(slugDir, entry.name));
18134
18585
  } else if (entry.isDirectory() && entry.name === "subagents") {
18135
- const subDir = join26(slugDir, entry.name);
18586
+ const subDir = join27(slugDir, entry.name);
18136
18587
  let subs;
18137
18588
  try {
18138
18589
  subs = await readdir6(subDir, { withFileTypes: true });
@@ -18140,7 +18591,7 @@ async function collectLiveSidecars(projectsRoot) {
18140
18591
  continue;
18141
18592
  }
18142
18593
  for (const s of subs) {
18143
- if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join26(subDir, s.name));
18594
+ if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join27(subDir, s.name));
18144
18595
  }
18145
18596
  }
18146
18597
  }
@@ -18152,7 +18603,7 @@ function shortId(path2) {
18152
18603
  return base.slice(0, 8);
18153
18604
  }
18154
18605
  async function migrateAdminWebchatSidecars(opts = {}) {
18155
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join26(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
18606
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join27(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
18156
18607
  const usersFile = opts.usersFile ?? USERS_FILE;
18157
18608
  const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
18158
18609
  const resolveName = opts.resolveName ?? defaultResolveName;
@@ -18959,16 +19410,16 @@ var WebchatGateway = class {
18959
19410
  * The public /api/chat route awaits this to return the reply on the same SSE
18960
19411
  * response, preserving the pre-756 POST→single-blob browser contract. */
18961
19412
  awaitReply(key, timeoutMs) {
18962
- return new Promise((resolve32) => {
19413
+ return new Promise((resolve34) => {
18963
19414
  const timer2 = setTimeout(() => {
18964
19415
  this.replyAwaiters.delete(key);
18965
- resolve32({ timeout: true });
19416
+ resolve34({ timeout: true });
18966
19417
  }, timeoutMs);
18967
19418
  if (timer2.unref) timer2.unref();
18968
19419
  this.replyAwaiters.set(key, (r) => {
18969
19420
  clearTimeout(timer2);
18970
19421
  this.replyAwaiters.delete(key);
18971
- resolve32(r);
19422
+ resolve34(r);
18972
19423
  });
18973
19424
  });
18974
19425
  }
@@ -19104,7 +19555,7 @@ var WebchatGateway = class {
19104
19555
 
19105
19556
  // app/lib/webchat/gateway/public-spawn-request.ts
19106
19557
  function buildPublicWebchatSpawnRequest(input) {
19107
- const { agentSlug, sliceToken, personId, name } = input.public;
19558
+ const { agentSlug, sliceToken, personId, visitorId, name } = input.public;
19108
19559
  return {
19109
19560
  senderId: input.key,
19110
19561
  role: "public",
@@ -19115,6 +19566,7 @@ function buildPublicWebchatSpawnRequest(input) {
19115
19566
  // public agents carry neither (mirrors the bridge's open-mode key).
19116
19567
  ...sliceToken ? { sliceToken } : {},
19117
19568
  ...personId ? { personId } : {},
19569
+ ...visitorId ? { visitorId } : {},
19118
19570
  name,
19119
19571
  webchatChannel: { key: input.key, gatewayUrl: input.gatewayUrl, serverPath: input.serverPath }
19120
19572
  };
@@ -19138,7 +19590,9 @@ function startNativeFileFollower(input) {
19138
19590
  followerRunning: false,
19139
19591
  sliceToken: "",
19140
19592
  personId: null,
19141
- name: null
19593
+ name: null,
19594
+ // WhatsApp addresses the document to senderId; no separate reply target.
19595
+ replyTarget: null
19142
19596
  };
19143
19597
  const sid = input.senderId.slice(0, 8);
19144
19598
  let lastComposingAt = Number.NEGATIVE_INFINITY;
@@ -19237,8 +19691,8 @@ function broadcastAdminShutdown(reason) {
19237
19691
 
19238
19692
  // ../lib/entitlement/src/index.ts
19239
19693
  import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
19240
- import { existsSync as existsSync30, readFileSync as readFileSync28, statSync as statSync12 } from "fs";
19241
- import { resolve as resolve30 } from "path";
19694
+ import { existsSync as existsSync30, readFileSync as readFileSync30, statSync as statSync12 } from "fs";
19695
+ import { resolve as resolve32 } from "path";
19242
19696
 
19243
19697
  // ../lib/entitlement/src/canonicalize.ts
19244
19698
  function canonicalize(value) {
@@ -19273,7 +19727,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
19273
19727
  var GRACE_DAYS = 7;
19274
19728
  var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
19275
19729
  function pubkeyPath(brand) {
19276
- return resolve30(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
19730
+ return resolve32(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
19277
19731
  }
19278
19732
  var memo = null;
19279
19733
  function memoKey(mtimeMs, account) {
@@ -19285,12 +19739,12 @@ function resolveEntitlement(brand, account) {
19285
19739
  if (brand.commercialMode !== true) {
19286
19740
  return logResolved(implicitTrust(account), null);
19287
19741
  }
19288
- const entitlementPath = resolve30(brand.configDir, "entitlement.json");
19742
+ const entitlementPath = resolve32(brand.configDir, "entitlement.json");
19289
19743
  if (!existsSync30(entitlementPath)) {
19290
19744
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
19291
19745
  }
19292
- const stat7 = statSync12(entitlementPath);
19293
- const key = memoKey(stat7.mtimeMs, account);
19746
+ const stat8 = statSync12(entitlementPath);
19747
+ const key = memoKey(stat8.mtimeMs, account);
19294
19748
  if (memo && memo.key === key) {
19295
19749
  return memo.result;
19296
19750
  }
@@ -19301,7 +19755,7 @@ function resolveEntitlement(brand, account) {
19301
19755
  function verifyAndResolve(brand, entitlementPath, account) {
19302
19756
  let pubkeyPem;
19303
19757
  try {
19304
- pubkeyPem = readFileSync28(pubkeyPath(brand), "utf-8");
19758
+ pubkeyPem = readFileSync30(pubkeyPath(brand), "utf-8");
19305
19759
  } catch (err) {
19306
19760
  return logResolved(anonymousFallback("pubkey-missing"), {
19307
19761
  reason: "pubkey-missing"
@@ -19315,7 +19769,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
19315
19769
  }
19316
19770
  let envelope;
19317
19771
  try {
19318
- envelope = JSON.parse(readFileSync28(entitlementPath, "utf-8"));
19772
+ envelope = JSON.parse(readFileSync30(entitlementPath, "utf-8"));
19319
19773
  } catch {
19320
19774
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
19321
19775
  }
@@ -19476,14 +19930,14 @@ function clientFrom(c) {
19476
19930
  );
19477
19931
  }
19478
19932
  var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
19479
- var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join27(PLATFORM_ROOT8, "config", "brand.json") : "";
19933
+ var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join28(PLATFORM_ROOT8, "config", "brand.json") : "";
19480
19934
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
19481
19935
  if (BRAND_JSON_PATH && !existsSync31(BRAND_JSON_PATH)) {
19482
19936
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
19483
19937
  }
19484
19938
  if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
19485
19939
  try {
19486
- const parsed = JSON.parse(readFileSync29(BRAND_JSON_PATH, "utf-8"));
19940
+ const parsed = JSON.parse(readFileSync31(BRAND_JSON_PATH, "utf-8"));
19487
19941
  BRAND = { ...BRAND, ...parsed };
19488
19942
  } catch (err) {
19489
19943
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -19502,11 +19956,11 @@ var brandLoginOpts = {
19502
19956
  bodyFont: BRAND.defaultFonts?.body,
19503
19957
  logoContainsName: !!BRAND.logoContainsName
19504
19958
  };
19505
- var ALIAS_DOMAINS_PATH = join27(homedir3(), BRAND.configDir, "alias-domains.json");
19959
+ var ALIAS_DOMAINS_PATH = join28(homedir3(), BRAND.configDir, "alias-domains.json");
19506
19960
  function loadAliasDomains() {
19507
19961
  try {
19508
19962
  if (!existsSync31(ALIAS_DOMAINS_PATH)) return null;
19509
- const parsed = JSON.parse(readFileSync29(ALIAS_DOMAINS_PATH, "utf-8"));
19963
+ const parsed = JSON.parse(readFileSync31(ALIAS_DOMAINS_PATH, "utf-8"));
19510
19964
  if (!Array.isArray(parsed)) {
19511
19965
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
19512
19966
  return null;
@@ -19534,7 +19988,7 @@ var app52 = new Hono();
19534
19988
  var nativeFileFollowers = /* @__PURE__ */ new Map();
19535
19989
  var waGateway = new WaGateway({
19536
19990
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
19537
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve31(process.env.MAXY_PLATFORM_ROOT ?? join27(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
19991
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
19538
19992
  // Task 751 — file delivery on the native channel: resolve the platform
19539
19993
  // account + path validation here and funnel through the shared send core.
19540
19994
  sendDocument: async ({ senderId, accountId, filePath, caption }) => {
@@ -19550,7 +20004,7 @@ var waGateway = new WaGateway({
19550
20004
  caption,
19551
20005
  accountId,
19552
20006
  maxyAccountId,
19553
- platformRoot: resolve31(process.env.MAXY_PLATFORM_ROOT ?? join27(__dirname, ".."))
20007
+ platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, ".."))
19554
20008
  });
19555
20009
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
19556
20010
  },
@@ -19973,8 +20427,8 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
19973
20427
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
19974
20428
  return c.text("Not found", 404);
19975
20429
  }
19976
- const filePath = resolve31(account.accountDir, "agents", slug, "assets", filename);
19977
- const expectedDir = resolve31(account.accountDir, "agents", slug, "assets");
20430
+ const filePath = resolve33(account.accountDir, "agents", slug, "assets", filename);
20431
+ const expectedDir = resolve33(account.accountDir, "agents", slug, "assets");
19978
20432
  if (!filePath.startsWith(expectedDir + "/")) {
19979
20433
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
19980
20434
  return c.text("Forbidden", 403);
@@ -19986,7 +20440,7 @@ app52.get("/agent-assets/:slug/:filename", (c) => {
19986
20440
  const ext = "." + filename.split(".").pop()?.toLowerCase();
19987
20441
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
19988
20442
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
19989
- const body = readFileSync29(filePath);
20443
+ const body = readFileSync31(filePath);
19990
20444
  return c.body(body, 200, {
19991
20445
  "Content-Type": contentType,
19992
20446
  "Cache-Control": "public, max-age=3600"
@@ -20003,8 +20457,8 @@ app52.get("/generated/:filename", (c) => {
20003
20457
  console.error(`[generated] serve file=${filename} status=404`);
20004
20458
  return c.text("Not found", 404);
20005
20459
  }
20006
- const filePath = resolve31(account.accountDir, "generated", filename);
20007
- const expectedDir = resolve31(account.accountDir, "generated");
20460
+ const filePath = resolve33(account.accountDir, "generated", filename);
20461
+ const expectedDir = resolve33(account.accountDir, "generated");
20008
20462
  if (!filePath.startsWith(expectedDir + "/")) {
20009
20463
  console.error(`[generated] serve file=${filename} status=403`);
20010
20464
  return c.text("Forbidden", 403);
@@ -20016,7 +20470,7 @@ app52.get("/generated/:filename", (c) => {
20016
20470
  const ext = "." + filename.split(".").pop()?.toLowerCase();
20017
20471
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
20018
20472
  console.log(`[generated] serve file=${filename} status=200`);
20019
- const body = readFileSync29(filePath);
20473
+ const body = readFileSync31(filePath);
20020
20474
  return c.body(body, 200, {
20021
20475
  "Content-Type": contentType,
20022
20476
  "Cache-Control": "public, max-age=86400"
@@ -20031,7 +20485,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
20031
20485
  var brandIconPath = "/brand/maxy-monochrome.png";
20032
20486
  if (BRAND_JSON_PATH && existsSync31(BRAND_JSON_PATH)) {
20033
20487
  try {
20034
- const fullBrand = JSON.parse(readFileSync29(BRAND_JSON_PATH, "utf-8"));
20488
+ const fullBrand = JSON.parse(readFileSync31(BRAND_JSON_PATH, "utf-8"));
20035
20489
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
20036
20490
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
20037
20491
  } catch {
@@ -20047,11 +20501,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
20047
20501
  })}</script>`;
20048
20502
  var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
20049
20503
  var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
20050
- var brandIconFsPath = resolve31(process.cwd(), "public", brandIconPath.replace(/^\//, ""));
20504
+ var brandIconFsPath = resolve33(process.cwd(), "public", brandIconPath.replace(/^\//, ""));
20051
20505
  var brandIconExists = existsSync31(brandIconFsPath);
20052
20506
  var SW_SOURCE = (() => {
20053
20507
  try {
20054
- return readFileSync29(resolve31(process.cwd(), "public", "sw.js"), "utf-8");
20508
+ return readFileSync31(resolve33(process.cwd(), "public", "sw.js"), "utf-8");
20055
20509
  } catch {
20056
20510
  return null;
20057
20511
  }
@@ -20059,9 +20513,9 @@ var SW_SOURCE = (() => {
20059
20513
  function readInstalledVersion() {
20060
20514
  try {
20061
20515
  if (!PLATFORM_ROOT8) return "unknown";
20062
- const versionFile = join27(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
20516
+ const versionFile = join28(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
20063
20517
  if (!existsSync31(versionFile)) return "unknown";
20064
- const content = readFileSync29(versionFile, "utf-8").trim();
20518
+ const content = readFileSync31(versionFile, "utf-8").trim();
20065
20519
  return content || "unknown";
20066
20520
  } catch {
20067
20521
  return "unknown";
@@ -20102,7 +20556,7 @@ var clientErrorReporterScript = `<script>
20102
20556
  function cachedHtml(file) {
20103
20557
  let html = htmlCache.get(file);
20104
20558
  if (!html) {
20105
- html = readFileSync29(resolve31(process.cwd(), "public", file), "utf-8");
20559
+ html = readFileSync31(resolve33(process.cwd(), "public", file), "utf-8");
20106
20560
  const productNameEsc = escapeHtml(BRAND.productName);
20107
20561
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
20108
20562
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -20120,13 +20574,13 @@ ${clientErrorReporterScript}
20120
20574
  }
20121
20575
  var brandedHtmlCache = /* @__PURE__ */ new Map();
20122
20576
  function loadBrandingCache(agentSlug) {
20123
- const configDir2 = join27(homedir3(), BRAND.configDir);
20577
+ const configDir2 = join28(homedir3(), BRAND.configDir);
20124
20578
  try {
20125
20579
  const accountId = getDefaultAccountId();
20126
20580
  if (!accountId) return null;
20127
- const cachePath = join27(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
20581
+ const cachePath = join28(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
20128
20582
  if (!existsSync31(cachePath)) return null;
20129
- return JSON.parse(readFileSync29(cachePath, "utf-8"));
20583
+ return JSON.parse(readFileSync31(cachePath, "utf-8"));
20130
20584
  } catch {
20131
20585
  return null;
20132
20586
  }
@@ -20226,7 +20680,7 @@ app52.use("/vnc-popout.html", logViewerFetch);
20226
20680
  app52.get("/vnc-popout.html", (c) => {
20227
20681
  let html = htmlCache.get("vnc-popout.html");
20228
20682
  if (!html) {
20229
- html = readFileSync29(resolve31(process.cwd(), "public", "vnc-popout.html"), "utf-8");
20683
+ html = readFileSync31(resolve33(process.cwd(), "public", "vnc-popout.html"), "utf-8");
20230
20684
  const name = escapeHtml(BRAND.productName);
20231
20685
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
20232
20686
  html = html.replace("</head>", ` ${brandScript}
@@ -20438,7 +20892,7 @@ async function runAdminUserReconcileTick() {
20438
20892
  console.error("[adminuser-self-heal] skip reason=no-users-file");
20439
20893
  return;
20440
20894
  }
20441
- const usersRaw = readFileSync29(USERS_FILE, "utf-8").trim();
20895
+ const usersRaw = readFileSync31(USERS_FILE, "utf-8").trim();
20442
20896
  if (!usersRaw) {
20443
20897
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
20444
20898
  return;
@@ -20490,7 +20944,7 @@ try {
20490
20944
  } catch (err) {
20491
20945
  console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
20492
20946
  }
20493
- var configDirForWhatsApp = basename9(MAXY_DIR) || ".maxy";
20947
+ var configDirForWhatsApp = basename10(MAXY_DIR) || ".maxy";
20494
20948
  var bootAccount = resolveAccount();
20495
20949
  if (bootAccount) {
20496
20950
  migrateRemovedConfigKeys(bootAccount.accountDir);
@@ -20552,7 +21006,7 @@ if (bootAccountConfig?.whatsapp) {
20552
21006
  }
20553
21007
  init({
20554
21008
  configDir: configDirForWhatsApp,
20555
- platformRoot: resolve31(process.env.MAXY_PLATFORM_ROOT ?? join27(__dirname, "..")),
21009
+ platformRoot: resolve33(process.env.MAXY_PLATFORM_ROOT ?? join28(__dirname, "..")),
20556
21010
  accountConfig: bootAccountConfig,
20557
21011
  onMessage: async (msg) => {
20558
21012
  if (msg.isOwnerMirror) {