@rubytech/create-sitedesk-code 0.1.612 → 0.1.614

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 (82) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/agent-dispatch-rule/dist/index.d.ts +34 -0
  3. package/payload/platform/lib/agent-dispatch-rule/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/agent-dispatch-rule/dist/index.js +39 -0
  5. package/payload/platform/lib/agent-dispatch-rule/dist/index.js.map +1 -1
  6. package/payload/platform/lib/agent-dispatch-rule/src/__tests__/carrier-verdict.test.ts +92 -0
  7. package/payload/platform/lib/agent-dispatch-rule/src/index.ts +59 -0
  8. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +67 -10
  9. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +11 -0
  10. package/payload/platform/plugins/docs/references/telegram-guide.md +66 -9
  11. package/payload/platform/plugins/scheduling/mcp/dist/index.js +2 -0
  12. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  13. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.d.ts +2 -0
  14. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.d.ts.map +1 -0
  15. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.js +72 -0
  16. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.js.map +1 -0
  17. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.d.ts +2 -0
  18. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.d.ts.map +1 -0
  19. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.js +327 -0
  20. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.js.map +1 -0
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.d.ts +2 -0
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.d.ts.map +1 -0
  23. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.js +130 -0
  24. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.js.map +1 -0
  25. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +27 -58
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -1
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts +8 -3
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts.map +1 -1
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js +37 -3
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js.map +1 -1
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.d.ts +67 -0
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.d.ts.map +1 -0
  33. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.js +133 -0
  34. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.js.map +1 -0
  35. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +67 -12
  36. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -1
  37. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js +226 -72
  38. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -1
  39. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +66 -4
  40. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
  41. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.d.ts +2 -0
  42. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.d.ts.map +1 -0
  43. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.js +45 -0
  44. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.js.map +1 -0
  45. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +3 -1
  46. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  47. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +32 -7
  48. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  49. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +12 -1
  50. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  51. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +15 -2
  52. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  53. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +14 -0
  54. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  55. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +4 -0
  56. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  57. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +3 -0
  58. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
  59. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +2 -0
  60. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
  61. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  62. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +14 -0
  63. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  64. package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.d.ts +2 -0
  65. package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.d.ts.map +1 -0
  66. package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.js +237 -0
  67. package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.js.map +1 -0
  68. package/payload/platform/plugins/telegram/mcp/dist/index.js +6 -4
  69. package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
  70. package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.d.ts +96 -0
  71. package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.d.ts.map +1 -0
  72. package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.js +156 -0
  73. package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.js.map +1 -0
  74. package/payload/platform/plugins/telegram/mcp/dist/tools/card.d.ts.map +1 -1
  75. package/payload/platform/plugins/telegram/mcp/dist/tools/card.js +53 -0
  76. package/payload/platform/plugins/telegram/mcp/dist/tools/card.js.map +1 -1
  77. package/payload/platform/plugins/telegram/mcp/dist/tools/instruct.d.ts +7 -5
  78. package/payload/platform/plugins/telegram/mcp/dist/tools/instruct.d.ts.map +1 -1
  79. package/payload/platform/plugins/telegram/mcp/dist/tools/instruct.js +14 -12
  80. package/payload/platform/plugins/telegram/mcp/dist/tools/instruct.js.map +1 -1
  81. package/payload/server/public/operator.html +1 -1
  82. package/payload/server/server.js +804 -724
@@ -3220,10 +3220,10 @@ var require_dist10 = __commonJS({
3220
3220
  });
3221
3221
 
3222
3222
  // server/index.ts
3223
- import { readFileSync as readFileSync63, existsSync as existsSync63, watchFile, readdirSync as readdirSync37, statSync as statSync33 } from "fs";
3223
+ import { readFileSync as readFileSync64, existsSync as existsSync63, watchFile, readdirSync as readdirSync38, statSync as statSync34 } from "fs";
3224
3224
  import { spawn as spawn3 } from "child_process";
3225
3225
  import { createHash as createHash8 } from "crypto";
3226
- import { resolve as resolve55, join as join77, basename as basename19 } from "path";
3226
+ import { resolve as resolve55, join as join78, basename as basename19 } from "path";
3227
3227
  import { homedir as homedir5 } from "os";
3228
3228
 
3229
3229
  // server/lib/route-cache-watchers.ts
@@ -6248,6 +6248,82 @@ function parseTelegramChannelKey(key2) {
6248
6248
  return { botId, senderId: rest, business: false };
6249
6249
  }
6250
6250
 
6251
+ // app/lib/telegram/access-control.ts
6252
+ function checkTelegramAccess(params) {
6253
+ const { senderId, entry: entry3, chatId, chatType } = params;
6254
+ const isGroup = chatType === "group" || chatType === "supergroup";
6255
+ const isChannel = chatType === "channel";
6256
+ if (isChannel && entry3.role !== "public") {
6257
+ return {
6258
+ allowed: false,
6259
+ reason: entry3.role === "admin" ? "admin-entry-no-channels" : entry3.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
6260
+ agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
6261
+ };
6262
+ }
6263
+ if (isGroup && entry3.role !== "public") {
6264
+ return {
6265
+ allowed: false,
6266
+ reason: entry3.role === "admin" ? "admin-entry-no-groups" : entry3.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
6267
+ agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
6268
+ };
6269
+ }
6270
+ if (entry3.role === "admin") {
6271
+ const adminUsers = entry3.adminUsers ?? [];
6272
+ if (adminUsers.includes(senderId)) {
6273
+ return { allowed: true, reason: "admin-binding", agentType: "admin" };
6274
+ }
6275
+ return { allowed: false, reason: "not-admin-user", agentType: "admin" };
6276
+ }
6277
+ if (isGroup && entry3.role === "public") {
6278
+ const groupPolicy = entry3.groupPolicy ?? "disabled";
6279
+ if (groupPolicy === "allowlist") {
6280
+ const allowGroups = entry3.allowGroups ?? [];
6281
+ if (allowGroups.includes(chatId)) {
6282
+ return { allowed: true, reason: "group-allowlist-match", agentType: "public" };
6283
+ }
6284
+ return { allowed: false, reason: "not-in-allowGroups", agentType: "public" };
6285
+ }
6286
+ return { allowed: false, reason: "group-policy-disabled", agentType: "public" };
6287
+ }
6288
+ if (isChannel && entry3.role === "public") {
6289
+ const channelPolicy = entry3.channelPolicy ?? "disabled";
6290
+ if (channelPolicy === "allowlist") {
6291
+ const allowChannels = entry3.allowChannels ?? [];
6292
+ if (allowChannels.includes(chatId)) {
6293
+ return { allowed: true, reason: "channel-allowlist-match", agentType: "public" };
6294
+ }
6295
+ return { allowed: false, reason: "not-in-allowChannels", agentType: "public" };
6296
+ }
6297
+ return { allowed: false, reason: "channel-policy-disabled", agentType: "public" };
6298
+ }
6299
+ if (entry3.role === "unbound") {
6300
+ return { allowed: false, reason: "bot-unbound", agentType: "public" };
6301
+ }
6302
+ if (entry3.role === "specialist") {
6303
+ const allowFrom = entry3.allowFrom ?? [];
6304
+ if (allowFrom.includes(senderId)) {
6305
+ return { allowed: true, reason: "specialist-allowlist-match", agentType: "specialist" };
6306
+ }
6307
+ return { allowed: false, reason: "not-in-allowlist", agentType: "specialist" };
6308
+ }
6309
+ const policy = entry3.dmPolicy ?? "disabled";
6310
+ switch (policy) {
6311
+ case "open":
6312
+ return { allowed: true, reason: "dm-policy-open", agentType: "public" };
6313
+ case "allowlist": {
6314
+ const allowFrom = entry3.allowFrom ?? [];
6315
+ if (allowFrom.includes(senderId)) {
6316
+ return { allowed: true, reason: "allowlist-match", agentType: "public" };
6317
+ }
6318
+ return { allowed: false, reason: "not-in-allowlist", agentType: "public" };
6319
+ }
6320
+ case "disabled":
6321
+ return { allowed: false, reason: "dm-policy-disabled", agentType: "public" };
6322
+ default:
6323
+ return { allowed: false, reason: "unknown-dm-policy", agentType: "public" };
6324
+ }
6325
+ }
6326
+
6251
6327
  // app/lib/telegram/chat-send-policy.ts
6252
6328
  var BROADCAST_PREFIX = "-100";
6253
6329
  function checkTelegramChatSend(params) {
@@ -6263,6 +6339,21 @@ function chatSendAllowlists(entry3) {
6263
6339
  if (!entry3 || entry3.role !== "public") return { allowChannels: [], allowGroups: [] };
6264
6340
  return { allowChannels: entry3.allowChannels ?? [], allowGroups: entry3.allowGroups ?? [] };
6265
6341
  }
6342
+ function checkTelegramOutboundTarget(params) {
6343
+ const { chatId, entry: entry3 } = params;
6344
+ if (String(chatId).startsWith(BROADCAST_PREFIX)) {
6345
+ const lists = chatSendAllowlists(entry3);
6346
+ const verdict2 = checkTelegramChatSend({
6347
+ chatId,
6348
+ allowChannels: lists.allowChannels,
6349
+ allowGroups: lists.allowGroups
6350
+ });
6351
+ return { allowed: verdict2.allowed, reason: verdict2.reason, kind: "broadcast" };
6352
+ }
6353
+ const kind = chatId < 0 ? "group" : "private";
6354
+ const verdict = checkTelegramAccess({ senderId: chatId, entry: entry3, chatId, chatType: kind });
6355
+ return { allowed: verdict.allowed, reason: verdict.reason, kind };
6356
+ }
6266
6357
 
6267
6358
  // app/lib/channel-pty-bridge/admin-session-id.ts
6268
6359
  import { createHash } from "crypto";
@@ -7105,17 +7196,17 @@ async function computeSessionList(scopeAccountId) {
7105
7196
  if (adminUserId && role === ADMIN_ROLE && sidecarChannel === WEBCHAT_CHANNEL) {
7106
7197
  adminUserIdBySession.set(sessionId, adminUserId);
7107
7198
  }
7108
- const tail = await lastAssistantModelFromTail(path, size);
7109
- if (tail.failure) {
7199
+ const tail2 = await lastAssistantModelFromTail(path, size);
7200
+ if (tail2.failure) {
7110
7201
  tailReadFails += 1;
7111
7202
  console.error(
7112
- `[admin-sessions-list] op=tail-read-failed sessionId=${sessionId.slice(0, 8)} reason=${tail.failure}`
7203
+ `[admin-sessions-list] op=tail-read-failed sessionId=${sessionId.slice(0, 8)} reason=${tail2.failure}`
7113
7204
  );
7114
7205
  } else {
7115
- if (tail.bytesRead > 0) tailReads += 1;
7116
- if (tail.model === null) tailModelMiss += 1;
7206
+ if (tail2.bytesRead > 0) tailReads += 1;
7207
+ if (tail2.model === null) tailModelMiss += 1;
7117
7208
  }
7118
- tailBytes += tail.bytesRead;
7209
+ tailBytes += tail2.bytesRead;
7119
7210
  rows.push({
7120
7211
  sessionId,
7121
7212
  title: resolved.title,
@@ -7134,7 +7225,7 @@ async function computeSessionList(scopeAccountId) {
7134
7225
  // diverge from the sidecar (e.g. an ai-titled channel row).
7135
7226
  channel: sidecarChannel,
7136
7227
  personName: null,
7137
- model: tail.model,
7228
+ model: tail2.model,
7138
7229
  accountId: rowAccountId,
7139
7230
  senderId,
7140
7231
  whatsappName: null
@@ -7354,33 +7445,53 @@ function createScheduleInjectRoutes(deps) {
7354
7445
  console.error(`${TAG6} op=inject-spawn eventId=${eventId} sessionId=${sessionId2} result=ok status=-`);
7355
7446
  return c.json({ ok: true, sessionKey: sessionId2, ...transcriptBoundary(sessionId2) }, 200);
7356
7447
  }
7357
- const resolution = deps.telegramAccountForDestination(destination);
7358
- if (resolution.kind === "none") {
7359
- console.error(`${TAG6} op=schedule-telegram-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
7360
- return c.json({ ok: false, error: "unresolved-effective-account" }, 403);
7361
- }
7448
+ const namedBotId = typeof body.botId === "string" && body.botId.length > 0 ? body.botId : null;
7362
7449
  let claimant;
7363
- if (resolution.kind === "one") {
7364
- claimant = resolution;
7365
- } else {
7366
- const mine = resolution.claimants.filter((c2) => c2.accountId === eventAccountId);
7367
- if (mine.length === 0) {
7450
+ let carrierSource;
7451
+ let claimantCount = 0;
7452
+ if (namedBotId !== null && eventAccountId !== null) {
7453
+ carrierSource = "named";
7454
+ const carrier = deps.carrierFor(eventAccountId, namedBotId, destination);
7455
+ if ("error" in carrier) {
7368
7456
  console.error(
7369
- `${TAG6} op=event-account-scope-mismatch eventId=${eventId} eventAccount=${eventAccountId ?? "-"} effectiveAccount=none claimants=${resolution.claimants.length} destination=${destination} dispatched=no`
7457
+ `${TAG6} op=carrier-unavailable eventId=${eventId} botId=${namedBotId} reason=${carrier.error}`
7370
7458
  );
7371
- return c.json({ ok: false, error: "event-account-scope-mismatch" }, 403);
7459
+ return c.json({ ok: false, error: "carrier-unavailable" }, 403);
7372
7460
  }
7373
- if (mine.length > 1) {
7461
+ claimant = carrier;
7462
+ } else {
7463
+ carrierSource = "resolved";
7464
+ const resolution = deps.telegramAccountForDestination(destination);
7465
+ if (resolution.kind === "none") {
7466
+ console.error(`${TAG6} op=schedule-telegram-reject eventId=${eventId} destination=${destination} reason=unresolved-effective-account`);
7467
+ return c.json({ ok: false, error: "unresolved-effective-account" }, 403);
7468
+ }
7469
+ claimantCount = resolution.kind === "ambiguous" ? resolution.claimants.length : 1;
7470
+ if (resolution.kind === "one") {
7471
+ claimant = resolution;
7472
+ } else {
7473
+ const mine = resolution.claimants.filter((c2) => c2.accountId === eventAccountId);
7474
+ if (mine.length === 0) {
7475
+ console.error(
7476
+ `${TAG6} op=event-account-scope-mismatch eventId=${eventId} eventAccount=${eventAccountId ?? "-"} effectiveAccount=none claimants=${resolution.claimants.length} destination=${destination} dispatched=no`
7477
+ );
7478
+ return c.json({ ok: false, error: "event-account-scope-mismatch" }, 403);
7479
+ }
7480
+ if (mine.length > 1) {
7481
+ console.error(
7482
+ `${TAG6} op=telegram-destination-ambiguous eventId=${eventId} destination=${destination} accounts=${mine.length} scope=within-account dispatched=no`
7483
+ );
7484
+ return c.json({ ok: false, error: "telegram-destination-ambiguous" }, 403);
7485
+ }
7486
+ claimant = mine[0];
7374
7487
  console.error(
7375
- `${TAG6} op=telegram-destination-ambiguous eventId=${eventId} destination=${destination} accounts=${mine.length} scope=within-account dispatched=no`
7488
+ `${TAG6} op=telegram-destination-narrowed eventId=${eventId} destination=${destination} claimants=${resolution.claimants.length} account=${claimant.accountId} botId=${claimant.botId}`
7376
7489
  );
7377
- return c.json({ ok: false, error: "telegram-destination-ambiguous" }, 403);
7378
7490
  }
7379
- claimant = mine[0];
7380
- console.error(
7381
- `${TAG6} op=telegram-destination-narrowed eventId=${eventId} destination=${destination} claimants=${resolution.claimants.length} account=${claimant.accountId} botId=${claimant.botId}`
7382
- );
7383
7491
  }
7492
+ console.error(
7493
+ `${TAG6} op=carrier eventId=${eventId} botId=${claimant.botId} source=${carrierSource} claimants=${claimantCount}`
7494
+ );
7384
7495
  const { accountId: effectiveAccount, botId, botToken } = claimant;
7385
7496
  console.error(`${TAG6} op=effective-account eventId=${eventId} effectiveAccount=${effectiveAccount} botId=${botId}`);
7386
7497
  if (eventAccountId === null || eventAccountId !== effectiveAccount) {
@@ -7732,13 +7843,24 @@ function createOperatorInjectRoutes(deps) {
7732
7843
  const { accountId, accountDir, entry: entry3 } = resolved;
7733
7844
  const role = entry3.role;
7734
7845
  const agent = agentOf(entry3);
7735
- if (role !== "public") {
7736
- report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: "not-public-bot" });
7846
+ if (role === "admin") {
7847
+ report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: "admin-bot" });
7737
7848
  return c.json(
7738
7849
  {
7739
7850
  ok: false,
7740
- error: "not-public-bot",
7741
- detail: `Bot ${botId} is a ${role} bot, not a public one. Only a public agent's conversation with a visitor can be instructed this way.`
7851
+ error: "admin-bot",
7852
+ detail: `Bot ${botId} is this account's admin bot. Its chat is your own conversation with the agent, so there is nothing to instruct.`
7853
+ },
7854
+ 403
7855
+ );
7856
+ }
7857
+ if (role === "unbound") {
7858
+ report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: "unbound-bot" });
7859
+ return c.json(
7860
+ {
7861
+ ok: false,
7862
+ error: "unbound-bot",
7863
+ detail: `Bot ${botId} is not bound to an agent or a specialist card, so it answers nobody. Bind it before instructing it.`
7742
7864
  },
7743
7865
  403
7744
7866
  );
@@ -7771,30 +7893,18 @@ function createOperatorInjectRoutes(deps) {
7771
7893
  403
7772
7894
  );
7773
7895
  }
7774
- const lists = chatSendAllowlists(entry3);
7775
- const verdict = checkTelegramChatSend({
7776
- chatId: Number(chatId),
7777
- allowChannels: lists.allowChannels,
7778
- allowGroups: lists.allowGroups
7779
- });
7896
+ const verdict = checkTelegramOutboundTarget({ chatId: Number(chatId), entry: entry3 });
7780
7897
  console.error(
7781
7898
  `[telegram-outbound] op=channel-send botId=${logValue2(botId)} chatId=${logValue2(chatId)} allowed=${verdict.allowed ? "yes" : "no"} reason=${verdict.reason} messageId=none`
7782
7899
  );
7783
7900
  if (!verdict.allowed) {
7784
- report({
7785
- botId,
7786
- chatId,
7787
- sessionId: "none",
7788
- role,
7789
- agent,
7790
- accepted: false,
7791
- reason: "chat-not-allowlisted"
7792
- });
7901
+ const outcome = verdict.kind === "broadcast" ? "chat-not-allowlisted" : "chat-not-permitted";
7902
+ report({ botId, chatId, sessionId: "none", role, agent, accepted: false, reason: outcome });
7793
7903
  return c.json(
7794
7904
  {
7795
7905
  ok: false,
7796
- error: "chat-not-allowlisted",
7797
- detail: `Chat ${chatId} is a broadcast channel that bot ${botId} does not allowlist, so nothing was sent.`
7906
+ error: outcome,
7907
+ detail: verdict.kind === "broadcast" ? `Chat ${chatId} is a broadcast channel that bot ${botId} does not allowlist, so nothing was sent.` : `Bot ${botId} may not talk to chat ${chatId} (${verdict.reason}), so nothing was sent. It would refuse that chat's own messages too.`
7798
7908
  },
7799
7909
  403
7800
7910
  );
@@ -7823,13 +7933,22 @@ function createOperatorInjectRoutes(deps) {
7823
7933
  accountDir,
7824
7934
  botId,
7825
7935
  botToken: entry3.token,
7826
- agentSlug: entry3.agent,
7936
+ // Task 2773 — `entryAgentSlug`, not `entry.agent`: a specialist entry
7937
+ // carries a card name and no `agent` field at all, so the property read
7938
+ // is `undefined` there. The webhook resolves the same way
7939
+ // (`routes/telegram.ts`), so both doors name the spawn's agent from one
7940
+ // definition.
7941
+ agentSlug: entryAgentSlug(entry3),
7827
7942
  senderId: chatId,
7828
7943
  conversationSubject: chatId,
7829
7944
  businessChatId: null,
7830
- role: "public",
7945
+ // Task 2773 — the entry's OWN role, replacing a hardcoded 'public'. This
7946
+ // picks the session derivation in `sessionIdFor` and the framing in the
7947
+ // channel server; a value disagreeing with the registry would land the
7948
+ // instruction in a session the counterparty's real messages never reach.
7949
+ role,
7831
7950
  personId: null,
7832
- specialist: null,
7951
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
7833
7952
  chatId,
7834
7953
  text: instruction,
7835
7954
  // An instruction carries no file by construction: there is no message to
@@ -8171,8 +8290,8 @@ function auditGroupOutbound() {
8171
8290
  }
8172
8291
  function auditGroupOutboundNote() {
8173
8292
  const a = auditGroupOutbound();
8174
- const tail = a.agent > 0 ? ` offenders=${a.offenders.join(",")}` : "";
8175
- const note = `convos=${a.convos} outbound=${a.outbound} operator=${a.operator} agent=${a.agent} unstamped=${a.unstamped}${tail}`;
8293
+ const tail2 = a.agent > 0 ? ` offenders=${a.offenders.join(",")}` : "";
8294
+ const note = `convos=${a.convos} outbound=${a.outbound} operator=${a.operator} agent=${a.agent} unstamped=${a.unstamped}${tail2}`;
8176
8295
  console.log(`${TAG13} op=group-outbound-audit ${note}`);
8177
8296
  return note;
8178
8297
  }
@@ -9203,10 +9322,10 @@ function resolveKey(accountId, ctx) {
9203
9322
  function setAccountPolicy(accountDir, accountId, policy, ctx) {
9204
9323
  const id = accountId.trim();
9205
9324
  if (!id) return { ok: false, reason: "missing-account-id", error: "Missing account id." };
9206
- const fields = ["dmPolicy", "adminPhones", "allowFrom"].filter(
9325
+ const fields2 = ["dmPolicy", "adminPhones", "allowFrom"].filter(
9207
9326
  (f) => policy[f] !== void 0
9208
9327
  );
9209
- if (fields.length === 0) {
9328
+ if (fields2.length === 0) {
9210
9329
  return {
9211
9330
  ok: false,
9212
9331
  reason: "empty-policy",
@@ -9228,7 +9347,7 @@ function setAccountPolicy(accountDir, accountId, policy, ctx) {
9228
9347
  const accounts = wa.accounts;
9229
9348
  if (!accounts[key2] || typeof accounts[key2] !== "object") accounts[key2] = { name: "Main" };
9230
9349
  const slice = accounts[key2];
9231
- for (const field2 of fields) slice[field2] = policy[field2];
9350
+ for (const field2 of fields2) slice[field2] = policy[field2];
9232
9351
  const parsed = WhatsAppConfigSchema.safeParse(wa);
9233
9352
  if (!parsed.success) {
9234
9353
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
@@ -9238,7 +9357,7 @@ function setAccountPolicy(accountDir, accountId, policy, ctx) {
9238
9357
  config.whatsapp = parsed.data;
9239
9358
  writeConfig(accountDir, config);
9240
9359
  console.error(
9241
- `${TAG15} op=set-account-policy account=${id} key=${key2} keyKind=${verdict.kind} fields=[${fields.join(",")}] ok=true`
9360
+ `${TAG15} op=set-account-policy account=${id} key=${key2} keyKind=${verdict.kind} fields=[${fields2.join(",")}] ok=true`
9242
9361
  );
9243
9362
  reloadManagerConfig(accountDir);
9244
9363
  return { ok: true, key: key2 };
@@ -9500,8 +9619,8 @@ function resolvePublicAgentEnabled(accountDir, opts) {
9500
9619
  return { enabled: false, source: "default" };
9501
9620
  }
9502
9621
  }
9503
- function updateConfig(accountDir, fields) {
9504
- const fieldNames = Object.keys(fields);
9622
+ function updateConfig(accountDir, fields2) {
9623
+ const fieldNames = Object.keys(fields2);
9505
9624
  if (fieldNames.length === 0) {
9506
9625
  return { ok: false, error: "No fields provided to update." };
9507
9626
  }
@@ -9511,7 +9630,7 @@ function updateConfig(accountDir, fields) {
9511
9630
  config.whatsapp = {};
9512
9631
  }
9513
9632
  const wa = config.whatsapp;
9514
- for (const [key2, value] of Object.entries(fields)) {
9633
+ for (const [key2, value] of Object.entries(fields2)) {
9515
9634
  wa[key2] = value;
9516
9635
  }
9517
9636
  const parsed = WhatsAppConfigSchema.safeParse(wa);
@@ -10221,12 +10340,12 @@ function unwrapToShape(schema) {
10221
10340
  }
10222
10341
  function extractFields(schema) {
10223
10342
  const shape = unwrapToShape(schema);
10224
- const fields = [];
10343
+ const fields2 = [];
10225
10344
  for (const [name, fieldSchema] of Object.entries(shape)) {
10226
10345
  const def = fieldSchema?._def;
10227
10346
  const resolved = resolveZodType(def);
10228
10347
  const description = extractDescription(fieldSchema, def);
10229
- fields.push({
10348
+ fields2.push({
10230
10349
  name,
10231
10350
  type: resolved.type,
10232
10351
  required: resolved.required,
@@ -10234,14 +10353,14 @@ function extractFields(schema) {
10234
10353
  ...description !== void 0 ? { description } : {}
10235
10354
  });
10236
10355
  }
10237
- return fields;
10356
+ return fields2;
10238
10357
  }
10239
10358
  function escapeTableCell(text) {
10240
10359
  return text.replace(/\|/g, "\\|");
10241
10360
  }
10242
- function formatFieldTable(title, fields) {
10361
+ function formatFieldTable(title, fields2) {
10243
10362
  const lines = [`## ${title}`, "", "| Field | Type | Required | Default | Description |", "|-------|------|----------|---------|-------------|"];
10244
- for (const f of fields) {
10363
+ for (const f of fields2) {
10245
10364
  const desc = f.description ? escapeTableCell(f.description) : "\u2014";
10246
10365
  lines.push(`| ${f.name} | ${f.type} | ${f.required ? "yes" : "no"} | ${f.default ?? "\u2014"} | ${desc} |`);
10247
10366
  }
@@ -10398,12 +10517,12 @@ function houseSocketScopeDenial(c, op) {
10398
10517
  }
10399
10518
  function selfOrHouseScopeDenial(c, targetAccountId, op, targetSource = "explicit") {
10400
10519
  const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, targetAccountId);
10401
- const tail = `target=${targetAccountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`;
10520
+ const tail2 = `target=${targetAccountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`;
10402
10521
  if (decision.authorized) {
10403
- console.error(`${TAG20} op=authz-ok route=${op} ${tail}`);
10522
+ console.error(`${TAG20} op=authz-ok route=${op} ${tail2}`);
10404
10523
  return null;
10405
10524
  }
10406
- console.error(`${TAG20} op=authz-deny route=${op} ${tail}`);
10525
+ console.error(`${TAG20} op=authz-deny route=${op} ${tail2}`);
10407
10526
  return c.json({ ok: false, error: "Not authorized. This session may only pair or manage its own account." }, 403);
10408
10527
  }
10409
10528
  function bindingScopeDenial(c, targetAccountId, op) {
@@ -10677,7 +10796,7 @@ app3.post("/reconnect", async (c) => {
10677
10796
  app3.post("/config", async (c) => {
10678
10797
  try {
10679
10798
  const body = await c.req.json().catch(() => ({}));
10680
- const { action, phone, slug, fields, accountId, managesAccount, mode, policy, party, account: partyAccount, enabled } = body;
10799
+ const { action, phone, slug, fields: fields2, accountId, managesAccount, mode, policy, party, account: partyAccount, enabled } = body;
10681
10800
  if (!action || typeof action !== "string") {
10682
10801
  return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
10683
10802
  }
@@ -10941,11 +11060,11 @@ app3.post("/config", async (c) => {
10941
11060
  }
10942
11061
  }
10943
11062
  case "update-config": {
10944
- if (!fields || typeof fields !== "object" || Array.isArray(fields)) {
11063
+ if (!fields2 || typeof fields2 !== "object" || Array.isArray(fields2)) {
10945
11064
  return c.json({ ok: false, error: 'Missing required field "fields" (object with config field names and values).' }, 400);
10946
11065
  }
10947
- const result = updateConfig(account.accountDir, fields);
10948
- const fieldNames = Object.keys(fields);
11066
+ const result = updateConfig(account.accountDir, fields2);
11067
+ const fieldNames = Object.keys(fields2);
10949
11068
  console.error(`${TAG20} config action=update-config fields=[${fieldNames.join(",")}] ok=${result.ok}`);
10950
11069
  return c.json(result, result.ok ? 200 : 400);
10951
11070
  }
@@ -14095,13 +14214,13 @@ async function pullVisitStatusForAccount(opts) {
14095
14214
  log2(
14096
14215
  `${TAG24} op=merged account=${accountId} id=${row.id} labour=${row.dayHundredths > 0} forWorker=${row.forOwnerId !== ""}`
14097
14216
  );
14098
- const fields = ["status"];
14099
- if (row.workDate !== "") fields.push("workDate");
14100
- if (row.workNote !== "") fields.push("workNote");
14101
- if (row.assignedOwnerId !== "") fields.push("assignedOwnerId");
14102
- if (row.startDate !== "") fields.push("startDate");
14103
- if (row.forOwnerId !== "") fields.push("forOwnerId");
14104
- for (const field2 of fields) {
14217
+ const fields2 = ["status"];
14218
+ if (row.workDate !== "") fields2.push("workDate");
14219
+ if (row.workNote !== "") fields2.push("workNote");
14220
+ if (row.assignedOwnerId !== "") fields2.push("assignedOwnerId");
14221
+ if (row.startDate !== "") fields2.push("startDate");
14222
+ if (row.forOwnerId !== "") fields2.push("forOwnerId");
14223
+ for (const field2 of fields2) {
14105
14224
  log2(`${TAG24} op=applied visitId=${row.visitId} field=${field2}`);
14106
14225
  }
14107
14226
  } catch (err) {
@@ -15516,11 +15635,19 @@ async function recordManualSendContext(input, deps) {
15516
15635
  mechanism,
15517
15636
  reason
15518
15637
  });
15519
- if (input.entry.role !== "public") return refuse2("not-public-bot");
15638
+ if (input.entry.role === "admin") return refuse2("admin-bot");
15639
+ if (input.entry.role === "unbound") return refuse2("unbound-bot");
15520
15640
  const businessKey = telegramBusinessChannelKey(input.botId, String(input.chatId));
15521
15641
  if (deps.newestInboundTelegramDate(input.accountId, businessKey) !== null) {
15522
15642
  return refuse2("business-chat");
15523
15643
  }
15644
+ const target = checkTelegramOutboundTarget({ chatId: input.chatId, entry: input.entry });
15645
+ if (!target.allowed) {
15646
+ deps.log(
15647
+ `[telegram-manual-send] op=target-refused botId=${input.botId} chatId=${input.chatId} kind=${target.kind} reason=${target.reason}`
15648
+ );
15649
+ return refuse2("chat-not-permitted");
15650
+ }
15524
15651
  const projectDir = deps.findSessionProjectDir(input.sessionId);
15525
15652
  if (projectDir === null) return refuse2("no-transcript");
15526
15653
  const live = deps.isLive(input.channelKey);
@@ -15541,16 +15668,22 @@ async function recordManualSendContext(input, deps) {
15541
15668
  accountDir: input.accountDir,
15542
15669
  botId: input.botId,
15543
15670
  botToken: input.entry.token,
15544
- agentSlug: input.entry.agent,
15671
+ // Task 2773 — `entryAgentSlug`, not `entry.agent`: a specialist entry has
15672
+ // no `agent` field, so the property read is `undefined` there.
15673
+ agentSlug: entryAgentSlug(input.entry),
15545
15674
  senderId: input.senderId,
15546
15675
  // The route admits only a private chat, where the conversation's subject IS
15547
15676
  // the sender. Passed explicitly so the gateway's key and its session id are
15548
15677
  // derived from the same value the store row already carries.
15549
15678
  conversationSubject: input.senderId,
15550
15679
  businessChatId: null,
15551
- role: "public",
15680
+ // Task 2773 — the entry's OWN role, replacing a hardcoded 'public'. It
15681
+ // picks the session derivation in `sessionIdFor`, which for a specialist is
15682
+ // `telegramAdminSessionId` — the same value the caller already derived for
15683
+ // `input.sessionId`.
15684
+ role: input.entry.role,
15552
15685
  personId: null,
15553
- specialist: null,
15686
+ specialist: input.entry.role === "specialist" ? input.entry.specialist : null,
15554
15687
  chatId: String(input.chatId),
15555
15688
  text: input.text,
15556
15689
  // No file by construction: this is text the operator typed into the send
@@ -16185,10 +16318,97 @@ function runTelegramStoreCensus() {
16185
16318
  return c;
16186
16319
  }
16187
16320
 
16321
+ // app/lib/telegram/card-row-census.ts
16322
+ import { closeSync as closeSync6, openSync as openSync6, readdirSync as readdirSync15, readFileSync as readFileSync24, readSync as readSync6, statSync as statSync15 } from "fs";
16323
+ import { join as join30 } from "path";
16324
+ var CARD_ROW_CENSUS_TAG = "[telegram-card-row-census]";
16325
+ var CARD_ROW_WINDOW_MS = 60 * 60 * 1e3;
16326
+ var MAX_BYTES_PER_FILE = 4e6;
16327
+ function tail(path, size) {
16328
+ if (size <= MAX_BYTES_PER_FILE) return readFileSync24(path, "utf-8");
16329
+ const buf = Buffer.alloc(MAX_BYTES_PER_FILE);
16330
+ const fd = openSync6(path, "r");
16331
+ try {
16332
+ const read = readSync6(fd, buf, 0, MAX_BYTES_PER_FILE, size - MAX_BYTES_PER_FILE);
16333
+ const text = buf.subarray(0, read).toString("utf-8");
16334
+ const nl = text.indexOf("\n");
16335
+ return nl === -1 ? "" : text.slice(nl + 1);
16336
+ } finally {
16337
+ closeSync6(fd);
16338
+ }
16339
+ }
16340
+ function fields(line) {
16341
+ const out = {};
16342
+ for (const part of line.split(" ")) {
16343
+ const i = part.indexOf("=");
16344
+ if (i > 0) out[part.slice(0, i)] = part.slice(i + 1);
16345
+ }
16346
+ return out;
16347
+ }
16348
+ function parseCardSends(lines) {
16349
+ const out = [];
16350
+ for (const line of lines) {
16351
+ if (!line.startsWith("[telegram-tools] op=card-sent ")) continue;
16352
+ const f = fields(line);
16353
+ if (f.outcome !== "sent") continue;
16354
+ if (!f.accountId || !f.key || !f.messageId) continue;
16355
+ const atMs = Date.parse(f.at ?? "");
16356
+ if (!Number.isFinite(atMs)) continue;
16357
+ out.push({ accountId: f.accountId, channelKey: f.key, messageId: f.messageId, atMs });
16358
+ }
16359
+ return out;
16360
+ }
16361
+ function rowExists(send) {
16362
+ const rowId = `telegram:${send.accountId}:${send.channelKey}:card:${send.messageId}`;
16363
+ for (const row of readRows(send.accountId, send.channelKey)) {
16364
+ if (isUpdateRecord(row)) continue;
16365
+ if (row.messageId === rowId) return true;
16366
+ }
16367
+ return false;
16368
+ }
16369
+ function collectCardRowCensus(input) {
16370
+ const floor = input.nowMs - CARD_ROW_WINDOW_MS;
16371
+ const sends = /* @__PURE__ */ new Map();
16372
+ try {
16373
+ if (input.logDir) {
16374
+ for (const name of readdirSync15(input.logDir)) {
16375
+ if (!name.startsWith("mcp-") || !name.endsWith(".log")) continue;
16376
+ const path = join30(input.logDir, name);
16377
+ let text;
16378
+ try {
16379
+ text = tail(path, statSync15(path).size);
16380
+ } catch {
16381
+ continue;
16382
+ }
16383
+ for (const send of parseCardSends(text.split("\n"))) {
16384
+ if (send.atMs < floor || send.atMs > input.nowMs) continue;
16385
+ sends.set(`${send.accountId}:${send.channelKey}:${send.messageId}`, send);
16386
+ }
16387
+ }
16388
+ }
16389
+ } catch {
16390
+ }
16391
+ let rows = 0;
16392
+ const missingIds = [];
16393
+ for (const send of sends.values()) {
16394
+ if (rowExists(send)) rows += 1;
16395
+ else missingIds.push(send.messageId);
16396
+ }
16397
+ return { sent: sends.size, rows, missing: missingIds.length, missingIds };
16398
+ }
16399
+ function cardRowCensusLine(input) {
16400
+ const c = collectCardRowCensus(input);
16401
+ const head = `${CARD_ROW_CENSUS_TAG} sent=${c.sent} rows=${c.rows} missing=${c.missing} window=60m`;
16402
+ return c.missing > 0 ? `${head} missing-ids=[${c.missingIds.join(",")}]` : head;
16403
+ }
16404
+ function runTelegramCardRowCensus(logDir, nowMs = Date.now()) {
16405
+ console.error(cardRowCensusLine({ logDir, nowMs }));
16406
+ }
16407
+
16188
16408
  // app/lib/telegram/reach-census.ts
16189
16409
  var import_dist4 = __toESM(require_dist2(), 1);
16190
16410
  import { existsSync as existsSync21 } from "fs";
16191
- import { join as join30 } from "path";
16411
+ import { join as join31 } from "path";
16192
16412
  function collectTelegramReachCensus(accounts) {
16193
16413
  const out = {
16194
16414
  accounts: accounts.length,
@@ -16198,7 +16418,7 @@ function collectTelegramReachCensus(accounts) {
16198
16418
  };
16199
16419
  for (const account of accounts) {
16200
16420
  const reach = (0, import_dist4.collectTelegramReach)({
16201
- storeDir: join30(STORE_ROOT2(), account.accountId),
16421
+ storeDir: join31(STORE_ROOT2(), account.accountId),
16202
16422
  bots: account.bots
16203
16423
  });
16204
16424
  for (const bot of reach.bots) {
@@ -16252,7 +16472,7 @@ function runTelegramCheckoutCensus(bots, now) {
16252
16472
  }
16253
16473
 
16254
16474
  // app/lib/telegram/media-census.ts
16255
- import { join as join31, resolve as resolve20 } from "path";
16475
+ import { join as join32, resolve as resolve20 } from "path";
16256
16476
  function idsByTarget(rows) {
16257
16477
  const out = /* @__PURE__ */ new Map();
16258
16478
  for (const row of rows) {
@@ -16333,8 +16553,8 @@ function readWaOperatorTurns() {
16333
16553
  }
16334
16554
 
16335
16555
  // server/routes/session-reader.ts
16336
- import { readFileSync as readFileSync24, watch as watch4, statSync as statSync15, existsSync as existsSync22, readdirSync as readdirSync15, realpathSync as realpathSync5 } from "fs";
16337
- import { isAbsolute as isAbsolute2, join as join32, relative as relative3, resolve as resolve21, sep as sep5 } from "path";
16556
+ import { readFileSync as readFileSync25, watch as watch4, statSync as statSync16, existsSync as existsSync22, readdirSync as readdirSync16, realpathSync as realpathSync5 } from "fs";
16557
+ import { isAbsolute as isAbsolute2, join as join33, relative as relative3, resolve as resolve21, sep as sep5 } from "path";
16338
16558
 
16339
16559
  // server/routes/admin/manager-client.ts
16340
16560
  var SESSION_ID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -16439,7 +16659,7 @@ function openTaskKey(turns) {
16439
16659
  }
16440
16660
  function readSubagentMeta(dir, hex) {
16441
16661
  try {
16442
- const m = JSON.parse(readFileSync24(join32(dir, `agent-${hex}.meta.json`), "utf8"));
16662
+ const m = JSON.parse(readFileSync25(join33(dir, `agent-${hex}.meta.json`), "utf8"));
16443
16663
  if (typeof m.agentType === "string" && typeof m.description === "string" && typeof m.toolUseId === "string") {
16444
16664
  return { agentType: m.agentType, description: m.description, toolUseId: m.toolUseId };
16445
16665
  }
@@ -16462,7 +16682,7 @@ function runActivityTick(c) {
16462
16682
  c.parentGrewThisTick = false;
16463
16683
  let names = [];
16464
16684
  try {
16465
- names = readdirSync15(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
16685
+ names = readdirSync16(c.subagentsDir).filter((n) => AGENT_JSONL_RE.test(n));
16466
16686
  } catch {
16467
16687
  }
16468
16688
  const entries2 = [];
@@ -16471,7 +16691,7 @@ function runActivityTick(c) {
16471
16691
  let size = 0;
16472
16692
  let mtimeMs = 0;
16473
16693
  try {
16474
- const st = statSync15(join32(c.subagentsDir, name));
16694
+ const st = statSync16(join33(c.subagentsDir, name));
16475
16695
  size = st.size;
16476
16696
  mtimeMs = st.mtimeMs;
16477
16697
  } catch {
@@ -16516,11 +16736,11 @@ app7.get("/stream", requireAdminSession, (c) => {
16516
16736
  const sessionId = c.req.query("sessionId") ?? "";
16517
16737
  const projectDir = c.req.query("projectDir") ?? "";
16518
16738
  const cfg = claudeConfigDir();
16519
- const projectsRoot = cfg ? resolve21(join32(cfg, "projects")) : null;
16739
+ const projectsRoot = cfg ? resolve21(join33(cfg, "projects")) : null;
16520
16740
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
16521
16741
  return c.json({ error: "bad session reference" }, 400);
16522
16742
  }
16523
- const jsonlPath = resolve21(join32(projectDir, `${sessionId}.jsonl`));
16743
+ const jsonlPath = resolve21(join33(projectDir, `${sessionId}.jsonl`));
16524
16744
  if (!jsonlPath.startsWith(projectsRoot + sep5)) {
16525
16745
  return c.json({ error: "bad session reference" }, 400);
16526
16746
  }
@@ -16540,7 +16760,7 @@ data: ${JSON.stringify(turn)}
16540
16760
  start(controller) {
16541
16761
  const fileEnd0 = (() => {
16542
16762
  try {
16543
- return statSync15(jsonlPath).size;
16763
+ return statSync16(jsonlPath).size;
16544
16764
  } catch {
16545
16765
  return 0;
16546
16766
  }
@@ -16548,7 +16768,7 @@ data: ${JSON.stringify(turn)}
16548
16768
  let offset = resumeOffset(lastEventId, fileEnd0);
16549
16769
  console.log(`[wa-stream] op=open conn=${connId} sessionId=${sessionId} channel=reader resume=${offset}`);
16550
16770
  const sessionKey = c.req.query("session_key") ?? "";
16551
- const subagentsDir = resolve21(join32(projectDir, sessionId, "subagents"));
16771
+ const subagentsDir = resolve21(join33(projectDir, sessionId, "subagents"));
16552
16772
  console.log(
16553
16773
  `[webchat-activity] op=watch-subagents key=${sessionKey} dir=${subagentsDir} exists=${existsSync22(subagentsDir)}`
16554
16774
  );
@@ -16701,11 +16921,11 @@ app7.get("/backfill", requireAdminSession, (c) => {
16701
16921
  const beforeRaw = c.req.query("before") ?? "";
16702
16922
  const before = Number(beforeRaw);
16703
16923
  const cfg = claudeConfigDir();
16704
- const projectsRoot = cfg ? resolve21(join32(cfg, "projects")) : null;
16924
+ const projectsRoot = cfg ? resolve21(join33(cfg, "projects")) : null;
16705
16925
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId) || beforeRaw === "" || !Number.isFinite(before) || before < 0) {
16706
16926
  return c.json({ error: "bad session reference" }, 400);
16707
16927
  }
16708
- const jsonlPath = resolve21(join32(projectDir, `${sessionId}.jsonl`));
16928
+ const jsonlPath = resolve21(join33(projectDir, `${sessionId}.jsonl`));
16709
16929
  if (!jsonlPath.startsWith(projectsRoot + sep5)) {
16710
16930
  return c.json({ error: "bad session reference" }, 400);
16711
16931
  }
@@ -16717,7 +16937,7 @@ app7.get("/backfill", requireAdminSession, (c) => {
16717
16937
  const scratchpadRoot = sessionScratchpadRoot(projectDir, sessionId);
16718
16938
  const fileEnd = (() => {
16719
16939
  try {
16720
- return statSync15(jsonlPath).size;
16940
+ return statSync16(jsonlPath).size;
16721
16941
  } catch {
16722
16942
  return 0;
16723
16943
  }
@@ -16760,7 +16980,7 @@ app7.get("/directives", requireAdminSession, (c) => {
16760
16980
  if (!dir || !existsSync22(dir)) return c.json({ entries: [] });
16761
16981
  let names;
16762
16982
  try {
16763
- names = readdirSync15(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
16983
+ names = readdirSync16(dir).filter((n) => DIRECTIVE_NAME_RE.test(n));
16764
16984
  } catch {
16765
16985
  return c.json({ entries: [] });
16766
16986
  }
@@ -16770,7 +16990,7 @@ app7.get("/directives", requireAdminSession, (c) => {
16770
16990
  const pid = Number(pidPart);
16771
16991
  let len = 0;
16772
16992
  try {
16773
- len = statSync15(join32(dir, name)).size;
16993
+ len = statSync16(join33(dir, name)).size;
16774
16994
  } catch {
16775
16995
  }
16776
16996
  return { name, secs, pid, ts: Number.isFinite(secs) ? new Date(secs * 1e3).toISOString() : null, len };
@@ -16785,11 +17005,11 @@ app7.get("/directive", requireAdminSession, (c) => {
16785
17005
  }
16786
17006
  const dir = directiveStoreDir(sessionId);
16787
17007
  if (!dir) return c.json({ error: "no account" }, 404);
16788
- const path = resolve21(join32(dir, name));
17008
+ const path = resolve21(join33(dir, name));
16789
17009
  if (!path.startsWith(dir + sep5)) return c.json({ error: "bad reference" }, 400);
16790
17010
  let body;
16791
17011
  try {
16792
- body = readFileSync24(path, "utf8");
17012
+ body = readFileSync25(path, "utf8");
16793
17013
  } catch {
16794
17014
  return c.json({ error: "not found" }, 404);
16795
17015
  }
@@ -16798,8 +17018,8 @@ app7.get("/directive", requireAdminSession, (c) => {
16798
17018
  var session_reader_default = app7;
16799
17019
 
16800
17020
  // server/routes/operator-conversations.ts
16801
- import { readFileSync as readFileSync25 } from "fs";
16802
- import { basename as basename9, dirname as dirname9, join as join33 } from "path";
17021
+ import { readFileSync as readFileSync26 } from "fs";
17022
+ import { basename as basename9, dirname as dirname9, join as join34 } from "path";
16803
17023
 
16804
17024
  // app/lib/reader/operator-conversations.ts
16805
17025
  function operatorConvName(row) {
@@ -16887,7 +17107,7 @@ app8.get("/conversations", requireAdminSession, async (c) => {
16887
17107
  }
16888
17108
  const cfg = claudeConfigDir();
16889
17109
  if (!cfg) return c.json({ conversations: [] });
16890
- const projectsRoot = join33(cfg, "projects");
17110
+ const projectsRoot = join34(cfg, "projects");
16891
17111
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
16892
17112
  const rows = [];
16893
17113
  let sessionRowsExcludedUntagged = 0;
@@ -16905,7 +17125,7 @@ app8.get("/conversations", requireAdminSession, async (c) => {
16905
17125
  enumeratedCount += 1;
16906
17126
  const sessionId = basename9(path).replace(/\.jsonl$/, "");
16907
17127
  const projectDir = dirname9(path);
16908
- const meta = readSidecarMeta(join33(projectDir, `${sessionId}.meta.json`));
17128
+ const meta = readSidecarMeta(join34(projectDir, `${sessionId}.meta.json`));
16909
17129
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
16910
17130
  if (multiAccount) {
16911
17131
  if (meta.accountId === null) {
@@ -16917,7 +17137,7 @@ app8.get("/conversations", requireAdminSession, async (c) => {
16917
17137
  let body = "";
16918
17138
  let bodyRead = false;
16919
17139
  try {
16920
- body = readFileSync25(path, "utf8");
17140
+ body = readFileSync26(path, "utf8");
16921
17141
  bodyRead = true;
16922
17142
  bodyReadCount += 1;
16923
17143
  } catch (err) {
@@ -17093,7 +17313,7 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
17093
17313
  const { value, state } = await getOrCompute("conversations-all", houseId, async () => {
17094
17314
  const cfg = claudeConfigDir();
17095
17315
  if (!cfg) return { conversations: [] };
17096
- const projectsRoot = join33(cfg, "projects");
17316
+ const projectsRoot = join34(cfg, "projects");
17097
17317
  const userTitles = loadUserTitles(ACCOUNTS_DIR ?? null);
17098
17318
  const validAccounts = listValidAccounts();
17099
17319
  const validIds = new Set(validAccounts.map((a) => a.accountId));
@@ -17113,7 +17333,7 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
17113
17333
  enumeratedCount += 1;
17114
17334
  const sessionId = basename9(path).replace(/\.jsonl$/, "");
17115
17335
  const projectDir = dirname9(path);
17116
- const meta = readSidecarMeta(join33(projectDir, `${sessionId}.meta.json`));
17336
+ const meta = readSidecarMeta(join34(projectDir, `${sessionId}.meta.json`));
17117
17337
  if (!isReaderChannelSession(meta.role, meta.channel)) continue;
17118
17338
  if (meta.accountId === null) {
17119
17339
  untaggedCount += 1;
@@ -17124,7 +17344,7 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
17124
17344
  let body = "";
17125
17345
  let bodyRead = false;
17126
17346
  try {
17127
- body = readFileSync25(path, "utf8");
17347
+ body = readFileSync26(path, "utf8");
17128
17348
  bodyRead = true;
17129
17349
  bodyReadCount += 1;
17130
17350
  } catch (err) {
@@ -17249,8 +17469,8 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
17249
17469
  var operator_conversations_default = app8;
17250
17470
 
17251
17471
  // server/routes/public-reader.ts
17252
- import { statSync as statSync17, watch as watch6, readFileSync as readFileSync26, readdirSync as readdirSync17 } from "fs";
17253
- import { basename as basename10, dirname as dirname10, join as join34, resolve as resolve23, sep as sep7 } from "path";
17472
+ import { statSync as statSync18, watch as watch6, readFileSync as readFileSync27, readdirSync as readdirSync18 } from "fs";
17473
+ import { basename as basename10, dirname as dirname10, join as join35, resolve as resolve23, sep as sep7 } from "path";
17254
17474
 
17255
17475
  // app/lib/reader/delivered-kinds.ts
17256
17476
  var PUBLIC_DELIVERED_KINDS = /* @__PURE__ */ new Set([
@@ -17339,10 +17559,10 @@ function enumeratePublicRows() {
17339
17559
  const cfg = claudeConfigDir();
17340
17560
  if (!cfg) return [];
17341
17561
  const rows = [];
17342
- for (const { path } of enumerateJsonls(join34(cfg, "projects"))) {
17562
+ for (const { path } of enumerateJsonls(join35(cfg, "projects"))) {
17343
17563
  const sessionId = basename10(path).replace(/\.jsonl$/, "");
17344
17564
  const projectDir = dirname10(path);
17345
- const meta = readSidecarMeta(join34(projectDir, `${sessionId}.meta.json`));
17565
+ const meta = readSidecarMeta(join35(projectDir, `${sessionId}.meta.json`));
17346
17566
  rows.push({
17347
17567
  sessionId,
17348
17568
  projectDir,
@@ -17409,15 +17629,15 @@ app9.get("/backfill", (c) => {
17409
17629
  const beforeRaw = c.req.query("before") ?? "";
17410
17630
  const before = Number(beforeRaw);
17411
17631
  const cfg = claudeConfigDir();
17412
- const projectsRoot = cfg ? resolve23(join34(cfg, "projects")) : null;
17632
+ const projectsRoot = cfg ? resolve23(join35(cfg, "projects")) : null;
17413
17633
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId) || beforeRaw === "" || !Number.isFinite(before) || before < 0) {
17414
17634
  return c.json({ error: "bad session reference" }, 400);
17415
17635
  }
17416
- const jsonlPath = resolve23(join34(projectDir, `${sessionId}.jsonl`));
17636
+ const jsonlPath = resolve23(join35(projectDir, `${sessionId}.jsonl`));
17417
17637
  if (!jsonlPath.startsWith(projectsRoot + sep7)) {
17418
17638
  return c.json({ error: "bad session reference" }, 400);
17419
17639
  }
17420
- const meta = readSidecarMeta(join34(projectDir, `${sessionId}.meta.json`));
17640
+ const meta = readSidecarMeta(join35(projectDir, `${sessionId}.meta.json`));
17421
17641
  const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
17422
17642
  const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
17423
17643
  if (!(isPublicWebchat && owns)) {
@@ -17433,7 +17653,7 @@ app9.get("/backfill", (c) => {
17433
17653
  }
17434
17654
  const fileEnd = (() => {
17435
17655
  try {
17436
- return statSync17(jsonlPath).size;
17656
+ return statSync18(jsonlPath).size;
17437
17657
  } catch {
17438
17658
  return 0;
17439
17659
  }
@@ -17476,15 +17696,15 @@ app9.get("/stream", (c) => {
17476
17696
  const sessionId = c.req.query("sessionId") ?? "";
17477
17697
  const projectDir = c.req.query("projectDir") ?? "";
17478
17698
  const cfg = claudeConfigDir();
17479
- const projectsRoot = cfg ? resolve23(join34(cfg, "projects")) : null;
17699
+ const projectsRoot = cfg ? resolve23(join35(cfg, "projects")) : null;
17480
17700
  if (!cfg || !projectsRoot || !SESSION_ID_RE2.test(sessionId)) {
17481
17701
  return c.json({ error: "bad session reference" }, 400);
17482
17702
  }
17483
- const jsonlPath = resolve23(join34(projectDir, `${sessionId}.jsonl`));
17703
+ const jsonlPath = resolve23(join35(projectDir, `${sessionId}.jsonl`));
17484
17704
  if (!jsonlPath.startsWith(projectsRoot + sep7)) {
17485
17705
  return c.json({ error: "bad session reference" }, 400);
17486
17706
  }
17487
- const meta = readSidecarMeta(join34(projectDir, `${sessionId}.meta.json`));
17707
+ const meta = readSidecarMeta(join35(projectDir, `${sessionId}.meta.json`));
17488
17708
  const isPublicWebchat = meta.role === "public" && meta.channel === "webchat";
17489
17709
  const owns = visitor.kind === "person" ? meta.personId === visitor.personId : meta.personId === null && meta.visitorId === visitor.visitorId;
17490
17710
  if (!(isPublicWebchat && owns)) {
@@ -17513,7 +17733,7 @@ data: ${JSON.stringify(turn)}
17513
17733
  };
17514
17734
  const fileEnd0 = (() => {
17515
17735
  try {
17516
- return statSync17(jsonlPath).size;
17736
+ return statSync18(jsonlPath).size;
17517
17737
  } catch {
17518
17738
  return 0;
17519
17739
  }
@@ -17628,9 +17848,9 @@ app9.get("/attachment/:attachmentId", (c) => {
17628
17848
  let dataFile;
17629
17849
  let buffer;
17630
17850
  try {
17631
- dataFile = readdirSync17(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
17851
+ dataFile = readdirSync18(dir).find((f) => !f.endsWith(".meta.json") && !f.endsWith(EXTRACTED_TEXT_SUFFIX));
17632
17852
  if (!dataFile) throw new Error("no data file");
17633
- buffer = readFileSync26(resolve23(dir, dataFile));
17853
+ buffer = readFileSync27(resolve23(dir, dataFile));
17634
17854
  } catch {
17635
17855
  console.error(`${ATT_TAG} op=denied session=${ses} att=${at} reason=not-found`);
17636
17856
  return new Response("Not found", { status: 404 });
@@ -17652,20 +17872,20 @@ var public_reader_default = app9;
17652
17872
 
17653
17873
  // server/routes/webchat.ts
17654
17874
  import { basename as basename11, dirname as dirname11 } from "path";
17655
- import { join as join37 } from "path";
17656
- import { existsSync as existsSync25, readdirSync as readdirSync19, readFileSync as readFileSync29, renameSync as renameSync4, statSync as statSync18, writeFileSync as writeFileSync11 } from "fs";
17875
+ import { join as join38 } from "path";
17876
+ import { existsSync as existsSync25, readdirSync as readdirSync20, readFileSync as readFileSync30, renameSync as renameSync4, statSync as statSync19, writeFileSync as writeFileSync11 } from "fs";
17657
17877
 
17658
17878
  // server/canonical-webchat-override.ts
17659
- import { readFileSync as readFileSync27, writeFileSync as writeFileSync9, renameSync as renameSync2 } from "fs";
17660
- import { join as join35 } from "path";
17879
+ import { readFileSync as readFileSync28, writeFileSync as writeFileSync9, renameSync as renameSync2 } from "fs";
17880
+ import { join as join36 } from "path";
17661
17881
  var FILE = "canonical-webchat-session.json";
17662
17882
  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}$/;
17663
17883
  function canonicalOverridePath(accountDir) {
17664
- return join35(accountDir, FILE);
17884
+ return join36(accountDir, FILE);
17665
17885
  }
17666
17886
  function readRaw(accountDir) {
17667
17887
  try {
17668
- return JSON.parse(readFileSync27(canonicalOverridePath(accountDir), "utf8"));
17888
+ return JSON.parse(readFileSync28(canonicalOverridePath(accountDir), "utf8"));
17669
17889
  } catch {
17670
17890
  return null;
17671
17891
  }
@@ -17778,8 +17998,8 @@ function isNewSessionEffortLevel(value) {
17778
17998
  }
17779
17999
 
17780
18000
  // ../services/claude-session-manager/src/sidecar-store.ts
17781
- import { existsSync as existsSync24, mkdirSync as mkdirSync4, readdirSync as readdirSync18, readFileSync as readFileSync28, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
17782
- import { join as join36 } from "path";
18001
+ import { existsSync as existsSync24, mkdirSync as mkdirSync4, readdirSync as readdirSync19, readFileSync as readFileSync29, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
18002
+ import { join as join37 } from "path";
17783
18003
  function escapeRegExp(s) {
17784
18004
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
17785
18005
  }
@@ -17787,7 +18007,7 @@ function createSidecarStore(config) {
17787
18007
  const { suffix, validate: validate3 } = config;
17788
18008
  const suffixRe = new RegExp(`${escapeRegExp(suffix)}$`);
17789
18009
  function pathFor(sessionsDir, id) {
17790
- return join36(sessionsDir, `${id}${suffix}`);
18010
+ return join37(sessionsDir, `${id}${suffix}`);
17791
18011
  }
17792
18012
  function write(sessionsDir, id, record) {
17793
18013
  mkdirSync4(sessionsDir, { recursive: true });
@@ -17808,7 +18028,7 @@ function createSidecarStore(config) {
17808
18028
  function readFileAt(path) {
17809
18029
  let raw;
17810
18030
  try {
17811
- raw = readFileSync28(path, "utf8");
18031
+ raw = readFileSync29(path, "utf8");
17812
18032
  } catch {
17813
18033
  return null;
17814
18034
  }
@@ -17824,17 +18044,17 @@ function createSidecarStore(config) {
17824
18044
  function readAll3(sessionsDir, onSkip) {
17825
18045
  let names;
17826
18046
  try {
17827
- names = readdirSync18(sessionsDir, { withFileTypes: true }).filter((entry3) => entry3.isFile()).map((entry3) => entry3.name);
18047
+ names = readdirSync19(sessionsDir, { withFileTypes: true }).filter((entry3) => entry3.isFile()).map((entry3) => entry3.name);
17828
18048
  } catch {
17829
18049
  return [];
17830
18050
  }
17831
18051
  const out = [];
17832
18052
  for (const name of names) {
17833
18053
  if (!suffixRe.test(name)) continue;
17834
- const path = join36(sessionsDir, name);
18054
+ const path = join37(sessionsDir, name);
17835
18055
  let raw;
17836
18056
  try {
17837
- raw = readFileSync28(path, "utf8");
18057
+ raw = readFileSync29(path, "utf8");
17838
18058
  } catch {
17839
18059
  onSkip?.(name, "unreadable");
17840
18060
  continue;
@@ -17907,7 +18127,7 @@ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9
17907
18127
  function locateSession(sessionId) {
17908
18128
  const dir = findSessionProjectDir(sessionId);
17909
18129
  if (!dir) return { projectDir: null, channel: null, accountId: null };
17910
- const meta = readSidecarMeta(join37(dir, `${sessionId}.meta.json`));
18130
+ const meta = readSidecarMeta(join38(dir, `${sessionId}.meta.json`));
17911
18131
  return { projectDir: dir, channel: meta.channel, accountId: meta.accountId };
17912
18132
  }
17913
18133
  function resolveQuoted(sessionId, quotedUuid, sendId) {
@@ -17925,7 +18145,7 @@ function resolveQuoted(sessionId, quotedUuid, sendId) {
17925
18145
  let line;
17926
18146
  try {
17927
18147
  const needle = `"uuid":"${quotedUuid}"`;
17928
- line = readFileSync29(join37(projectDir, `${sessionId}.jsonl`), "utf8").split("\n").find((l) => l.includes(needle));
18148
+ line = readFileSync30(join38(projectDir, `${sessionId}.jsonl`), "utf8").split("\n").find((l) => l.includes(needle));
17929
18149
  } catch {
17930
18150
  log2(false, 0);
17931
18151
  return void 0;
@@ -17942,15 +18162,15 @@ function resolveQuoted(sessionId, quotedUuid, sendId) {
17942
18162
  function locateSidecar(sessionId) {
17943
18163
  const cfg = claudeConfigDir();
17944
18164
  if (!cfg) return null;
17945
- const projectsRoot = join37(cfg, "projects");
18165
+ const projectsRoot = join38(cfg, "projects");
17946
18166
  let slugs;
17947
18167
  try {
17948
- slugs = readdirSync19(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
18168
+ slugs = readdirSync20(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
17949
18169
  } catch {
17950
18170
  return null;
17951
18171
  }
17952
18172
  for (const slug of slugs) {
17953
- const metaPath = join37(projectsRoot, slug, `${sessionId}.meta.json`);
18173
+ const metaPath = join38(projectsRoot, slug, `${sessionId}.meta.json`);
17954
18174
  if (existsSync25(metaPath)) return readSidecarMeta(metaPath);
17955
18175
  }
17956
18176
  return null;
@@ -17960,7 +18180,7 @@ function readTargetOwner(sessionId) {
17960
18180
  if (sidecar !== null) return { found: true, owner: sidecar.adminUserId };
17961
18181
  const { projectDir } = locateSession(sessionId);
17962
18182
  if (projectDir !== null) {
17963
- return { found: true, owner: readSidecarMeta(join37(projectDir, `${sessionId}.meta.json`)).adminUserId };
18183
+ return { found: true, owner: readSidecarMeta(join38(projectDir, `${sessionId}.meta.json`)).adminUserId };
17964
18184
  }
17965
18185
  return { found: false, owner: null };
17966
18186
  }
@@ -17968,7 +18188,7 @@ function readSourceRowMeta(sessionId) {
17968
18188
  const sidecar = locateSidecar(sessionId);
17969
18189
  if (sidecar !== null) return sidecar;
17970
18190
  const { projectDir } = locateSession(sessionId);
17971
- if (projectDir !== null) return readSidecarMeta(join37(projectDir, `${sessionId}.meta.json`));
18191
+ if (projectDir !== null) return readSidecarMeta(join38(projectDir, `${sessionId}.meta.json`));
17972
18192
  return null;
17973
18193
  }
17974
18194
  function ownedByRequester(owner, requesterUserId, primaryUserId) {
@@ -17981,7 +18201,7 @@ function sessionSidecarExists(sessionId) {
17981
18201
  function jsonlSizeBytes(projectDir, sessionId) {
17982
18202
  if (projectDir === null) return null;
17983
18203
  try {
17984
- return statSync18(join37(projectDir, `${sessionId}.jsonl`)).size;
18204
+ return statSync19(join38(projectDir, `${sessionId}.jsonl`)).size;
17985
18205
  } catch {
17986
18206
  return null;
17987
18207
  }
@@ -18109,15 +18329,15 @@ function latestAdminWebchatSessionId(requesterUserId, primaryUserId, scopeAccoun
18109
18329
  const cfg = claudeConfigDir();
18110
18330
  if (!cfg) return null;
18111
18331
  let best = null;
18112
- for (const { path, isSubagent, archived } of enumerateJsonls(join37(cfg, "projects"))) {
18332
+ for (const { path, isSubagent, archived } of enumerateJsonls(join38(cfg, "projects"))) {
18113
18333
  if (isSubagent || archived) continue;
18114
18334
  const id = basename11(path).slice(0, -".jsonl".length);
18115
- const meta = readSidecarMeta(join37(dirname11(path), `${id}.meta.json`));
18335
+ const meta = readSidecarMeta(join38(dirname11(path), `${id}.meta.json`));
18116
18336
  if (meta.role !== "admin" || meta.channel !== "webchat") continue;
18117
18337
  if (multiAccount && meta.accountId !== scopeAccountId) continue;
18118
18338
  let mtimeMs;
18119
18339
  try {
18120
- mtimeMs = statSync18(path).mtimeMs;
18340
+ mtimeMs = statSync19(path).mtimeMs;
18121
18341
  } catch {
18122
18342
  continue;
18123
18343
  }
@@ -18472,9 +18692,9 @@ ${timeline}`;
18472
18692
  console.error(`[webchat:settings] op=${op} outcome=refused value=${value} accountId=none reason=no-account-for-session`);
18473
18693
  return c.json({ error: "account unresolved" }, 503);
18474
18694
  }
18475
- const accountJsonPath = join37(account.accountDir, "account.json");
18695
+ const accountJsonPath = join38(account.accountDir, "account.json");
18476
18696
  try {
18477
- const parsed = JSON.parse(readFileSync29(accountJsonPath, "utf8"));
18697
+ const parsed = JSON.parse(readFileSync30(accountJsonPath, "utf8"));
18478
18698
  if (op === "model") parsed.adminModel = value;
18479
18699
  else if (op === "effort") parsed.effort = value;
18480
18700
  else parsed.adminPermissionMode = value;
@@ -18520,7 +18740,7 @@ ${timeline}`;
18520
18740
  const cfg2 = claudeConfigDir();
18521
18741
  if (cfg2) {
18522
18742
  const source2 = resolveBridgeSource(
18523
- join37(cfg2, "sessions"),
18743
+ join38(cfg2, "sessions"),
18524
18744
  target,
18525
18745
  (id) => locateSession(id).projectDir !== null
18526
18746
  );
@@ -18547,7 +18767,7 @@ ${timeline}`;
18547
18767
  const cfg = claudeConfigDir();
18548
18768
  let projectDir = null;
18549
18769
  if (cfg) {
18550
- for (const { path } of enumerateJsonls(join37(cfg, "projects"))) {
18770
+ for (const { path } of enumerateJsonls(join38(cfg, "projects"))) {
18551
18771
  if (basename11(path) === `${sessionId}.jsonl`) {
18552
18772
  projectDir = dirname11(path);
18553
18773
  break;
@@ -18555,7 +18775,7 @@ ${timeline}`;
18555
18775
  }
18556
18776
  }
18557
18777
  if (source === "latest" && requesterUserId !== null) {
18558
- const owner = readSidecarMeta(join37(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
18778
+ const owner = readSidecarMeta(join38(projectDir ?? "", `${sessionId}.meta.json`)).adminUserId;
18559
18779
  if (owner !== null && owner !== requesterUserId && requesterUserId !== primaryUserId) {
18560
18780
  console.error(`[webchat:inbound] op=session-resolve-foreign key=${sessionId.slice(0, 8)} owner=${owner.slice(0, 8)} requester=${requesterUserId.slice(0, 8)}`);
18561
18781
  }
@@ -18680,13 +18900,13 @@ var webchat_greeting_default = app10;
18680
18900
 
18681
18901
  // server/routes/telegram.ts
18682
18902
  import { homedir as homedir2 } from "os";
18683
- import { resolve as resolve25, join as join42 } from "path";
18684
- import { existsSync as existsSync29, readFileSync as readFileSync33 } from "fs";
18903
+ import { resolve as resolve25, join as join43 } from "path";
18904
+ import { existsSync as existsSync29, readFileSync as readFileSync34 } from "fs";
18685
18905
 
18686
18906
  // ../lib/telegram-managed-bot/src/index.ts
18687
18907
  import { randomBytes } from "crypto";
18688
- import { existsSync as existsSync26, readFileSync as readFileSync30, writeFileSync as writeFileSync12, mkdirSync as mkdirSync5 } from "fs";
18689
- import { dirname as dirname12, join as join38 } from "path";
18908
+ import { existsSync as existsSync26, readFileSync as readFileSync31, writeFileSync as writeFileSync12, mkdirSync as mkdirSync5 } from "fs";
18909
+ import { dirname as dirname12, join as join39 } from "path";
18690
18910
  var TELEGRAM_API = "https://api.telegram.org";
18691
18911
  var TELEGRAM_ALLOWED_UPDATES = [
18692
18912
  "message",
@@ -18753,13 +18973,13 @@ async function setTelegramWebhook(botToken, webhookUrl, secretFilePath, fetchImp
18753
18973
  return { ok: true, secret };
18754
18974
  }
18755
18975
  function writeTelegramBotEntry(accountDir, entry3) {
18756
- const configPath3 = join38(accountDir, "account.json");
18976
+ const configPath3 = join39(accountDir, "account.json");
18757
18977
  if (!existsSync26(configPath3)) {
18758
18978
  return { ok: false, error: `account.json not found at ${configPath3}` };
18759
18979
  }
18760
18980
  let config;
18761
18981
  try {
18762
- config = JSON.parse(readFileSync30(configPath3, "utf-8"));
18982
+ config = JSON.parse(readFileSync31(configPath3, "utf-8"));
18763
18983
  } catch (e) {
18764
18984
  return { ok: false, error: `account.json parse failed: ${e.message}` };
18765
18985
  }
@@ -18842,82 +19062,6 @@ async function setManagedBotAccessSettings(managingBotToken, userId, isAccessRes
18842
19062
  return r.ok ? { ok: true } : { ok: false, error: r.error };
18843
19063
  }
18844
19064
 
18845
- // app/lib/telegram/access-control.ts
18846
- function checkTelegramAccess(params) {
18847
- const { senderId, entry: entry3, chatId, chatType } = params;
18848
- const isGroup = chatType === "group" || chatType === "supergroup";
18849
- const isChannel = chatType === "channel";
18850
- if (isChannel && entry3.role !== "public") {
18851
- return {
18852
- allowed: false,
18853
- reason: entry3.role === "admin" ? "admin-entry-no-channels" : entry3.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
18854
- agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
18855
- };
18856
- }
18857
- if (isGroup && entry3.role !== "public") {
18858
- return {
18859
- allowed: false,
18860
- reason: entry3.role === "admin" ? "admin-entry-no-groups" : entry3.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
18861
- agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
18862
- };
18863
- }
18864
- if (entry3.role === "admin") {
18865
- const adminUsers = entry3.adminUsers ?? [];
18866
- if (adminUsers.includes(senderId)) {
18867
- return { allowed: true, reason: "admin-binding", agentType: "admin" };
18868
- }
18869
- return { allowed: false, reason: "not-admin-user", agentType: "admin" };
18870
- }
18871
- if (isGroup && entry3.role === "public") {
18872
- const groupPolicy = entry3.groupPolicy ?? "disabled";
18873
- if (groupPolicy === "allowlist") {
18874
- const allowGroups = entry3.allowGroups ?? [];
18875
- if (allowGroups.includes(chatId)) {
18876
- return { allowed: true, reason: "group-allowlist-match", agentType: "public" };
18877
- }
18878
- return { allowed: false, reason: "not-in-allowGroups", agentType: "public" };
18879
- }
18880
- return { allowed: false, reason: "group-policy-disabled", agentType: "public" };
18881
- }
18882
- if (isChannel && entry3.role === "public") {
18883
- const channelPolicy = entry3.channelPolicy ?? "disabled";
18884
- if (channelPolicy === "allowlist") {
18885
- const allowChannels = entry3.allowChannels ?? [];
18886
- if (allowChannels.includes(chatId)) {
18887
- return { allowed: true, reason: "channel-allowlist-match", agentType: "public" };
18888
- }
18889
- return { allowed: false, reason: "not-in-allowChannels", agentType: "public" };
18890
- }
18891
- return { allowed: false, reason: "channel-policy-disabled", agentType: "public" };
18892
- }
18893
- if (entry3.role === "unbound") {
18894
- return { allowed: false, reason: "bot-unbound", agentType: "public" };
18895
- }
18896
- if (entry3.role === "specialist") {
18897
- const allowFrom = entry3.allowFrom ?? [];
18898
- if (allowFrom.includes(senderId)) {
18899
- return { allowed: true, reason: "specialist-allowlist-match", agentType: "specialist" };
18900
- }
18901
- return { allowed: false, reason: "not-in-allowlist", agentType: "specialist" };
18902
- }
18903
- const policy = entry3.dmPolicy ?? "disabled";
18904
- switch (policy) {
18905
- case "open":
18906
- return { allowed: true, reason: "dm-policy-open", agentType: "public" };
18907
- case "allowlist": {
18908
- const allowFrom = entry3.allowFrom ?? [];
18909
- if (allowFrom.includes(senderId)) {
18910
- return { allowed: true, reason: "allowlist-match", agentType: "public" };
18911
- }
18912
- return { allowed: false, reason: "not-in-allowlist", agentType: "public" };
18913
- }
18914
- case "disabled":
18915
- return { allowed: false, reason: "dm-policy-disabled", agentType: "public" };
18916
- default:
18917
- return { allowed: false, reason: "unknown-dm-policy", agentType: "public" };
18918
- }
18919
- }
18920
-
18921
19065
  // app/lib/telegram/managed-bot.ts
18922
19066
  function parseManagedBotUpdate(u) {
18923
19067
  const id = u?.bot?.id;
@@ -19730,7 +19874,7 @@ function routeTelegramUpdate(input) {
19730
19874
 
19731
19875
  // app/lib/telegram/inbound/media.ts
19732
19876
  var import_dist5 = __toESM(require_dist4(), 1);
19733
- import { join as join39 } from "path";
19877
+ import { join as join40 } from "path";
19734
19878
  var TAG30 = "[telegram-inbound]";
19735
19879
  var TELEGRAM_MEDIA_DIR = "/tmp/maxy-media";
19736
19880
  var DOWNLOAD_TIMEOUT_MS = 45e3;
@@ -19758,7 +19902,7 @@ async function downloadTelegramMedia(input) {
19758
19902
  if (!meta.ok) {
19759
19903
  result = { ...base, failure: meta.failure };
19760
19904
  } else {
19761
- const path = join39(
19905
+ const path = join40(
19762
19906
  dir,
19763
19907
  `tg-${(0, import_dist5.pathSafe)(input.botId)}-${(0, import_dist5.pathSafe)(item.fileUniqueId)}.${(0, import_dist5.extensionFor)(meta.filePath, item.mimetype)}`
19764
19908
  );
@@ -20625,8 +20769,8 @@ async function handleDispatchMedia(p) {
20625
20769
  }
20626
20770
 
20627
20771
  // app/lib/telegram/live-location.ts
20628
- import { existsSync as existsSync27, mkdirSync as mkdirSync6, readFileSync as readFileSync31, readdirSync as readdirSync20, statSync as statSync19, writeFileSync as writeFileSync13 } from "fs";
20629
- import { join as join40 } from "path";
20772
+ import { existsSync as existsSync27, mkdirSync as mkdirSync6, readFileSync as readFileSync32, readdirSync as readdirSync21, statSync as statSync20, writeFileSync as writeFileSync13 } from "fs";
20773
+ import { join as join41 } from "path";
20630
20774
  var TAG37 = "[telegram-inbound]";
20631
20775
  var LIVE_LOCATION_FILE = "telegram-live-location.json";
20632
20776
  var LIVE_LOCATION_CENSUS_TAG = "[telegram-live-location-census]";
@@ -20641,7 +20785,7 @@ function isRecord(v) {
20641
20785
  function readAll(path) {
20642
20786
  if (!existsSync27(path)) return {};
20643
20787
  try {
20644
- const parsed = JSON.parse(readFileSync31(path, "utf-8"));
20788
+ const parsed = JSON.parse(readFileSync32(path, "utf-8"));
20645
20789
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
20646
20790
  const out = {};
20647
20791
  for (const [k, v] of Object.entries(parsed)) {
@@ -20658,7 +20802,7 @@ function isExpired(rec, now) {
20658
20802
  function writeLiveLocation(input) {
20659
20803
  const { accountDir, botId, senderId, now, emit } = input;
20660
20804
  const key2 = liveLocationKey(botId, senderId);
20661
- const path = join40(accountDir, LIVE_LOCATION_FILE);
20805
+ const path = join41(accountDir, LIVE_LOCATION_FILE);
20662
20806
  const stored = readAll(path);
20663
20807
  const livePeriodS = input.livePeriodS ?? stored[key2]?.livePeriodS ?? null;
20664
20808
  if (livePeriodS === null) {
@@ -20707,9 +20851,9 @@ function liveLocationCensusLine(accountsRoot, now) {
20707
20851
  let expired = 0;
20708
20852
  let oldestAgeMs = 0;
20709
20853
  try {
20710
- for (const entry3 of readdirSync20(accountsRoot)) {
20711
- const path = join40(accountsRoot, entry3, LIVE_LOCATION_FILE);
20712
- if (!existsSync27(path) || !statSync19(path).isFile()) continue;
20854
+ for (const entry3 of readdirSync21(accountsRoot)) {
20855
+ const path = join41(accountsRoot, entry3, LIVE_LOCATION_FILE);
20856
+ if (!existsSync27(path) || !statSync20(path).isFile()) continue;
20713
20857
  for (const rec of Object.values(readAll(path))) {
20714
20858
  records5 += 1;
20715
20859
  if (isExpired(rec, now)) expired += 1;
@@ -20728,8 +20872,8 @@ function liveLocationCensusLine(accountsRoot, now) {
20728
20872
  }
20729
20873
 
20730
20874
  // app/lib/telegram/business-connection.ts
20731
- import { existsSync as existsSync28, mkdirSync as mkdirSync7, readFileSync as readFileSync32, writeFileSync as writeFileSync14 } from "fs";
20732
- import { join as join41 } from "path";
20875
+ import { existsSync as existsSync28, mkdirSync as mkdirSync7, readFileSync as readFileSync33, writeFileSync as writeFileSync14 } from "fs";
20876
+ import { join as join42 } from "path";
20733
20877
  var TAG38 = "[telegram-inbound]";
20734
20878
  var BUSINESS_CONNECTION_FILE = "telegram-business-connection.json";
20735
20879
  function truncateConnectionId(id) {
@@ -20743,7 +20887,7 @@ function isRecord2(v) {
20743
20887
  function readAll2(path) {
20744
20888
  if (!existsSync28(path)) return {};
20745
20889
  try {
20746
- const parsed = JSON.parse(readFileSync32(path, "utf-8"));
20890
+ const parsed = JSON.parse(readFileSync33(path, "utf-8"));
20747
20891
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
20748
20892
  const out = {};
20749
20893
  for (const [k, v] of Object.entries(parsed)) {
@@ -20779,7 +20923,7 @@ function writeBusinessConnection(input) {
20779
20923
  rights,
20780
20924
  at: now
20781
20925
  };
20782
- const path = join41(accountDir, BUSINESS_CONNECTION_FILE);
20926
+ const path = join42(accountDir, BUSINESS_CONNECTION_FILE);
20783
20927
  const all = readAll2(path);
20784
20928
  all[botId] = next;
20785
20929
  try {
@@ -20793,12 +20937,12 @@ function writeBusinessConnection(input) {
20793
20937
  return { written: true, reason: "ok" };
20794
20938
  }
20795
20939
  function readBusinessConnection(accountDir, botId) {
20796
- const rec = readAll2(join41(accountDir, BUSINESS_CONNECTION_FILE))[botId];
20940
+ const rec = readAll2(join42(accountDir, BUSINESS_CONNECTION_FILE))[botId];
20797
20941
  if (!rec || !rec.isEnabled) return null;
20798
20942
  return rec;
20799
20943
  }
20800
20944
  function readAllBusinessConnections(accountDir) {
20801
- return readAll2(join41(accountDir, BUSINESS_CONNECTION_FILE));
20945
+ return readAll2(join42(accountDir, BUSINESS_CONNECTION_FILE));
20802
20946
  }
20803
20947
 
20804
20948
  // app/lib/telegram/business-window.ts
@@ -20854,10 +20998,10 @@ function logValue4(v) {
20854
20998
  }
20855
20999
  function configDirName() {
20856
21000
  const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
20857
- const brandPath = join42(platformRoot5, "config", "brand.json");
21001
+ const brandPath = join43(platformRoot5, "config", "brand.json");
20858
21002
  if (existsSync29(brandPath)) {
20859
21003
  try {
20860
- return JSON.parse(readFileSync33(brandPath, "utf-8")).configDir ?? ".maxy";
21004
+ return JSON.parse(readFileSync34(brandPath, "utf-8")).configDir ?? ".maxy";
20861
21005
  } catch {
20862
21006
  }
20863
21007
  }
@@ -20885,7 +21029,7 @@ function provisionedWebhookUrl(c, botId) {
20885
21029
  }
20886
21030
  function accountConfig(accountDir) {
20887
21031
  try {
20888
- return JSON.parse(readFileSync33(join42(accountDir, "account.json"), "utf-8"));
21032
+ return JSON.parse(readFileSync34(join43(accountDir, "account.json"), "utf-8"));
20889
21033
  } catch {
20890
21034
  return {};
20891
21035
  }
@@ -20967,7 +21111,7 @@ app11.post("/", async (c) => {
20967
21111
  console.error(`${TAG39} op=secret botId=${botId} result=missing-file`);
20968
21112
  return c.json({ ok: false }, 401);
20969
21113
  }
20970
- const expected = readFileSync33(sp, "utf-8").trim();
21114
+ const expected = readFileSync34(sp, "utf-8").trim();
20971
21115
  const got = c.req.header("x-telegram-bot-api-secret-token") ?? "";
20972
21116
  if (got !== expected) {
20973
21117
  console.error(`${TAG39} op=secret botId=${botId} result=mismatch`);
@@ -21912,16 +22056,16 @@ function mountTelegramRoutes(app81, telegramRoutes) {
21912
22056
  }
21913
22057
 
21914
22058
  // server/routes/quickbooks.ts
21915
- import { join as join43 } from "path";
21916
- import { existsSync as existsSync30, readFileSync as readFileSync34, writeFileSync as writeFileSync15, mkdirSync as mkdirSync8, rmSync as rmSync4, renameSync as renameSync5 } from "fs";
22059
+ import { join as join44 } from "path";
22060
+ import { existsSync as existsSync30, readFileSync as readFileSync35, writeFileSync as writeFileSync15, mkdirSync as mkdirSync8, rmSync as rmSync4, renameSync as renameSync5 } from "fs";
21917
22061
  var TAG40 = "[quickbooks]";
21918
22062
  var INTUIT_TOKEN_URL = "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer";
21919
22063
  var STATE_RE = /^[A-Za-z0-9_-]+$/;
21920
22064
  function pendingPath(accountDir, state) {
21921
- return join43(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
22065
+ return join44(accountDir, "secrets", "quickbooks-pending", `${state}.json`);
21922
22066
  }
21923
22067
  function storePath(accountDir) {
21924
- return join43(accountDir, "secrets", "quickbooks.json");
22068
+ return join44(accountDir, "secrets", "quickbooks.json");
21925
22069
  }
21926
22070
  async function completeConsent(args) {
21927
22071
  const { accountDir, code, realmId } = args;
@@ -21940,7 +22084,7 @@ async function completeConsent(args) {
21940
22084
  try {
21941
22085
  let pending;
21942
22086
  try {
21943
- pending = JSON.parse(readFileSync34(claimFile, "utf-8"));
22087
+ pending = JSON.parse(readFileSync35(claimFile, "utf-8"));
21944
22088
  } catch {
21945
22089
  console.error(`${TAG40} op=callback state=${state} stateValid=false realmId=${realmId ?? ""}`);
21946
22090
  return { ok: false, status: 400, message: "Authorization state could not be read.", stateValid: false };
@@ -21956,7 +22100,7 @@ async function completeConsent(args) {
21956
22100
  if (!existsSync30(storePath(accountDir))) {
21957
22101
  return { ok: false, status: 500, message: "No QuickBooks credentials are stored for this account.", stateValid: true };
21958
22102
  }
21959
- const store2 = JSON.parse(readFileSync34(storePath(accountDir), "utf-8"));
22103
+ const store2 = JSON.parse(readFileSync35(storePath(accountDir), "utf-8"));
21960
22104
  const form = new URLSearchParams({
21961
22105
  grant_type: "authorization_code",
21962
22106
  code,
@@ -21988,9 +22132,9 @@ async function completeConsent(args) {
21988
22132
  refreshTokenExpiry: now + (body.x_refresh_token_expires_in ?? 864e4) * 1e3,
21989
22133
  lastRefresh: now
21990
22134
  };
21991
- mkdirSync8(join43(accountDir, "secrets"), { recursive: true });
22135
+ mkdirSync8(join44(accountDir, "secrets"), { recursive: true });
21992
22136
  writeFileSync15(storePath(accountDir), JSON.stringify(store2, null, 2), { mode: 384 });
21993
- const persisted = JSON.parse(readFileSync34(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
22137
+ const persisted = JSON.parse(readFileSync35(storePath(accountDir), "utf-8")).connections?.[realmId]?.refreshToken === body.refresh_token;
21994
22138
  console.error(`${TAG40} op=token-exchange realmId=${realmId} persisted=${persisted}`);
21995
22139
  return { ok: persisted, status: persisted ? 200 : 500, message: persisted ? `Connected company ${realmId}.` : "Failed to persist the connection.", stateValid: true };
21996
22140
  } finally {
@@ -22021,7 +22165,7 @@ var quickbooks_default = app12;
22021
22165
 
22022
22166
  // server/routes/onboarding.ts
22023
22167
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync4 } from "child_process";
22024
- import { openSync as openSync8, closeSync as closeSync8, appendFileSync as appendFileSync4, writeFileSync as writeFileSync17, writeSync, existsSync as existsSync32, readFileSync as readFileSync36, unlinkSync as unlinkSync3 } from "fs";
22168
+ import { openSync as openSync9, closeSync as closeSync9, appendFileSync as appendFileSync4, writeFileSync as writeFileSync17, writeSync, existsSync as existsSync32, readFileSync as readFileSync37, unlinkSync as unlinkSync3 } from "fs";
22025
22169
  import { createHash as createHash4, randomUUID as randomUUID5 } from "crypto";
22026
22170
 
22027
22171
  // app/lib/claude-spawn-env.ts
@@ -22030,7 +22174,7 @@ function claudeBin() {
22030
22174
  }
22031
22175
 
22032
22176
  // server/routes/onboarding.ts
22033
- import { dirname as dirname14, join as join45 } from "path";
22177
+ import { dirname as dirname14, join as join46 } from "path";
22034
22178
 
22035
22179
  // app/lib/claude-keychain.ts
22036
22180
  import { execFileSync as execFileSync3 } from "child_process";
@@ -22161,19 +22305,19 @@ function readHostCredsBlob(service) {
22161
22305
  }
22162
22306
 
22163
22307
  // ../lib/admins-write/src/index.ts
22164
- import { existsSync as existsSync31, readFileSync as readFileSync35, writeFileSync as writeFileSync16, renameSync as renameSync6, mkdirSync as mkdirSync9, readdirSync as readdirSync21, statSync as statSync20, appendFileSync as appendFileSync3 } from "fs";
22165
- import { dirname as dirname13, join as join44 } from "path";
22308
+ import { existsSync as existsSync31, readFileSync as readFileSync36, writeFileSync as writeFileSync16, renameSync as renameSync6, mkdirSync as mkdirSync9, readdirSync as readdirSync22, statSync as statSync21, appendFileSync as appendFileSync3 } from "fs";
22309
+ import { dirname as dirname13, join as join45 } from "path";
22166
22310
  function id8(value) {
22167
22311
  return value.slice(0, 8);
22168
22312
  }
22169
22313
  function formatAuditRowIds(userIds) {
22170
22314
  return userIds.map(id8).join(",");
22171
22315
  }
22172
- function appendUsersAuditLine(audit, fields) {
22316
+ function appendUsersAuditLine(audit, fields2) {
22173
22317
  const sess = audit.session ? ` session=${id8(audit.session)}` : "";
22174
22318
  const actor = audit.actor.includes("-") ? id8(audit.actor) : audit.actor;
22175
22319
  const targetFields = audit.target !== void 0 ? ` target=${audit.target.includes("-") ? id8(audit.target) : audit.target} explicitUserId=${audit.explicitUserId ? "true" : "false"}` : "";
22176
- const line = `[users-audit] action=${fields.action} actor=${actor}${targetFields}${sess} field=${fields.field} rowsBefore=${fields.rowsBefore} rowsAfter=${fields.rowsAfter} ts=${(/* @__PURE__ */ new Date()).toISOString()}
22320
+ const line = `[users-audit] action=${fields2.action} actor=${actor}${targetFields}${sess} field=${fields2.field} rowsBefore=${fields2.rowsBefore} rowsAfter=${fields2.rowsAfter} ts=${(/* @__PURE__ */ new Date()).toISOString()}
22177
22321
  `;
22178
22322
  try {
22179
22323
  mkdirSync9(dirname13(audit.logFile), { recursive: true, mode: 448 });
@@ -22184,8 +22328,8 @@ function appendUsersAuditLine(audit, fields) {
22184
22328
  );
22185
22329
  }
22186
22330
  }
22187
- function logUsersAudit(audit, fields) {
22188
- appendUsersAuditLine(audit, fields);
22331
+ function logUsersAudit(audit, fields2) {
22332
+ appendUsersAuditLine(audit, fields2);
22189
22333
  }
22190
22334
  function logLine(input, result) {
22191
22335
  const userIdShort = input.userId.slice(0, 8);
@@ -22204,7 +22348,7 @@ function writeAdminEntry(input) {
22204
22348
  try {
22205
22349
  let users = [];
22206
22350
  if (existsSync31(input.usersFile)) {
22207
- const raw = readFileSync35(input.usersFile, "utf-8").trim();
22351
+ const raw = readFileSync36(input.usersFile, "utf-8").trim();
22208
22352
  if (raw) {
22209
22353
  const parsed = JSON.parse(raw);
22210
22354
  if (!Array.isArray(parsed)) {
@@ -22246,11 +22390,11 @@ function writeAdminEntry(input) {
22246
22390
  return result;
22247
22391
  }
22248
22392
  try {
22249
- const accountJsonPath = join44(input.accountDir, "account.json");
22393
+ const accountJsonPath = join45(input.accountDir, "account.json");
22250
22394
  if (!existsSync31(accountJsonPath)) {
22251
22395
  throw new Error(`account.json not found at ${accountJsonPath}`);
22252
22396
  }
22253
- const config = JSON.parse(readFileSync35(accountJsonPath, "utf-8"));
22397
+ const config = JSON.parse(readFileSync36(accountJsonPath, "utf-8"));
22254
22398
  const admins = config.admins ?? [];
22255
22399
  if (admins.some((a) => a.userId === input.userId)) {
22256
22400
  result.accountJsonResult = "noop";
@@ -22272,7 +22416,7 @@ function computeAdminStoreDivergence(input) {
22272
22416
  const usersUserIds = /* @__PURE__ */ new Set();
22273
22417
  if (existsSync31(input.usersFile)) {
22274
22418
  try {
22275
- const raw = readFileSync35(input.usersFile, "utf-8").trim();
22419
+ const raw = readFileSync36(input.usersFile, "utf-8").trim();
22276
22420
  if (raw) {
22277
22421
  const users = JSON.parse(raw);
22278
22422
  for (const u of users) {
@@ -22287,24 +22431,24 @@ function computeAdminStoreDivergence(input) {
22287
22431
  if (existsSync31(input.accountsDir)) {
22288
22432
  let entries2;
22289
22433
  try {
22290
- entries2 = readdirSync21(input.accountsDir);
22434
+ entries2 = readdirSync22(input.accountsDir);
22291
22435
  } catch (err) {
22292
22436
  result.errors.push({ source: input.accountsDir, detail: err instanceof Error ? err.message : String(err) });
22293
22437
  return result;
22294
22438
  }
22295
22439
  for (const entry3 of entries2) {
22296
22440
  if (entry3.startsWith(".")) continue;
22297
- const accountDir = join44(input.accountsDir, entry3);
22441
+ const accountDir = join45(input.accountsDir, entry3);
22298
22442
  try {
22299
- if (!statSync20(accountDir).isDirectory()) continue;
22443
+ if (!statSync21(accountDir).isDirectory()) continue;
22300
22444
  } catch {
22301
22445
  continue;
22302
22446
  }
22303
- const accountJsonPath = join44(accountDir, "account.json");
22447
+ const accountJsonPath = join45(accountDir, "account.json");
22304
22448
  if (!existsSync31(accountJsonPath)) continue;
22305
22449
  let admins = [];
22306
22450
  try {
22307
- const config = JSON.parse(readFileSync35(accountJsonPath, "utf-8"));
22451
+ const config = JSON.parse(readFileSync36(accountJsonPath, "utf-8"));
22308
22452
  admins = config.admins ?? [];
22309
22453
  } catch (err) {
22310
22454
  result.errors.push({ source: accountJsonPath, detail: err instanceof Error ? err.message : String(err) });
@@ -22353,7 +22497,7 @@ function hashPin2(pin) {
22353
22497
  }
22354
22498
  function readUsersFile2() {
22355
22499
  if (!existsSync32(USERS_FILE)) return null;
22356
- const raw = readFileSync36(USERS_FILE, "utf-8").trim();
22500
+ const raw = readFileSync37(USERS_FILE, "utf-8").trim();
22357
22501
  if (!raw) return [];
22358
22502
  return JSON.parse(raw);
22359
22503
  }
@@ -22484,7 +22628,7 @@ app13.post("/claude-auth", async (c) => {
22484
22628
  ensureLogDir();
22485
22629
  appendFileSync4(logPath("claude-auth"), `==== attempt ${attemptId} ${(/* @__PURE__ */ new Date()).toISOString()} ====
22486
22630
  `);
22487
- const claudeAuthLogFd = openSync8(logPath("claude-auth"), "a");
22631
+ const claudeAuthLogFd = openSync9(logPath("claude-auth"), "a");
22488
22632
  const onClaudeOutput = (chunk) => writeSync(claudeAuthLogFd, chunk);
22489
22633
  if (process.platform === "darwin") {
22490
22634
  vncLog("claude-auth", { action: "start", transport: "native", platform: "darwin" });
@@ -22501,7 +22645,7 @@ app13.post("/claude-auth", async (c) => {
22501
22645
  claudeProc2.stdout?.on("data", onClaudeOutput);
22502
22646
  claudeProc2.stderr?.on("data", onClaudeOutput);
22503
22647
  claudeProc2.once("close", () => {
22504
- closeSync8(claudeAuthLogFd);
22648
+ closeSync9(claudeAuthLogFd);
22505
22649
  const writebackResult = bridgeKeychainToFile({
22506
22650
  platform: process.platform,
22507
22651
  before: darwinCredsBefore,
@@ -22514,7 +22658,7 @@ app13.post("/claude-auth", async (c) => {
22514
22658
  });
22515
22659
  persistKeychainService(
22516
22660
  writebackResult,
22517
- (service) => writeFileSync17(join45(dirname14(CLAUDE_CREDENTIALS_FILE), ".keychain-service"), service, { mode: 384 }),
22661
+ (service) => writeFileSync17(join46(dirname14(CLAUDE_CREDENTIALS_FILE), ".keychain-service"), service, { mode: 384 }),
22518
22662
  (line) => console.log(line)
22519
22663
  );
22520
22664
  if (pendingAuthLogin?.child === claudeProc2) pendingAuthLogin = null;
@@ -22539,7 +22683,7 @@ app13.post("/claude-auth", async (c) => {
22539
22683
  claudeProc.stdout?.on("data", onClaudeOutput);
22540
22684
  claudeProc.stderr?.on("data", onClaudeOutput);
22541
22685
  claudeProc.once("close", () => {
22542
- closeSync8(claudeAuthLogFd);
22686
+ closeSync9(claudeAuthLogFd);
22543
22687
  if (pendingAuthLogin?.child === claudeProc) pendingAuthLogin = null;
22544
22688
  });
22545
22689
  await waitForAuthPage(2e4);
@@ -22604,7 +22748,7 @@ app13.post("/set-pin", async (c) => {
22604
22748
  let installOwner = null;
22605
22749
  if (existsSync32(INSTALL_OWNER_FILE)) {
22606
22750
  try {
22607
- const raw = readFileSync36(INSTALL_OWNER_FILE, "utf-8").trim();
22751
+ const raw = readFileSync37(INSTALL_OWNER_FILE, "utf-8").trim();
22608
22752
  if (raw.length > 0) installOwner = raw;
22609
22753
  } catch (err) {
22610
22754
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -22752,7 +22896,7 @@ app13.put("/set-pin", requireAdminSession, async (c) => {
22752
22896
  let installOwner = null;
22753
22897
  if (existsSync32(INSTALL_OWNER_FILE)) {
22754
22898
  try {
22755
- const raw = readFileSync36(INSTALL_OWNER_FILE, "utf-8").trim();
22899
+ const raw = readFileSync37(INSTALL_OWNER_FILE, "utf-8").trim();
22756
22900
  if (raw.length > 0) installOwner = raw;
22757
22901
  } catch (err) {
22758
22902
  console.error(`[set-pin] install-owner-read-failed path=${INSTALL_OWNER_FILE} error=${err instanceof Error ? err.message : String(err)}`);
@@ -23209,9 +23353,9 @@ app14.post("/probe", async (c) => {
23209
23353
  var field_default = app14;
23210
23354
 
23211
23355
  // server/routes/client-error.ts
23212
- import { appendFileSync as appendFileSync5, existsSync as existsSync33, renameSync as renameSync8, statSync as statSync21 } from "fs";
23213
- import { join as join46 } from "path";
23214
- var CLIENT_ERRORS_LOG = join46(LOG_DIR, "client-errors.log");
23356
+ import { appendFileSync as appendFileSync5, existsSync as existsSync33, renameSync as renameSync8, statSync as statSync22 } from "fs";
23357
+ import { join as join47 } from "path";
23358
+ var CLIENT_ERRORS_LOG = join47(LOG_DIR, "client-errors.log");
23215
23359
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
23216
23360
  var MAX_BODY_SIZE = 8 * 1024;
23217
23361
  var MAX_STACK_LEN = 2e3;
@@ -23269,7 +23413,7 @@ function stackHead(stack) {
23269
23413
  function rotateIfNeeded() {
23270
23414
  try {
23271
23415
  if (!existsSync33(CLIENT_ERRORS_LOG)) return;
23272
- const stats = statSync21(CLIENT_ERRORS_LOG);
23416
+ const stats = statSync22(CLIENT_ERRORS_LOG);
23273
23417
  if (stats.size < MAX_LOG_SIZE) return;
23274
23418
  renameSync8(CLIENT_ERRORS_LOG, CLIENT_ERRORS_LOG + ".1");
23275
23419
  } catch (err) {
@@ -23625,17 +23769,17 @@ app17.get("/", requireAdminSession, async (c) => {
23625
23769
  var accounts_default = app17;
23626
23770
 
23627
23771
  // server/routes/admin/logs.ts
23628
- import { existsSync as existsSync35, readdirSync as readdirSync22, readFileSync as readFileSync37, statSync as statSync22 } from "fs";
23772
+ import { existsSync as existsSync35, readdirSync as readdirSync23, readFileSync as readFileSync38, statSync as statSync23 } from "fs";
23629
23773
  import { resolve as resolve26, basename as basename13 } from "path";
23630
23774
 
23631
23775
  // app/lib/logs-read-resolve.ts
23632
23776
  import { existsSync as existsSync34 } from "fs";
23633
- import { join as join47 } from "path";
23777
+ import { join as join48 } from "path";
23634
23778
  function resolveSessionLogPaths(filename, logDirs) {
23635
23779
  const tried = [filename];
23636
23780
  const hits = [];
23637
23781
  for (const dir of logDirs) {
23638
- const fullPath = join47(dir, filename);
23782
+ const fullPath = join48(dir, filename);
23639
23783
  if (existsSync34(fullPath)) {
23640
23784
  hits.push({ path: fullPath, dir });
23641
23785
  }
@@ -23661,8 +23805,8 @@ app18.get("/", async (c) => {
23661
23805
  const filePath = resolve26(dir, safe);
23662
23806
  searched.push(filePath);
23663
23807
  try {
23664
- const buffer = readFileSync37(filePath);
23665
- const onDiskBytes = statSync22(filePath).size;
23808
+ const buffer = readFileSync38(filePath);
23809
+ const onDiskBytes = statSync23(filePath).size;
23666
23810
  const headers = {
23667
23811
  "Content-Type": "text/plain; charset=utf-8",
23668
23812
  "Content-Length": String(buffer.byteLength)
@@ -23726,8 +23870,8 @@ app18.get("/", async (c) => {
23726
23870
  console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} sessionId=${sessionIdSlice} via=${primaryId === cacheKey ? "cacheKey" : primaryId === sessionKeyFromConv ? "reverse-lookup" : "sessionId-fallback"}`);
23727
23871
  try {
23728
23872
  const filename = basename13(hit.path);
23729
- const buffer = readFileSync37(hit.path);
23730
- const onDiskBytes = statSync22(hit.path).size;
23873
+ const buffer = readFileSync38(hit.path);
23874
+ const onDiskBytes = statSync23(hit.path).size;
23731
23875
  const headers = {
23732
23876
  "Content-Type": "text/plain; charset=utf-8",
23733
23877
  "Content-Length": String(buffer.byteLength)
@@ -23764,18 +23908,18 @@ app18.get("/", async (c) => {
23764
23908
  if (!existsSync35(dir)) continue;
23765
23909
  let files;
23766
23910
  try {
23767
- files = readdirSync22(dir).filter((f) => f.endsWith(".log"));
23911
+ files = readdirSync23(dir).filter((f) => f.endsWith(".log"));
23768
23912
  } catch (err) {
23769
23913
  const reason = err instanceof Error ? err.message : String(err);
23770
23914
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
23771
23915
  continue;
23772
23916
  }
23773
- files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync22(resolve26(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
23917
+ files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync23(resolve26(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
23774
23918
  seen.add(name);
23775
23919
  try {
23776
- const content = readFileSync37(resolve26(dir, name));
23777
- const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
23778
- logs[name] = tail.trim() || "(empty)";
23920
+ const content = readFileSync38(resolve26(dir, name));
23921
+ const tail2 = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
23922
+ logs[name] = tail2.trim() || "(empty)";
23779
23923
  } catch (err) {
23780
23924
  const reason = err instanceof Error ? err.message : String(err);
23781
23925
  console.debug(`[admin/logs] read-fail name=${name} reason=${reason}`);
@@ -23895,7 +24039,7 @@ var attachment_default = app20;
23895
24039
 
23896
24040
  // server/routes/admin/session-upload.ts
23897
24041
  import { readFile as readFile8 } from "fs/promises";
23898
- import { existsSync as existsSync37, statSync as statSync23 } from "fs";
24042
+ import { existsSync as existsSync37, statSync as statSync24 } from "fs";
23899
24043
  var app21 = new Hono();
23900
24044
  app21.get("/", requireAdminSession, async (c) => {
23901
24045
  const cacheKey = c.var.cacheKey;
@@ -23916,7 +24060,7 @@ app21.get("/", requireAdminSession, async (c) => {
23916
24060
  return c.json({ error: "Not found" }, 404);
23917
24061
  }
23918
24062
  const resolution = resolveClaudeUploadsPath(`${sessionId}/${file}`);
23919
- if (!resolution.ok || !existsSync37(resolution.absolute) || !statSync23(resolution.absolute).isFile()) {
24063
+ if (!resolution.ok || !existsSync37(resolution.absolute) || !statSync24(resolution.absolute).isFile()) {
23920
24064
  console.log(`[session-upload] op=miss session=${short2} file=${file} status=404 reason=absent`);
23921
24065
  return c.json({ error: "Not found" }, 404);
23922
24066
  }
@@ -23938,13 +24082,13 @@ var session_upload_default = app21;
23938
24082
 
23939
24083
  // server/routes/admin/agents.ts
23940
24084
  import { resolve as resolve28 } from "path";
23941
- import { readdirSync as readdirSync24, readFileSync as readFileSync39, existsSync as existsSync39, rmSync as rmSync5, renameSync as renameSync9, mkdirSync as mkdirSync10, writeFileSync as writeFileSync18, chmodSync } from "fs";
24085
+ import { readdirSync as readdirSync25, readFileSync as readFileSync40, existsSync as existsSync39, rmSync as rmSync5, renameSync as renameSync9, mkdirSync as mkdirSync10, writeFileSync as writeFileSync18, chmodSync } from "fs";
23942
24086
  import { execFileSync as execFileSync6 } from "child_process";
23943
24087
 
23944
24088
  // ../services/claude-session-manager/src/tool-surface.ts
23945
- import { readFileSync as readFileSync38, readdirSync as readdirSync23, statSync as statSync24, existsSync as existsSync38 } from "fs";
24089
+ import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync25, existsSync as existsSync38 } from "fs";
23946
24090
  import { createHash as createHash5 } from "crypto";
23947
- import { join as join48 } from "path";
24091
+ import { join as join49 } from "path";
23948
24092
 
23949
24093
  // ../services/claude-session-manager/src/canonical-tool-names.ts
23950
24094
  function canonicalToolName(plugin, tool) {
@@ -24197,15 +24341,15 @@ function loadToolSurface(pluginsRoots) {
24197
24341
  const riskByTool = /* @__PURE__ */ new Map();
24198
24342
  const manifestPaths = /* @__PURE__ */ new Map();
24199
24343
  const loadOne = (pluginsRoot, plugin) => {
24200
- const pluginDir = join48(pluginsRoot, plugin);
24344
+ const pluginDir = join49(pluginsRoot, plugin);
24201
24345
  try {
24202
- if (!statSync24(pluginDir).isDirectory()) return;
24346
+ if (!statSync25(pluginDir).isDirectory()) return;
24203
24347
  } catch {
24204
24348
  return;
24205
24349
  }
24206
- const manifestPath = join48(pluginDir, "PLUGIN.md");
24350
+ const manifestPath = join49(pluginDir, "PLUGIN.md");
24207
24351
  if (!existsSync38(manifestPath)) return;
24208
- const raw = readFileSync38(manifestPath, "utf-8");
24352
+ const raw = readFileSync39(manifestPath, "utf-8");
24209
24353
  const frontmatter = extractFrontmatter(raw, plugin);
24210
24354
  const tools = parseToolsBlock(frontmatter, plugin);
24211
24355
  if (tools.length === 0) return;
@@ -24224,7 +24368,7 @@ function loadToolSurface(pluginsRoots) {
24224
24368
  `${plugin}: mcpServers has entry but manifestPaths is missing \u2014 invariant violated, both maps must be written together`
24225
24369
  );
24226
24370
  }
24227
- const priorSha = createHash5("sha256").update(readFileSync38(priorPath)).digest("hex").slice(0, 8);
24371
+ const priorSha = createHash5("sha256").update(readFileSync39(priorPath)).digest("hex").slice(0, 8);
24228
24372
  const newSha = createHash5("sha256").update(raw).digest("hex").slice(0, 8);
24229
24373
  throw new ToolSurfaceError(
24230
24374
  "mcp-plugin-duplicate",
@@ -24247,15 +24391,15 @@ function loadToolSurface(pluginsRoots) {
24247
24391
  }
24248
24392
  };
24249
24393
  for (const root of roots) {
24250
- const entries2 = readdirSync23(root).sort();
24394
+ const entries2 = readdirSync24(root).sort();
24251
24395
  for (const plugin of entries2) {
24252
- const pluginDir = join48(root, plugin);
24396
+ const pluginDir = join49(root, plugin);
24253
24397
  loadOne(root, plugin);
24254
- const subRoot = join48(pluginDir, "plugins");
24398
+ const subRoot = join49(pluginDir, "plugins");
24255
24399
  let subEntries;
24256
24400
  try {
24257
- if (!statSync24(subRoot).isDirectory()) continue;
24258
- subEntries = readdirSync23(subRoot).sort();
24401
+ if (!statSync25(subRoot).isDirectory()) continue;
24402
+ subEntries = readdirSync24(subRoot).sort();
24259
24403
  } catch {
24260
24404
  continue;
24261
24405
  }
@@ -24387,7 +24531,7 @@ var SLUG_UNSAFE = (s) => s.includes("/") || s.includes("..") || s.includes("\\")
24387
24531
  function subdirs(dir) {
24388
24532
  if (!existsSync39(dir)) return [];
24389
24533
  try {
24390
- return readdirSync24(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
24534
+ return readdirSync25(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
24391
24535
  } catch {
24392
24536
  return [];
24393
24537
  }
@@ -24419,7 +24563,7 @@ function loadRiskSurface() {
24419
24563
  function readDisabledStoreStrict(accountDir) {
24420
24564
  const p = resolve28(accountDir, DISABLED_STORE);
24421
24565
  if (!existsSync39(p)) return /* @__PURE__ */ new Set();
24422
- const parsed = JSON.parse(readFileSync39(p, "utf-8"));
24566
+ const parsed = JSON.parse(readFileSync40(p, "utf-8"));
24423
24567
  if (!Array.isArray(parsed.disabled)) throw new Error("disabled is not an array");
24424
24568
  return new Set(parsed.disabled.filter((x) => typeof x === "string"));
24425
24569
  }
@@ -24439,7 +24583,7 @@ function authoredAgentFiles(accountDir) {
24439
24583
  const dir = resolve28(pluginsDir, plugin, "agents");
24440
24584
  if (!existsSync39(dir)) continue;
24441
24585
  try {
24442
- for (const entry3 of readdirSync24(dir)) {
24586
+ for (const entry3 of readdirSync25(dir)) {
24443
24587
  if (!entry3.endsWith(".md")) continue;
24444
24588
  const path = resolve28(dir, entry3);
24445
24589
  const paths = byName.get(entry3);
@@ -24460,7 +24604,7 @@ function shippedSourceDirs(accountDir) {
24460
24604
  ];
24461
24605
  }
24462
24606
  function readRowFields(path) {
24463
- const { fm } = splitFrontmatter(readFileSync39(path, "utf-8"));
24607
+ const { fm } = splitFrontmatter(readFileSync40(path, "utf-8"));
24464
24608
  return {
24465
24609
  name: fm.name ?? "",
24466
24610
  description: fm.description ?? "",
@@ -24493,9 +24637,9 @@ function compareToAuthored(candidatePath, authoredPaths, filename) {
24493
24637
  anyUnreadable = true;
24494
24638
  continue;
24495
24639
  }
24496
- const fields = divergentFields(candidate, authored);
24497
- if (fields.length === 0) return { verdict: "matched" };
24498
- firstDivergence ??= { authoredPath: path, fields };
24640
+ const fields2 = divergentFields(candidate, authored);
24641
+ if (fields2.length === 0) return { verdict: "matched" };
24642
+ firstDivergence ??= { authoredPath: path, fields: fields2 };
24499
24643
  }
24500
24644
  if (firstDivergence) return { verdict: "divergent", ...firstDivergence };
24501
24645
  if (anyUnreadable) return { verdict: "unreadable" };
@@ -24513,7 +24657,7 @@ function listShipped(accountDir, riskByTool, disabled, riskSurfaceFailed, author
24513
24657
  for (const dir of dirs) {
24514
24658
  if (!existsSync39(dir)) continue;
24515
24659
  try {
24516
- for (const entry3 of readdirSync24(dir)) if (entry3.endsWith(".md")) names.add(entry3);
24660
+ for (const entry3 of readdirSync25(dir)) if (entry3.endsWith(".md")) names.add(entry3);
24517
24661
  } catch (err) {
24518
24662
  console.error(`[admin/agents] op=list-shipped read-failed dir=${dir} error="${err}"`);
24519
24663
  }
@@ -24541,7 +24685,7 @@ function listShipped(accountDir, riskByTool, disabled, riskSurfaceFailed, author
24541
24685
  }
24542
24686
  }
24543
24687
  try {
24544
- const { fm } = splitFrontmatter(readFileSync39(resolve28(source, filename), "utf-8"));
24688
+ const { fm } = splitFrontmatter(readFileSync40(resolve28(source, filename), "utf-8"));
24545
24689
  if (!fm.name) {
24546
24690
  shippedSkipped++;
24547
24691
  console.error(`[admin/agents] op=list-shipped-skip file=${filename} reason=no-name`);
@@ -24603,14 +24747,14 @@ function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
24603
24747
  if (!existsSync39(agentsDirP)) continue;
24604
24748
  let entries2;
24605
24749
  try {
24606
- entries2 = readdirSync24(agentsDirP, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
24750
+ entries2 = readdirSync25(agentsDirP, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
24607
24751
  } catch {
24608
24752
  continue;
24609
24753
  }
24610
24754
  for (const entry3 of entries2) {
24611
24755
  if (!entry3.isFile() || !entry3.name.endsWith(".md")) continue;
24612
24756
  try {
24613
- const { fm } = splitFrontmatter(readFileSync39(resolve28(agentsDirP, entry3.name), "utf-8"));
24757
+ const { fm } = splitFrontmatter(readFileSync40(resolve28(agentsDirP, entry3.name), "utf-8"));
24614
24758
  if (!fm.name) {
24615
24759
  specialistsSkipped++;
24616
24760
  console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry3.name}`);
@@ -24672,14 +24816,14 @@ app22.get("/", requireAdminSession, (c) => {
24672
24816
  const agentsDir = resolve28(account.accountDir, "agents");
24673
24817
  if (existsSync39(agentsDir)) {
24674
24818
  try {
24675
- const entries2 = readdirSync24(agentsDir, { withFileTypes: true });
24819
+ const entries2 = readdirSync25(agentsDir, { withFileTypes: true });
24676
24820
  for (const entry3 of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
24677
24821
  if (!entry3.isDirectory()) continue;
24678
24822
  if (entry3.name === "admin") continue;
24679
24823
  const configPath3 = resolve28(agentsDir, entry3.name, "config.json");
24680
24824
  if (!existsSync39(configPath3)) continue;
24681
24825
  try {
24682
- const config = JSON.parse(readFileSync39(configPath3, "utf-8"));
24826
+ const config = JSON.parse(readFileSync40(configPath3, "utf-8"));
24683
24827
  agents.push({
24684
24828
  slug: entry3.name,
24685
24829
  displayName: config.displayName ?? entry3.name,
@@ -24761,7 +24905,7 @@ app22.get("/:slug", requireAdminSession, (c) => {
24761
24905
  const dirs = shippedSourceDirs(account.accountDir);
24762
24906
  const source = dirs.find((d) => existsSync39(resolve28(d, `${slug}.md`)));
24763
24907
  if (!source) return c.json({ error: "Agent not found" }, 404);
24764
- const { fm, body } = splitFrontmatter(readFileSync39(resolve28(source, `${slug}.md`), "utf-8"));
24908
+ const { fm, body } = splitFrontmatter(readFileSync40(resolve28(source, `${slug}.md`), "utf-8"));
24765
24909
  const { riskByTool, failed: failed2 } = loadRiskSurface();
24766
24910
  const tools = parseToolsLine(fm.tools);
24767
24911
  const r = classify(tools, riskByTool, failed2 !== null);
@@ -24793,7 +24937,7 @@ app22.get("/:slug", requireAdminSession, (c) => {
24793
24937
  if (SLUG_UNSAFE(pluginParam)) return c.json({ error: "Invalid plugin" }, 400);
24794
24938
  const file = resolve28(account.accountDir, "plugins", pluginParam, "agents", `${slug}.md`);
24795
24939
  if (!existsSync39(file)) return c.json({ error: "Agent not found" }, 404);
24796
- const { fm, body } = splitFrontmatter(readFileSync39(file, "utf-8"));
24940
+ const { fm, body } = splitFrontmatter(readFileSync40(file, "utf-8"));
24797
24941
  const skills = subdirs(resolve28(account.accountDir, "plugins", pluginParam, "skills"));
24798
24942
  const { riskByTool, failed: failed2 } = loadRiskSurface();
24799
24943
  const tools = parseToolsLine(fm.tools);
@@ -24826,7 +24970,7 @@ app22.get("/:slug", requireAdminSession, (c) => {
24826
24970
  if (!existsSync39(configPath3)) return c.json({ error: "Agent not found" }, 404);
24827
24971
  let config;
24828
24972
  try {
24829
- config = JSON.parse(readFileSync39(configPath3, "utf-8"));
24973
+ config = JSON.parse(readFileSync40(configPath3, "utf-8"));
24830
24974
  } catch {
24831
24975
  console.error(`[admin/agents] op=detail slug=${slug} error="config.json parse failed"`);
24832
24976
  return c.json({ error: "Agent config unreadable" }, 500);
@@ -24837,7 +24981,7 @@ app22.get("/:slug", requireAdminSession, (c) => {
24837
24981
  const p = resolve28(agentDir, file);
24838
24982
  try {
24839
24983
  if (existsSync39(p)) {
24840
- docs[key2] = readFileSync39(p, "utf-8");
24984
+ docs[key2] = readFileSync40(p, "utf-8");
24841
24985
  present[key2] = true;
24842
24986
  } else {
24843
24987
  docs[key2] = "";
@@ -24899,9 +25043,9 @@ function reconcileAuthoredRoutingLine(accountDir, accountId, op, source) {
24899
25043
  const agentsMd = resolve28(accountDir, "agents", "admin", "AGENTS.md");
24900
25044
  if (!existsSync39(agentsMd) || !existsSync39(source)) return false;
24901
25045
  try {
24902
- const { fm } = splitFrontmatter(readFileSync39(source, "utf-8"));
25046
+ const { fm } = splitFrontmatter(readFileSync40(source, "utf-8"));
24903
25047
  if (!fm.name) return false;
24904
- const lines = readFileSync39(agentsMd, "utf-8").split("\n");
25048
+ const lines = readFileSync40(agentsMd, "utf-8").split("\n");
24905
25049
  const prefix = `- **specialists:${fm.name}**:`;
24906
25050
  const without = lines.filter((l) => !l.startsWith(prefix));
24907
25051
  const next = op === "disable" ? without : [...without, `${prefix} ${fm.description ?? ""}`.trimEnd()];
@@ -25479,21 +25623,21 @@ app23.post("/:id/resume", async (c) => {
25479
25623
  if (bridge.reason === "conversation-not-found") {
25480
25624
  return c.json({ error: "Conversation not found" }, 404);
25481
25625
  }
25482
- const tail = cacheKey.slice(0, 8);
25483
- console.error(`[session] middleware-reject status=401 code=session-not-registered reason="cookie-bridge-rejected:${bridge.reason}" path=${c.req.path} cacheKey=${tail}\u2026`);
25626
+ const tail2 = cacheKey.slice(0, 8);
25627
+ console.error(`[session] middleware-reject status=401 code=session-not-registered reason="cookie-bridge-rejected:${bridge.reason}" path=${c.req.path} cacheKey=${tail2}\u2026`);
25484
25628
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
25485
25629
  }
25486
25630
  bridged = true;
25487
25631
  result = validateSession(cacheKey, "admin", signedSessionToken);
25488
25632
  if (!result.ok) {
25489
- const tail = cacheKey.slice(0, 8);
25490
- console.error(`[session] middleware-reject status=401 code=session-not-registered reason="post-bridge re-validate failed" path=${c.req.path} cacheKey=${tail}\u2026`);
25633
+ const tail2 = cacheKey.slice(0, 8);
25634
+ console.error(`[session] middleware-reject status=401 code=session-not-registered reason="post-bridge re-validate failed" path=${c.req.path} cacheKey=${tail2}\u2026`);
25491
25635
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
25492
25636
  }
25493
25637
  } else {
25494
- const tail = cacheKey.slice(0, 8);
25638
+ const tail2 = cacheKey.slice(0, 8);
25495
25639
  const wireCode = result.reason === "agent-type-mismatch" ? "session-not-registered" : result.reason;
25496
- console.error(`[session] middleware-reject status=401 code=${wireCode} reason="invalid or expired admin session" path=${c.req.path} cacheKey=${tail}\u2026`);
25640
+ console.error(`[session] middleware-reject status=401 code=${wireCode} reason="invalid or expired admin session" path=${c.req.path} cacheKey=${tail2}\u2026`);
25497
25641
  return c.json({ error: "Invalid or expired admin session", code: wireCode }, 401);
25498
25642
  }
25499
25643
  }
@@ -25759,8 +25903,8 @@ app23.put("/:id/label", requireAdminSession, async (c) => {
25759
25903
  var sessions_default = app23;
25760
25904
 
25761
25905
  // app/lib/claude-agent/spawn-context.ts
25762
- import { existsSync as existsSync41, readFileSync as readFileSync40, readdirSync as readdirSync25, statSync as statSync25 } from "fs";
25763
- import { dirname as dirname15, resolve as resolve29, join as join49 } from "path";
25906
+ import { existsSync as existsSync41, readFileSync as readFileSync41, readdirSync as readdirSync26, statSync as statSync26 } from "fs";
25907
+ import { dirname as dirname15, resolve as resolve29, join as join50 } from "path";
25764
25908
  async function resolveOwnerProfileBlock(accountId, userId) {
25765
25909
  if (!userId) return { ok: false, reason: "missing-user-id" };
25766
25910
  try {
@@ -25801,14 +25945,14 @@ function frontmatterField(manifest, field2) {
25801
25945
  function extractPluginToolDescriptions(pluginDir) {
25802
25946
  const out = /* @__PURE__ */ new Map();
25803
25947
  const candidates = [
25804
- join49(pluginDir, "mcp/dist/index.js"),
25805
- join49(pluginDir, "mcp/src/index.ts")
25948
+ join50(pluginDir, "mcp/dist/index.js"),
25949
+ join50(pluginDir, "mcp/src/index.ts")
25806
25950
  ];
25807
25951
  let raw = null;
25808
25952
  for (const path of candidates) {
25809
25953
  if (!existsSync41(path)) continue;
25810
25954
  try {
25811
- raw = readFileSync40(path, "utf-8");
25955
+ raw = readFileSync41(path, "utf-8");
25812
25956
  break;
25813
25957
  } catch {
25814
25958
  }
@@ -25859,24 +26003,24 @@ function listPluginDirs(accountPluginsDir) {
25859
26003
  const out = /* @__PURE__ */ new Map();
25860
26004
  const platformPlugins = resolve29(PLATFORM_ROOT, "plugins");
25861
26005
  if (existsSync41(platformPlugins)) {
25862
- for (const name of readdirSync25(platformPlugins)) {
25863
- const dir = join49(platformPlugins, name);
26006
+ for (const name of readdirSync26(platformPlugins)) {
26007
+ const dir = join50(platformPlugins, name);
25864
26008
  try {
25865
- if (statSync25(dir).isDirectory()) out.set(name, dir);
26009
+ if (statSync26(dir).isDirectory()) out.set(name, dir);
25866
26010
  } catch {
25867
26011
  }
25868
26012
  }
25869
26013
  }
25870
26014
  const premiumRoot = resolve29(dirname15(PLATFORM_ROOT), "premium-plugins");
25871
26015
  if (existsSync41(premiumRoot)) {
25872
- for (const bundle of readdirSync25(premiumRoot)) {
25873
- const bundlePlugins = join49(premiumRoot, bundle, "plugins");
26016
+ for (const bundle of readdirSync26(premiumRoot)) {
26017
+ const bundlePlugins = join50(premiumRoot, bundle, "plugins");
25874
26018
  if (!existsSync41(bundlePlugins)) continue;
25875
26019
  try {
25876
- for (const name of readdirSync25(bundlePlugins)) {
25877
- const dir = join49(bundlePlugins, name);
26020
+ for (const name of readdirSync26(bundlePlugins)) {
26021
+ const dir = join50(bundlePlugins, name);
25878
26022
  try {
25879
- if (statSync25(dir).isDirectory()) out.set(name, dir);
26023
+ if (statSync26(dir).isDirectory()) out.set(name, dir);
25880
26024
  } catch {
25881
26025
  }
25882
26026
  }
@@ -25886,11 +26030,11 @@ function listPluginDirs(accountPluginsDir) {
25886
26030
  }
25887
26031
  if (accountPluginsDir && existsSync41(accountPluginsDir)) {
25888
26032
  try {
25889
- for (const name of readdirSync25(accountPluginsDir)) {
26033
+ for (const name of readdirSync26(accountPluginsDir)) {
25890
26034
  if (out.has(name)) continue;
25891
- const dir = join49(accountPluginsDir, name);
26035
+ const dir = join50(accountPluginsDir, name);
25892
26036
  try {
25893
- if (statSync25(dir).isDirectory()) out.set(name, dir);
26037
+ if (statSync26(dir).isDirectory()) out.set(name, dir);
25894
26038
  } catch {
25895
26039
  }
25896
26040
  }
@@ -25905,9 +26049,9 @@ function accountPluginsDirFor(accountId) {
25905
26049
  function listAccountPluginNames(accountPluginsDir) {
25906
26050
  if (!existsSync41(accountPluginsDir)) return [];
25907
26051
  try {
25908
- return readdirSync25(accountPluginsDir).filter((name) => {
26052
+ return readdirSync26(accountPluginsDir).filter((name) => {
25909
26053
  try {
25910
- return statSync25(join49(accountPluginsDir, name)).isDirectory();
26054
+ return statSync26(join50(accountPluginsDir, name)).isDirectory();
25911
26055
  } catch {
25912
26056
  return false;
25913
26057
  }
@@ -25920,7 +26064,7 @@ function readEnabledPlugins(accountId) {
25920
26064
  const accountFile = resolve29(PLATFORM_ROOT, "..", "data/accounts", accountId, "account.json");
25921
26065
  if (!existsSync41(accountFile)) return /* @__PURE__ */ new Set();
25922
26066
  try {
25923
- const parsed = JSON.parse(readFileSync40(accountFile, "utf-8"));
26067
+ const parsed = JSON.parse(readFileSync41(accountFile, "utf-8"));
25924
26068
  return new Set(
25925
26069
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
25926
26070
  );
@@ -25932,7 +26076,7 @@ function readFrontmatter(path) {
25932
26076
  if (!existsSync41(path)) return null;
25933
26077
  let raw;
25934
26078
  try {
25935
- raw = readFileSync40(path, "utf-8");
26079
+ raw = readFileSync41(path, "utf-8");
25936
26080
  } catch {
25937
26081
  return null;
25938
26082
  }
@@ -25950,7 +26094,7 @@ function computeActivePlugins(accountId) {
25950
26094
  for (const name of emitNames) {
25951
26095
  const dir = pluginDirs.get(name);
25952
26096
  if (!dir) continue;
25953
- const fm = readFrontmatter(join49(dir, "PLUGIN.md"));
26097
+ const fm = readFrontmatter(join50(dir, "PLUGIN.md"));
25954
26098
  if (!fm) continue;
25955
26099
  const description = frontmatterField(`---
25956
26100
  ${fm}
@@ -25967,7 +26111,7 @@ ${fm}
25967
26111
  `plugin-manifest-tool-coverage plugin=${name} tools=${tools.length} with-desc=${withDesc}`
25968
26112
  );
25969
26113
  if (toolNames.length > 0 && descMap.size === 0) {
25970
- const hasSource = existsSync41(join49(dir, "mcp/dist/index.js")) || existsSync41(join49(dir, "mcp/src/index.ts"));
26114
+ const hasSource = existsSync41(join50(dir, "mcp/dist/index.js")) || existsSync41(join50(dir, "mcp/src/index.ts"));
25971
26115
  if (hasSource) {
25972
26116
  console.warn(
25973
26117
  `[spawn-context] WARN plugin=${name} mcp source present but tool-description regex matched zero entries \u2014 SDK upgrade may have changed the registration shape`
@@ -25977,7 +26121,7 @@ ${fm}
25977
26121
  const skillPaths = parseSkillPathsFromFrontmatter(fm);
25978
26122
  const skills = [];
25979
26123
  for (const relPath of skillPaths) {
25980
- const skillPath = join49(dir, relPath);
26124
+ const skillPath = join50(dir, relPath);
25981
26125
  const skillFm = readFrontmatter(skillPath);
25982
26126
  if (!skillFm) continue;
25983
26127
  const skillName = frontmatterField(`---
@@ -25997,7 +26141,7 @@ function computeSpecialistDomains(accountId) {
25997
26141
  if (!existsSync41(specialistsDir)) return [];
25998
26142
  let entries2;
25999
26143
  try {
26000
- entries2 = readdirSync25(specialistsDir);
26144
+ entries2 = readdirSync26(specialistsDir);
26001
26145
  } catch {
26002
26146
  return [];
26003
26147
  }
@@ -26021,7 +26165,7 @@ function computeSpecialistDomains(accountId) {
26021
26165
  const out = [];
26022
26166
  for (const file of entries2.sort()) {
26023
26167
  if (!file.endsWith(".md")) continue;
26024
- const fm = readFrontmatter(join49(specialistsDir, file));
26168
+ const fm = readFrontmatter(join50(specialistsDir, file));
26025
26169
  if (!fm) continue;
26026
26170
  const wrapped = `---
26027
26171
  ${fm}
@@ -26043,7 +26187,7 @@ function computeDormantPlugins(accountId) {
26043
26187
  if (!existsSync41(accountFile)) return [];
26044
26188
  let enabled;
26045
26189
  try {
26046
- const raw = readFileSync40(accountFile, "utf-8");
26190
+ const raw = readFileSync41(accountFile, "utf-8");
26047
26191
  const parsed = JSON.parse(raw);
26048
26192
  enabled = new Set(
26049
26193
  Array.isArray(parsed.enabledPlugins) ? parsed.enabledPlugins.filter((p) => typeof p === "string") : []
@@ -26058,7 +26202,7 @@ function computeDormantPlugins(accountId) {
26058
26202
  if (!existsSync41(pluginsDir)) return [];
26059
26203
  let entries2;
26060
26204
  try {
26061
- entries2 = readdirSync25(pluginsDir);
26205
+ entries2 = readdirSync26(pluginsDir);
26062
26206
  } catch {
26063
26207
  return [];
26064
26208
  }
@@ -26069,7 +26213,7 @@ function computeDormantPlugins(accountId) {
26069
26213
  if (!existsSync41(manifestPath)) continue;
26070
26214
  let manifest;
26071
26215
  try {
26072
- manifest = readFileSync40(manifestPath, "utf-8");
26216
+ manifest = readFileSync41(manifestPath, "utf-8");
26073
26217
  } catch {
26074
26218
  continue;
26075
26219
  }
@@ -26083,13 +26227,13 @@ function computeDormantPlugins(accountId) {
26083
26227
  }
26084
26228
 
26085
26229
  // server/routes/admin/claude-sessions.ts
26086
- import { existsSync as existsSync42, readFileSync as readFileSync41 } from "fs";
26230
+ import { existsSync as existsSync42, readFileSync as readFileSync42 } from "fs";
26087
26231
  import { resolve as resolve30 } from "path";
26088
26232
  function readTunnelState(brandConfigDir) {
26089
26233
  const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
26090
26234
  if (!existsSync42(statePath)) return null;
26091
26235
  try {
26092
- const parsed = JSON.parse(readFileSync41(statePath, "utf-8"));
26236
+ const parsed = JSON.parse(readFileSync42(statePath, "utf-8"));
26093
26237
  const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
26094
26238
  const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
26095
26239
  const domain = typeof parsed.domain === "string" ? parsed.domain : null;
@@ -26103,7 +26247,7 @@ function resolveTunnelUrl() {
26103
26247
  const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? process.env.PLATFORM_ROOT ?? resolve30(process.cwd(), "..");
26104
26248
  let brand;
26105
26249
  try {
26106
- brand = JSON.parse(readFileSync41(resolve30(platformRoot5, "config", "brand.json"), "utf-8"));
26250
+ brand = JSON.parse(readFileSync42(resolve30(platformRoot5, "config", "brand.json"), "utf-8"));
26107
26251
  } catch {
26108
26252
  return null;
26109
26253
  }
@@ -26304,7 +26448,7 @@ app25.post("/", async (c) => {
26304
26448
  var log_ingest_default = app25;
26305
26449
 
26306
26450
  // server/routes/admin/session-blocked.ts
26307
- import { join as join50 } from "path";
26451
+ import { join as join51 } from "path";
26308
26452
  var TAG44 = "[blocked-tool-notify]";
26309
26453
  function isLoopbackAddr4(addr) {
26310
26454
  return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
@@ -26351,7 +26495,7 @@ app26.post("/", async (c) => {
26351
26495
  if (!tool) return c.json({ error: "tool-required" }, 400);
26352
26496
  const text = blockedNotificationText({ sessionId, agentId, tool, blockedForMs });
26353
26497
  const projectDir = findSessionProjectDir(sessionId);
26354
- const meta = projectDir ? readSidecarMeta(join50(projectDir, `${sessionId}.meta.json`)) : { role: null, channel: null, senderId: null, accountId: null };
26498
+ const meta = projectDir ? readSidecarMeta(join51(projectDir, `${sessionId}.meta.json`)) : { role: null, channel: null, senderId: null, accountId: null };
26355
26499
  const account = validateAccountId2(meta.accountId ?? "");
26356
26500
  if (!account) {
26357
26501
  console.error(
@@ -26550,7 +26694,7 @@ import { createReadStream as createReadStream2, createWriteStream as createWrite
26550
26694
  import { readdir as readdir4, readFile as readFile10, stat as stat6, mkdir as mkdir2, unlink as unlink3, rename as rename3, rmdir } from "fs/promises";
26551
26695
  import { realpathSync as realpathSync7 } from "fs";
26552
26696
  import { randomUUID as randomUUID7 } from "crypto";
26553
- import { basename as basename15, dirname as dirname16, join as join52, relative as relative6, resolve as resolve32, sep as sep9 } from "path";
26697
+ import { basename as basename15, dirname as dirname16, join as join53, relative as relative6, resolve as resolve32, sep as sep9 } from "path";
26554
26698
  import { Readable as Readable2 } from "stream";
26555
26699
 
26556
26700
  // ../lib/graph-trash/src/index.ts
@@ -26868,7 +27012,7 @@ async function cascadeDeleteDocument(params) {
26868
27012
 
26869
27013
  // app/lib/file-index.ts
26870
27014
  import * as fsp from "fs/promises";
26871
- import { resolve as resolve31, relative as relative5, join as join51, basename as basename14, extname, sep as sep8 } from "path";
27015
+ import { resolve as resolve31, relative as relative5, join as join52, basename as basename14, extname, sep as sep8 } from "path";
26872
27016
  import { tmpdir as tmpdir3 } from "os";
26873
27017
  import { execFile } from "child_process";
26874
27018
  import { promisify } from "util";
@@ -26950,7 +27094,7 @@ async function extractPdf(absolute) {
26950
27094
  return stdout.trim();
26951
27095
  }
26952
27096
  async function ocrPdf(absolute) {
26953
- const outPdf = join51(tmpdir3(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
27097
+ const outPdf = join52(tmpdir3(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
26954
27098
  try {
26955
27099
  await execFileAsync(
26956
27100
  "ocrmypdf",
@@ -27012,7 +27156,7 @@ async function readDisplayName(absolute) {
27012
27156
  const stem = dot === -1 ? base : base.slice(0, dot);
27013
27157
  if (base === `${stem}.meta.json`) return null;
27014
27158
  try {
27015
- const raw = await fsp.readFile(join51(dir, `${stem}.meta.json`), "utf-8");
27159
+ const raw = await fsp.readFile(join52(dir, `${stem}.meta.json`), "utf-8");
27016
27160
  const meta = JSON.parse(raw);
27017
27161
  const dn = meta.displayName ?? meta.filename;
27018
27162
  return typeof dn === "string" && dn.length > 0 ? dn : null;
@@ -27033,7 +27177,7 @@ async function walkSubtree(root, dataRoot, out) {
27033
27177
  for (const entry3 of entries2) {
27034
27178
  if (entry3.isSymbolicLink()) continue;
27035
27179
  if (entry3.isDirectory() && entry3.name === ".uploads-tmp") continue;
27036
- const abs = join51(root, entry3.name);
27180
+ const abs = join52(root, entry3.name);
27037
27181
  if (entry3.isDirectory()) {
27038
27182
  const sub = await walkSubtree(abs, dataRoot, out);
27039
27183
  if (!sub.clean) clean = false;
@@ -27406,7 +27550,7 @@ var UPLOAD_TMP_DIR = ".uploads-tmp";
27406
27550
  var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
27407
27551
  async function readMeta2(absDir, baseName) {
27408
27552
  try {
27409
- const raw = await readFile10(join52(absDir, `${baseName}.meta.json`), "utf8");
27553
+ const raw = await readFile10(join53(absDir, `${baseName}.meta.json`), "utf8");
27410
27554
  const parsed = JSON.parse(raw);
27411
27555
  if (typeof parsed?.filename === "string") {
27412
27556
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -27461,7 +27605,7 @@ async function servableFilesIn(dirAbs, uuid) {
27461
27605
  }
27462
27606
  async function enrich(absolute, entry3, accountNames) {
27463
27607
  if (entry3.kind === "directory" && UUID_RE3.test(entry3.name)) {
27464
- const dirAbs = join52(absolute, entry3.name);
27608
+ const dirAbs = join53(absolute, entry3.name);
27465
27609
  const meta = await readMeta2(dirAbs, entry3.name);
27466
27610
  if (meta?.filename) {
27467
27611
  const servable = await servableFilesIn(dirAbs, entry3.name);
@@ -27470,7 +27614,7 @@ async function enrich(absolute, entry3, accountNames) {
27470
27614
  let size = null;
27471
27615
  let modifiedAt = entry3.modifiedAt;
27472
27616
  try {
27473
- const st = await stat6(join52(dirAbs, innerName));
27617
+ const st = await stat6(join53(dirAbs, innerName));
27474
27618
  size = st.size;
27475
27619
  modifiedAt = st.mtime.toISOString();
27476
27620
  } catch {
@@ -27682,7 +27826,7 @@ app28.get("/", requireAdminSession, async (c) => {
27682
27826
  const childRel = relPath === "" || relPath === "." ? name : `${relPath}/${name}`;
27683
27827
  const protectedEntry = isProtectedFromDeletion(childRel).protected;
27684
27828
  try {
27685
- const entryPath = join52(absolute, name);
27829
+ const entryPath = join53(absolute, name);
27686
27830
  const s = await stat6(entryPath);
27687
27831
  entries2.push({
27688
27832
  name,
@@ -27711,7 +27855,7 @@ app28.get("/", requireAdminSession, async (c) => {
27711
27855
  const displayPath = buildDisplayPath(relPath, accountNames);
27712
27856
  if (groupReq || isAccountRoot(relPath)) {
27713
27857
  const acctId = relPath.split("/")[1] ?? accountId;
27714
- const schemaMd = await readFile10(join52(absolute, "SCHEMA.md"), "utf8").catch(() => null);
27858
+ const schemaMd = await readFile10(join53(absolute, "SCHEMA.md"), "utf8").catch(() => null);
27715
27859
  const groups = classifyAccountRoot(schemaMd, entries2.map((e) => e.name));
27716
27860
  console.error(
27717
27861
  `[data-schema] op=classify account=${acctId} status=${groups.parsed ? "ok" : "parse-failed"} reason=${groups.reason} entries=${entries2.length} home=${groups.home.join(",") || "none"} system=${groups.system.length} unknown=${groups.unknown.join(",") || "none"}`
@@ -27749,7 +27893,7 @@ app28.get("/", requireAdminSession, async (c) => {
27749
27893
  for (const name of homeDirs) {
27750
27894
  const base = byName.get(name);
27751
27895
  try {
27752
- const dirents = await readdir4(join52(absolute, name), { withFileTypes: true });
27896
+ const dirents = await readdir4(join53(absolute, name), { withFileTypes: true });
27753
27897
  const visible = dirents.filter((d) => !d.name.startsWith("."));
27754
27898
  visible.sort((a, b) => {
27755
27899
  const ad = a.isDirectory() ? 0 : 1;
@@ -27814,7 +27958,7 @@ app28.get("/download", requireAdminSession, async (c) => {
27814
27958
  console.error(`[data] op=scratchpad-no-session sessionId="${sessionId.slice(0, 8)}\u2026"`);
27815
27959
  return c.json({ error: "Not found" }, 404);
27816
27960
  }
27817
- const meta = readSidecarMeta(join52(projectDir, `${sessionId}.meta.json`));
27961
+ const meta = readSidecarMeta(join53(projectDir, `${sessionId}.meta.json`));
27818
27962
  if (!meta.accountId || meta.accountId !== accountId) {
27819
27963
  console.error(`[data] account-scope-blocked root="scratchpad" sessionId="${sessionId.slice(0, 8)}\u2026" account=${accountId.slice(0, 8)}\u2026`);
27820
27964
  return c.json({ error: "Not found" }, 404);
@@ -27908,7 +28052,7 @@ async function* walkRegularFiles(dirAbsolute) {
27908
28052
  const dirents = await readdir4(dirAbsolute, { withFileTypes: true });
27909
28053
  for (const d of dirents) {
27910
28054
  if (d.isSymbolicLink()) continue;
27911
- const child = join52(dirAbsolute, d.name);
28055
+ const child = join53(dirAbsolute, d.name);
27912
28056
  if (d.isDirectory()) {
27913
28057
  yield* walkRegularFiles(child);
27914
28058
  continue;
@@ -28014,7 +28158,7 @@ async function resolveUploadTarget(c, accountId, uid) {
28014
28158
  const base = resolveSharedOrOwnAccountWrite(rawDir, accountId, "POST /api/admin/files/upload");
28015
28159
  if (!base.ok) return { ok: false, status: base.status, error: base.error };
28016
28160
  const relDir = relpath ? dirname16(relpath) : ".";
28017
- const destRel = relDir === "." ? base.relative : join52(base.relative, relDir);
28161
+ const destRel = relDir === "." ? base.relative : join53(base.relative, relDir);
28018
28162
  if ((0, import_dist.isSharedPath)(destRel)) {
28019
28163
  const gate2 = gateSharedWrite(base.absolute, destRel, accountId, "POST /api/admin/files/upload");
28020
28164
  if (!gate2.ok) return { ok: false, status: gate2.status, error: gate2.error };
@@ -28400,7 +28544,7 @@ app28.delete("/", requireAdminSession, async (c) => {
28400
28544
  const dot = base.lastIndexOf(".");
28401
28545
  const stem = dot === -1 ? base : base.slice(0, dot);
28402
28546
  const containerDir = dirname16(absolute);
28403
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join52(containerDir, `${stem}.meta.json`) : null;
28547
+ const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join53(containerDir, `${stem}.meta.json`) : null;
28404
28548
  const isAttachmentContainer = UUID_RE3.test(stem) && basename15(containerDir) === stem;
28405
28549
  await unlink3(absolute);
28406
28550
  if (sidecarPath) {
@@ -28410,7 +28554,7 @@ app28.delete("/", requireAdminSession, async (c) => {
28410
28554
  }
28411
28555
  }
28412
28556
  if (isAttachmentContainer) {
28413
- await unlink3(join52(containerDir, `${stem}${EXTRACTED_TEXT_SUFFIX}`)).catch(() => {
28557
+ await unlink3(join53(containerDir, `${stem}${EXTRACTED_TEXT_SUFFIX}`)).catch(() => {
28414
28558
  });
28415
28559
  await rmdir(containerDir).catch(() => {
28416
28560
  });
@@ -29525,8 +29669,8 @@ function isHiddenByDefault(label) {
29525
29669
 
29526
29670
  // server/lib/top-level-labels.ts
29527
29671
  var import_dist14 = __toESM(require_dist9(), 1);
29528
- import { readdirSync as readdirSync26, readFileSync as readFileSync42 } from "fs";
29529
- import { join as join53, resolve as resolve33 } from "path";
29672
+ import { readdirSync as readdirSync27, readFileSync as readFileSync43 } from "fs";
29673
+ import { join as join54, resolve as resolve33 } from "path";
29530
29674
  var STATIC_TOP_LEVEL_LABELS = Object.freeze(
29531
29675
  /* @__PURE__ */ new Set([
29532
29676
  // Base-schema infra (schema-base.md has no top-level section).
@@ -29630,7 +29774,7 @@ function parseTopLevelLabels(content) {
29630
29774
  }
29631
29775
  function resolveReferencesDir() {
29632
29776
  if (process.env.MAXY_PLATFORM_ROOT) {
29633
- return join53(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
29777
+ return join54(process.env.MAXY_PLATFORM_ROOT, "plugins", "memory", "references");
29634
29778
  }
29635
29779
  return resolve33(import.meta.dirname, "../../../plugins/memory/references");
29636
29780
  }
@@ -29638,9 +29782,9 @@ function parseTableTopLevelLabels(referencesDir) {
29638
29782
  const labels = /* @__PURE__ */ new Set();
29639
29783
  const contributingFiles = [];
29640
29784
  try {
29641
- const fileNames = readdirSync26(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
29785
+ const fileNames = readdirSync27(referencesDir).filter((f) => f.startsWith("schema-") && f.endsWith(".md")).sort();
29642
29786
  for (const fileName of fileNames) {
29643
- const content = readFileSync42(join53(referencesDir, fileName), "utf-8");
29787
+ const content = readFileSync43(join54(referencesDir, fileName), "utf-8");
29644
29788
  const parsed = parseTopLevelLabels(content);
29645
29789
  if (parsed.length > 0) contributingFiles.push(fileName);
29646
29790
  for (const label of parsed) labels.add(label);
@@ -30978,19 +31122,19 @@ function isWithin(target, root) {
30978
31122
  var sidebar_artefacts_default = app35;
30979
31123
 
30980
31124
  // server/channel-session-override.ts
30981
- import { readFileSync as readFileSync43, writeFileSync as writeFileSync19, renameSync as renameSync10 } from "fs";
30982
- import { join as join54 } from "path";
31125
+ import { readFileSync as readFileSync44, writeFileSync as writeFileSync19, renameSync as renameSync10 } from "fs";
31126
+ import { join as join55 } from "path";
30983
31127
  var FILE2 = "channel-session-override.json";
30984
31128
  var UUID3 = /^[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}$/;
30985
31129
  function channelOverridePath(accountDir) {
30986
- return join54(accountDir, FILE2);
31130
+ return join55(accountDir, FILE2);
30987
31131
  }
30988
31132
  function senderKey(channel, senderId) {
30989
31133
  return `${channel}:${senderId}`;
30990
31134
  }
30991
31135
  function readRaw2(accountDir) {
30992
31136
  try {
30993
- return JSON.parse(readFileSync43(channelOverridePath(accountDir), "utf8"));
31137
+ return JSON.parse(readFileSync44(channelOverridePath(accountDir), "utf8"));
30994
31138
  } catch {
30995
31139
  return null;
30996
31140
  }
@@ -31428,24 +31572,24 @@ var session_rc_spawn_default = app41;
31428
31572
  import { randomUUID as randomUUID9 } from "crypto";
31429
31573
 
31430
31574
  // server/wa-descriptor.ts
31431
- import { resolve as resolve35, join as join55 } from "path";
31575
+ import { resolve as resolve35, join as join56 } from "path";
31432
31576
  function waGatewayUrl() {
31433
31577
  return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
31434
31578
  }
31435
31579
  function waServerPath() {
31436
- return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join55(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
31580
+ return process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve35(process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, ".."), "services/whatsapp-channel/dist/server.js");
31437
31581
  }
31438
31582
  function waChannelDescriptor(senderId) {
31439
31583
  return { senderId, gatewayUrl: waGatewayUrl(), serverPath: waServerPath() };
31440
31584
  }
31441
31585
 
31442
31586
  // server/telegram-descriptor.ts
31443
- import { resolve as resolve36, join as join56 } from "path";
31587
+ import { resolve as resolve36, join as join57 } from "path";
31444
31588
  function telegramGatewayUrl() {
31445
31589
  return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
31446
31590
  }
31447
31591
  function telegramServerPath() {
31448
- return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve36(process.env.MAXY_PLATFORM_ROOT ?? join56(__dirname, ".."), "services/telegram-channel/dist/server.js");
31592
+ return process.env.MAXY_TELEGRAM_CHANNEL_SERVER_PATH ?? resolve36(process.env.MAXY_PLATFORM_ROOT ?? join57(__dirname, ".."), "services/telegram-channel/dist/server.js");
31449
31593
  }
31450
31594
  function telegramChannelDescriptor(senderId) {
31451
31595
  return { senderId, gatewayUrl: telegramGatewayUrl(), serverPath: telegramServerPath() };
@@ -31616,12 +31760,12 @@ function parseCpuStat(blob) {
31616
31760
  const line = blob.split("\n", 1)[0] ?? "";
31617
31761
  const parts = line.trim().split(/\s+/);
31618
31762
  if (parts[0] !== "cpu") throw new Error(`unexpected /proc/stat header: ${parts[0] ?? "(empty)"}`);
31619
- const fields = parts.slice(1).map((n) => Number.parseInt(n, 10));
31620
- if (fields.length < 4 || fields.some((n) => !Number.isFinite(n))) {
31763
+ const fields2 = parts.slice(1).map((n) => Number.parseInt(n, 10));
31764
+ if (fields2.length < 4 || fields2.some((n) => !Number.isFinite(n))) {
31621
31765
  throw new Error("malformed /proc/stat cpu line");
31622
31766
  }
31623
- const idle = fields[3] + (fields[4] ?? 0);
31624
- const total = fields.reduce((a, b) => a + b, 0);
31767
+ const idle = fields2[3] + (fields2[4] ?? 0);
31768
+ const total = fields2.reduce((a, b) => a + b, 0);
31625
31769
  return { idle, total };
31626
31770
  }
31627
31771
  function cpuPctFromSamples(a, b) {
@@ -31731,14 +31875,14 @@ app43.get("/", async (c) => {
31731
31875
  var system_stats_default = app43;
31732
31876
 
31733
31877
  // server/routes/admin/health.ts
31734
- import { existsSync as existsSync45, readFileSync as readFileSync44 } from "fs";
31735
- import { resolve as resolve37, join as join57 } from "path";
31878
+ import { existsSync as existsSync45, readFileSync as readFileSync45 } from "fs";
31879
+ import { resolve as resolve37, join as join58 } from "path";
31736
31880
  var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve37(process.cwd(), "..");
31737
31881
  var brandHostname = "maxy";
31738
- var brandJsonPath = join57(PLATFORM_ROOT4, "config", "brand.json");
31882
+ var brandJsonPath = join58(PLATFORM_ROOT4, "config", "brand.json");
31739
31883
  if (existsSync45(brandJsonPath)) {
31740
31884
  try {
31741
- const brand = JSON.parse(readFileSync44(brandJsonPath, "utf-8"));
31885
+ const brand = JSON.parse(readFileSync45(brandJsonPath, "utf-8"));
31742
31886
  if (brand.hostname) brandHostname = brand.hostname;
31743
31887
  } catch {
31744
31888
  }
@@ -31748,7 +31892,7 @@ var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
31748
31892
  var PROBE_TIMEOUT_MS = 1e3;
31749
31893
  function readVersion() {
31750
31894
  if (!existsSync45(VERSION_FILE)) return "unknown";
31751
- return readFileSync44(VERSION_FILE, "utf-8").trim() || "unknown";
31895
+ return readFileSync45(VERSION_FILE, "utf-8").trim() || "unknown";
31752
31896
  }
31753
31897
  async function probeConversationDb() {
31754
31898
  let session;
@@ -31803,7 +31947,7 @@ import { randomUUID as randomUUID10 } from "crypto";
31803
31947
 
31804
31948
  // ../lib/account-ingest-token/src/index.ts
31805
31949
  import { randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "crypto";
31806
- import { existsSync as existsSync46, readdirSync as readdirSync27, readFileSync as readFileSync45, writeFileSync as writeFileSync20 } from "fs";
31950
+ import { existsSync as existsSync46, readdirSync as readdirSync28, readFileSync as readFileSync46, writeFileSync as writeFileSync20 } from "fs";
31807
31951
  import { resolve as resolve38 } from "path";
31808
31952
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
31809
31953
  var INGEST_TOKEN_FIELD = "ingestToken";
@@ -31815,7 +31959,7 @@ function readConfig2(accountsDir, accountId) {
31815
31959
  const path = configPath2(accountsDir, accountId);
31816
31960
  if (!existsSync46(path)) return null;
31817
31961
  try {
31818
- const parsed = JSON.parse(readFileSync45(path, "utf-8"));
31962
+ const parsed = JSON.parse(readFileSync46(path, "utf-8"));
31819
31963
  return parsed !== null && typeof parsed === "object" ? parsed : null;
31820
31964
  } catch {
31821
31965
  return null;
@@ -31829,7 +31973,7 @@ function listAccountIdsOnDisk(accountsDir) {
31829
31973
  if (!existsSync46(accountsDir)) return [];
31830
31974
  let entries2;
31831
31975
  try {
31832
- entries2 = readdirSync27(accountsDir);
31976
+ entries2 = readdirSync28(accountsDir);
31833
31977
  } catch {
31834
31978
  return [];
31835
31979
  }
@@ -32356,9 +32500,9 @@ function renderWrite(write) {
32356
32500
  const val = String(v).slice(0, 200);
32357
32501
  kv.push(`${k}=${val}`);
32358
32502
  }
32359
- const tail = [parts.join(" "), kv.join("; ")].filter((s) => s.length > 0).join(" \u2014 ");
32360
- if (tail.length === 0) return null;
32361
- return `- ${label}: ${tail}`;
32503
+ const tail2 = [parts.join(" "), kv.join("; ")].filter((s) => s.length > 0).join(" \u2014 ");
32504
+ if (tail2.length === 0) return null;
32505
+ return `- ${label}: ${tail2}`;
32362
32506
  }
32363
32507
  function formatPreviousContext(writes) {
32364
32508
  if (writes.length === 0) return null;
@@ -32668,7 +32812,7 @@ var browser_default = app51;
32668
32812
  // server/routes/admin/calendar.ts
32669
32813
  import { existsSync as existsSync47 } from "fs";
32670
32814
  import { randomUUID as randomUUID11 } from "crypto";
32671
- import { join as join59 } from "path";
32815
+ import { join as join60 } from "path";
32672
32816
 
32673
32817
  // server/lib/calendar-ics.ts
32674
32818
  var CRLF = "\r\n";
@@ -32768,8 +32912,8 @@ function countVEvents(ics) {
32768
32912
  }
32769
32913
 
32770
32914
  // server/lib/calendar-availability.ts
32771
- import { readFileSync as readFileSync46 } from "fs";
32772
- import { join as join58 } from "path";
32915
+ import { readFileSync as readFileSync47 } from "fs";
32916
+ import { join as join59 } from "path";
32773
32917
  var AVAILABILITY_FILENAME = "calendar-availability.json";
32774
32918
  var REQUIRED_KEYS = [
32775
32919
  "timezone",
@@ -32778,10 +32922,10 @@ var REQUIRED_KEYS = [
32778
32922
  "weekly"
32779
32923
  ];
32780
32924
  function readAvailabilityConfig(accountDir) {
32781
- const path = join58(accountDir, AVAILABILITY_FILENAME);
32925
+ const path = join59(accountDir, AVAILABILITY_FILENAME);
32782
32926
  let raw;
32783
32927
  try {
32784
- raw = readFileSync46(path, "utf-8");
32928
+ raw = readFileSync47(path, "utf-8");
32785
32929
  } catch {
32786
32930
  throw new Error(`availability not configured: ${path} not found`);
32787
32931
  }
@@ -33167,16 +33311,16 @@ app52.patch("/meetings/:id", requireAdminSession, async (c) => {
33167
33311
  }
33168
33312
  const id = c.req.param("id");
33169
33313
  const body = await safeJson(c) ?? {};
33170
- const fields = EDITABLE_FIELDS.filter((f) => f in body);
33171
- if (fields.length === 0) {
33314
+ const fields2 = EDITABLE_FIELDS.filter((f) => f in body);
33315
+ if (fields2.length === 0) {
33172
33316
  return c.json({ error: "No editable field supplied (title, startsAt, endsAt, location, description, privateNote)" }, 400);
33173
33317
  }
33174
- const setClauses = fields.map(
33318
+ const setClauses = fields2.map(
33175
33319
  (f) => f === "startsAt" || f === "endsAt" ? `m.${f} = datetime($${f})` : `m.${f} = $${f}`
33176
33320
  );
33177
33321
  setClauses.push("m.updatedAt = datetime()");
33178
33322
  const params = { accountId, id };
33179
- for (const f of fields) params[f] = body[f];
33323
+ for (const f of fields2) params[f] = body[f];
33180
33324
  const session = getSession();
33181
33325
  try {
33182
33326
  const res = await session.run(
@@ -33187,12 +33331,12 @@ app52.patch("/meetings/:id", requireAdminSession, async (c) => {
33187
33331
  params
33188
33332
  );
33189
33333
  if (res.records.length === 0) {
33190
- console.warn(`[calendar-edit] account=${accountId.slice(0, 8)} uid=absent fields=${fields.join(",")} found=0`);
33334
+ console.warn(`[calendar-edit] account=${accountId.slice(0, 8)} uid=absent fields=${fields2.join(",")} found=0`);
33191
33335
  return c.json({ error: "Meeting not found for this account" }, 404);
33192
33336
  }
33193
33337
  const r = res.records[0];
33194
33338
  const uid = r.get("uid");
33195
- console.log(`[calendar-edit] account=${accountId.slice(0, 8)} uid=${uid ?? "absent"} fields=${fields.join(",")}`);
33339
+ console.log(`[calendar-edit] account=${accountId.slice(0, 8)} uid=${uid ?? "absent"} fields=${fields2.join(",")}`);
33196
33340
  return c.json({ meeting: mapMeeting(r) });
33197
33341
  } catch (err) {
33198
33342
  const message = err instanceof Error ? err.message : String(err);
@@ -33336,7 +33480,7 @@ app52.get("/booking-link", requireAdminSession, (c) => {
33336
33480
  }
33337
33481
  const id83 = account.accountId.slice(0, 8);
33338
33482
  try {
33339
- if (!existsSync47(join59(account.accountDir, AVAILABILITY_FILENAME))) {
33483
+ if (!existsSync47(join60(account.accountDir, AVAILABILITY_FILENAME))) {
33340
33484
  console.log(`[calendar] op=booking-link accountId=${id83} bookingDomain=none source=availability-config`);
33341
33485
  return c.json({ bookingDomain: null });
33342
33486
  }
@@ -33355,8 +33499,8 @@ app52.get("/booking-link", requireAdminSession, (c) => {
33355
33499
  var calendar_default = app52;
33356
33500
 
33357
33501
  // server/routes/admin/data-portal.ts
33358
- import { existsSync as existsSync48, readFileSync as readFileSync47 } from "fs";
33359
- import { join as join60 } from "path";
33502
+ import { existsSync as existsSync48, readFileSync as readFileSync48 } from "fs";
33503
+ import { join as join61 } from "path";
33360
33504
  var app53 = new Hono();
33361
33505
  var PORTAL_FILENAME = "data-portal.json";
33362
33506
  app53.get("/portal-link", requireAdminSession, (c) => {
@@ -33367,12 +33511,12 @@ app53.get("/portal-link", requireAdminSession, (c) => {
33367
33511
  }
33368
33512
  const id83 = account.accountId.slice(0, 8);
33369
33513
  try {
33370
- const path = join60(account.accountDir, PORTAL_FILENAME);
33514
+ const path = join61(account.accountDir, PORTAL_FILENAME);
33371
33515
  if (!existsSync48(path)) {
33372
33516
  console.log(`[data-portal] op=portal-link accountId=${id83} portalDomain=none source=data-portal-config`);
33373
33517
  return c.json({ portalDomain: null });
33374
33518
  }
33375
- const cfg = JSON.parse(readFileSync47(path, "utf-8"));
33519
+ const cfg = JSON.parse(readFileSync48(path, "utf-8"));
33376
33520
  const portalDomain = typeof cfg.portalDomain === "string" && cfg.portalDomain.trim() ? cfg.portalDomain.trim() : null;
33377
33521
  console.log(`[data-portal] op=portal-link accountId=${id83} portalDomain=${portalDomain ?? "none"} source=data-portal-config`);
33378
33522
  return c.json({ portalDomain });
@@ -33385,8 +33529,8 @@ app53.get("/portal-link", requireAdminSession, (c) => {
33385
33529
  var data_portal_default = app53;
33386
33530
 
33387
33531
  // server/routes/admin/dispatch-planner.ts
33388
- import { existsSync as existsSync49, readFileSync as readFileSync48 } from "fs";
33389
- import { join as join61 } from "path";
33532
+ import { existsSync as existsSync49, readFileSync as readFileSync49 } from "fs";
33533
+ import { join as join62 } from "path";
33390
33534
  var app54 = new Hono();
33391
33535
  var PORTAL_FILENAME2 = "data-portal.json";
33392
33536
  app54.get("/planner-link", requireAdminSession, (c) => {
@@ -33399,15 +33543,15 @@ app54.get("/planner-link", requireAdminSession, (c) => {
33399
33543
  }
33400
33544
  const id83 = account.accountId.slice(0, 8);
33401
33545
  try {
33402
- const portalPath = join61(account.accountDir, PORTAL_FILENAME2);
33403
- const dispatchConfigured = existsSync49(join61(account.accountDir, "dispatch", "config.json"));
33546
+ const portalPath = join62(account.accountDir, PORTAL_FILENAME2);
33547
+ const dispatchConfigured = existsSync49(join62(account.accountDir, "dispatch", "config.json"));
33404
33548
  if (!existsSync49(portalPath) || !dispatchConfigured) {
33405
33549
  console.log(
33406
33550
  `[dispatch-planner] op=planner-link accountId=${id83} plannerUrl=none portal=${existsSync49(portalPath)} dispatch=${dispatchConfigured}`
33407
33551
  );
33408
33552
  return c.json({ plannerUrl: null });
33409
33553
  }
33410
- const cfg = JSON.parse(readFileSync48(portalPath, "utf-8"));
33554
+ const cfg = JSON.parse(readFileSync49(portalPath, "utf-8"));
33411
33555
  const domain = typeof cfg.portalDomain === "string" && cfg.portalDomain.trim() ? cfg.portalDomain.trim() : null;
33412
33556
  const plannerUrl = domain ? `https://${domain}/planner` : null;
33413
33557
  console.log(
@@ -33425,11 +33569,11 @@ var dispatch_planner_default = app54;
33425
33569
  // server/routes/admin/routines.ts
33426
33570
  import cronParser from "cron-parser";
33427
33571
  import { int as int2 } from "neo4j-driver";
33428
- import { readFileSync as readFileSync50 } from "fs";
33572
+ import { readFileSync as readFileSync51 } from "fs";
33429
33573
  import { resolve as resolve40 } from "path";
33430
33574
 
33431
33575
  // ../lib/agent-dispatch-rule/src/index.ts
33432
- import { readFileSync as readFileSync49 } from "fs";
33576
+ import { readFileSync as readFileSync50 } from "fs";
33433
33577
  import { resolve as resolve39 } from "path";
33434
33578
  function normalizeE1642(value) {
33435
33579
  let candidate = value.trim().replace(/[\s\-().]/g, "");
@@ -33447,7 +33591,7 @@ function phonesMatch2(a, b) {
33447
33591
  function readChannelAdmins(accountDir, channel) {
33448
33592
  let config = {};
33449
33593
  try {
33450
- config = JSON.parse(readFileSync49(resolve39(accountDir, "account.json"), "utf-8"));
33594
+ config = JSON.parse(readFileSync50(resolve39(accountDir, "account.json"), "utf-8"));
33451
33595
  } catch {
33452
33596
  config = {};
33453
33597
  }
@@ -33554,23 +33698,39 @@ function listDispatchDestinations(input) {
33554
33698
  const byDestination = (a, b) => a.destination.localeCompare(b.destination);
33555
33699
  return [...managers.sort(byDestination), ...admins.sort(byDestination), ...telegram];
33556
33700
  }
33557
- function isDispatchable(fields) {
33558
- if (fields.agentChannel && fields.agentDestination && fields.agentPrompt) return true;
33559
- return Boolean(fields.actionPlugin && fields.actionTool);
33701
+ function isDispatchable(fields2) {
33702
+ if (fields2.agentChannel && fields2.agentDestination && fields2.agentPrompt) return true;
33703
+ return Boolean(fields2.actionPlugin && fields2.actionTool);
33560
33704
  }
33561
- function dispatchGapReason(fields) {
33562
- if (fields.agentChannel || fields.agentDestination || fields.agentPrompt) {
33705
+ function dispatchGapReason(fields2) {
33706
+ if (fields2.agentChannel || fields2.agentDestination || fields2.agentPrompt) {
33563
33707
  const missing = [];
33564
- if (!fields.agentChannel) missing.push("channel");
33565
- if (!fields.agentDestination) missing.push("destination");
33566
- if (!fields.agentPrompt) missing.push("prompt");
33708
+ if (!fields2.agentChannel) missing.push("channel");
33709
+ if (!fields2.agentDestination) missing.push("destination");
33710
+ if (!fields2.agentPrompt) missing.push("prompt");
33567
33711
  return `no-${missing.join("+")}`;
33568
33712
  }
33569
- if (fields.actionPlugin || fields.actionTool) {
33570
- return fields.actionTool ? "no-action-plugin" : "no-action-tool";
33713
+ if (fields2.actionPlugin || fields2.actionTool) {
33714
+ return fields2.actionTool ? "no-action-plugin" : "no-action-tool";
33571
33715
  }
33572
33716
  return "no-dispatch-configured";
33573
33717
  }
33718
+ function carrierVerdict(accountDir, botId, destination) {
33719
+ let config = {};
33720
+ try {
33721
+ config = JSON.parse(readFileSync50(resolve39(accountDir, "account.json"), "utf-8"));
33722
+ } catch {
33723
+ return { ok: false, reason: "absent" };
33724
+ }
33725
+ const bots = Array.isArray(config.telegram?.bots) ? config.telegram.bots : [];
33726
+ const entry3 = bots.find(
33727
+ (raw) => raw && typeof raw === "object" && raw.id === botId
33728
+ );
33729
+ if (!entry3) return { ok: false, reason: "absent" };
33730
+ if (entry3.role !== "admin") return { ok: false, reason: "not-admin" };
33731
+ const users = Array.isArray(entry3.adminUsers) ? entry3.adminUsers.filter((u) => typeof u === "number") : [];
33732
+ return users.includes(Number(destination)) ? { ok: true } : { ok: false, reason: "not-listed" };
33733
+ }
33574
33734
 
33575
33735
  // server/routes/admin/routines.ts
33576
33736
  var { parseExpression } = cronParser;
@@ -33615,7 +33775,7 @@ function gateWatermarkPath(eventId) {
33615
33775
  }
33616
33776
  function readGateLastCommitAt(eventId) {
33617
33777
  try {
33618
- const parsed = JSON.parse(readFileSync50(gateWatermarkPath(eventId), "utf-8"));
33778
+ const parsed = JSON.parse(readFileSync51(gateWatermarkPath(eventId), "utf-8"));
33619
33779
  return typeof parsed?.lastCommitAt === "string" ? parsed.lastCommitAt : null;
33620
33780
  } catch {
33621
33781
  return null;
@@ -33725,9 +33885,9 @@ function describeDispatchGap(reason) {
33725
33885
  destination: "a destination",
33726
33886
  prompt: "an instruction"
33727
33887
  };
33728
- const fields = reason.startsWith("no-") ? reason.slice(3).split("+") : [];
33729
- if (fields.length > 0 && fields.every((f) => f in words)) {
33730
- const list = fields.map((f) => words[f]);
33888
+ const fields2 = reason.startsWith("no-") ? reason.slice(3).split("+") : [];
33889
+ if (fields2.length > 0 && fields2.every((f) => f in words)) {
33890
+ const list = fields2.map((f) => words[f]);
33731
33891
  const joined = list.length === 1 ? list[0] : `${list.slice(0, -1).join(", ")} and ${list[list.length - 1]}`;
33732
33892
  return `This routine has no ${joined.replace(/^an? /, "")} set, so arming it would deliver nothing.`;
33733
33893
  }
@@ -34056,7 +34216,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34056
34216
  const id83 = eventId.slice(0, 8);
34057
34217
  const setClauses = [];
34058
34218
  const params = { eventId, accountId };
34059
- const fields = [];
34219
+ const fields2 = [];
34060
34220
  let nextRunLog = "unchanged";
34061
34221
  let bindingLog = "";
34062
34222
  const hasChannel = "agentChannel" in body;
@@ -34140,7 +34300,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34140
34300
  setClauses.push("e.agentChannel = $agentChannel", "e.agentDestination = $agentDestination");
34141
34301
  params.agentChannel = channel;
34142
34302
  params.agentDestination = destination;
34143
- fields.push("agentChannel", "agentDestination");
34303
+ fields2.push("agentChannel", "agentDestination");
34144
34304
  bindingLog = ` channel=${channel} destination=${destination.slice(-4)}`;
34145
34305
  }
34146
34306
  if (clearing) {
@@ -34153,13 +34313,13 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34153
34313
  );
34154
34314
  }
34155
34315
  setClauses.push("e.agentChannel = null", "e.agentDestination = null");
34156
- fields.push("agentChannel", "agentDestination");
34316
+ fields2.push("agentChannel", "agentDestination");
34157
34317
  bindingLog = " channel=none destination=none";
34158
34318
  }
34159
34319
  if (typeof body.agentPrompt === "string") {
34160
34320
  setClauses.push("e.agentPrompt = $agentPrompt");
34161
34321
  params.agentPrompt = body.agentPrompt;
34162
- fields.push("agentPrompt");
34322
+ fields2.push("agentPrompt");
34163
34323
  setClauses.push("e.operatorEdited = $operatorEdited");
34164
34324
  params.operatorEdited = (/* @__PURE__ */ new Date()).toISOString();
34165
34325
  }
@@ -34176,7 +34336,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34176
34336
  }
34177
34337
  setClauses.push("e.name = $name");
34178
34338
  params.name = nextName;
34179
- fields.push("name");
34339
+ fields2.push("name");
34180
34340
  setClauses.push("e.operatorEdited = $operatorEdited");
34181
34341
  params.operatorEdited = (/* @__PURE__ */ new Date()).toISOString();
34182
34342
  }
@@ -34190,7 +34350,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34190
34350
  nextDescription = typeof raw === "string" && raw.trim().length > 0 ? raw : null;
34191
34351
  setClauses.push("e.description = $description");
34192
34352
  params.description = nextDescription;
34193
- fields.push("description");
34353
+ fields2.push("description");
34194
34354
  setClauses.push("e.operatorEdited = $operatorEdited");
34195
34355
  params.operatorEdited = (/* @__PURE__ */ new Date()).toISOString();
34196
34356
  }
@@ -34205,7 +34365,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34205
34365
  params.embedding = embedding;
34206
34366
  } else {
34207
34367
  embedLog = " embed=failed";
34208
- console.error(`[admin:routines] op=patch id=${id83} fields=${fields.join("+")} embed=failed`);
34368
+ console.error(`[admin:routines] op=patch id=${id83} fields=${fields2.join("+")} embed=failed`);
34209
34369
  }
34210
34370
  }
34211
34371
  if (typeof body.recurrence === "string") {
@@ -34215,7 +34375,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34215
34375
  setClauses.push("e.recurrence = $recurrence", "e.nextRun = $nextRun");
34216
34376
  params.recurrence = body.recurrence;
34217
34377
  params.nextRun = nextRun;
34218
- fields.push("recurrence");
34378
+ fields2.push("recurrence");
34219
34379
  nextRunLog = nextRun;
34220
34380
  } catch (err) {
34221
34381
  const reason = err instanceof Error ? err.message : String(err);
@@ -34241,7 +34401,7 @@ app55.patch("/:eventId", requireAdminSession, async (c) => {
34241
34401
  return c.json({ error: "Routine not found" }, 404);
34242
34402
  }
34243
34403
  const nameLog = nextName === null ? "" : ` name=len:${nextName.length} operatorEdited=stamped`;
34244
- console.log(`[admin:routines] op=patch id=${id83} fields=${fields.join("+")} outcome=ok nextRun=${nextRunLog} cron=${params.recurrence ?? "unchanged"}${bindingLog}${nameLog}${embedLog}`);
34404
+ console.log(`[admin:routines] op=patch id=${id83} fields=${fields2.join("+")} outcome=ok nextRun=${nextRunLog} cron=${params.recurrence ?? "unchanged"}${bindingLog}${nameLog}${embedLog}`);
34245
34405
  return c.json({ routine: mapRoutine(res.records[0]) });
34246
34406
  } catch (err) {
34247
34407
  const message = err instanceof Error ? err.message : String(err);
@@ -34492,7 +34652,7 @@ app55.delete("/:eventId", requireAdminSession, async (c) => {
34492
34652
  var routines_default = app55;
34493
34653
 
34494
34654
  // server/routes/admin/skills.ts
34495
- import { existsSync as existsSync50, readdirSync as readdirSync28, readFileSync as readFileSync51, rmSync as rmSync6 } from "fs";
34655
+ import { existsSync as existsSync50, readdirSync as readdirSync29, readFileSync as readFileSync52, rmSync as rmSync6 } from "fs";
34496
34656
  import { resolve as resolve41 } from "path";
34497
34657
  var app56 = new Hono();
34498
34658
  var NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
@@ -34518,7 +34678,7 @@ function stripQuotes(s) {
34518
34678
  function subdirs2(dir) {
34519
34679
  if (!existsSync50(dir)) return [];
34520
34680
  try {
34521
- return readdirSync28(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
34681
+ return readdirSync29(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
34522
34682
  } catch {
34523
34683
  return [];
34524
34684
  }
@@ -34541,9 +34701,9 @@ app56.get("/", requireAdminSession, async (c) => {
34541
34701
  const skillMd = resolve41(skillsDir, skill, "SKILL.md");
34542
34702
  if (!existsSync50(skillMd)) continue;
34543
34703
  try {
34544
- const { frontmatter } = splitSkillMd(readFileSync51(skillMd, "utf-8"));
34704
+ const { frontmatter } = splitSkillMd(readFileSync52(skillMd, "utf-8"));
34545
34705
  const refsDir = resolve41(skillsDir, skill, "references");
34546
- const refCount = existsSync50(refsDir) ? readdirSync28(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
34706
+ const refCount = existsSync50(refsDir) ? readdirSync29(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).length : 0;
34547
34707
  skills.push({ plugin, skill, description: frontmatter.description, refCount });
34548
34708
  } catch (err) {
34549
34709
  console.error(`[admin:skills] op=list-skip plugin=${plugin} skill=${skill} reason="${err instanceof Error ? err.message : String(err)}"`);
@@ -34571,9 +34731,9 @@ app56.get("/:plugin/:skill", requireAdminSession, async (c) => {
34571
34731
  console.log(`[admin:skills] op=get plugin=${plugin} skill=${skill} found=${found}`);
34572
34732
  if (!found) return c.json({ error: "Skill not found" }, 404);
34573
34733
  try {
34574
- const { frontmatter, body } = splitSkillMd(readFileSync51(skillMd, "utf-8"));
34734
+ const { frontmatter, body } = splitSkillMd(readFileSync52(skillMd, "utf-8"));
34575
34735
  const refsDir = resolve41(skillDir, "references");
34576
- const references = existsSync50(refsDir) ? readdirSync28(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
34736
+ const references = existsSync50(refsDir) ? readdirSync29(refsDir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort() : [];
34577
34737
  return c.json({ skill: { plugin, skill, frontmatter, body, references } });
34578
34738
  } catch (err) {
34579
34739
  const message = err instanceof Error ? err.message : String(err);
@@ -34617,7 +34777,7 @@ app56.delete("/:plugin/:skill", requireAdminSession, async (c) => {
34617
34777
  var skills_default = app56;
34618
34778
 
34619
34779
  // server/routes/admin/tasks-list.ts
34620
- import { basename as basename17, join as join62 } from "path";
34780
+ import { basename as basename17, join as join63 } from "path";
34621
34781
 
34622
34782
  // app/lib/time-entry-format.ts
34623
34783
  function secondsBetween(startIso, endIso) {
@@ -34793,7 +34953,7 @@ async function computeTaskList(accountId) {
34793
34953
  dangling = 0;
34794
34954
  } else if (cfg !== null) {
34795
34955
  try {
34796
- const walk = enumerateJsonlsWithSkips(join62(cfg, "projects"));
34956
+ const walk = enumerateJsonlsWithSkips(join63(cfg, "projects"));
34797
34957
  if (walk.skipped > 0) {
34798
34958
  console.error(
34799
34959
  `${TAG51} op=session-dangling-census-incomplete account=${accountId} skipped=${walk.skipped} detail="a slug the walk could not read contributes no session ids, so its links cannot be judged"`
@@ -35205,12 +35365,12 @@ app62.post("/", requireAdminSession, async (c) => {
35205
35365
  var task_delete_default = app62;
35206
35366
 
35207
35367
  // app/lib/admin-roster.ts
35208
- import { existsSync as existsSync51, readdirSync as readdirSync29, readFileSync as readFileSync52 } from "fs";
35368
+ import { existsSync as existsSync51, readdirSync as readdirSync30, readFileSync as readFileSync53 } from "fs";
35209
35369
  import { resolve as resolve42 } from "path";
35210
35370
  function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
35211
35371
  let entries2;
35212
35372
  try {
35213
- entries2 = readdirSync29(accountsDir);
35373
+ entries2 = readdirSync30(accountsDir);
35214
35374
  } catch {
35215
35375
  return [];
35216
35376
  }
@@ -35218,7 +35378,7 @@ function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
35218
35378
  const cfgPath = resolve42(accountsDir, entry3, "account.json");
35219
35379
  if (!existsSync51(cfgPath)) continue;
35220
35380
  try {
35221
- const cfg = JSON.parse(readFileSync52(cfgPath, "utf-8"));
35381
+ const cfg = JSON.parse(readFileSync53(cfgPath, "utf-8"));
35222
35382
  if (cfg.role !== "house") continue;
35223
35383
  if (!Array.isArray(cfg.admins)) return [];
35224
35384
  return cfg.admins.map(
@@ -35633,7 +35793,7 @@ app67.get("/", requireAdminSession, async (c) => {
35633
35793
  var dispatch_destinations_default = app67;
35634
35794
 
35635
35795
  // server/routes/admin/activity.ts
35636
- import { readdirSync as readdirSync30 } from "fs";
35796
+ import { readdirSync as readdirSync31 } from "fs";
35637
35797
  var app68 = new Hono();
35638
35798
  var TAG62 = "[activity]";
35639
35799
  function resolveHouseAccountId2(accounts) {
@@ -35649,7 +35809,7 @@ function requestToken() {
35649
35809
  var short = (id) => id ? id.slice(0, 8) : "unset";
35650
35810
  function countAccountDirsOnDisk() {
35651
35811
  try {
35652
- return readdirSync30(ACCOUNTS_DIR, { withFileTypes: true }).filter(
35812
+ return readdirSync31(ACCOUNTS_DIR, { withFileTypes: true }).filter(
35653
35813
  (e) => e.isDirectory() && !e.name.startsWith(".")
35654
35814
  ).length;
35655
35815
  } catch {
@@ -36060,7 +36220,7 @@ app72.route("/request-magic-link", request_magic_link_default);
36060
36220
  var access_default = app72;
36061
36221
 
36062
36222
  // server/routes/sites.ts
36063
- import { existsSync as existsSync52, readFileSync as readFileSync53, realpathSync as realpathSync8, statSync as statSync27 } from "fs";
36223
+ import { existsSync as existsSync52, readFileSync as readFileSync54, realpathSync as realpathSync8, statSync as statSync28 } from "fs";
36064
36224
  import { resolve as resolve45 } from "path";
36065
36225
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
36066
36226
  var MIME = {
@@ -36126,7 +36286,7 @@ app73.get("/:rel{.*}", (c) => {
36126
36286
  }
36127
36287
  let stat11;
36128
36288
  try {
36129
- stat11 = existsSync52(filePath) ? statSync27(filePath) : null;
36289
+ stat11 = existsSync52(filePath) ? statSync28(filePath) : null;
36130
36290
  } catch {
36131
36291
  stat11 = null;
36132
36292
  }
@@ -36164,7 +36324,7 @@ app73.get("/:rel{.*}", (c) => {
36164
36324
  }
36165
36325
  let body;
36166
36326
  try {
36167
- body = readFileSync53(realPath);
36327
+ body = readFileSync54(realPath);
36168
36328
  } catch (err) {
36169
36329
  const code = err?.code;
36170
36330
  if (code === "EISDIR") {
@@ -36200,7 +36360,7 @@ var sites_default = app73;
36200
36360
 
36201
36361
  // app/lib/visitor-token.ts
36202
36362
  import { createHmac as createHmac2, randomBytes as randomBytes3, timingSafeEqual as timingSafeEqual3 } from "crypto";
36203
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync54, writeFileSync as writeFileSync21 } from "fs";
36363
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync55, writeFileSync as writeFileSync21 } from "fs";
36204
36364
  import { dirname as dirname17 } from "path";
36205
36365
  var TOKEN_PREFIX = "v1.";
36206
36366
  var SECRET_BYTES = 32;
@@ -36209,7 +36369,7 @@ var cachedSecret = null;
36209
36369
  function getSecret() {
36210
36370
  if (cachedSecret) return cachedSecret;
36211
36371
  try {
36212
- const hex2 = readFileSync54(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
36372
+ const hex2 = readFileSync55(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
36213
36373
  if (hex2.length === SECRET_BYTES * 2) {
36214
36374
  cachedSecret = Buffer.from(hex2, "hex");
36215
36375
  return cachedSecret;
@@ -36223,7 +36383,7 @@ function getSecret() {
36223
36383
  console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
36224
36384
  } catch {
36225
36385
  }
36226
- const hex = readFileSync54(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
36386
+ const hex = readFileSync55(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
36227
36387
  cachedSecret = Buffer.from(hex, "hex");
36228
36388
  return cachedSecret;
36229
36389
  }
@@ -36276,8 +36436,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
36276
36436
  var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
36277
36437
 
36278
36438
  // app/lib/brand-config.ts
36279
- import { existsSync as existsSync53, readFileSync as readFileSync55 } from "fs";
36280
- import { join as join63 } from "path";
36439
+ import { existsSync as existsSync53, readFileSync as readFileSync56 } from "fs";
36440
+ import { join as join64 } from "path";
36281
36441
  var cached2 = null;
36282
36442
  var cachedAttempted = false;
36283
36443
  function readBrandConfig() {
@@ -36285,10 +36445,10 @@ function readBrandConfig() {
36285
36445
  cachedAttempted = true;
36286
36446
  const platformRoot5 = process.env.MAXY_PLATFORM_ROOT;
36287
36447
  if (!platformRoot5) return null;
36288
- const brandPath = join63(platformRoot5, "config", "brand.json");
36448
+ const brandPath = join64(platformRoot5, "config", "brand.json");
36289
36449
  if (!existsSync53(brandPath)) return null;
36290
36450
  try {
36291
- cached2 = JSON.parse(readFileSync55(brandPath, "utf-8"));
36451
+ cached2 = JSON.parse(readFileSync56(brandPath, "utf-8"));
36292
36452
  return cached2;
36293
36453
  } catch {
36294
36454
  return null;
@@ -37054,7 +37214,7 @@ var calendar_public_default = app78;
37054
37214
  // server/routes/portal-fetch.ts
37055
37215
  import { createReadStream as createReadStream3 } from "fs";
37056
37216
  import { stat as stat8, readFile as readFile12, realpath } from "fs/promises";
37057
- import { join as join64, resolve as resolve47, sep as sep11 } from "path";
37217
+ import { join as join65, resolve as resolve47, sep as sep11 } from "path";
37058
37218
  import { Readable as Readable3 } from "stream";
37059
37219
  var app79 = new Hono();
37060
37220
  var TAG65 = "[portal-fetch]";
@@ -37127,7 +37287,7 @@ function isValidAccountId2(value) {
37127
37287
  }
37128
37288
  async function readExposeFolders2(accountDir) {
37129
37289
  try {
37130
- const cfg = JSON.parse(await readFile12(join64(accountDir, "data-portal.json"), "utf8"));
37290
+ const cfg = JSON.parse(await readFile12(join65(accountDir, "data-portal.json"), "utf8"));
37131
37291
  return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
37132
37292
  } catch {
37133
37293
  return [];
@@ -37136,7 +37296,7 @@ async function readExposeFolders2(accountDir) {
37136
37296
  async function resolveFetchTarget(accountDir, relPath, ownerId) {
37137
37297
  let schemaText = null;
37138
37298
  try {
37139
- schemaText = await readFile12(join64(accountDir, "SCHEMA.md"), "utf8");
37299
+ schemaText = await readFile12(join65(accountDir, "SCHEMA.md"), "utf8");
37140
37300
  } catch {
37141
37301
  schemaText = null;
37142
37302
  }
@@ -37306,8 +37466,8 @@ function startTimeEntryCensus(openSession, opts = {}) {
37306
37466
  }
37307
37467
 
37308
37468
  // app/lib/ledger-census.ts
37309
- import { readdirSync as readdirSync31, readFileSync as readFileSync56, statSync as statSync28 } from "fs";
37310
- import { join as join65 } from "path";
37469
+ import { readdirSync as readdirSync32, readFileSync as readFileSync57, statSync as statSync29 } from "fs";
37470
+ import { join as join66 } from "path";
37311
37471
 
37312
37472
  // ../lib/ledger-core/dist/reconcile.js
37313
37473
  var LEDGER_INDEX_NAMES = [
@@ -37355,16 +37515,16 @@ var DAY_MS = 864e5;
37355
37515
  function countWriteRejects24h(logDir, now) {
37356
37516
  let entries2;
37357
37517
  try {
37358
- entries2 = readdirSync31(logDir).filter((f) => f.startsWith("mcp-ledger-") && f.endsWith(".log"));
37518
+ entries2 = readdirSync32(logDir).filter((f) => f.startsWith("mcp-ledger-") && f.endsWith(".log"));
37359
37519
  } catch {
37360
37520
  return 0;
37361
37521
  }
37362
37522
  let total = 0;
37363
37523
  for (const name of entries2) {
37364
- const path = join65(logDir, name);
37524
+ const path = join66(logDir, name);
37365
37525
  try {
37366
- if (now - statSync28(path).mtimeMs > DAY_MS) continue;
37367
- for (const line of readFileSync56(path, "utf8").split("\n")) {
37526
+ if (now - statSync29(path).mtimeMs > DAY_MS) continue;
37527
+ for (const line of readFileSync57(path, "utf8").split("\n")) {
37368
37528
  if (line.includes("[graph-write] reject")) total += 1;
37369
37529
  }
37370
37530
  } catch {
@@ -37465,8 +37625,8 @@ function startLedgerCensus(openSession, opts = {}) {
37465
37625
  }
37466
37626
 
37467
37627
  // app/lib/dispatch-census.ts
37468
- import { readFileSync as readFileSync57 } from "fs";
37469
- import { join as join66 } from "path";
37628
+ import { readFileSync as readFileSync58 } from "fs";
37629
+ import { join as join67 } from "path";
37470
37630
 
37471
37631
  // ../lib/canonical-instant/src/index.ts
37472
37632
  var CANONICAL_INSTANT_PATTERN = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z";
@@ -37504,7 +37664,7 @@ function percentile(sortedAsc, p) {
37504
37664
  function auditConfigFile(accountDir) {
37505
37665
  let cfg;
37506
37666
  try {
37507
- cfg = JSON.parse(readFileSync57(join66(accountDir, "dispatch", "config.json"), "utf-8"));
37667
+ cfg = JSON.parse(readFileSync58(join67(accountDir, "dispatch", "config.json"), "utf-8"));
37508
37668
  } catch {
37509
37669
  return null;
37510
37670
  }
@@ -37529,7 +37689,7 @@ function auditConfigFile(accountDir) {
37529
37689
  function terminalStatuses(accountDir) {
37530
37690
  try {
37531
37691
  const cfg = JSON.parse(
37532
- readFileSync57(join66(accountDir, "dispatch", "config.json"), "utf-8")
37692
+ readFileSync58(join67(accountDir, "dispatch", "config.json"), "utf-8")
37533
37693
  );
37534
37694
  return {
37535
37695
  job: (cfg.statuses?.job ?? []).filter((s) => s.terminal).map((s) => s.name),
@@ -37959,8 +38119,8 @@ function startGraphHealthTimer() {
37959
38119
  }
37960
38120
 
37961
38121
  // app/lib/shared-folder-census.ts
37962
- import { existsSync as existsSync55, statSync as statSync29, readdirSync as readdirSync32 } from "fs";
37963
- import { join as join67 } from "path";
38122
+ import { existsSync as existsSync55, statSync as statSync30, readdirSync as readdirSync33 } from "fs";
38123
+ import { join as join68 } from "path";
37964
38124
  var import_dist17 = __toESM(require_dist(), 1);
37965
38125
  var TAG69 = "[shared-census]";
37966
38126
  var WIRED_SURFACES = 3;
@@ -37968,12 +38128,12 @@ function countFiles(dir) {
37968
38128
  let n = 0;
37969
38129
  let entries2;
37970
38130
  try {
37971
- entries2 = readdirSync32(dir, { withFileTypes: true });
38131
+ entries2 = readdirSync33(dir, { withFileTypes: true });
37972
38132
  } catch {
37973
38133
  return 0;
37974
38134
  }
37975
38135
  for (const e of entries2) {
37976
- if (e.isDirectory()) n += countFiles(join67(dir, e.name));
38136
+ if (e.isDirectory()) n += countFiles(join68(dir, e.name));
37977
38137
  else if (e.isFile()) n++;
37978
38138
  }
37979
38139
  return n;
@@ -37984,7 +38144,7 @@ function runSharedFolderCensus(dataRoot = DATA_ROOT) {
37984
38144
  let isDir = false;
37985
38145
  if (exists) {
37986
38146
  try {
37987
- isDir = statSync29(root).isDirectory();
38147
+ isDir = statSync30(root).isDirectory();
37988
38148
  } catch {
37989
38149
  isDir = false;
37990
38150
  }
@@ -38022,9 +38182,9 @@ function startAccountsStateCensus() {
38022
38182
  }
38023
38183
 
38024
38184
  // app/lib/cred-refresh-census.ts
38025
- import { readdirSync as readdirSync33, readFileSync as readFileSync58 } from "fs";
38185
+ import { readdirSync as readdirSync34, readFileSync as readFileSync59 } from "fs";
38026
38186
  import { homedir as homedir4 } from "os";
38027
- import { join as join68 } from "path";
38187
+ import { join as join69 } from "path";
38028
38188
  var TAG71 = "[claude-auth]";
38029
38189
  function refreshTokenLength(raw) {
38030
38190
  let data;
@@ -38040,7 +38200,7 @@ function refreshTokenLength(raw) {
38040
38200
  function surveyBrandCredentials(home) {
38041
38201
  let entries2;
38042
38202
  try {
38043
- entries2 = readdirSync33(home);
38203
+ entries2 = readdirSync34(home);
38044
38204
  } catch {
38045
38205
  return [];
38046
38206
  }
@@ -38049,7 +38209,7 @@ function surveyBrandCredentials(home) {
38049
38209
  if (!entry3.startsWith(".")) continue;
38050
38210
  let raw;
38051
38211
  try {
38052
- raw = readFileSync58(join68(home, entry3, ".claude", ".credentials.json"), "utf-8");
38212
+ raw = readFileSync59(join69(home, entry3, ".claude", ".credentials.json"), "utf-8");
38053
38213
  } catch {
38054
38214
  continue;
38055
38215
  }
@@ -38311,7 +38471,7 @@ async function migrateUploads(opts = {}) {
38311
38471
  // app/lib/migrate-admin-webchat-sidecars.ts
38312
38472
  import { readdir as readdir7, readFile as readFile13, rename as rename5, writeFile as writeFile4, unlink as unlink4 } from "fs/promises";
38313
38473
  import { existsSync as existsSync56 } from "fs";
38314
- import { join as join69 } from "path";
38474
+ import { join as join70 } from "path";
38315
38475
  var ADMIN_ROLE2 = "admin";
38316
38476
  var WEBCHAT_CHANNEL2 = "webchat";
38317
38477
  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;
@@ -38356,7 +38516,7 @@ async function collectLiveSidecars(projectsRoot) {
38356
38516
  }
38357
38517
  for (const slug of slugs) {
38358
38518
  if (!slug.isDirectory()) continue;
38359
- const slugDir = join69(projectsRoot, slug.name);
38519
+ const slugDir = join70(projectsRoot, slug.name);
38360
38520
  let entries2;
38361
38521
  try {
38362
38522
  entries2 = await readdir7(slugDir, { withFileTypes: true });
@@ -38365,9 +38525,9 @@ async function collectLiveSidecars(projectsRoot) {
38365
38525
  }
38366
38526
  for (const entry3 of entries2) {
38367
38527
  if (entry3.isFile() && SESSION_META_RE.test(entry3.name)) {
38368
- out.push(join69(slugDir, entry3.name));
38528
+ out.push(join70(slugDir, entry3.name));
38369
38529
  } else if (entry3.isDirectory() && entry3.name === "subagents") {
38370
- const subDir = join69(slugDir, entry3.name);
38530
+ const subDir = join70(slugDir, entry3.name);
38371
38531
  let subs;
38372
38532
  try {
38373
38533
  subs = await readdir7(subDir, { withFileTypes: true });
@@ -38375,7 +38535,7 @@ async function collectLiveSidecars(projectsRoot) {
38375
38535
  continue;
38376
38536
  }
38377
38537
  for (const s of subs) {
38378
- if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join69(subDir, s.name));
38538
+ if (s.isFile() && SESSION_META_RE.test(s.name)) out.push(join70(subDir, s.name));
38379
38539
  }
38380
38540
  }
38381
38541
  }
@@ -38387,7 +38547,7 @@ function shortId(path) {
38387
38547
  return base.slice(0, 8);
38388
38548
  }
38389
38549
  async function migrateAdminWebchatSidecars(opts = {}) {
38390
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join69(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
38550
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join70(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
38391
38551
  const usersFile = opts.usersFile ?? USERS_FILE;
38392
38552
  const accountId = opts.accountId ?? resolveAccount()?.accountId ?? null;
38393
38553
  const resolveName = opts.resolveName ?? defaultResolveName;
@@ -38464,7 +38624,7 @@ async function migrateAdminWebchatSidecars(opts = {}) {
38464
38624
  // app/lib/migrate-sidecar-account-ids.ts
38465
38625
  import { readdir as readdir8, readFile as readFile14, rename as rename6, writeFile as writeFile5, unlink as unlink5 } from "fs/promises";
38466
38626
  import { existsSync as existsSync57 } from "fs";
38467
- import { join as join70 } from "path";
38627
+ import { join as join71 } from "path";
38468
38628
  var SESSION_META_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.meta\.json$/i;
38469
38629
  function projectSlugForCwd(cwd) {
38470
38630
  return cwd.replace(/\//g, "-");
@@ -38510,7 +38670,7 @@ async function collectSidecars(projectsRoot) {
38510
38670
  }
38511
38671
  for (const slug of slugs) {
38512
38672
  if (!slug.isDirectory()) continue;
38513
- const slugDir = join70(projectsRoot, slug.name);
38673
+ const slugDir = join71(projectsRoot, slug.name);
38514
38674
  let entries2;
38515
38675
  try {
38516
38676
  entries2 = await readdir8(slugDir, { withFileTypes: true });
@@ -38519,9 +38679,9 @@ async function collectSidecars(projectsRoot) {
38519
38679
  }
38520
38680
  for (const entry3 of entries2) {
38521
38681
  if (entry3.isFile() && SESSION_META_RE2.test(entry3.name)) {
38522
- out.push({ path: join70(slugDir, entry3.name), slug: slug.name });
38682
+ out.push({ path: join71(slugDir, entry3.name), slug: slug.name });
38523
38683
  } else if (entry3.isDirectory() && (entry3.name === "subagents" || entry3.name === "archive")) {
38524
- const subDir = join70(slugDir, entry3.name);
38684
+ const subDir = join71(slugDir, entry3.name);
38525
38685
  let subs;
38526
38686
  try {
38527
38687
  subs = await readdir8(subDir, { withFileTypes: true });
@@ -38530,7 +38690,7 @@ async function collectSidecars(projectsRoot) {
38530
38690
  }
38531
38691
  for (const s of subs) {
38532
38692
  if (s.isFile() && SESSION_META_RE2.test(s.name)) {
38533
- out.push({ path: join70(subDir, s.name), slug: slug.name });
38693
+ out.push({ path: join71(subDir, s.name), slug: slug.name });
38534
38694
  }
38535
38695
  }
38536
38696
  }
@@ -38539,7 +38699,7 @@ async function collectSidecars(projectsRoot) {
38539
38699
  return out;
38540
38700
  }
38541
38701
  async function migrateSidecarAccountIds(opts = {}) {
38542
- const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join70(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
38702
+ const projectsRoot = opts.projectsRoot ?? (process.env.CLAUDE_CONFIG_DIR ? join71(process.env.CLAUDE_CONFIG_DIR, "projects") : null) ?? "";
38543
38703
  const accounts = opts.accounts ?? listValidAccounts();
38544
38704
  const result = {
38545
38705
  scanned: 0,
@@ -40162,8 +40322,8 @@ function createWebchatChannelRoutes(deps) {
40162
40322
  }
40163
40323
 
40164
40324
  // app/lib/webchat/gateway/resolve-turn-answer.ts
40165
- import { existsSync as existsSync58, openSync as openSync9, readSync as readSync8, closeSync as closeSync9, readdirSync as readdirSync34 } from "fs";
40166
- import { join as join71 } from "path";
40325
+ import { existsSync as existsSync58, openSync as openSync10, readSync as readSync9, closeSync as closeSync10, readdirSync as readdirSync35 } from "fs";
40326
+ import { join as join72 } from "path";
40167
40327
 
40168
40328
  // app/lib/channel-delivery/outbound-guard.ts
40169
40329
  var INTERNAL_LEAK_PATTERNS = [
@@ -40205,13 +40365,13 @@ function resolveTurnAnswer(p) {
40205
40365
  if (!(length > 0)) return "";
40206
40366
  let text;
40207
40367
  try {
40208
- const fd = openSync9(path, "r");
40368
+ const fd = openSync10(path, "r");
40209
40369
  try {
40210
40370
  const buf = Buffer.alloc(length);
40211
- readSync8(fd, buf, 0, length, p.segmentStart);
40371
+ readSync9(fd, buf, 0, length, p.segmentStart);
40212
40372
  text = buf.toString("utf8");
40213
40373
  } finally {
40214
- closeSync9(fd);
40374
+ closeSync10(fd);
40215
40375
  }
40216
40376
  } catch {
40217
40377
  return "";
@@ -40227,12 +40387,12 @@ function findJsonl(projectsRoot, sessionId) {
40227
40387
  if (!existsSync58(projectsRoot)) return null;
40228
40388
  let slugs;
40229
40389
  try {
40230
- slugs = readdirSync34(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
40390
+ slugs = readdirSync35(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
40231
40391
  } catch {
40232
40392
  return null;
40233
40393
  }
40234
40394
  for (const slug of slugs) {
40235
- const live = join71(projectsRoot, slug, `${sessionId}.jsonl`);
40395
+ const live = join72(projectsRoot, slug, `${sessionId}.jsonl`);
40236
40396
  if (existsSync58(live)) return live;
40237
40397
  }
40238
40398
  return null;
@@ -41614,21 +41774,21 @@ function refusalNoticeText(reason) {
41614
41774
  }
41615
41775
  async function sendRefusalNotice(input) {
41616
41776
  if (notified.has(input.doorId)) return;
41617
- const fields = `channel=${input.channel} sender=${input.sender} reason=${input.reason}`;
41777
+ const fields2 = `channel=${input.channel} sender=${input.sender} reason=${input.reason}`;
41618
41778
  if (!input.send) {
41619
- console.error(`${TAG74} op=refusal-notice-failed ${fields} error=no-target`);
41779
+ console.error(`${TAG74} op=refusal-notice-failed ${fields2} error=no-target`);
41620
41780
  return;
41621
41781
  }
41622
41782
  try {
41623
41783
  await input.send(guardOutbound(input.sender, refusalNoticeText(input.reason)));
41624
41784
  } catch (e) {
41625
41785
  console.error(
41626
- `${TAG74} op=refusal-notice-failed ${fields} error=${e instanceof Error ? e.message : String(e)}`
41786
+ `${TAG74} op=refusal-notice-failed ${fields2} error=${e instanceof Error ? e.message : String(e)}`
41627
41787
  );
41628
41788
  return;
41629
41789
  }
41630
41790
  notified.add(input.doorId);
41631
- console.error(`${TAG74} op=refusal-notice ${fields} status=${input.status}`);
41791
+ console.error(`${TAG74} op=refusal-notice ${fields2} status=${input.status}`);
41632
41792
  }
41633
41793
  function clearRefusalNotice(doorId) {
41634
41794
  notified.delete(doorId);
@@ -42635,7 +42795,7 @@ var sessions3 = /* @__PURE__ */ new Map();
42635
42795
  function entry2(sessionId) {
42636
42796
  let e = sessions3.get(sessionId);
42637
42797
  if (!e) {
42638
- e = { armed: 0, suppressed: 0, delivered: 0, persisted: 0, sent: 0, crossChat: 0 };
42798
+ e = { armed: 0, suppressed: 0, delivered: 0 };
42639
42799
  sessions3.set(sessionId, e);
42640
42800
  }
42641
42801
  return e;
@@ -42650,59 +42810,23 @@ function noteCardDelivered(sessionId) {
42650
42810
  const e = sessions3.get(sessionId);
42651
42811
  if (e && e.armed > 0) e.delivered += 1;
42652
42812
  }
42653
- function noteCardPersisted(sessionId) {
42654
- entry2(sessionId).persisted += 1;
42655
- }
42656
- function noteCardSent(sessionId) {
42657
- entry2(sessionId).sent += 1;
42658
- }
42659
- function noteCardCrossChat(sessionId) {
42660
- entry2(sessionId).crossChat += 1;
42661
- }
42662
42813
  function cardCensusLines() {
42663
42814
  let armed3 = 0;
42664
42815
  let suppressed = 0;
42665
42816
  let delivered = 0;
42666
- let persisted = 0;
42667
- let sent = 0;
42668
- let crossChat = 0;
42669
42817
  for (const e of sessions3.values()) {
42670
42818
  armed3 += e.armed;
42671
42819
  suppressed += e.suppressed;
42672
42820
  delivered += e.delivered;
42673
- persisted += e.persisted;
42674
- sent += e.sent;
42675
- crossChat += e.crossChat;
42676
42821
  }
42677
42822
  const silent = Math.max(0, armed3 - suppressed - delivered);
42678
42823
  return [
42679
- `[telegram-card-suppression-census] sessions=${sessions3.size} armed=${armed3} suppressed=${suppressed} silent-turns=${silent} sent=${sent} persisted=${persisted} cross-chat=${crossChat}`
42824
+ `[telegram-card-suppression-census] sessions=${sessions3.size} armed=${armed3} suppressed=${suppressed} silent-turns=${silent}`
42680
42825
  ];
42681
42826
  }
42682
42827
 
42683
42828
  // app/lib/telegram/gateway/native-file-follower.ts
42684
42829
  var TELEGRAM_CARD_TOOL = "mcp__telegram__telegram-card";
42685
- var CARD_TOOL_NAMES = /* @__PURE__ */ new Set([
42686
- "mcp__telegram__telegram-card",
42687
- "mcp__plugin_telegram_telegram__telegram-card"
42688
- ]);
42689
- var CARD_RESULT_RE = /card-result: outcome=(sent|edited|unchanged|none) messageId=(\d+|none)(?: botId=(-?\d+) chatId=(-?\d+))?/;
42690
- function parseCardButtons(input) {
42691
- const rows = input?.buttons;
42692
- if (!Array.isArray(rows)) return [];
42693
- const out = [];
42694
- for (const row of rows) {
42695
- if (!Array.isArray(row)) continue;
42696
- for (const b of row) {
42697
- if (!b || typeof b !== "object") continue;
42698
- const text = b.text;
42699
- if (typeof text !== "string") continue;
42700
- const data = b.callbackData;
42701
- out.push({ text, data: typeof data === "string" ? data : null });
42702
- }
42703
- }
42704
- return out;
42705
- }
42706
42830
  var CHAT_ACTION_REFRESH_MS = 4e3;
42707
42831
  function startTelegramNativeFileFollower(input) {
42708
42832
  const entry3 = {
@@ -42723,9 +42847,7 @@ function startTelegramNativeFileFollower(input) {
42723
42847
  // The chat id the document is addressed to (set per spawn from the inbound).
42724
42848
  replyTarget: input.chatId
42725
42849
  };
42726
- const pendingCards = /* @__PURE__ */ new Map();
42727
42850
  const channelKey = input.senderId;
42728
- const numericSenderId = parseTelegramChannelKey(channelKey)?.senderId ?? input.chatId;
42729
42851
  let lastChatActionAt = Number.NEGATIVE_INFINITY;
42730
42852
  let turnOpen = false;
42731
42853
  let cardSentThisTurn = false;
@@ -42814,13 +42936,6 @@ function startTelegramNativeFileFollower(input) {
42814
42936
  // before it reaches the optional answerCallbackQuery, so there is no card
42815
42937
  // call that posts nothing and no bare acknowledgement to keep the text for.
42816
42938
  onToolUse: (tool) => {
42817
- if (CARD_TOOL_NAMES.has(tool.name) && tool.id) {
42818
- const text = tool.input?.text;
42819
- pendingCards.set(tool.id, {
42820
- text: typeof text === "string" ? text : "",
42821
- buttons: parseCardButtons(tool.input)
42822
- });
42823
- }
42824
42939
  if (tool.name !== TELEGRAM_CARD_TOOL) return;
42825
42940
  cardSentThisTurn = true;
42826
42941
  noteCardArmed(input.sessionId);
@@ -42828,75 +42943,6 @@ function startTelegramNativeFileFollower(input) {
42828
42943
  `[telegram-public] op=card-armed sessionId=${input.sessionId.slice(0, 8)} tool=${tool.name}`
42829
42944
  );
42830
42945
  },
42831
- // Task 2677 — the card's send outcome and message id arrive here, parsed out
42832
- // of the result text card.ts encoded them into. On a confirmed send write one
42833
- // outbound store row keyed by the card message id; on an edit patch that same
42834
- // row (Telegram reuses the id for an edit); unchanged/none write nothing.
42835
- onToolResult: ({ toolUseId, text }) => {
42836
- const pending = pendingCards.get(toolUseId);
42837
- if (!pending) return;
42838
- pendingCards.delete(toolUseId);
42839
- const m = CARD_RESULT_RE.exec(text);
42840
- if (!m) return;
42841
- const outcome = m[1];
42842
- const messageId = m[2];
42843
- if (outcome !== "sent" && outcome !== "edited" || messageId === "none") return;
42844
- noteCardSent(input.sessionId);
42845
- const destBot = m[3];
42846
- const destChat = m[4];
42847
- const follower = parseTelegramChannelKey(channelKey);
42848
- let destKey = channelKey;
42849
- let rowChat = input.chatId;
42850
- let rowSender = numericSenderId;
42851
- if (destBot === void 0 || destChat === void 0) {
42852
- console.error(
42853
- `[telegram-adaptor] op=card-destination-absent sessionId=${input.sessionId.slice(0, 8)} toolUseId=${toolUseId} follower=${channelKey}`
42854
- );
42855
- } else if (follower?.business === true && follower.botId === destBot && follower.senderId === destChat) {
42856
- rowChat = destChat;
42857
- rowSender = destChat;
42858
- } else {
42859
- destKey = `${destBot}:${destChat}`;
42860
- rowChat = destChat;
42861
- rowSender = destChat;
42862
- }
42863
- if (destKey !== channelKey) noteCardCrossChat(input.sessionId);
42864
- const at = (/* @__PURE__ */ new Date()).toISOString();
42865
- const rowId = `telegram:${input.accountId}:${destKey}:card:${messageId}`;
42866
- let created;
42867
- if (outcome === "sent") {
42868
- created = appendMessage2(input.accountId, {
42869
- messageId: rowId,
42870
- sessionId: input.sessionId,
42871
- dateSent: at,
42872
- body: pending.text,
42873
- fromMe: true,
42874
- senderId: rowSender,
42875
- senderName: null,
42876
- chatId: rowChat,
42877
- channelKey: destKey,
42878
- // Telegram row scope is not consumed for rendering or routing (only
42879
- // WhatsApp rows feed the operator-conversations scope counter); 'admin'
42880
- // matches the operator-surface intent of the card row.
42881
- scope: "admin",
42882
- origin: "agent",
42883
- ...pending.buttons.length > 0 ? { buttons: pending.buttons } : {},
42884
- createdAt: at
42885
- }).created;
42886
- } else {
42887
- created = appendUpdate(input.accountId, destKey, {
42888
- kind: "update",
42889
- targetMessageId: rowId,
42890
- body: pending.text,
42891
- ...pending.buttons.length > 0 ? { buttons: pending.buttons } : {},
42892
- createdAt: at
42893
- }).created;
42894
- }
42895
- if (created) noteCardPersisted(input.sessionId);
42896
- console.error(
42897
- `[telegram-adaptor] op=${created ? "card-persisted" : "card-persist-refused"} sessionId=${input.sessionId.slice(0, 8)} messageId=${messageId} outcome=${outcome} buttons=${pending.buttons.length} channelKey=${destKey} follower=${channelKey}`
42898
- );
42899
- },
42900
42946
  // Task 2521 — the answer reaches every bound door through the one fan-out.
42901
42947
  // Ungated by design: whatever woke the turn, the reader and the chats show
42902
42948
  // the same thing. Task 2666 adds the one exception above.
@@ -42936,15 +42982,15 @@ function startTelegramNativeFileFollower(input) {
42936
42982
  }
42937
42983
 
42938
42984
  // server/telegram-background-active.ts
42939
- import { join as join72, resolve as resolve52 } from "path";
42940
- import { readdirSync as readdirSync35, statSync as statSync30 } from "fs";
42985
+ import { join as join73, resolve as resolve52 } from "path";
42986
+ import { readdirSync as readdirSync36, statSync as statSync31 } from "fs";
42941
42987
  var SUBAGENT_STALE_WINDOW_MS = 6e4;
42942
42988
  var AGENT_JSONL_RE2 = /^agent-([0-9a-f]+)\.jsonl$/;
42943
42989
  function makeBackgroundActive(sessionId, deps = {}) {
42944
42990
  const findProjectDir = deps.findProjectDir ?? findSessionProjectDir;
42945
- const readDir = deps.readDir ?? ((d) => readdirSync35(d));
42991
+ const readDir = deps.readDir ?? ((d) => readdirSync36(d));
42946
42992
  const statFile = deps.statFile ?? ((p) => {
42947
- const st = statSync30(p);
42993
+ const st = statSync31(p);
42948
42994
  return { size: st.size, mtimeMs: st.mtimeMs };
42949
42995
  });
42950
42996
  const now = deps.now ?? (() => Date.now());
@@ -42955,7 +43001,7 @@ function makeBackgroundActive(sessionId, deps = {}) {
42955
43001
  if (subagentsDir === null) {
42956
43002
  const projectDir = findProjectDir(sessionId);
42957
43003
  if (projectDir === null) return false;
42958
- subagentsDir = resolve52(join72(projectDir, sessionId, "subagents"));
43004
+ subagentsDir = resolve52(join73(projectDir, sessionId, "subagents"));
42959
43005
  }
42960
43006
  let names;
42961
43007
  try {
@@ -42969,7 +43015,7 @@ function makeBackgroundActive(sessionId, deps = {}) {
42969
43015
  let size;
42970
43016
  let mtimeMs;
42971
43017
  try {
42972
- const st = statFile(join72(subagentsDir, name));
43018
+ const st = statFile(join73(subagentsDir, name));
42973
43019
  size = st.size;
42974
43020
  mtimeMs = st.mtimeMs;
42975
43021
  } catch {
@@ -43289,9 +43335,9 @@ function warnOnChannelAdminBindingDrift() {
43289
43335
  );
43290
43336
  }
43291
43337
  for (const d of drift) {
43292
- const tail = d.reason === "userid-not-admin" ? ` userId=${d.userId}` : "";
43338
+ const tail2 = d.reason === "userid-not-admin" ? ` userId=${d.userId}` : "";
43293
43339
  console.error(
43294
- `[admin-identity] binding-drift accountId=${acct.accountId} phone=${d.phone} reason=${d.reason}${tail}`
43340
+ `[admin-identity] binding-drift accountId=${acct.accountId} phone=${d.phone} reason=${d.reason}${tail2}`
43295
43341
  );
43296
43342
  }
43297
43343
  const managerDrift = findAccountManagerDrift(readAccountManagers(acct.accountDir), validAccountIds);
@@ -43335,8 +43381,8 @@ function runPublicAddressCollisionAudit(deps) {
43335
43381
  }
43336
43382
 
43337
43383
  // server/telegram-audit.ts
43338
- import { statSync as statSync31 } from "fs";
43339
- import { join as join73 } from "path";
43384
+ import { statSync as statSync32 } from "fs";
43385
+ import { join as join74 } from "path";
43340
43386
  function buildTelegramAuditLines(input) {
43341
43387
  const lines = [];
43342
43388
  const entries2 = input.accounts.flatMap(
@@ -43444,19 +43490,19 @@ function countTelegramSessionsForAccount(accountId) {
43444
43490
  function* walkTelegramSidecars() {
43445
43491
  const cfg = claudeConfigDir();
43446
43492
  if (!cfg) return;
43447
- const projectsRoot = join73(cfg, "projects");
43493
+ const projectsRoot = join74(cfg, "projects");
43448
43494
  for (const { path } of enumerateSessionSidecars(projectsRoot)) {
43449
43495
  const meta = readSidecarMeta(path);
43450
43496
  if (meta.channel !== "telegram" || !meta.accountId) continue;
43451
43497
  let spawnMs;
43452
43498
  try {
43453
- spawnMs = statSync31(path).mtimeMs;
43499
+ spawnMs = statSync32(path).mtimeMs;
43454
43500
  } catch {
43455
43501
  continue;
43456
43502
  }
43457
43503
  let turnMs;
43458
43504
  try {
43459
- turnMs = statSync31(path.replace(/\.meta\.json$/, ".jsonl")).mtimeMs;
43505
+ turnMs = statSync32(path.replace(/\.meta\.json$/, ".jsonl")).mtimeMs;
43460
43506
  } catch {
43461
43507
  turnMs = null;
43462
43508
  }
@@ -43656,8 +43702,8 @@ function businessConnectionCensusLine(c) {
43656
43702
  }
43657
43703
 
43658
43704
  // app/lib/telegram/business-window-census.ts
43659
- import { readdirSync as readdirSync36, existsSync as existsSync59 } from "fs";
43660
- import { join as join74 } from "path";
43705
+ import { readdirSync as readdirSync37, existsSync as existsSync59 } from "fs";
43706
+ import { join as join75 } from "path";
43661
43707
  var BUSINESS_WINDOW_CENSUS_TAG = "[telegram-audit]";
43662
43708
  var EXPIRING_SOON_S = 3600;
43663
43709
  function collectBusinessWindowCensus(input) {
@@ -43685,11 +43731,11 @@ function businessWindowCensusLine(c) {
43685
43731
  function readBusinessChatClocks(accountIds) {
43686
43732
  const out = [];
43687
43733
  for (const accountId of accountIds) {
43688
- const dir = join74(STORE_ROOT2(), accountId);
43734
+ const dir = join75(STORE_ROOT2(), accountId);
43689
43735
  if (!existsSync59(dir)) continue;
43690
43736
  let names;
43691
43737
  try {
43692
- names = readdirSync36(dir);
43738
+ names = readdirSync37(dir);
43693
43739
  } catch {
43694
43740
  continue;
43695
43741
  }
@@ -43747,15 +43793,15 @@ async function getTelegramBusinessConnection(botToken, connectionId, opts = {})
43747
43793
  }
43748
43794
 
43749
43795
  // server/telegram-migrate.ts
43750
- import { readFileSync as readFileSync59, writeFileSync as writeFileSync23, existsSync as existsSync60, renameSync as renameSync11, mkdirSync as mkdirSync14 } from "fs";
43751
- import { dirname as dirname19, join as join75 } from "path";
43796
+ import { readFileSync as readFileSync60, writeFileSync as writeFileSync23, existsSync as existsSync60, renameSync as renameSync11, mkdirSync as mkdirSync14 } from "fs";
43797
+ import { dirname as dirname19, join as join76 } from "path";
43752
43798
  function migrateTelegramTwoSlot(input) {
43753
43799
  const out = [];
43754
43800
  for (const account of input.accounts) {
43755
- const configPath3 = join75(account.accountDir, "account.json");
43801
+ const configPath3 = join76(account.accountDir, "account.json");
43756
43802
  let config;
43757
43803
  try {
43758
- config = JSON.parse(readFileSync59(configPath3, "utf-8"));
43804
+ config = JSON.parse(readFileSync60(configPath3, "utf-8"));
43759
43805
  } catch {
43760
43806
  console.error(`[telegram-audit] op=migrate-unreadable accountId=${account.accountId}`);
43761
43807
  continue;
@@ -43814,16 +43860,16 @@ function moveSecret(from, to) {
43814
43860
  }
43815
43861
 
43816
43862
  // server/telegram-unadopt.ts
43817
- import { readFileSync as readFileSync60, writeFileSync as writeFileSync24, renameSync as renameSync12 } from "fs";
43818
- import { join as join76 } from "path";
43863
+ import { readFileSync as readFileSync61, writeFileSync as writeFileSync24, renameSync as renameSync12 } from "fs";
43864
+ import { join as join77 } from "path";
43819
43865
  var FILE3 = "channel-session-override.json";
43820
43866
  function unadoptTelegramPointers(input) {
43821
43867
  return input.accounts.map(({ accountId, accountDir }) => {
43822
43868
  const out = { accountId, dropped: [], kept: 0, unknown: 0 };
43823
- const path = join76(accountDir, FILE3);
43869
+ const path = join77(accountDir, FILE3);
43824
43870
  const bySender = {};
43825
43871
  try {
43826
- const raw = JSON.parse(readFileSync60(path, "utf8"));
43872
+ const raw = JSON.parse(readFileSync61(path, "utf8"));
43827
43873
  if (!raw.bySender || typeof raw.bySender !== "object") return out;
43828
43874
  for (const [k, v] of Object.entries(raw.bySender)) if (typeof v === "string") bySender[k] = v;
43829
43875
  } catch {
@@ -43894,14 +43940,14 @@ function backfillMisfiledRecords(params) {
43894
43940
  }
43895
43941
 
43896
43942
  // server/lib/booking-site-accounts.ts
43897
- import { existsSync as existsSync61, readFileSync as readFileSync61 } from "fs";
43943
+ import { existsSync as existsSync61, readFileSync as readFileSync62 } from "fs";
43898
43944
  import { resolve as resolve53 } from "path";
43899
43945
  function listBookingSiteAccounts(accounts) {
43900
43946
  return accounts.filter((a) => {
43901
43947
  const availPath = resolve53(a.accountDir, "calendar-availability.json");
43902
43948
  if (!existsSync61(availPath)) return false;
43903
43949
  try {
43904
- const cfg = JSON.parse(readFileSync61(availPath, "utf-8"));
43950
+ const cfg = JSON.parse(readFileSync62(availPath, "utf-8"));
43905
43951
  return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
43906
43952
  } catch {
43907
43953
  return false;
@@ -43939,7 +43985,7 @@ function broadcastAdminShutdown(reason) {
43939
43985
 
43940
43986
  // ../lib/entitlement/src/index.ts
43941
43987
  import { createPublicKey, createHash as createHash7, verify as cryptoVerify } from "crypto";
43942
- import { existsSync as existsSync62, readFileSync as readFileSync62, statSync as statSync32 } from "fs";
43988
+ import { existsSync as existsSync62, readFileSync as readFileSync63, statSync as statSync33 } from "fs";
43943
43989
  import { resolve as resolve54 } from "path";
43944
43990
 
43945
43991
  // ../lib/entitlement/src/canonicalize.ts
@@ -43991,7 +44037,7 @@ function resolveEntitlement(brand, account) {
43991
44037
  if (!existsSync62(entitlementPath)) {
43992
44038
  return logResolved(anonymousFallback("missing"), { reason: "missing" });
43993
44039
  }
43994
- const stat11 = statSync32(entitlementPath);
44040
+ const stat11 = statSync33(entitlementPath);
43995
44041
  const key2 = memoKey(stat11.mtimeMs, account);
43996
44042
  if (memo && memo.key === key2) {
43997
44043
  return memo.result;
@@ -44003,7 +44049,7 @@ function resolveEntitlement(brand, account) {
44003
44049
  function verifyAndResolve(brand, entitlementPath, account) {
44004
44050
  let pubkeyPem;
44005
44051
  try {
44006
- pubkeyPem = readFileSync62(pubkeyPath(brand), "utf-8");
44052
+ pubkeyPem = readFileSync63(pubkeyPath(brand), "utf-8");
44007
44053
  } catch (err) {
44008
44054
  return logResolved(anonymousFallback("pubkey-missing"), {
44009
44055
  reason: "pubkey-missing"
@@ -44017,7 +44063,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
44017
44063
  }
44018
44064
  let envelope;
44019
44065
  try {
44020
- envelope = JSON.parse(readFileSync62(entitlementPath, "utf-8"));
44066
+ envelope = JSON.parse(readFileSync63(entitlementPath, "utf-8"));
44021
44067
  } catch {
44022
44068
  return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
44023
44069
  }
@@ -44179,14 +44225,14 @@ function clientFrom(c) {
44179
44225
  }
44180
44226
  installMediaDownloadGuard();
44181
44227
  var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT || "";
44182
- var BRAND_JSON_PATH = PLATFORM_ROOT6 ? join77(PLATFORM_ROOT6, "config", "brand.json") : "";
44228
+ var BRAND_JSON_PATH = PLATFORM_ROOT6 ? join78(PLATFORM_ROOT6, "config", "brand.json") : "";
44183
44229
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
44184
44230
  if (BRAND_JSON_PATH && !existsSync63(BRAND_JSON_PATH)) {
44185
44231
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
44186
44232
  }
44187
44233
  if (BRAND_JSON_PATH && existsSync63(BRAND_JSON_PATH)) {
44188
44234
  try {
44189
- const parsed = JSON.parse(readFileSync63(BRAND_JSON_PATH, "utf-8"));
44235
+ const parsed = JSON.parse(readFileSync64(BRAND_JSON_PATH, "utf-8"));
44190
44236
  BRAND = { ...BRAND, ...parsed };
44191
44237
  } catch (err) {
44192
44238
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -44218,11 +44264,11 @@ var brandLoginOpts = {
44218
44264
  appleTouchIconPath: brandAppIcon512Path,
44219
44265
  themeColor: BRAND.defaultColors?.primary
44220
44266
  };
44221
- var ALIAS_DOMAINS_PATH = join77(homedir5(), BRAND.configDir, "alias-domains.json");
44267
+ var ALIAS_DOMAINS_PATH = join78(homedir5(), BRAND.configDir, "alias-domains.json");
44222
44268
  function loadAliasDomains() {
44223
44269
  try {
44224
44270
  if (!existsSync63(ALIAS_DOMAINS_PATH)) return null;
44225
- const parsed = JSON.parse(readFileSync63(ALIAS_DOMAINS_PATH, "utf-8"));
44271
+ const parsed = JSON.parse(readFileSync64(ALIAS_DOMAINS_PATH, "utf-8"));
44226
44272
  if (!Array.isArray(parsed)) {
44227
44273
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
44228
44274
  return null;
@@ -44364,7 +44410,7 @@ var waGateway = new WaGateway({
44364
44410
  // resolver and `fanTargetsFor` is a constructor dep, not a callable method.
44365
44411
  fanTargetsFor: whatsappFanTargets,
44366
44412
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
44367
- serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve55(process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
44413
+ serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve55(process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
44368
44414
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
44369
44415
  // path-validation scope) is the sender's effective SESSION account, resolved
44370
44416
  // once at the inbound gate and threaded here via the gateway's per-sender doc
@@ -44379,7 +44425,7 @@ var waGateway = new WaGateway({
44379
44425
  caption,
44380
44426
  accountId,
44381
44427
  maxyAccountId,
44382
- platformRoot: resolve55(process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, ".."))
44428
+ platformRoot: resolve55(process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, ".."))
44383
44429
  });
44384
44430
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
44385
44431
  },
@@ -44554,7 +44600,7 @@ function runDuplicateSenderAudit() {
44554
44600
  try {
44555
44601
  const configDir = process.env.CLAUDE_CONFIG_DIR;
44556
44602
  if (!configDir) return;
44557
- const projectsRoot = join77(configDir, "projects");
44603
+ const projectsRoot = join78(configDir, "projects");
44558
44604
  const records5 = [];
44559
44605
  for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
44560
44606
  if (isSubagent || archived) continue;
@@ -44606,7 +44652,7 @@ var webchatGateway = new WebchatGateway({
44606
44652
  // rather than hanging the POST.
44607
44653
  publicProjectsRoot: () => {
44608
44654
  const cfg = claudeConfigDir();
44609
- return cfg ? join77(cfg, "projects") : "";
44655
+ return cfg ? join78(cfg, "projects") : "";
44610
44656
  },
44611
44657
  gatewayUrl: webchatGatewayUrl(),
44612
44658
  serverPath: webchatServerPath(),
@@ -44876,6 +44922,32 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
44876
44922
  if (hits.length === 0) return { kind: "none" };
44877
44923
  if (hits.length > 1) return { kind: "ambiguous", claimants: hits };
44878
44924
  return { kind: "one", ...hits[0] };
44925
+ },
44926
+ // Task 2776 — the carrier a routine NAMED, resolved against that account's own
44927
+ // bots and never install-wide. The verdict comes from `carrierVerdict`, the
44928
+ // same function `validateAgentDispatch` calls at write time, so a bot that
44929
+ // saved cannot be refused here for a reason the save would not have given.
44930
+ //
44931
+ // The token and allowlists are read off the entry this lookup just chose, for
44932
+ // the reason the resolver above states about its own: a second read of the
44933
+ // registry could disagree with the entry chosen.
44934
+ //
44935
+ // `absent` covers the two ways an entry can be missing — no such account, and
44936
+ // no such bot on it — because both mean the same thing to the operator: the id
44937
+ // this routine names is not a bot they can dispatch on.
44938
+ carrierFor: (accountId, botId, destination) => {
44939
+ const account = listValidAccounts().find((a) => a.accountId === accountId);
44940
+ if (!account) return { error: "absent" };
44941
+ const verdict = carrierVerdict(account.accountDir, botId, destination);
44942
+ if (!verdict.ok) return { error: verdict.reason };
44943
+ const entry3 = listBotEntries(account.config.telegram).find((e) => e.id === botId);
44944
+ if (!entry3) return { error: "absent" };
44945
+ return {
44946
+ accountId: account.accountId,
44947
+ botId,
44948
+ botToken: entry3.token,
44949
+ ...chatSendAllowlists(entry3)
44950
+ };
44879
44951
  }
44880
44952
  // Task 2078 — the same binding the live inbound resolves. Without it a
44881
44953
  // scheduled dispatch to a BOUND admin would derive the legacy per-chat id and
@@ -45280,7 +45352,7 @@ app80.get("/agent-assets/:slug/:filename", (c) => {
45280
45352
  const ext = "." + filename.split(".").pop()?.toLowerCase();
45281
45353
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
45282
45354
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
45283
- const body = readFileSync63(filePath);
45355
+ const body = readFileSync64(filePath);
45284
45356
  return c.body(body, 200, {
45285
45357
  "Content-Type": contentType,
45286
45358
  "Cache-Control": "public, max-age=3600"
@@ -45310,7 +45382,7 @@ app80.get("/generated/:filename", (c) => {
45310
45382
  const ext = "." + filename.split(".").pop()?.toLowerCase();
45311
45383
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
45312
45384
  console.log(`[generated] serve file=${filename} status=200`);
45313
- const body = readFileSync63(filePath);
45385
+ const body = readFileSync64(filePath);
45314
45386
  return c.body(body, 200, {
45315
45387
  "Content-Type": contentType,
45316
45388
  "Cache-Control": "public, max-age=86400"
@@ -45325,7 +45397,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
45325
45397
  var brandIconPath = "/brand/maxy-monochrome.png";
45326
45398
  if (BRAND_JSON_PATH && existsSync63(BRAND_JSON_PATH)) {
45327
45399
  try {
45328
- const fullBrand = JSON.parse(readFileSync63(BRAND_JSON_PATH, "utf-8"));
45400
+ const fullBrand = JSON.parse(readFileSync64(BRAND_JSON_PATH, "utf-8"));
45329
45401
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
45330
45402
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
45331
45403
  } catch {
@@ -45356,7 +45428,7 @@ var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskabl
45356
45428
  );
45357
45429
  var SW_SOURCE = (() => {
45358
45430
  try {
45359
- return readFileSync63(resolve55(process.cwd(), "public", "sw.js"), "utf-8");
45431
+ return readFileSync64(resolve55(process.cwd(), "public", "sw.js"), "utf-8");
45360
45432
  } catch {
45361
45433
  return null;
45362
45434
  }
@@ -45364,9 +45436,9 @@ var SW_SOURCE = (() => {
45364
45436
  function readInstalledVersion() {
45365
45437
  try {
45366
45438
  if (!PLATFORM_ROOT6) return "unknown";
45367
- const versionFile = join77(PLATFORM_ROOT6, "config", `.${BRAND.hostname}-version`);
45439
+ const versionFile = join78(PLATFORM_ROOT6, "config", `.${BRAND.hostname}-version`);
45368
45440
  if (!existsSync63(versionFile)) return "unknown";
45369
- const content = readFileSync63(versionFile, "utf-8").trim();
45441
+ const content = readFileSync64(versionFile, "utf-8").trim();
45370
45442
  return content || "unknown";
45371
45443
  } catch {
45372
45444
  return "unknown";
@@ -45377,7 +45449,7 @@ var versionScript = `<script>window.__MAXY_VERSION__=${JSON.stringify(INSTALLED_
45377
45449
  function cachedHtml(file) {
45378
45450
  let html = htmlCache.get(file);
45379
45451
  if (!html) {
45380
- html = readFileSync63(resolve55(process.cwd(), "public", file), "utf-8");
45452
+ html = readFileSync64(resolve55(process.cwd(), "public", file), "utf-8");
45381
45453
  const productNameEsc = escapeHtml(BRAND.productName);
45382
45454
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
45383
45455
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -45394,14 +45466,14 @@ ${clientErrorReporterScript}
45394
45466
  return html;
45395
45467
  }
45396
45468
  function loadBrandingCache(agentSlug) {
45397
- const configDir = join77(homedir5(), BRAND.configDir);
45469
+ const configDir = join78(homedir5(), BRAND.configDir);
45398
45470
  try {
45399
45471
  const verdict = resolvePublicAddressFromDisk(agentSlug);
45400
45472
  const accountId = verdict.kind === "served" ? verdict.owner.accountId : getDefaultAccountId();
45401
45473
  if (!accountId) return null;
45402
- const cachePath = join77(configDir, "branding-cache", accountId, `${agentSlug}.json`);
45474
+ const cachePath = join78(configDir, "branding-cache", accountId, `${agentSlug}.json`);
45403
45475
  if (!existsSync63(cachePath)) return null;
45404
- return JSON.parse(readFileSync63(cachePath, "utf-8"));
45476
+ return JSON.parse(readFileSync64(cachePath, "utf-8"));
45405
45477
  } catch {
45406
45478
  return null;
45407
45479
  }
@@ -45496,7 +45568,7 @@ app80.use("/vnc-popout.html", logViewerFetch);
45496
45568
  app80.get("/vnc-popout.html", (c) => {
45497
45569
  let html = htmlCache.get("vnc-popout.html");
45498
45570
  if (!html) {
45499
- html = readFileSync63(resolve55(process.cwd(), "public", "vnc-popout.html"), "utf-8");
45571
+ html = readFileSync64(resolve55(process.cwd(), "public", "vnc-popout.html"), "utf-8");
45500
45572
  const name = escapeHtml(BRAND.productName);
45501
45573
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
45502
45574
  html = html.replace("</head>", ` ${brandScript}
@@ -45650,7 +45722,7 @@ var hostname = process.env.HOSTNAME ?? "127.0.0.1";
45650
45722
  var httpServer = serve({ fetch: app80.fetch, port, hostname });
45651
45723
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45652
45724
  {
45653
- const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
45725
+ const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
45654
45726
  const reconcileScript = resolve55(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
45655
45727
  const runReconcile = (ctx) => {
45656
45728
  if (!existsSync63(reconcileScript)) return;
@@ -45673,7 +45745,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45673
45745
  detached: true,
45674
45746
  run: runReconcile
45675
45747
  });
45676
- const reconcileStatePath = join77(MAXY_DIR, "booking-reconcile-state.json");
45748
+ const reconcileStatePath = join78(MAXY_DIR, "booking-reconcile-state.json");
45677
45749
  registerLoop({
45678
45750
  name: "calendar-reconcile-liveness",
45679
45751
  intervalMs: 6e5,
@@ -45691,7 +45763,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45691
45763
  }
45692
45764
  {
45693
45765
  const DREAM_CYCLE_SWEEP_INTERVAL_MS = 36e5;
45694
- const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
45766
+ const dreamPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
45695
45767
  const dreamSweepScript = resolve55(dreamPlatformRoot, "plugins/memory/mcp/dist/scripts/dream-cycle-sweep.js");
45696
45768
  registerLoop({
45697
45769
  name: "dream-cycle-sweep",
@@ -45715,7 +45787,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45715
45787
  });
45716
45788
  }
45717
45789
  {
45718
- const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
45790
+ const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
45719
45791
  const outlookScript = resolve55(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
45720
45792
  const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
45721
45793
  const runOutlookComplete = (ctx) => {
@@ -45741,7 +45813,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45741
45813
  });
45742
45814
  }
45743
45815
  {
45744
- const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
45816
+ const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
45745
45817
  const strandedAccountsDir = resolve55(auditRoot, "..", "data/accounts");
45746
45818
  const STRANDED_AUDIT_INTERVAL_MS = 3e5;
45747
45819
  const STRANDED_AGE_MS = 16 * 6e4;
@@ -45750,11 +45822,11 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45750
45822
  try {
45751
45823
  if (!existsSync63(strandedAccountsDir)) return;
45752
45824
  const now = Date.now();
45753
- for (const name of readdirSync37(strandedAccountsDir)) {
45825
+ for (const name of readdirSync38(strandedAccountsDir)) {
45754
45826
  if (!STRANDED_UUID_RE.test(name)) continue;
45755
45827
  const pendingPath2 = resolve55(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
45756
45828
  if (!existsSync63(pendingPath2)) continue;
45757
- const ageMs2 = now - statSync33(pendingPath2).mtimeMs;
45829
+ const ageMs2 = now - statSync34(pendingPath2).mtimeMs;
45758
45830
  if (ageMs2 > STRANDED_AGE_MS) {
45759
45831
  console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs2 / 1e3)}`);
45760
45832
  }
@@ -45771,7 +45843,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
45771
45843
  });
45772
45844
  }
45773
45845
  {
45774
- const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
45846
+ const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
45775
45847
  const googleAccountsDir = resolve55(googleRoot, "..", "data/accounts");
45776
45848
  const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
45777
45849
  const runGooglePendingAuditSafe = () => {
@@ -45827,7 +45899,7 @@ registerLoop({
45827
45899
  }
45828
45900
  });
45829
45901
  {
45830
- const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
45902
+ const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
45831
45903
  const STORAGE_AUDIT_INTERVAL_MS = 3e5;
45832
45904
  const runStorageAuditSafe = () => runStorageAudit(auditPlatformRoot).catch((err) => {
45833
45905
  console.error(`[storage-audit] error="${err.message}"`);
@@ -45894,7 +45966,7 @@ registerLoop({
45894
45966
  const lastByBot = newestTelegramSessionByBot();
45895
45967
  let secretFilesOnDisk = [];
45896
45968
  try {
45897
- secretFilesOnDisk = readdirSync37(telegramSecretsDir());
45969
+ secretFilesOnDisk = readdirSync38(telegramSecretsDir());
45898
45970
  } catch {
45899
45971
  }
45900
45972
  const lines = buildTelegramAuditLines({
@@ -45902,7 +45974,7 @@ registerLoop({
45902
45974
  secretFileExists: (botId) => existsSync63(secretPath(botId)),
45903
45975
  agentActive: isActiveAgentSlug,
45904
45976
  // Task 2619 — the card set a specialist binding is reconciled against.
45905
- specialistCards: (accountDir) => listSpecialistCardNames(join77(accountDir, "specialists", "agents")),
45977
+ specialistCards: (accountDir) => listSpecialistCardNames(join78(accountDir, "specialists", "agents")),
45906
45978
  // Task 2619 — the doors that can be sent to right now. The same accessor
45907
45979
  // the follower census reads (:1185), so "should have a reader" and "can be
45908
45980
  // sent to" stay one fact.
@@ -45988,6 +46060,14 @@ registerLoop({
45988
46060
  runTelegramMediaCensus();
45989
46061
  }
45990
46062
  });
46063
+ registerLoop({
46064
+ name: "telegram-card-row-census",
46065
+ intervalMs: STORAGE_AUDIT_INTERVAL_MS,
46066
+ firstRunDelayMs: 11e4,
46067
+ run: async () => {
46068
+ runTelegramCardRowCensus(process.env.LOG_DIR ?? LOG_DIR);
46069
+ }
46070
+ });
45991
46071
  registerLoop({
45992
46072
  name: "telegram-checkout-census",
45993
46073
  intervalMs: STORAGE_AUDIT_INTERVAL_MS,
@@ -46303,7 +46383,7 @@ registerLoop({
46303
46383
  });
46304
46384
  }
46305
46385
  {
46306
- const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
46386
+ const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
46307
46387
  const publishScript = resolve55(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
46308
46388
  const PUBLISH_INTERVAL_MS = 3e5;
46309
46389
  const bookingAccounts = () => {
@@ -46338,7 +46418,7 @@ registerLoop({
46338
46418
  let hasBookingSite = false;
46339
46419
  try {
46340
46420
  if (existsSync63(availPath)) {
46341
- const cfg = JSON.parse(readFileSync63(availPath, "utf-8"));
46421
+ const cfg = JSON.parse(readFileSync64(availPath, "utf-8"));
46342
46422
  hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
46343
46423
  }
46344
46424
  } catch {
@@ -46348,7 +46428,7 @@ registerLoop({
46348
46428
  let lastSuccessAt = null;
46349
46429
  if (existsSync63(statePath)) {
46350
46430
  try {
46351
- const rec = JSON.parse(readFileSync63(statePath, "utf-8"));
46431
+ const rec = JSON.parse(readFileSync64(statePath, "utf-8"));
46352
46432
  lastSuccessAt = rec.lastSuccessAt ?? null;
46353
46433
  } catch {
46354
46434
  console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
@@ -46383,7 +46463,7 @@ startDispatchCensus(() => getSession());
46383
46463
  startDispatchFieldCensus(() => getSession());
46384
46464
  startDispatchTgCensus(() => getSession());
46385
46465
  {
46386
- const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..");
46466
+ const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..");
46387
46467
  const auditScript = resolve55(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
46388
46468
  const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
46389
46469
  const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
@@ -46514,7 +46594,7 @@ async function runAdminUserReconcileTick() {
46514
46594
  console.error("[adminuser-self-heal] skip reason=no-users-file");
46515
46595
  return;
46516
46596
  }
46517
- const usersRaw = readFileSync63(USERS_FILE, "utf-8").trim();
46597
+ const usersRaw = readFileSync64(USERS_FILE, "utf-8").trim();
46518
46598
  if (!usersRaw) {
46519
46599
  console.error("[adminuser-self-heal] skip reason=empty-users-file");
46520
46600
  return;
@@ -46596,8 +46676,8 @@ var runIngestAuditTick = () => {
46596
46676
  },
46597
46677
  agentExists: (accountId, slug) => {
46598
46678
  if (!house) return false;
46599
- const dir = isHouseAccountKey(house.accountId, accountId) ? house.accountDir : join77(ACCOUNTS_DIR, accountId);
46600
- return existsSync63(join77(dir, "agents", slug, "config.json"));
46679
+ const dir = isHouseAccountKey(house.accountId, accountId) ? house.accountDir : join78(ACCOUNTS_DIR, accountId);
46680
+ return existsSync63(join78(dir, "agents", slug, "config.json"));
46601
46681
  }
46602
46682
  });
46603
46683
  } catch (err) {
@@ -46645,9 +46725,9 @@ registerLoop({
46645
46725
  runPublicAddressCollisionAudit({
46646
46726
  houseAccountId: () => resolveAccount()?.accountId ?? null,
46647
46727
  activeSlugs: () => listValidAccounts().map((a) => {
46648
- const agentsRoot = join77(a.accountDir, "agents");
46728
+ const agentsRoot = join78(a.accountDir, "agents");
46649
46729
  if (!existsSync63(agentsRoot)) return { accountId: a.accountId, slugs: [] };
46650
- const slugs = readdirSync37(agentsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).filter((slug) => isActiveAgentSlug(a.accountDir, slug));
46730
+ const slugs = readdirSync38(agentsRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).filter((slug) => isActiveAgentSlug(a.accountDir, slug));
46651
46731
  return { accountId: a.accountId, slugs };
46652
46732
  })
46653
46733
  });
@@ -46661,7 +46741,7 @@ registerLoop({
46661
46741
  var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
46662
46742
  function countUsersRows() {
46663
46743
  if (!existsSync63(USERS_FILE)) return 0;
46664
- const raw = readFileSync63(USERS_FILE, "utf-8").trim();
46744
+ const raw = readFileSync64(USERS_FILE, "utf-8").trim();
46665
46745
  if (!raw) return 0;
46666
46746
  const users = JSON.parse(raw);
46667
46747
  return users.filter((u) => typeof u.userId === "string").length;
@@ -46764,7 +46844,7 @@ function sessionChannelResolver() {
46764
46844
  byId = /* @__PURE__ */ new Map();
46765
46845
  const cfg = claudeConfigDir();
46766
46846
  if (cfg) {
46767
- for (const { path } of enumerateSessionSidecars(join77(cfg, "projects"))) {
46847
+ for (const { path } of enumerateSessionSidecars(join78(cfg, "projects"))) {
46768
46848
  const channel = readSidecarMeta(path).channel;
46769
46849
  if (channel) byId.set(basename19(path).replace(/\.meta\.json$/, ""), channel);
46770
46850
  }
@@ -46839,7 +46919,7 @@ if (bootAccountConfig?.whatsapp) {
46839
46919
  }
46840
46920
  init({
46841
46921
  configDir: configDirForWhatsApp,
46842
- platformRoot: resolve55(process.env.MAXY_PLATFORM_ROOT ?? join77(__dirname, "..")),
46922
+ platformRoot: resolve55(process.env.MAXY_PLATFORM_ROOT ?? join78(__dirname, "..")),
46843
46923
  accountConfig: bootAccountConfig,
46844
46924
  // Task 2074 — enrich with the sub-account name and fan out to every open
46845
46925
  // admin SSE stream. Fire-and-forget: the manager's persist path must not