@rubytech/create-maxy-code 0.1.594 → 0.1.595

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.
@@ -363,10 +363,10 @@ var require_dist2 = __commonJS({
363
363
  } catch {
364
364
  return { newest, storeRows };
365
365
  }
366
- for (const entry2 of entries2) {
367
- if (!entry2.endsWith(".jsonl"))
366
+ for (const entry3 of entries2) {
367
+ if (!entry3.endsWith(".jsonl"))
368
368
  continue;
369
- const stem = entry2.slice(0, -".jsonl".length);
369
+ const stem = entry3.slice(0, -".jsonl".length);
370
370
  const channelKey = fileStemToChannelKey3(stem);
371
371
  if (!isStorableChannelKey3(channelKey))
372
372
  continue;
@@ -375,7 +375,7 @@ var require_dist2 = __commonJS({
375
375
  const botId = channelKey.slice(0, channelKey.indexOf(":"));
376
376
  let raw;
377
377
  try {
378
- raw = (0, node_fs_1.readFileSync)((0, node_path_1.join)(storeDir, entry2), "utf8");
378
+ raw = (0, node_fs_1.readFileSync)((0, node_path_1.join)(storeDir, entry3), "utf8");
379
379
  } catch {
380
380
  continue;
381
381
  }
@@ -604,6 +604,67 @@ var require_dist4 = __commonJS({
604
604
  }
605
605
  });
606
606
 
607
+ // ../lib/telegram-button-labels/dist/index.js
608
+ var require_dist5 = __commonJS({
609
+ "../lib/telegram-button-labels/dist/index.js"(exports) {
610
+ "use strict";
611
+ Object.defineProperty(exports, "__esModule", { value: true });
612
+ exports.BUTTON_LABEL_TTL_MS = void 0;
613
+ exports.buttonLabelKey = buttonLabelKey2;
614
+ exports.writeButtonLabels = writeButtonLabels;
615
+ exports.readButtonLabel = readButtonLabel2;
616
+ var node_fs_1 = __require("fs");
617
+ var node_path_1 = __require("path");
618
+ var FILE4 = "telegram-button-labels.json";
619
+ exports.BUTTON_LABEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
620
+ function isEntry(v) {
621
+ if (!v || typeof v !== "object" || Array.isArray(v))
622
+ return false;
623
+ const e = v;
624
+ return typeof e.label === "string" && typeof e.at === "number" && Number.isFinite(e.at);
625
+ }
626
+ function buttonLabelKey2(botId, chatId, data) {
627
+ return `${botId}:${chatId}:${data}`;
628
+ }
629
+ function readAll3(path) {
630
+ if (!(0, node_fs_1.existsSync)(path))
631
+ return {};
632
+ try {
633
+ const parsed = JSON.parse((0, node_fs_1.readFileSync)(path, "utf-8"));
634
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
635
+ return {};
636
+ const out = {};
637
+ for (const [k, v] of Object.entries(parsed)) {
638
+ if (isEntry(v))
639
+ out[k] = v;
640
+ }
641
+ return out;
642
+ } catch {
643
+ return {};
644
+ }
645
+ }
646
+ function writeButtonLabels(storeDir, entries2, now = Date.now()) {
647
+ if (entries2.length === 0)
648
+ return;
649
+ const path = (0, node_path_1.join)(storeDir, FILE4);
650
+ const kept = {};
651
+ for (const [key2, entry3] of Object.entries(readAll3(path))) {
652
+ if (now - entry3.at < exports.BUTTON_LABEL_TTL_MS)
653
+ kept[key2] = entry3;
654
+ }
655
+ for (const e of entries2)
656
+ kept[e.key] = { label: e.label, at: now };
657
+ (0, node_fs_1.writeFileSync)(path, JSON.stringify(kept, null, 2) + "\n", "utf-8");
658
+ }
659
+ function readButtonLabel2(storeDir, key2, now = Date.now()) {
660
+ const entry3 = readAll3((0, node_path_1.join)(storeDir, FILE4))[key2];
661
+ if (!entry3)
662
+ return null;
663
+ return now - entry3.at >= exports.BUTTON_LABEL_TTL_MS ? null : entry3.label;
664
+ }
665
+ }
666
+ });
667
+
607
668
  // ../lib/dispatch-read/dist/config.js
608
669
  var require_config = __commonJS({
609
670
  "../lib/dispatch-read/dist/config.js"(exports) {
@@ -1514,7 +1575,7 @@ var require_geo = __commonJS({
1514
1575
  });
1515
1576
 
1516
1577
  // ../lib/dispatch-read/dist/index.js
1517
- var require_dist5 = __commonJS({
1578
+ var require_dist6 = __commonJS({
1518
1579
  "../lib/dispatch-read/dist/index.js"(exports) {
1519
1580
  "use strict";
1520
1581
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1673,7 +1734,7 @@ var require_dist5 = __commonJS({
1673
1734
  });
1674
1735
 
1675
1736
  // ../lib/account-schema-regions/dist/index.js
1676
- var require_dist6 = __commonJS({
1737
+ var require_dist7 = __commonJS({
1677
1738
  "../lib/account-schema-regions/dist/index.js"(exports) {
1678
1739
  "use strict";
1679
1740
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1743,7 +1804,7 @@ var require_dist6 = __commonJS({
1743
1804
  });
1744
1805
 
1745
1806
  // ../lib/graph-trash/dist/index.js
1746
- var require_dist7 = __commonJS({
1807
+ var require_dist8 = __commonJS({
1747
1808
  "../lib/graph-trash/dist/index.js"(exports) {
1748
1809
  "use strict";
1749
1810
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -2024,7 +2085,7 @@ var require_dist7 = __commonJS({
2024
2085
  });
2025
2086
 
2026
2087
  // ../lib/graph-style/dist/index.js
2027
- var require_dist8 = __commonJS({
2088
+ var require_dist9 = __commonJS({
2028
2089
  "../lib/graph-style/dist/index.js"(exports) {
2029
2090
  "use strict";
2030
2091
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -2784,7 +2845,7 @@ var require_conversation_provenance = __commonJS({
2784
2845
  "use strict";
2785
2846
  Object.defineProperty(exports, "__esModule", { value: true });
2786
2847
  exports.injectConversationProvenance = injectConversationProvenance;
2787
- var index_js_1 = require_dist9();
2848
+ var index_js_1 = require_dist10();
2788
2849
  async function injectConversationProvenance(params) {
2789
2850
  const { session, relationships, accountId, writeLabels, conversationNodeId, logNamespace, tool } = params;
2790
2851
  const original = [...relationships];
@@ -2834,7 +2895,7 @@ var require_conversation_provenance = __commonJS({
2834
2895
  });
2835
2896
 
2836
2897
  // ../lib/graph-write/dist/index.js
2837
- var require_dist9 = __commonJS({
2898
+ var require_dist10 = __commonJS({
2838
2899
  "../lib/graph-write/dist/index.js"(exports) {
2839
2900
  "use strict";
2840
2901
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
@@ -3169,26 +3230,26 @@ function keyOf(resource, accountId) {
3169
3230
  function generationOf(resource, key2) {
3170
3231
  return (keyGenerations.get(key2) ?? 0) + (resourceGenerations.get(resource) ?? 0);
3171
3232
  }
3172
- function recomputeFloorMs(entry2) {
3233
+ function recomputeFloorMs(entry3) {
3173
3234
  return Math.min(
3174
- Math.max(entry2.computeMs * RECOMPUTE_FLOOR_FACTOR, MIN_RECOMPUTE_FLOOR_MS),
3235
+ Math.max(entry3.computeMs * RECOMPUTE_FLOOR_FACTOR, MIN_RECOMPUTE_FLOOR_MS),
3175
3236
  MAX_RECOMPUTE_FLOOR_MS
3176
3237
  );
3177
3238
  }
3178
3239
  async function getOrCompute(resource, accountId, compute) {
3179
3240
  const key2 = keyOf(resource, accountId);
3180
- const entry2 = entries.get(key2);
3181
- if (entry2 !== void 0) {
3182
- const age = Date.now() - entry2.computedAt;
3183
- if (!entry2.dirty && age <= BACKSTOP_MS) {
3184
- return { value: entry2.value, state: "hit" };
3241
+ const entry3 = entries.get(key2);
3242
+ if (entry3 !== void 0) {
3243
+ const age = Date.now() - entry3.computedAt;
3244
+ if (!entry3.dirty && age <= BACKSTOP_MS) {
3245
+ return { value: entry3.value, state: "hit" };
3185
3246
  }
3186
- const sinceAttempt = Date.now() - entry2.lastAttemptAt;
3187
- if (entry2.dirty && sinceAttempt < recomputeFloorMs(entry2) && age <= BACKSTOP_MS) {
3188
- return { value: entry2.value, state: "dirty-floor" };
3247
+ const sinceAttempt = Date.now() - entry3.lastAttemptAt;
3248
+ if (entry3.dirty && sinceAttempt < recomputeFloorMs(entry3) && age <= BACKSTOP_MS) {
3249
+ return { value: entry3.value, state: "dirty-floor" };
3189
3250
  }
3190
3251
  }
3191
- const state = entry2 === void 0 ? "miss" : "stale-backstop";
3252
+ const state = entry3 === void 0 ? "miss" : "stale-backstop";
3192
3253
  const existing = inFlight.get(key2);
3193
3254
  if (existing) return { value: await existing, state };
3194
3255
  const startedGeneration = generationOf(resource, key2);
@@ -3233,8 +3294,8 @@ function invalidate(resource, accountId, source = "action") {
3233
3294
  entries.delete(key2);
3234
3295
  return;
3235
3296
  }
3236
- const entry2 = entries.get(key2);
3237
- if (entry2) entry2.dirty = true;
3297
+ const entry3 = entries.get(key2);
3298
+ if (entry3) entry3.dirty = true;
3238
3299
  };
3239
3300
  if (accountId === null) {
3240
3301
  resourceGenerations.set(resource, (resourceGenerations.get(resource) ?? 0) + 1);
@@ -3427,9 +3488,9 @@ function findPollConversation(accountId, pollId) {
3427
3488
  const needle = `:poll:${pollId}:`;
3428
3489
  try {
3429
3490
  if (!existsSync(dir)) return null;
3430
- for (const entry2 of readdirSync(dir)) {
3431
- if (!entry2.endsWith(".jsonl")) continue;
3432
- const stem = entry2.slice(0, -".jsonl".length);
3491
+ for (const entry3 of readdirSync(dir)) {
3492
+ if (!entry3.endsWith(".jsonl")) continue;
3493
+ const stem = entry3.slice(0, -".jsonl".length);
3433
3494
  if (!isStorableStem(stem)) continue;
3434
3495
  const channelKey = (0, import_dist2.fileStemToChannelKey)(stem);
3435
3496
  for (const row of readRows(accountId, channelKey)) {
@@ -3458,12 +3519,12 @@ function readConversationSummaries2(accountId) {
3458
3519
  const seen = /* @__PURE__ */ new Set();
3459
3520
  try {
3460
3521
  if (!existsSync(dir)) return out;
3461
- for (const entry2 of readdirSync(dir)) {
3462
- if (!entry2.endsWith(".jsonl")) continue;
3463
- const stem = entry2.slice(0, -".jsonl".length);
3522
+ for (const entry3 of readdirSync(dir)) {
3523
+ if (!entry3.endsWith(".jsonl")) continue;
3524
+ const stem = entry3.slice(0, -".jsonl".length);
3464
3525
  if (!isStorableStem(stem)) continue;
3465
3526
  const channelKey = (0, import_dist2.fileStemToChannelKey)(stem);
3466
- const file = join(dir, entry2);
3527
+ const file = join(dir, entry3);
3467
3528
  seen.add(file);
3468
3529
  let size;
3469
3530
  let mtimeMs;
@@ -5125,12 +5186,12 @@ function getLanIp() {
5125
5186
  let fallback = null;
5126
5187
  for (const entries2 of Object.values(nets)) {
5127
5188
  if (!entries2) continue;
5128
- for (const entry2 of entries2) {
5129
- if (entry2.family !== "IPv4" || entry2.internal) continue;
5130
- if (entry2.address.startsWith("192.168.") || entry2.address.startsWith("10.")) {
5131
- return entry2.address;
5189
+ for (const entry3 of entries2) {
5190
+ if (entry3.family !== "IPv4" || entry3.internal) continue;
5191
+ if (entry3.address.startsWith("192.168.") || entry3.address.startsWith("10.")) {
5192
+ return entry3.address;
5132
5193
  }
5133
- if (!fallback) fallback = entry2.address;
5194
+ if (!fallback) fallback = entry3.address;
5134
5195
  }
5135
5196
  }
5136
5197
  return fallback;
@@ -6092,9 +6153,9 @@ function checkTelegramChatSend(params) {
6092
6153
  }
6093
6154
  return { allowed: false, reason: "not-in-allowChannels" };
6094
6155
  }
6095
- function chatSendAllowlists(entry2) {
6096
- if (!entry2 || entry2.role !== "public") return { allowChannels: [], allowGroups: [] };
6097
- return { allowChannels: entry2.allowChannels ?? [], allowGroups: entry2.allowGroups ?? [] };
6156
+ function chatSendAllowlists(entry3) {
6157
+ if (!entry3 || entry3.role !== "public") return { allowChannels: [], allowGroups: [] };
6158
+ return { allowChannels: entry3.allowChannels ?? [], allowGroups: entry3.allowGroups ?? [] };
6098
6159
  }
6099
6160
 
6100
6161
  // app/lib/channel-pty-bridge/admin-session-id.ts
@@ -6193,7 +6254,7 @@ function parseTitleCache(raw) {
6193
6254
  }
6194
6255
  function serializeTitleCache(cache2) {
6195
6256
  const obj = {};
6196
- for (const [sessionId, entry2] of cache2) obj[sessionId] = entry2;
6257
+ for (const [sessionId, entry3] of cache2) obj[sessionId] = entry3;
6197
6258
  return JSON.stringify(obj);
6198
6259
  }
6199
6260
  async function loadTitleCacheOutcome(accountDir) {
@@ -6226,7 +6287,7 @@ async function saveTitleCacheMerged(accountDir, updates, evict) {
6226
6287
  if (err.code !== "ENOENT") throw err;
6227
6288
  merged = /* @__PURE__ */ new Map();
6228
6289
  }
6229
- for (const [sessionId, entry2] of updates) merged.set(sessionId, entry2);
6290
+ for (const [sessionId, entry3] of updates) merged.set(sessionId, entry3);
6230
6291
  let evicted = 0;
6231
6292
  if (evict !== void 0) {
6232
6293
  for (const sessionId of evict) {
@@ -6653,11 +6714,11 @@ function enumerateJsonlsWithSkips(projectsRoot, fileRe = SESSION_ID_RE) {
6653
6714
  skipped += 1;
6654
6715
  continue;
6655
6716
  }
6656
- for (const entry2 of entries2) {
6657
- if (entry2.isFile() && fileRe.test(entry2.name)) {
6658
- out.push({ path: join11(slugDir, entry2.name), isSubagent: false, archived: false });
6659
- } else if (entry2.isDirectory() && entry2.name === "subagents") {
6660
- const subDir = join11(slugDir, entry2.name);
6717
+ for (const entry3 of entries2) {
6718
+ if (entry3.isFile() && fileRe.test(entry3.name)) {
6719
+ out.push({ path: join11(slugDir, entry3.name), isSubagent: false, archived: false });
6720
+ } else if (entry3.isDirectory() && entry3.name === "subagents") {
6721
+ const subDir = join11(slugDir, entry3.name);
6661
6722
  let subEntries;
6662
6723
  try {
6663
6724
  subEntries = readdirSync5(subDir, { withFileTypes: true });
@@ -6672,8 +6733,8 @@ function enumerateJsonlsWithSkips(projectsRoot, fileRe = SESSION_ID_RE) {
6672
6733
  out.push({ path: join11(subDir, sub.name), isSubagent: true, archived: false });
6673
6734
  }
6674
6735
  }
6675
- } else if (entry2.isDirectory() && entry2.name === "archive") {
6676
- const archiveDir = join11(slugDir, entry2.name);
6736
+ } else if (entry3.isDirectory() && entry3.name === "archive") {
6737
+ const archiveDir = join11(slugDir, entry3.name);
6677
6738
  let archiveEntries;
6678
6739
  try {
6679
6740
  archiveEntries = readdirSync5(archiveDir, { withFileTypes: true });
@@ -7281,7 +7342,7 @@ function createScheduleInjectRoutes(deps) {
7281
7342
  }
7282
7343
 
7283
7344
  // server/index.ts
7284
- var import_dist17 = __toESM(require_dist3(), 1);
7345
+ var import_dist18 = __toESM(require_dist3(), 1);
7285
7346
 
7286
7347
  // app/lib/whatsapp/avatar-store.ts
7287
7348
  import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync6, statSync as statSync8, rmSync as rmSync3 } from "fs";
@@ -7518,12 +7579,12 @@ async function sweepAvatars() {
7518
7579
  var TAG9 = "[wa-chatmeta]";
7519
7580
  function diffStoredAgainstLive(stored, live) {
7520
7581
  const out = [];
7521
- for (const entry2 of stored.values()) {
7522
- if (!entry2.isGroup) continue;
7523
- const seen = live.get(entry2.jid);
7582
+ for (const entry3 of stored.values()) {
7583
+ if (!entry3.isGroup) continue;
7584
+ const seen = live.get(entry3.jid);
7524
7585
  if (seen === void 0) continue;
7525
- if (seen.name !== entry2.name) {
7526
- out.push({ jid: entry2.jid, stored: entry2.name, live: seen.name, observedAt: seen.observedAt });
7586
+ if (seen.name !== entry3.name) {
7587
+ out.push({ jid: entry3.jid, stored: entry3.name, live: seen.name, observedAt: seen.observedAt });
7527
7588
  }
7528
7589
  }
7529
7590
  return out;
@@ -7539,13 +7600,13 @@ async function sweepChatMetadata() {
7539
7600
  const sock = conn.sock;
7540
7601
  const stored = readChatMetadata(conn.platformAccountId);
7541
7602
  const live = /* @__PURE__ */ new Map();
7542
- for (const entry2 of stored.values()) {
7543
- if (!entry2.isGroup) continue;
7603
+ for (const entry3 of stored.values()) {
7604
+ if (!entry3.isGroup) continue;
7544
7605
  groups += 1;
7545
7606
  try {
7546
- const meta = await sock.groupMetadata(entry2.jid);
7607
+ const meta = await sock.groupMetadata(entry3.jid);
7547
7608
  if (typeof meta.subject === "string" && meta.subject.length > 0) {
7548
- live.set(entry2.jid, { name: meta.subject, observedAt: (/* @__PURE__ */ new Date()).toISOString() });
7609
+ live.set(entry3.jid, { name: meta.subject, observedAt: (/* @__PURE__ */ new Date()).toISOString() });
7549
7610
  checked2 += 1;
7550
7611
  }
7551
7612
  } catch {
@@ -8208,9 +8269,9 @@ function purgeNonSocketAccountManagers(accountDir, socketOwnerId) {
8208
8269
  if (targetId === socketOwnerId) return { removed: [] };
8209
8270
  const map = wa.accountManagers;
8210
8271
  if (!map || typeof map !== "object" || Array.isArray(map)) return { removed: [] };
8211
- const removed = Object.entries(map).map(([phone, entry2]) => ({
8272
+ const removed = Object.entries(map).map(([phone, entry3]) => ({
8212
8273
  phone,
8213
- managesAccount: typeof entry2 === "string" ? entry2 : entry2 && typeof entry2 === "object" && typeof entry2.managesAccount === "string" ? entry2.managesAccount : ""
8274
+ managesAccount: typeof entry3 === "string" ? entry3 : entry3 && typeof entry3 === "object" && typeof entry3.managesAccount === "string" ? entry3.managesAccount : ""
8214
8275
  }));
8215
8276
  if (removed.length === 0) return { removed: [] };
8216
8277
  delete wa.accountManagers;
@@ -8513,10 +8574,10 @@ function purgeNonSocketRegisteredParties(accountDir, socketOwnerId) {
8513
8574
  if (targetId === socketOwnerId) return { removed: [] };
8514
8575
  const map = wa.registeredParties;
8515
8576
  if (!map || typeof map !== "object" || Array.isArray(map)) return { removed: [] };
8516
- const removed = Object.entries(map).map(([phone, entry2]) => ({
8577
+ const removed = Object.entries(map).map(([phone, entry3]) => ({
8517
8578
  phone,
8518
- account: entry2 && typeof entry2 === "object" && typeof entry2.account === "string" ? entry2.account : "",
8519
- party: entry2 && typeof entry2 === "object" && typeof entry2.party === "string" ? entry2.party : ""
8579
+ account: entry3 && typeof entry3 === "object" && typeof entry3.account === "string" ? entry3.account : "",
8580
+ party: entry3 && typeof entry3 === "object" && typeof entry3.party === "string" ? entry3.party : ""
8520
8581
  }));
8521
8582
  if (removed.length === 0) return { removed: [] };
8522
8583
  delete wa.registeredParties;
@@ -9350,27 +9411,27 @@ function bare(value) {
9350
9411
  function reconcilePersonIdentities(rows) {
9351
9412
  const perAccount = /* @__PURE__ */ new Map();
9352
9413
  for (const row of rows) {
9353
- let entry2 = perAccount.get(row.accountId);
9354
- if (!entry2) {
9355
- entry2 = { seen: /* @__PURE__ */ new Map(), divergent: 0 };
9356
- perAccount.set(row.accountId, entry2);
9414
+ let entry3 = perAccount.get(row.accountId);
9415
+ if (!entry3) {
9416
+ entry3 = { seen: /* @__PURE__ */ new Map(), divergent: 0 };
9417
+ perAccount.set(row.accountId, entry3);
9357
9418
  }
9358
9419
  const p = row.phone ? bare(row.phone) : null;
9359
9420
  const t = row.telephone ? bare(row.telephone) : null;
9360
- if (p && t && p !== t) entry2.divergent += 1;
9421
+ if (p && t && p !== t) entry3.divergent += 1;
9361
9422
  const key2 = p ?? t;
9362
- if (key2) entry2.seen.set(key2, (entry2.seen.get(key2) ?? 0) + 1);
9423
+ if (key2) entry3.seen.set(key2, (entry3.seen.get(key2) ?? 0) + 1);
9363
9424
  }
9364
9425
  const accounts = [];
9365
9426
  let duplicates = 0;
9366
9427
  let divergent = 0;
9367
- for (const [accountId, entry2] of perAccount) {
9428
+ for (const [accountId, entry3] of perAccount) {
9368
9429
  let dup = 0;
9369
- for (const count of entry2.seen.values()) if (count > 1) dup += count - 1;
9430
+ for (const count of entry3.seen.values()) if (count > 1) dup += count - 1;
9370
9431
  duplicates += dup;
9371
- divergent += entry2.divergent;
9372
- if (dup > 0 || entry2.divergent > 0) {
9373
- accounts.push({ accountId, duplicates: dup, divergent: entry2.divergent });
9432
+ divergent += entry3.divergent;
9433
+ if (dup > 0 || entry3.divergent > 0) {
9434
+ accounts.push({ accountId, duplicates: dup, divergent: entry3.divergent });
9374
9435
  }
9375
9436
  }
9376
9437
  return {
@@ -9392,7 +9453,7 @@ function reconcileSelfPhoneAdmins(input) {
9392
9453
  for (const cred of nonHouse) {
9393
9454
  const selfPhone = cred.selfPhone;
9394
9455
  if (!selfPhone) continue;
9395
- if (input.adminPhones.some((entry2) => phonesMatch(entry2, selfPhone))) {
9456
+ if (input.adminPhones.some((entry3) => phonesMatch(entry3, selfPhone))) {
9396
9457
  matches.push({ accountId: cred.accountId, phone: selfPhone });
9397
9458
  }
9398
9459
  }
@@ -9773,11 +9834,11 @@ function authorizeRecallRead(input) {
9773
9834
  function buildRegisteredPartyReconcile(params) {
9774
9835
  const rows = [];
9775
9836
  const entries2 = Object.entries(params.registeredParties);
9776
- for (const [phone, entry2] of entries2) {
9837
+ for (const [phone, entry3] of entries2) {
9777
9838
  const shadowedBy = isAdminPhone(phone, params.adminPhones) ? "adminPhones" : managedAccountFor(params.accountManagers, phone) ? "accountManagers" : null;
9778
- const unresolved2 = !params.isValidAccount(entry2.account);
9839
+ const unresolved2 = !params.isValidAccount(entry3.account);
9779
9840
  if (shadowedBy || unresolved2) {
9780
- rows.push({ phone, account: entry2.account, party: entry2.party, shadowedBy, unresolved: unresolved2 });
9841
+ rows.push({ phone, account: entry3.account, party: entry3.party, shadowedBy, unresolved: unresolved2 });
9781
9842
  }
9782
9843
  }
9783
9844
  const shadowed = rows.filter((r) => r.shadowedBy !== null).length;
@@ -10187,15 +10248,15 @@ app3.post("/config", async (c) => {
10187
10248
  const agents = [];
10188
10249
  if (existsSync9(agentsDir)) {
10189
10250
  try {
10190
- for (const entry2 of readdirSync8(agentsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
10191
- if (!entry2.isDirectory() || entry2.name === "admin") continue;
10192
- const configPath3 = resolve8(agentsDir, entry2.name, "config.json");
10251
+ for (const entry3 of readdirSync8(agentsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
10252
+ if (!entry3.isDirectory() || entry3.name === "admin") continue;
10253
+ const configPath3 = resolve8(agentsDir, entry3.name, "config.json");
10193
10254
  if (!existsSync9(configPath3)) continue;
10194
10255
  try {
10195
10256
  const parsed = JSON.parse(readFileSync11(configPath3, "utf-8"));
10196
- agents.push({ slug: entry2.name, displayName: parsed.displayName ?? entry2.name });
10257
+ agents.push({ slug: entry3.name, displayName: parsed.displayName ?? entry3.name });
10197
10258
  } catch {
10198
- console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry2.name}" \u2014 skipping`);
10259
+ console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry3.name}" \u2014 skipping`);
10199
10260
  }
10200
10261
  }
10201
10262
  } catch (err) {
@@ -10215,8 +10276,8 @@ app3.post("/config", async (c) => {
10215
10276
  const listDenial = bindingScopeDenial(c, callerAccountId, "list-account-managers");
10216
10277
  if (listDenial) return listDenial;
10217
10278
  const all = readAccountManagers(acct.accountDir);
10218
- const boundAccountOf = (entry2) => typeof entry2 === "string" ? entry2 : entry2.managesAccount;
10219
- const accountManagers = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry2]) => boundAccountOf(entry2) === callerAccountId));
10279
+ const boundAccountOf = (entry3) => typeof entry3 === "string" ? entry3 : entry3.managesAccount;
10280
+ const accountManagers = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry3]) => boundAccountOf(entry3) === callerAccountId));
10220
10281
  console.error(`${TAG17} config action=list-account-managers caller=${callerAccountId || "none"} houseAdmin=${houseAdmin ? "y" : "n"} count=${Object.keys(accountManagers).length} of=${Object.keys(all).length}`);
10221
10282
  return c.json({ ok: true, accountManagers });
10222
10283
  }
@@ -10254,7 +10315,7 @@ app3.post("/config", async (c) => {
10254
10315
  const listDenial = bindingScopeDenial(c, callerAccountId, "list-registered-parties");
10255
10316
  if (listDenial) return listDenial;
10256
10317
  const all = readRegisteredParties(acct.accountDir);
10257
- const registeredParties = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry2]) => entry2.account === callerAccountId));
10318
+ const registeredParties = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry3]) => entry3.account === callerAccountId));
10258
10319
  console.error(`${TAG17} config action=list-registered-parties caller=${callerAccountId || "none"} houseAdmin=${houseAdmin ? "y" : "n"} count=${Object.keys(registeredParties).length} of=${Object.keys(all).length}`);
10259
10320
  return c.json({ ok: true, registeredParties });
10260
10321
  }
@@ -12159,10 +12220,10 @@ function readState(platformRoot5) {
12159
12220
  return {};
12160
12221
  }
12161
12222
  }
12162
- function recordSuccess(platformRoot5, accountId, entry2) {
12223
+ function recordSuccess(platformRoot5, accountId, entry3) {
12163
12224
  const path = stateFilePath(platformRoot5);
12164
12225
  const state = readState(platformRoot5);
12165
- state[accountId] = entry2;
12226
+ state[accountId] = entry3;
12166
12227
  try {
12167
12228
  writeFileSync6(path, JSON.stringify(state, null, 1));
12168
12229
  } catch (err) {
@@ -13259,10 +13320,10 @@ function readVisitsPushState(platformRoot5) {
13259
13320
  return {};
13260
13321
  }
13261
13322
  }
13262
- function recordVisitsPush(platformRoot5, accountId, entry2) {
13323
+ function recordVisitsPush(platformRoot5, accountId, entry3) {
13263
13324
  const path = visitsStateFilePath(platformRoot5);
13264
13325
  const state = readVisitsPushState(platformRoot5);
13265
- state[accountId] = entry2;
13326
+ state[accountId] = entry3;
13266
13327
  try {
13267
13328
  writeFileSync7(path, JSON.stringify(state, null, 1));
13268
13329
  } catch (err) {
@@ -14906,20 +14967,20 @@ function listBotEntries(config) {
14906
14967
  }
14907
14968
  return out;
14908
14969
  }
14909
- function entryAgentSlug(entry2) {
14910
- if (entry2.role === "admin") return "admin";
14911
- if (entry2.role === "unbound") return "unbound";
14912
- if (entry2.role === "specialist") return entry2.specialist;
14913
- return entry2.agent;
14970
+ function entryAgentSlug(entry3) {
14971
+ if (entry3.role === "admin") return "admin";
14972
+ if (entry3.role === "unbound") return "unbound";
14973
+ if (entry3.role === "specialist") return entry3.specialist;
14974
+ return entry3.agent;
14914
14975
  }
14915
- function isBindableEntry(entry2) {
14916
- return entry2.role !== "unbound";
14976
+ function isBindableEntry(entry3) {
14977
+ return entry3.role !== "unbound";
14917
14978
  }
14918
14979
  function resolveBotEntry(accounts, botId) {
14919
14980
  const hits = [];
14920
14981
  for (const a of accounts) {
14921
- for (const entry2 of listBotEntries(a.config.telegram)) {
14922
- if (entry2.id === botId) hits.push({ accountId: a.accountId, accountDir: a.accountDir, entry: entry2 });
14982
+ for (const entry3 of listBotEntries(a.config.telegram)) {
14983
+ if (entry3.id === botId) hits.push({ accountId: a.accountId, accountDir: a.accountDir, entry: entry3 });
14923
14984
  }
14924
14985
  }
14925
14986
  if (hits.length === 0) return { kind: "none" };
@@ -16964,7 +17025,7 @@ function createSidecarStore(config) {
16964
17025
  function readAll3(sessionsDir, onSkip) {
16965
17026
  let names;
16966
17027
  try {
16967
- names = readdirSync17(sessionsDir, { withFileTypes: true }).filter((entry2) => entry2.isFile()).map((entry2) => entry2.name);
17028
+ names = readdirSync17(sessionsDir, { withFileTypes: true }).filter((entry3) => entry3.isFile()).map((entry3) => entry3.name);
16968
17029
  } catch {
16969
17030
  return [];
16970
17031
  }
@@ -17897,7 +17958,7 @@ async function setTelegramWebhook(botToken, webhookUrl, secretFilePath, fetchImp
17897
17958
  }
17898
17959
  return { ok: true, secret };
17899
17960
  }
17900
- function writeTelegramBotEntry(accountDir, entry2) {
17961
+ function writeTelegramBotEntry(accountDir, entry3) {
17901
17962
  const configPath3 = join36(accountDir, "account.json");
17902
17963
  if (!existsSync25(configPath3)) {
17903
17964
  return { ok: false, error: `account.json not found at ${configPath3}` };
@@ -17910,9 +17971,9 @@ function writeTelegramBotEntry(accountDir, entry2) {
17910
17971
  }
17911
17972
  const telegram = config.telegram ?? {};
17912
17973
  const bots = Array.isArray(telegram.bots) ? telegram.bots : [];
17913
- const at = bots.findIndex((b) => b && typeof b === "object" && b.id === entry2.id);
17914
- if (at >= 0) bots[at] = entry2;
17915
- else bots.push(entry2);
17974
+ const at = bots.findIndex((b) => b && typeof b === "object" && b.id === entry3.id);
17975
+ if (at >= 0) bots[at] = entry3;
17976
+ else bots.push(entry3);
17916
17977
  telegram.bots = bots;
17917
17978
  config.telegram = telegram;
17918
17979
  writeFileSync12(configPath3, JSON.stringify(config, null, 2) + "\n", "utf-8");
@@ -17998,34 +18059,34 @@ function getTelegramGateway() {
17998
18059
 
17999
18060
  // app/lib/telegram/access-control.ts
18000
18061
  function checkTelegramAccess(params) {
18001
- const { senderId, entry: entry2, chatId, chatType } = params;
18062
+ const { senderId, entry: entry3, chatId, chatType } = params;
18002
18063
  const isGroup = chatType === "group" || chatType === "supergroup";
18003
18064
  const isChannel = chatType === "channel";
18004
- if (isChannel && entry2.role !== "public") {
18065
+ if (isChannel && entry3.role !== "public") {
18005
18066
  return {
18006
18067
  allowed: false,
18007
- reason: entry2.role === "admin" ? "admin-entry-no-channels" : entry2.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
18008
- agentType: entry2.role === "admin" ? "admin" : entry2.role === "specialist" ? "specialist" : "public"
18068
+ reason: entry3.role === "admin" ? "admin-entry-no-channels" : entry3.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
18069
+ agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
18009
18070
  };
18010
18071
  }
18011
- if (isGroup && entry2.role !== "public") {
18072
+ if (isGroup && entry3.role !== "public") {
18012
18073
  return {
18013
18074
  allowed: false,
18014
- reason: entry2.role === "admin" ? "admin-entry-no-groups" : entry2.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
18015
- agentType: entry2.role === "admin" ? "admin" : entry2.role === "specialist" ? "specialist" : "public"
18075
+ reason: entry3.role === "admin" ? "admin-entry-no-groups" : entry3.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
18076
+ agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
18016
18077
  };
18017
18078
  }
18018
- if (entry2.role === "admin") {
18019
- const adminUsers = entry2.adminUsers ?? [];
18079
+ if (entry3.role === "admin") {
18080
+ const adminUsers = entry3.adminUsers ?? [];
18020
18081
  if (adminUsers.includes(senderId)) {
18021
18082
  return { allowed: true, reason: "admin-binding", agentType: "admin" };
18022
18083
  }
18023
18084
  return { allowed: false, reason: "not-admin-user", agentType: "admin" };
18024
18085
  }
18025
- if (isGroup && entry2.role === "public") {
18026
- const groupPolicy = entry2.groupPolicy ?? "disabled";
18086
+ if (isGroup && entry3.role === "public") {
18087
+ const groupPolicy = entry3.groupPolicy ?? "disabled";
18027
18088
  if (groupPolicy === "allowlist") {
18028
- const allowGroups = entry2.allowGroups ?? [];
18089
+ const allowGroups = entry3.allowGroups ?? [];
18029
18090
  if (allowGroups.includes(chatId)) {
18030
18091
  return { allowed: true, reason: "group-allowlist-match", agentType: "public" };
18031
18092
  }
@@ -18033,10 +18094,10 @@ function checkTelegramAccess(params) {
18033
18094
  }
18034
18095
  return { allowed: false, reason: "group-policy-disabled", agentType: "public" };
18035
18096
  }
18036
- if (isChannel && entry2.role === "public") {
18037
- const channelPolicy = entry2.channelPolicy ?? "disabled";
18097
+ if (isChannel && entry3.role === "public") {
18098
+ const channelPolicy = entry3.channelPolicy ?? "disabled";
18038
18099
  if (channelPolicy === "allowlist") {
18039
- const allowChannels = entry2.allowChannels ?? [];
18100
+ const allowChannels = entry3.allowChannels ?? [];
18040
18101
  if (allowChannels.includes(chatId)) {
18041
18102
  return { allowed: true, reason: "channel-allowlist-match", agentType: "public" };
18042
18103
  }
@@ -18044,22 +18105,22 @@ function checkTelegramAccess(params) {
18044
18105
  }
18045
18106
  return { allowed: false, reason: "channel-policy-disabled", agentType: "public" };
18046
18107
  }
18047
- if (entry2.role === "unbound") {
18108
+ if (entry3.role === "unbound") {
18048
18109
  return { allowed: false, reason: "bot-unbound", agentType: "public" };
18049
18110
  }
18050
- if (entry2.role === "specialist") {
18051
- const allowFrom = entry2.allowFrom ?? [];
18111
+ if (entry3.role === "specialist") {
18112
+ const allowFrom = entry3.allowFrom ?? [];
18052
18113
  if (allowFrom.includes(senderId)) {
18053
18114
  return { allowed: true, reason: "specialist-allowlist-match", agentType: "specialist" };
18054
18115
  }
18055
18116
  return { allowed: false, reason: "not-in-allowlist", agentType: "specialist" };
18056
18117
  }
18057
- const policy = entry2.dmPolicy ?? "disabled";
18118
+ const policy = entry3.dmPolicy ?? "disabled";
18058
18119
  switch (policy) {
18059
18120
  case "open":
18060
18121
  return { allowed: true, reason: "dm-policy-open", agentType: "public" };
18061
18122
  case "allowlist": {
18062
- const allowFrom = entry2.allowFrom ?? [];
18123
+ const allowFrom = entry3.allowFrom ?? [];
18063
18124
  if (allowFrom.includes(senderId)) {
18064
18125
  return { allowed: true, reason: "allowlist-match", agentType: "public" };
18065
18126
  }
@@ -19082,6 +19143,9 @@ function runTelegramNotifyReconcile(now) {
19082
19143
  console.error(buildTelegramKindCensusLine(readTelegramKindCensus()));
19083
19144
  }
19084
19145
 
19146
+ // server/routes/telegram.ts
19147
+ var import_dist8 = __toESM(require_dist5(), 1);
19148
+
19085
19149
  // app/lib/telegram/outbound/answer-callback.ts
19086
19150
  async function answerTelegramCallback(botToken, callbackQueryId, text) {
19087
19151
  try {
@@ -19114,8 +19178,8 @@ var NOTIFY_MAX = 5e3;
19114
19178
  var cache = /* @__PURE__ */ new Map();
19115
19179
  function isTelegramNotifyDuplicate(notifyId) {
19116
19180
  const now = Date.now();
19117
- const entry2 = cache.get(notifyId);
19118
- if (entry2 && now - entry2.ts <= NOTIFY_TTL_MS) return true;
19181
+ const entry3 = cache.get(notifyId);
19182
+ if (entry3 && now - entry3.ts <= NOTIFY_TTL_MS) return true;
19119
19183
  for (const [key2, e] of cache) {
19120
19184
  if (now - e.ts > NOTIFY_TTL_MS) cache.delete(key2);
19121
19185
  }
@@ -19174,7 +19238,7 @@ function runTelegramNotifyPublishCensus(now) {
19174
19238
  }
19175
19239
 
19176
19240
  // ../lib/dispatch-write/src/lifecycle.ts
19177
- var import_dist6 = __toESM(require_dist5());
19241
+ var import_dist6 = __toESM(require_dist6());
19178
19242
  function checkTransition(kind, from, to, cfg) {
19179
19243
  const edges = kind === "job" ? cfg.statuses.jobTransitions : cfg.statuses.visitTransitions;
19180
19244
  const legal = edges.some(([f, t]) => f === from && t === to);
@@ -19216,7 +19280,7 @@ function formatTransitionRefused(p) {
19216
19280
  }
19217
19281
 
19218
19282
  // ../lib/dispatch-write/src/write.ts
19219
- var import_dist7 = __toESM(require_dist5());
19283
+ var import_dist7 = __toESM(require_dist6());
19220
19284
  function refuse(reason, message) {
19221
19285
  return { ok: false, reason, message };
19222
19286
  }
@@ -19793,8 +19857,8 @@ function liveLocationCensusLine(accountsRoot, now) {
19793
19857
  let expired = 0;
19794
19858
  let oldestAgeMs = 0;
19795
19859
  try {
19796
- for (const entry2 of readdirSync20(accountsRoot)) {
19797
- const path = join38(accountsRoot, entry2, LIVE_LOCATION_FILE);
19860
+ for (const entry3 of readdirSync20(accountsRoot)) {
19861
+ const path = join38(accountsRoot, entry3, LIVE_LOCATION_FILE);
19798
19862
  if (!existsSync26(path) || !statSync19(path).isFile()) continue;
19799
19863
  for (const rec of Object.values(readAll(path))) {
19800
19864
  records3 += 1;
@@ -19930,20 +19994,20 @@ async function editTelegramMessage(botToken, chatId, messageId, text, keyboard)
19930
19994
  // server/routes/telegram.ts
19931
19995
  var TAG34 = "[telegram-inbound]";
19932
19996
  var PROVISION_TAG = "[telegram-provision]";
19933
- function entryActive(entry2, accountDir, botId, noteSpecialist) {
19934
- if (entry2.role === "admin") return true;
19935
- if (entry2.role === "specialist") {
19997
+ function entryActive(entry3, accountDir, botId, noteSpecialist) {
19998
+ if (entry3.role === "admin") return true;
19999
+ if (entry3.role === "specialist") {
19936
20000
  const cards = listSpecialistCardNames(join40(accountDir, "specialists", "agents"));
19937
- const rosterValid = cards.includes(entry2.specialist);
20001
+ const rosterValid = cards.includes(entry3.specialist);
19938
20002
  noteSpecialist?.(rosterValid);
19939
20003
  if (rosterValid) return true;
19940
20004
  console.error(
19941
- `${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry2.specialist)} reason=specialist-unknown`
20005
+ `${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=specialist-unknown`
19942
20006
  );
19943
20007
  return false;
19944
20008
  }
19945
- if (entry2.role === "unbound") return false;
19946
- return isActiveAgentSlug(accountDir, entry2.agent);
20009
+ if (entry3.role === "unbound") return false;
20010
+ return isActiveAgentSlug(accountDir, entry3.agent);
19947
20011
  }
19948
20012
  function logValue2(v) {
19949
20013
  return v.replace(/[\r\n]+/g, " ");
@@ -20055,9 +20119,9 @@ app11.post("/", async (c) => {
20055
20119
  console.error(`${TAG34} op=reject reason=duplicate-bot-id botId=${botId} accounts=${resolved.count}`);
20056
20120
  return c.json({ ok: false }, 401);
20057
20121
  }
20058
- const { accountId, accountDir, entry: entry2 } = resolved;
20059
- const agentSlug = entryAgentSlug(entry2);
20060
- console.error(`${TAG34} op=entry botId=${botId} accountId=${accountId} role=${entry2.role} agent=${agentSlug}`);
20122
+ const { accountId, accountDir, entry: entry3 } = resolved;
20123
+ const agentSlug = entryAgentSlug(entry3);
20124
+ console.error(`${TAG34} op=entry botId=${botId} accountId=${accountId} role=${entry3.role} agent=${agentSlug}`);
20061
20125
  const sp = secretPath(botId);
20062
20126
  if (!existsSync28(sp)) {
20063
20127
  console.error(`${TAG34} op=secret botId=${botId} result=missing-file`);
@@ -20090,13 +20154,13 @@ app11.post("/", async (c) => {
20090
20154
  });
20091
20155
  }
20092
20156
  if (shape.chatType === "group" || shape.chatType === "supergroup") {
20093
- const allowlisted = entry2.role === "public" && (entry2.groupPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry2.allowGroups ?? []).includes(shape.chatId);
20157
+ const allowlisted = entry3.role === "public" && (entry3.groupPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry3.allowGroups ?? []).includes(shape.chatId);
20094
20158
  console.error(
20095
20159
  `${TAG34} op=group-inbound botId=${botId} chatType=${shape.chatType} chatId=${shape.chatId ?? "none"} senderId=${shape.senderId ?? "none"} threadId=${shape.threadId ?? "none"} allowlisted=${allowlisted ? "yes" : "no"}`
20096
20160
  );
20097
20161
  }
20098
20162
  if (shape.chatType === "channel") {
20099
- const allowlisted = entry2.role === "public" && (entry2.channelPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry2.allowChannels ?? []).includes(shape.chatId);
20163
+ const allowlisted = entry3.role === "public" && (entry3.channelPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry3.allowChannels ?? []).includes(shape.chatId);
20100
20164
  console.error(
20101
20165
  `${TAG34} op=channel-post botId=${botId} chatId=${shape.chatId ?? "none"} messageId=${shape.messageId ?? "none"} senderChat=${shape.senderChat ?? "none"} isEdit=${shape.isEdit ? "yes" : "no"} allowlisted=${allowlisted ? "yes" : "no"}`
20102
20166
  );
@@ -20104,7 +20168,7 @@ app11.post("/", async (c) => {
20104
20168
  const hostBots = update.managed_bot ? listBotEntries(accountConfig(accountDir).telegram) : [];
20105
20169
  const decision = routeTelegramUpdate({
20106
20170
  update,
20107
- entry: entry2,
20171
+ entry: entry3,
20108
20172
  ...update.managed_bot ? { knownBotIds: hostBots.map((b) => b.id) } : {}
20109
20173
  });
20110
20174
  const raisesTurn = decision.kind === "dispatch" || // Task 2615 — a customer asking a question raises one. `business-deleted`
@@ -20134,14 +20198,14 @@ app11.post("/", async (c) => {
20134
20198
  );
20135
20199
  }
20136
20200
  console.error(`${TAG34} op=access botId=${botId} senderId=- allowed=false reason=${decision.reason}`);
20137
- if (entry2.role === "specialist") {
20201
+ if (entry3.role === "specialist") {
20138
20202
  console.error(
20139
- `${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry2.specialist)} reason=${decision.reason}`
20203
+ `${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=${decision.reason}`
20140
20204
  );
20141
20205
  }
20142
20206
  return c.json({ ok: true }, 200);
20143
20207
  }
20144
- if (!isBindableEntry(entry2)) {
20208
+ if (!isBindableEntry(entry3)) {
20145
20209
  console.error(`${TAG34} op=ignore botId=${botId} reason=bot-unbound`);
20146
20210
  return c.json({ ok: true }, 200);
20147
20211
  }
@@ -20174,19 +20238,30 @@ app11.post("/", async (c) => {
20174
20238
  if (decision.allowed) {
20175
20239
  const cbKey = telegramChannelKey(botId, cbSubject);
20176
20240
  const cbAt = (/* @__PURE__ */ new Date()).toISOString();
20241
+ let cbBody = `[button: ${decision.data ?? "no-data"}]`;
20242
+ if (decision.data !== null) {
20243
+ const label = (0, import_dist8.readButtonLabel)(accountDir, (0, import_dist8.buttonLabelKey)(botId, decision.chatId, decision.data));
20244
+ if (label !== null) {
20245
+ cbBody = label;
20246
+ } else {
20247
+ console.error(
20248
+ `${TAG34} op=button-label-miss botId=${botId} chatId=${decision.chatId} data=${logValue2(decision.data)}`
20249
+ );
20250
+ }
20251
+ }
20177
20252
  appendMessage2(accountId, {
20178
20253
  messageId: `telegram:${accountId}:${cbKey}:cb:${decision.callbackId}`,
20179
20254
  // Task 2598 — the one shared derivation, so this row names the session
20180
20255
  // the press actually reaches.
20181
20256
  sessionId: telegramAdminSessionId(accountId, cbSubject, botId),
20182
20257
  dateSent: cbAt,
20183
- body: `[button: ${decision.data ?? "no-data"}]`,
20258
+ body: cbBody,
20184
20259
  fromMe: false,
20185
20260
  senderId: decision.senderId,
20186
20261
  senderName: decision.senderDisplay,
20187
20262
  chatId: String(decision.chatId),
20188
20263
  channelKey: cbKey,
20189
- scope: entry2.role === "admin" ? "admin" : "public",
20264
+ scope: entry3.role === "admin" ? "admin" : "public",
20190
20265
  origin: "inbound",
20191
20266
  createdAt: cbAt
20192
20267
  });
@@ -20202,7 +20277,7 @@ app11.post("/", async (c) => {
20202
20277
  session,
20203
20278
  accountId,
20204
20279
  cfg: load.value,
20205
- token: entry2.token,
20280
+ token: entry3.token,
20206
20281
  botId,
20207
20282
  updateId: decision.updateId,
20208
20283
  chatId: decision.chatId,
@@ -20228,22 +20303,22 @@ app11.post("/", async (c) => {
20228
20303
  }
20229
20304
  }
20230
20305
  const answerStartedAt = Date.now();
20231
- const answered4 = await answerTelegramCallback(entry2.token, decision.callbackId, answerText);
20306
+ const answered4 = await answerTelegramCallback(entry3.token, decision.callbackId, answerText);
20232
20307
  const outcome = !answered4.ok ? "error" : decision.allowed ? "stored" : "refused";
20233
20308
  console.error(
20234
20309
  `${TAG34} op=callback-answered botId=${botId} callbackId=${decision.callbackId} ms=${Date.now() - answerStartedAt} outcome=${outcome}${answered4.error ? ` error=${logValue2(answered4.error)}` : ""}`
20235
20310
  );
20236
20311
  if (decision.allowed && parseDispatchCallback(decision.data) === null) {
20237
- const cbActive = entryActive(entry2, accountDir, botId);
20312
+ const cbActive = entryActive(entry3, accountDir, botId);
20238
20313
  console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${cbActive}`);
20239
20314
  raiseChannelTurn({
20240
20315
  accountId,
20241
20316
  accountDir,
20242
20317
  botId,
20243
- botToken: entry2.token,
20244
- role: entry2.role,
20318
+ botToken: entry3.token,
20319
+ role: entry3.role,
20245
20320
  agentSlug,
20246
- specialist: entry2.role === "specialist" ? entry2.specialist : null,
20321
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
20247
20322
  active: cbActive,
20248
20323
  senderId: decision.senderId,
20249
20324
  senderDisplay: decision.senderDisplay,
@@ -20253,7 +20328,7 @@ app11.post("/", async (c) => {
20253
20328
  text: `[button: ${decision.data ?? "no-data"}]`,
20254
20329
  channelKey: telegramChannelKey(botId, cbSubject),
20255
20330
  conversationSubject: cbSubject,
20256
- scope: entry2.role === "admin" ? "admin" : "public",
20331
+ scope: entry3.role === "admin" ? "admin" : "public",
20257
20332
  sessionId: telegramAdminSessionId(accountId, cbSubject, botId)
20258
20333
  });
20259
20334
  }
@@ -20269,7 +20344,7 @@ app11.post("/", async (c) => {
20269
20344
  );
20270
20345
  if (!decision.allowed) return c.json({ ok: true }, 200);
20271
20346
  const pollKey = telegramChannelKey(botId, decision.senderId);
20272
- const pollScope = entry2.role === "admin" ? "admin" : "public";
20347
+ const pollScope = entry3.role === "admin" ? "admin" : "public";
20273
20348
  const pollSession = telegramAdminSessionId(accountId, decision.senderId, botId);
20274
20349
  const pollAt = (/* @__PURE__ */ new Date()).toISOString();
20275
20350
  const pollBody = `[poll vote: poll=${decision.pollId} options=${options}]`;
@@ -20287,16 +20362,16 @@ app11.post("/", async (c) => {
20287
20362
  origin: "inbound",
20288
20363
  createdAt: pollAt
20289
20364
  });
20290
- const pollActive = entryActive(entry2, accountDir, botId);
20365
+ const pollActive = entryActive(entry3, accountDir, botId);
20291
20366
  console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${pollActive}`);
20292
20367
  raiseChannelTurn({
20293
20368
  accountId,
20294
20369
  accountDir,
20295
20370
  botId,
20296
- botToken: entry2.token,
20297
- role: entry2.role,
20371
+ botToken: entry3.token,
20372
+ role: entry3.role,
20298
20373
  agentSlug,
20299
- specialist: entry2.role === "specialist" ? entry2.specialist : null,
20374
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
20300
20375
  active: pollActive,
20301
20376
  senderId: decision.senderId,
20302
20377
  senderDisplay: decision.senderDisplay,
@@ -20320,7 +20395,7 @@ app11.post("/", async (c) => {
20320
20395
  `${TAG34} op=reaction-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
20321
20396
  );
20322
20397
  if (!decision.allowed) return c.json({ ok: true }, 200);
20323
- const rScope = entry2.role === "admin" ? "admin" : "public";
20398
+ const rScope = entry3.role === "admin" ? "admin" : "public";
20324
20399
  const rSession = telegramAdminSessionId(accountId, rSubject, botId);
20325
20400
  const rAt = (/* @__PURE__ */ new Date()).toISOString();
20326
20401
  const rTarget = quotedBody === null ? `message ${decision.messageId} \u2014 not held in this conversation` : `"${quotedBody}"`;
@@ -20339,16 +20414,16 @@ app11.post("/", async (c) => {
20339
20414
  origin: "inbound",
20340
20415
  createdAt: rAt
20341
20416
  });
20342
- const rActive = entryActive(entry2, accountDir, botId);
20417
+ const rActive = entryActive(entry3, accountDir, botId);
20343
20418
  console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${rActive}`);
20344
20419
  raiseChannelTurn({
20345
20420
  accountId,
20346
20421
  accountDir,
20347
20422
  botId,
20348
- botToken: entry2.token,
20349
- role: entry2.role,
20423
+ botToken: entry3.token,
20424
+ role: entry3.role,
20350
20425
  agentSlug,
20351
- specialist: entry2.role === "specialist" ? entry2.specialist : null,
20426
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
20352
20427
  active: rActive,
20353
20428
  senderId: decision.senderId,
20354
20429
  senderDisplay: decision.senderDisplay,
@@ -20386,7 +20461,7 @@ app11.post("/", async (c) => {
20386
20461
  );
20387
20462
  if (!decision.allowed) return c.json({ ok: true }, 200);
20388
20463
  const sKey = telegramChannelKey(botId, decision.senderId);
20389
- const sScope = entry2.role === "admin" ? "admin" : "public";
20464
+ const sScope = entry3.role === "admin" ? "admin" : "public";
20390
20465
  const sSession = telegramAdminSessionId(accountId, decision.senderId, botId);
20391
20466
  const sAt = (/* @__PURE__ */ new Date()).toISOString();
20392
20467
  const sBody = `[subscription ${decision.state}: payload=${decision.payload}]`;
@@ -20404,16 +20479,16 @@ app11.post("/", async (c) => {
20404
20479
  origin: "inbound",
20405
20480
  createdAt: sAt
20406
20481
  });
20407
- const sActive = entryActive(entry2, accountDir, botId);
20482
+ const sActive = entryActive(entry3, accountDir, botId);
20408
20483
  console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${sActive}`);
20409
20484
  raiseChannelTurn({
20410
20485
  accountId,
20411
20486
  accountDir,
20412
20487
  botId,
20413
- botToken: entry2.token,
20414
- role: entry2.role,
20488
+ botToken: entry3.token,
20489
+ role: entry3.role,
20415
20490
  agentSlug,
20416
- specialist: entry2.role === "specialist" ? entry2.specialist : null,
20491
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
20417
20492
  active: sActive,
20418
20493
  senderId: decision.senderId,
20419
20494
  senderDisplay: decision.senderDisplay,
@@ -20432,7 +20507,7 @@ app11.post("/", async (c) => {
20432
20507
  `${TAG34} op=poll-closed botId=${botId} pollId=${logValue2(decision.pollId)} options=${decision.options.length} totalVotes=${decision.totalVotes} targetHeld=${where === null ? "no" : "yes"}`
20433
20508
  );
20434
20509
  if (where === null) return c.json({ ok: true }, 200);
20435
- const plScope = entry2.role === "admin" ? "admin" : "public";
20510
+ const plScope = entry3.role === "admin" ? "admin" : "public";
20436
20511
  const plSession = telegramAdminSessionId(accountId, where.senderId, botId);
20437
20512
  const plAt = (/* @__PURE__ */ new Date()).toISOString();
20438
20513
  const tally = decision.options.map((o) => `${o.text}:${o.votes}`).join(", ") || "no options";
@@ -20451,16 +20526,16 @@ app11.post("/", async (c) => {
20451
20526
  origin: "inbound",
20452
20527
  createdAt: plAt
20453
20528
  });
20454
- const plActive = entryActive(entry2, accountDir, botId);
20529
+ const plActive = entryActive(entry3, accountDir, botId);
20455
20530
  console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${plActive}`);
20456
20531
  raiseChannelTurn({
20457
20532
  accountId,
20458
20533
  accountDir,
20459
20534
  botId,
20460
- botToken: entry2.token,
20461
- role: entry2.role,
20535
+ botToken: entry3.token,
20536
+ role: entry3.role,
20462
20537
  agentSlug,
20463
- specialist: entry2.role === "specialist" ? entry2.specialist : null,
20538
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
20464
20539
  active: plActive,
20465
20540
  senderId: where.senderId,
20466
20541
  chatId: Number(where.chatId),
@@ -20489,7 +20564,7 @@ app11.post("/", async (c) => {
20489
20564
  console.error(
20490
20565
  `${TAG34} op=managed-bot botId=${botId} event=${event} newBotId=${newBotId} creator=${decision.creatorId ?? "none"}`
20491
20566
  );
20492
- const fetched = await getManagedBotToken(entry2.token, Number(newBotId));
20567
+ const fetched = await getManagedBotToken(entry3.token, Number(newBotId));
20493
20568
  console.error(
20494
20569
  `${PROVISION_TAG} op=provision-token botId=${botId} newBotId=${newBotId} fetched=${fetched.ok ? "yes" : "no"} reason=${fetched.ok ? "ok" : "api-error"}`
20495
20570
  );
@@ -20499,7 +20574,7 @@ app11.post("/", async (c) => {
20499
20574
  accountDir,
20500
20575
  existing ? { ...existing, token: fetched.token, managed: true } : { id: newBotId, token: fetched.token, role: "unbound", managed: true }
20501
20576
  );
20502
- const restricted = await setManagedBotAccessSettings(entry2.token, Number(newBotId), true, void 0);
20577
+ const restricted = await setManagedBotAccessSettings(entry3.token, Number(newBotId), true, void 0);
20503
20578
  console.error(
20504
20579
  `${PROVISION_TAG} op=provision-persist botId=${botId} newBotId=${newBotId} written=${persisted.ok ? "yes" : "no"} restricted=${restricted.ok ? "yes" : "no"}`
20505
20580
  );
@@ -20515,7 +20590,7 @@ app11.post("/", async (c) => {
20515
20590
  accountId,
20516
20591
  accountDir,
20517
20592
  botId,
20518
- botToken: entry2.token,
20593
+ botToken: entry3.token,
20519
20594
  // `authorised` above already established this entry is role 'admin', so
20520
20595
  // the admin agent is the one that runs and it is active by construction.
20521
20596
  role: "admin",
@@ -20588,7 +20663,7 @@ app11.post("/", async (c) => {
20588
20663
  });
20589
20664
  }
20590
20665
  const bizMedia = await downloadTelegramMedia({
20591
- token: entry2.token,
20666
+ token: entry3.token,
20592
20667
  botId,
20593
20668
  items: decision.media
20594
20669
  });
@@ -20628,7 +20703,7 @@ app11.post("/", async (c) => {
20628
20703
  return;
20629
20704
  }
20630
20705
  const sent = await sendTelegramText(
20631
- entry2.token,
20706
+ entry3.token,
20632
20707
  decision.chatId,
20633
20708
  replyText,
20634
20709
  void 0,
@@ -20657,7 +20732,7 @@ app11.post("/", async (c) => {
20657
20732
  accountId,
20658
20733
  accountDir,
20659
20734
  botId,
20660
- botToken: entry2.token,
20735
+ botToken: entry3.token,
20661
20736
  agentSlug,
20662
20737
  senderId: decision.senderId,
20663
20738
  // Task 2615 — load-bearing. The gateway keys the hub, the reply closure,
@@ -20707,7 +20782,7 @@ app11.post("/", async (c) => {
20707
20782
  console.error(`${TAG34} op=access botId=${botId} senderId=${senderId} allowed=true reason=${decision.reason}`);
20708
20783
  const subject = telegramConversationSubject(decision.chatType, chatId, senderId);
20709
20784
  const channelKey = telegramChannelKey(botId, subject);
20710
- const scope = entry2.role === "public" ? "public" : "admin";
20785
+ const scope = entry3.role === "public" ? "public" : "admin";
20711
20786
  const sessionId = telegramAdminSessionId(accountId, subject, botId);
20712
20787
  const inboundAt = (/* @__PURE__ */ new Date()).toISOString();
20713
20788
  const editSuffix = decision.isEdit ? `:edit:${decision.editDate}` : "";
@@ -20788,10 +20863,10 @@ app11.post("/", async (c) => {
20788
20863
  });
20789
20864
  }
20790
20865
  }
20791
- const active = entryActive(entry2, accountDir, botId, (rosterValid) => {
20792
- if (entry2.role !== "specialist") return;
20866
+ const active = entryActive(entry3, accountDir, botId, (rosterValid) => {
20867
+ if (entry3.role !== "specialist") return;
20793
20868
  console.error(
20794
- `${TAG34} op=specialist-inbound botId=${botId} specialist=${logValue2(entry2.specialist)} senderId=${senderId} sessionId=${sessionId} rosterValid=${rosterValid ? "yes" : "no"}`
20869
+ `${TAG34} op=specialist-inbound botId=${botId} specialist=${logValue2(entry3.specialist)} senderId=${senderId} sessionId=${sessionId} rosterValid=${rosterValid ? "yes" : "no"}`
20795
20870
  );
20796
20871
  });
20797
20872
  console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${active}`);
@@ -20806,7 +20881,7 @@ app11.post("/", async (c) => {
20806
20881
  }
20807
20882
  const replyThreadId = decision.threadId ?? void 0;
20808
20883
  const reply = async (replyText) => {
20809
- const sent = await sendTelegramText(entry2.token, chatId, replyText, replyThreadId);
20884
+ const sent = await sendTelegramText(entry3.token, chatId, replyText, replyThreadId);
20810
20885
  if (sent.ok) {
20811
20886
  const at = (/* @__PURE__ */ new Date()).toISOString();
20812
20887
  appendMessage2(accountId, {
@@ -20826,7 +20901,7 @@ app11.post("/", async (c) => {
20826
20901
  console.error(`${TAG34} op=reply-sent botId=${botId} ok=true`);
20827
20902
  } else console.error(`${TAG34} op=reply-dropped botId=${botId} reason=${sent.error}`);
20828
20903
  };
20829
- const media = await downloadTelegramMedia({ token: entry2.token, botId, items: decision.media });
20904
+ const media = await downloadTelegramMedia({ token: entry3.token, botId, items: decision.media });
20830
20905
  if (media.length > 0) {
20831
20906
  const attachmentIds = await storeTelegramServable({
20832
20907
  accountId,
@@ -20952,7 +21027,7 @@ app11.post("/", async (c) => {
20952
21027
  accountId,
20953
21028
  accountDir,
20954
21029
  botId,
20955
- botToken: entry2.token,
21030
+ botToken: entry3.token,
20956
21031
  agentSlug,
20957
21032
  senderId,
20958
21033
  // Task 2616 — the name beside the id on the turn's provenance line.
@@ -20962,10 +21037,10 @@ app11.post("/", async (c) => {
20962
21037
  // room reach one channel server rather than opening five sessions whose
20963
21038
  // reply closures all target the same chat. A DM subject IS the sender id.
20964
21039
  conversationSubject: subject,
20965
- role: entry2.role,
21040
+ role: entry3.role,
20966
21041
  personId: null,
20967
21042
  // Task 2619 — the card this bot runs as, null on every other role.
20968
- specialist: entry2.role === "specialist" ? entry2.specialist : null,
21043
+ specialist: entry3.role === "specialist" ? entry3.specialist : null,
20969
21044
  chatId: String(chatId),
20970
21045
  text: turnText,
20971
21046
  media: heard.media,
@@ -21369,9 +21444,9 @@ function computeAdminStoreDivergence(input) {
21369
21444
  result.errors.push({ source: input.accountsDir, detail: err instanceof Error ? err.message : String(err) });
21370
21445
  return result;
21371
21446
  }
21372
- for (const entry2 of entries2) {
21373
- if (entry2.startsWith(".")) continue;
21374
- const accountDir = join42(input.accountsDir, entry2);
21447
+ for (const entry3 of entries2) {
21448
+ if (entry3.startsWith(".")) continue;
21449
+ const accountDir = join42(input.accountsDir, entry3);
21375
21450
  try {
21376
21451
  if (!statSync20(accountDir).isDirectory()) continue;
21377
21452
  } catch {
@@ -23508,12 +23583,12 @@ function authoredAgentFiles(accountDir) {
23508
23583
  const dir = resolve28(pluginsDir, plugin, "agents");
23509
23584
  if (!existsSync38(dir)) continue;
23510
23585
  try {
23511
- for (const entry2 of readdirSync24(dir)) {
23512
- if (!entry2.endsWith(".md")) continue;
23513
- const path = resolve28(dir, entry2);
23514
- const paths = byName.get(entry2);
23586
+ for (const entry3 of readdirSync24(dir)) {
23587
+ if (!entry3.endsWith(".md")) continue;
23588
+ const path = resolve28(dir, entry3);
23589
+ const paths = byName.get(entry3);
23515
23590
  if (paths) paths.push(path);
23516
- else byName.set(entry2, [path]);
23591
+ else byName.set(entry3, [path]);
23517
23592
  }
23518
23593
  } catch (err) {
23519
23594
  console.error(`[admin/agents] op=authored-scan read-failed plugin=${plugin} error="${err}"`);
@@ -23582,7 +23657,7 @@ function listShipped(accountDir, riskByTool, disabled, riskSurfaceFailed, author
23582
23657
  for (const dir of dirs) {
23583
23658
  if (!existsSync38(dir)) continue;
23584
23659
  try {
23585
- for (const entry2 of readdirSync24(dir)) if (entry2.endsWith(".md")) names.add(entry2);
23660
+ for (const entry3 of readdirSync24(dir)) if (entry3.endsWith(".md")) names.add(entry3);
23586
23661
  } catch (err) {
23587
23662
  console.error(`[admin/agents] op=list-shipped read-failed dir=${dir} error="${err}"`);
23588
23663
  }
@@ -23676,19 +23751,19 @@ function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
23676
23751
  } catch {
23677
23752
  continue;
23678
23753
  }
23679
- for (const entry2 of entries2) {
23680
- if (!entry2.isFile() || !entry2.name.endsWith(".md")) continue;
23754
+ for (const entry3 of entries2) {
23755
+ if (!entry3.isFile() || !entry3.name.endsWith(".md")) continue;
23681
23756
  try {
23682
- const { fm } = splitFrontmatter(readFileSync38(resolve28(agentsDirP, entry2.name), "utf-8"));
23757
+ const { fm } = splitFrontmatter(readFileSync38(resolve28(agentsDirP, entry3.name), "utf-8"));
23683
23758
  if (!fm.name) {
23684
23759
  specialistsSkipped++;
23685
- console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry2.name}`);
23760
+ console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry3.name}`);
23686
23761
  continue;
23687
23762
  }
23688
23763
  const specialistTools = parseToolsLine(fm.tools);
23689
23764
  const r = classify(specialistTools, riskByTool, riskSurfaceFailed);
23690
23765
  specialists.push({
23691
- slug: entry2.name.replace(/\.md$/, ""),
23766
+ slug: entry3.name.replace(/\.md$/, ""),
23692
23767
  displayName: fm.name,
23693
23768
  kind: "specialist",
23694
23769
  origin: "specialist",
@@ -23711,11 +23786,11 @@ function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
23711
23786
  // have the surface claim an agent is stopped while it is still being
23712
23787
  // spawned. Absence from the live dir alone is also not the test: a
23713
23788
  // specialist that was never activated was never switched off either.
23714
- disabled: existsSync38(resolve28(accountDir, ...QUARANTINE_DIR, entry2.name)) && !existsSync38(resolve28(accountDir, ...SPECIALISTS_DIR, entry2.name))
23789
+ disabled: existsSync38(resolve28(accountDir, ...QUARANTINE_DIR, entry3.name)) && !existsSync38(resolve28(accountDir, ...SPECIALISTS_DIR, entry3.name))
23715
23790
  });
23716
23791
  } catch {
23717
23792
  specialistsSkipped++;
23718
- console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry2.name}`);
23793
+ console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry3.name}`);
23719
23794
  }
23720
23795
  }
23721
23796
  }
@@ -23742,16 +23817,16 @@ app22.get("/", requireAdminSession, (c) => {
23742
23817
  if (existsSync38(agentsDir)) {
23743
23818
  try {
23744
23819
  const entries2 = readdirSync24(agentsDir, { withFileTypes: true });
23745
- for (const entry2 of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
23746
- if (!entry2.isDirectory()) continue;
23747
- if (entry2.name === "admin") continue;
23748
- const configPath3 = resolve28(agentsDir, entry2.name, "config.json");
23820
+ for (const entry3 of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
23821
+ if (!entry3.isDirectory()) continue;
23822
+ if (entry3.name === "admin") continue;
23823
+ const configPath3 = resolve28(agentsDir, entry3.name, "config.json");
23749
23824
  if (!existsSync38(configPath3)) continue;
23750
23825
  try {
23751
23826
  const config = JSON.parse(readFileSync38(configPath3, "utf-8"));
23752
23827
  agents.push({
23753
- slug: entry2.name,
23754
- displayName: config.displayName ?? entry2.name,
23828
+ slug: entry3.name,
23829
+ displayName: config.displayName ?? entry3.name,
23755
23830
  status: config.status ?? "unknown",
23756
23831
  kind: "public",
23757
23832
  origin: "public",
@@ -23764,7 +23839,7 @@ app22.get("/", requireAdminSession, (c) => {
23764
23839
  });
23765
23840
  } catch {
23766
23841
  skipped++;
23767
- console.error(`[admin/agents] failed to parse config.json for agent "${entry2.name}" \u2014 skipping`);
23842
+ console.error(`[admin/agents] failed to parse config.json for agent "${entry3.name}" \u2014 skipping`);
23768
23843
  }
23769
23844
  }
23770
23845
  } catch (err) {
@@ -24418,7 +24493,7 @@ app23.get("/", requireAdminSession, async (c) => {
24418
24493
  if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
24419
24494
  try {
24420
24495
  const flushed = await listAdminSessions(accountId, userId, 20);
24421
- const sessions3 = flushed.map((r) => ({
24496
+ const sessions4 = flushed.map((r) => ({
24422
24497
  sessionId: r.sessionId,
24423
24498
  cacheKey: null,
24424
24499
  name: r.name,
@@ -24426,15 +24501,15 @@ app23.get("/", requireAdminSession, async (c) => {
24426
24501
  phase: "flushed",
24427
24502
  channel: r.channel
24428
24503
  })).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).slice(0, 20);
24429
- const channelCounts = sessions3.reduce((acc, s) => {
24504
+ const channelCounts = sessions4.reduce((acc, s) => {
24430
24505
  const k = s.channel ?? "unknown";
24431
24506
  acc[k] = (acc[k] ?? 0) + 1;
24432
24507
  return acc;
24433
24508
  }, {});
24434
24509
  console.error(
24435
- `[conversations-list] render rows=${sessions3.length} channels=${JSON.stringify(channelCounts)}`
24510
+ `[conversations-list] render rows=${sessions4.length} channels=${JSON.stringify(channelCounts)}`
24436
24511
  );
24437
- return c.json({ sessions: sessions3 });
24512
+ return c.json({ sessions: sessions4 });
24438
24513
  } catch (err) {
24439
24514
  console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
24440
24515
  return c.json({ error: "Failed to fetch sessions" }, 500);
@@ -26099,14 +26174,14 @@ async function walkSubtree(root, dataRoot, out) {
26099
26174
  return { clean: false };
26100
26175
  }
26101
26176
  let clean = true;
26102
- for (const entry2 of entries2) {
26103
- if (entry2.isSymbolicLink()) continue;
26104
- if (entry2.isDirectory() && entry2.name === ".uploads-tmp") continue;
26105
- const abs = join49(root, entry2.name);
26106
- if (entry2.isDirectory()) {
26177
+ for (const entry3 of entries2) {
26178
+ if (entry3.isSymbolicLink()) continue;
26179
+ if (entry3.isDirectory() && entry3.name === ".uploads-tmp") continue;
26180
+ const abs = join49(root, entry3.name);
26181
+ if (entry3.isDirectory()) {
26107
26182
  const sub = await walkSubtree(abs, dataRoot, out);
26108
26183
  if (!sub.clean) clean = false;
26109
- } else if (entry2.isFile()) {
26184
+ } else if (entry3.isFile()) {
26110
26185
  try {
26111
26186
  const st = await fsp.stat(abs);
26112
26187
  out.push({
@@ -26395,14 +26470,14 @@ function buildZip(entries2) {
26395
26470
  const localChunks = [];
26396
26471
  const centralChunks = [];
26397
26472
  let offset = 0;
26398
- for (const entry2 of entries2) {
26399
- const nameBytes = Buffer.from(entry2.name, "utf8");
26400
- const crc = crc32(entry2.data);
26401
- const uncompressedSize = entry2.data.length;
26402
- const deflated = deflateRawSync(entry2.data);
26473
+ for (const entry3 of entries2) {
26474
+ const nameBytes = Buffer.from(entry3.name, "utf8");
26475
+ const crc = crc32(entry3.data);
26476
+ const uncompressedSize = entry3.data.length;
26477
+ const deflated = deflateRawSync(entry3.data);
26403
26478
  const useDeflate = deflated.length < uncompressedSize;
26404
26479
  const method = useDeflate ? 8 : 0;
26405
- const payload = useDeflate ? deflated : entry2.data;
26480
+ const payload = useDeflate ? deflated : entry3.data;
26406
26481
  const compressedSize = payload.length;
26407
26482
  const localHeader = Buffer.alloc(30);
26408
26483
  localHeader.writeUInt32LE(LOCAL_SIG, 0);
@@ -26454,13 +26529,13 @@ function buildZip(entries2) {
26454
26529
  }
26455
26530
 
26456
26531
  // server/lib/account-root-groups.ts
26457
- var import_dist8 = __toESM(require_dist6(), 1);
26532
+ var import_dist9 = __toESM(require_dist7(), 1);
26458
26533
  function classifyAccountRoot(schemaMd, rootEntries) {
26459
- const regions = (0, import_dist8.parseSchemaRegions)(schemaMd);
26534
+ const regions = (0, import_dist9.parseSchemaRegions)(schemaMd);
26460
26535
  if (!regions.parsed) {
26461
26536
  return { home: [], system: [...rootEntries], unknown: [], parsed: false, reason: regions.reason };
26462
26537
  }
26463
- const homeOrder = [...regions.ontologyRoots, ...import_dist8.HOME_FIXED].filter((d, i, a) => a.indexOf(d) === i);
26538
+ const homeOrder = [...regions.ontologyRoots, ...import_dist9.HOME_FIXED].filter((d, i, a) => a.indexOf(d) === i);
26464
26539
  const homeSet = new Set(homeOrder);
26465
26540
  const present = new Set(rootEntries);
26466
26541
  const home = homeOrder.filter((d) => present.has(d));
@@ -26528,47 +26603,47 @@ async function servableFilesIn(dirAbs, uuid) {
26528
26603
  }
26529
26604
  return out;
26530
26605
  }
26531
- async function enrich(absolute, entry2, accountNames) {
26532
- if (entry2.kind === "directory" && UUID_RE3.test(entry2.name)) {
26533
- const dirAbs = join50(absolute, entry2.name);
26534
- const meta = await readMeta2(dirAbs, entry2.name);
26606
+ async function enrich(absolute, entry3, accountNames) {
26607
+ if (entry3.kind === "directory" && UUID_RE3.test(entry3.name)) {
26608
+ const dirAbs = join50(absolute, entry3.name);
26609
+ const meta = await readMeta2(dirAbs, entry3.name);
26535
26610
  if (meta?.filename) {
26536
- const servable = await servableFilesIn(dirAbs, entry2.name);
26611
+ const servable = await servableFilesIn(dirAbs, entry3.name);
26537
26612
  if (servable.length === 1) {
26538
26613
  const innerName = servable[0];
26539
26614
  let size = null;
26540
- let modifiedAt = entry2.modifiedAt;
26615
+ let modifiedAt = entry3.modifiedAt;
26541
26616
  try {
26542
26617
  const st = await stat6(join50(dirAbs, innerName));
26543
26618
  size = st.size;
26544
26619
  modifiedAt = st.mtime.toISOString();
26545
26620
  } catch {
26546
26621
  }
26547
- entry2.kind = "file";
26548
- entry2.displayName = meta.filename;
26549
- entry2.mimeType = meta.mimeType;
26550
- entry2.sizeBytes = size;
26551
- entry2.modifiedAt = modifiedAt;
26552
- entry2.entryPath = `${entry2.name}/${innerName}`;
26622
+ entry3.kind = "file";
26623
+ entry3.displayName = meta.filename;
26624
+ entry3.mimeType = meta.mimeType;
26625
+ entry3.sizeBytes = size;
26626
+ entry3.modifiedAt = modifiedAt;
26627
+ entry3.entryPath = `${entry3.name}/${innerName}`;
26553
26628
  return "flattened";
26554
26629
  }
26555
- entry2.displayName = meta.filename;
26630
+ entry3.displayName = meta.filename;
26556
26631
  return "kept-as-dir";
26557
26632
  }
26558
- const accountName = accountNames.get(entry2.name);
26633
+ const accountName = accountNames.get(entry3.name);
26559
26634
  if (accountName) {
26560
- entry2.displayName = accountName;
26635
+ entry3.displayName = accountName;
26561
26636
  }
26562
26637
  return null;
26563
26638
  }
26564
- if (entry2.kind === "file") {
26565
- const dot = entry2.name.lastIndexOf(".");
26566
- const base = dot === -1 ? entry2.name : entry2.name.slice(0, dot);
26639
+ if (entry3.kind === "file") {
26640
+ const dot = entry3.name.lastIndexOf(".");
26641
+ const base = dot === -1 ? entry3.name : entry3.name.slice(0, dot);
26567
26642
  if (UUID_RE3.test(base)) {
26568
26643
  const meta = await readMeta2(absolute, base);
26569
26644
  if (meta?.filename) {
26570
- entry2.displayName = meta.filename;
26571
- entry2.mimeType = meta.mimeType;
26645
+ entry3.displayName = meta.filename;
26646
+ entry3.mimeType = meta.mimeType;
26572
26647
  }
26573
26648
  }
26574
26649
  }
@@ -27603,7 +27678,7 @@ app28.post("/rename", requireAdminSession, async (c) => {
27603
27678
  var files_default = app28;
27604
27679
 
27605
27680
  // ../lib/graph-search/src/index.ts
27606
- var import_dist9 = __toESM(require_dist7());
27681
+ var import_dist10 = __toESM(require_dist8());
27607
27682
  import { int } from "neo4j-driver";
27608
27683
 
27609
27684
  // ../lib/graph-search/src/rrf-fusion.ts
@@ -27853,7 +27928,7 @@ async function bm25Only(session, params) {
27853
27928
  ${scopeClause}
27854
27929
  ${agentClause}
27855
27930
  ${labelClause}
27856
- AND ${(0, import_dist9.notTrashed)("node")}
27931
+ AND ${(0, import_dist10.notTrashed)("node")}
27857
27932
  ${kwClause}
27858
27933
  RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
27859
27934
  ORDER BY score DESC
@@ -28015,7 +28090,7 @@ async function hybrid(session, embed2, params) {
28015
28090
  WHERE node.accountId = $accountId
28016
28091
  ${scopeClause}
28017
28092
  ${agentClause}
28018
- AND ${(0, import_dist9.notTrashed)("node")}
28093
+ AND ${(0, import_dist10.notTrashed)("node")}
28019
28094
  ${keywordClause}
28020
28095
  RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
28021
28096
  ORDER BY score DESC
@@ -28114,7 +28189,7 @@ async function hybrid(session, embed2, params) {
28114
28189
  const propResult = await session.run(
28115
28190
  `MATCH (node)
28116
28191
  WHERE node.accountId = $accountId
28117
- AND ${(0, import_dist9.notTrashed)("node")}
28192
+ AND ${(0, import_dist10.notTrashed)("node")}
28118
28193
  AND node.keywords IS NOT NULL
28119
28194
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
28120
28195
  ${propScope.clause}
@@ -28166,7 +28241,7 @@ async function hybrid(session, embed2, params) {
28166
28241
  const propResult = await session.run(
28167
28242
  `MATCH (node)
28168
28243
  WHERE node.accountId = $accountId
28169
- AND ${(0, import_dist9.notTrashed)("node")}
28244
+ AND ${(0, import_dist10.notTrashed)("node")}
28170
28245
  AND node.keywords IS NOT NULL
28171
28246
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
28172
28247
  ${propScope.clause}
@@ -28273,7 +28348,7 @@ async function hybrid(session, embed2, params) {
28273
28348
  `UNWIND $nodeIds AS nid
28274
28349
  MATCH (n)-[r]-(related)
28275
28350
  WHERE elementId(n) = nid
28276
- AND ${(0, import_dist9.notTrashed)("related")}
28351
+ AND ${(0, import_dist10.notTrashed)("related")}
28277
28352
  ${expandScopeClause}
28278
28353
  ${expandAgentClause}
28279
28354
  WITH nid, n, r, related
@@ -28501,8 +28576,8 @@ var graph_search_default = app29;
28501
28576
  import neo4j2 from "neo4j-driver";
28502
28577
 
28503
28578
  // app/lib/graph-labels.ts
28504
- var import_dist10 = __toESM(require_dist8(), 1);
28505
- var import_dist11 = __toESM(require_dist8(), 1);
28579
+ var import_dist11 = __toESM(require_dist9(), 1);
28580
+ var import_dist12 = __toESM(require_dist9(), 1);
28506
28581
  var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
28507
28582
  /* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
28508
28583
  );
@@ -28586,14 +28661,14 @@ var EXCLUDED_EDGE_TYPES = Object.freeze(
28586
28661
  ])
28587
28662
  );
28588
28663
  function isKnownLabel(label) {
28589
- return Object.prototype.hasOwnProperty.call(import_dist11.GRAPH_LABEL_COLOURS, label);
28664
+ return Object.prototype.hasOwnProperty.call(import_dist12.GRAPH_LABEL_COLOURS, label);
28590
28665
  }
28591
28666
  function isHiddenByDefault(label) {
28592
28667
  return HIDDEN_BY_DEFAULT_LABELS.has(label);
28593
28668
  }
28594
28669
 
28595
28670
  // server/lib/top-level-labels.ts
28596
- var import_dist12 = __toESM(require_dist8(), 1);
28671
+ var import_dist13 = __toESM(require_dist9(), 1);
28597
28672
  import { readdirSync as readdirSync26, readFileSync as readFileSync41 } from "fs";
28598
28673
  import { join as join51, resolve as resolve33 } from "path";
28599
28674
  var STATIC_TOP_LEVEL_LABELS = Object.freeze(
@@ -28730,7 +28805,7 @@ function getTopLevelLabelAllowlist(opts = {}) {
28730
28805
  const referencesDir = opts.referencesDir ?? resolveReferencesDir();
28731
28806
  const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
28732
28807
  const excluded = [];
28733
- for (const label of import_dist12.ADDITIONAL_BASE_LABELS) {
28808
+ for (const label of import_dist13.ADDITIONAL_BASE_LABELS) {
28734
28809
  if (derived.delete(label)) excluded.push(label);
28735
28810
  }
28736
28811
  const derivedCount = derived.size;
@@ -29982,8 +30057,8 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
29982
30057
  if (!existsSync43(dir)) continue;
29983
30058
  try {
29984
30059
  const entries2 = await readdir5(dir);
29985
- for (const entry2 of entries2) {
29986
- if (entry2.endsWith(".md")) names.add(entry2);
30060
+ for (const entry3 of entries2) {
30061
+ if (entry3.endsWith(".md")) names.add(entry3);
29987
30062
  }
29988
30063
  } catch (err) {
29989
30064
  const message = err instanceof Error ? err.message : String(err);
@@ -34247,8 +34322,8 @@ function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
34247
34322
  } catch {
34248
34323
  return [];
34249
34324
  }
34250
- for (const entry2 of entries2) {
34251
- const cfgPath = resolve42(accountsDir, entry2, "account.json");
34325
+ for (const entry3 of entries2) {
34326
+ const cfgPath = resolve42(accountsDir, entry3, "account.json");
34252
34327
  if (!existsSync50(cfgPath)) continue;
34253
34328
  try {
34254
34329
  const cfg = JSON.parse(readFileSync51(cfgPath, "utf-8"));
@@ -35596,9 +35671,9 @@ function originAllowed(origin, allowlist) {
35596
35671
  try {
35597
35672
  const u = new URL(origin);
35598
35673
  const host = u.host;
35599
- return allowlist.some((entry2) => {
35600
- if (entry2 === host) return true;
35601
- if (entry2.startsWith("*.")) return host.endsWith(entry2.slice(1));
35674
+ return allowlist.some((entry3) => {
35675
+ if (entry3 === host) return true;
35676
+ if (entry3.startsWith("*.")) return host.endsWith(entry3.slice(1));
35602
35677
  return false;
35603
35678
  });
35604
35679
  } catch {
@@ -36875,7 +36950,7 @@ async function runConversationAudit() {
36875
36950
  }
36876
36951
 
36877
36952
  // app/lib/graph-health.ts
36878
- var import_dist14 = __toESM(require_dist9(), 1);
36953
+ var import_dist15 = __toESM(require_dist10(), 1);
36879
36954
  var HOUR_MS2 = 60 * 60 * 1e3;
36880
36955
  function renderLabelTop(rows) {
36881
36956
  return rows.map((b) => {
@@ -36920,7 +36995,7 @@ async function runGraphHealthTick() {
36920
36995
  console.error(
36921
36996
  `[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
36922
36997
  );
36923
- const indexed = [...import_dist14.VECTOR_INDEXED_LABELS];
36998
+ const indexed = [...import_dist15.VECTOR_INDEXED_LABELS];
36924
36999
  const embCount = await session.run(
36925
37000
  `MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
36926
37001
  RETURN count(n) AS total`,
@@ -36994,7 +37069,7 @@ function startGraphHealthTimer() {
36994
37069
  // app/lib/shared-folder-census.ts
36995
37070
  import { existsSync as existsSync54, statSync as statSync29, readdirSync as readdirSync32 } from "fs";
36996
37071
  import { join as join65 } from "path";
36997
- var import_dist15 = __toESM(require_dist(), 1);
37072
+ var import_dist16 = __toESM(require_dist(), 1);
36998
37073
  var TAG64 = "[shared-census]";
36999
37074
  var WIRED_SURFACES = 3;
37000
37075
  function countFiles(dir) {
@@ -37012,7 +37087,7 @@ function countFiles(dir) {
37012
37087
  return n;
37013
37088
  }
37014
37089
  function runSharedFolderCensus(dataRoot = DATA_ROOT) {
37015
- const root = (0, import_dist15.sharedRoot)(dataRoot);
37090
+ const root = (0, import_dist16.sharedRoot)(dataRoot);
37016
37091
  const exists = existsSync54(root);
37017
37092
  let isDir = false;
37018
37093
  if (exists) {
@@ -37060,15 +37135,15 @@ function surveyBrandCredentials(home) {
37060
37135
  return [];
37061
37136
  }
37062
37137
  const rows = [];
37063
- for (const entry2 of entries2.sort()) {
37064
- if (!entry2.startsWith(".")) continue;
37138
+ for (const entry3 of entries2.sort()) {
37139
+ if (!entry3.startsWith(".")) continue;
37065
37140
  let raw;
37066
37141
  try {
37067
- raw = readFileSync57(join66(home, entry2, ".claude", ".credentials.json"), "utf-8");
37142
+ raw = readFileSync57(join66(home, entry3, ".claude", ".credentials.json"), "utf-8");
37068
37143
  } catch {
37069
37144
  continue;
37070
37145
  }
37071
- rows.push({ brand: entry2.slice(1), refreshTokenLen: refreshTokenLength(raw) });
37146
+ rows.push({ brand: entry3.slice(1), refreshTokenLen: refreshTokenLength(raw) });
37072
37147
  }
37073
37148
  return rows;
37074
37149
  }
@@ -37277,8 +37352,8 @@ async function migrateUploads(opts = {}) {
37277
37352
  let moved = 0;
37278
37353
  let skipped = 0;
37279
37354
  try {
37280
- for (const entry2 of topEntries) {
37281
- const name = entry2.name;
37355
+ for (const entry3 of topEntries) {
37356
+ const name = entry3.name;
37282
37357
  if (ACCOUNT_UUID_RE4.test(name)) {
37283
37358
  moved += await relocateTree(
37284
37359
  resolve49(oldRoot, name),
@@ -37378,11 +37453,11 @@ async function collectLiveSidecars(projectsRoot) {
37378
37453
  } catch {
37379
37454
  continue;
37380
37455
  }
37381
- for (const entry2 of entries2) {
37382
- if (entry2.isFile() && SESSION_META_RE.test(entry2.name)) {
37383
- out.push(join67(slugDir, entry2.name));
37384
- } else if (entry2.isDirectory() && entry2.name === "subagents") {
37385
- const subDir = join67(slugDir, entry2.name);
37456
+ for (const entry3 of entries2) {
37457
+ if (entry3.isFile() && SESSION_META_RE.test(entry3.name)) {
37458
+ out.push(join67(slugDir, entry3.name));
37459
+ } else if (entry3.isDirectory() && entry3.name === "subagents") {
37460
+ const subDir = join67(slugDir, entry3.name);
37386
37461
  let subs;
37387
37462
  try {
37388
37463
  subs = await readdir7(subDir, { withFileTypes: true });
@@ -37532,11 +37607,11 @@ async function collectSidecars(projectsRoot) {
37532
37607
  } catch {
37533
37608
  continue;
37534
37609
  }
37535
- for (const entry2 of entries2) {
37536
- if (entry2.isFile() && SESSION_META_RE2.test(entry2.name)) {
37537
- out.push({ path: join68(slugDir, entry2.name), slug: slug.name });
37538
- } else if (entry2.isDirectory() && (entry2.name === "subagents" || entry2.name === "archive")) {
37539
- const subDir = join68(slugDir, entry2.name);
37610
+ for (const entry3 of entries2) {
37611
+ if (entry3.isFile() && SESSION_META_RE2.test(entry3.name)) {
37612
+ out.push({ path: join68(slugDir, entry3.name), slug: slug.name });
37613
+ } else if (entry3.isDirectory() && (entry3.name === "subagents" || entry3.name === "archive")) {
37614
+ const subDir = join68(slugDir, entry3.name);
37540
37615
  let subs;
37541
37616
  try {
37542
37617
  subs = await readdir8(subDir, { withFileTypes: true });
@@ -38465,24 +38540,24 @@ var WaGateway = class {
38465
38540
  for (const h of held) {
38466
38541
  const key2 = hubKey(h.accountId, h.senderId);
38467
38542
  if (this.spawning.has(key2)) continue;
38468
- const entry2 = this.heldSpawnArgs.get(key2);
38469
- if (!entry2) {
38543
+ const entry3 = this.heldSpawnArgs.get(key2);
38544
+ if (!entry3) {
38470
38545
  console.error(
38471
38546
  `[whatsapp-native] op=spawn-retry-skipped senderId=${h.senderId} accountId=${h.accountId} ageMs=${h.oldestAgeMs} queued=${h.count} reason=no-args`
38472
38547
  );
38473
38548
  continue;
38474
38549
  }
38475
- entry2.attempts++;
38550
+ entry3.attempts++;
38476
38551
  this.spawning.add(key2);
38477
38552
  calls++;
38478
38553
  try {
38479
- await this.deps.ensureChannelSession(entry2.args);
38554
+ await this.deps.ensureChannelSession(entry3.args);
38480
38555
  console.error(
38481
- `[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${entry2.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=true`
38556
+ `[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${entry3.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=true`
38482
38557
  );
38483
38558
  } catch (err) {
38484
38559
  console.error(
38485
- `[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${entry2.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=false reason=${err instanceof Error ? err.message : String(err)}`
38560
+ `[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${entry3.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=false reason=${err instanceof Error ? err.message : String(err)}`
38486
38561
  );
38487
38562
  } finally {
38488
38563
  this.spawning.delete(key2);
@@ -39027,8 +39102,8 @@ var InboundHub2 = class {
39027
39102
  if (!s) return null;
39028
39103
  const i = s.inFlight.findIndex((e) => e.payload.messageId === messageId && e.state === "queued");
39029
39104
  if (i < 0) return null;
39030
- const [entry2] = s.inFlight.splice(i, 1);
39031
- return entry2.payload;
39105
+ const [entry3] = s.inFlight.splice(i, 1);
39106
+ return entry3.payload;
39032
39107
  }
39033
39108
  /** Whether a key currently has a live channel server attached. The gateway
39034
39109
  * uses this to decide whether an inbound needs a cold-start spawn/resume. */
@@ -39360,9 +39435,9 @@ var WebchatGateway = class _WebchatGateway {
39360
39435
  * false (and is a no-op) when no prompt with this key+id is open — a late or
39361
39436
  * duplicate click. */
39362
39437
  resolvePermissionVerdict(key2, requestId, behavior) {
39363
- const entry2 = this.pendingPrompts.get(_WebchatGateway.promptKey(key2, requestId));
39364
- if (!entry2) return false;
39365
- entry2.resolve({ behavior });
39438
+ const entry3 = this.pendingPrompts.get(_WebchatGateway.promptKey(key2, requestId));
39439
+ if (!entry3) return false;
39440
+ entry3.resolve({ behavior });
39366
39441
  console.error(`[webchat:perm] op=verdict key=${keyDisplay(key2)} id=${requestId} behavior=${behavior}`);
39367
39442
  return true;
39368
39443
  }
@@ -39890,6 +39965,23 @@ var AnswerAccumulator = class {
39890
39965
  };
39891
39966
 
39892
39967
  // app/lib/channel-delivery/emitter.ts
39968
+ var CHANNEL_WRAPPER2 = "<channel source=";
39969
+ function classifyTurnStart(event) {
39970
+ const content = event.message?.content;
39971
+ if (typeof content === "string") {
39972
+ return content.trimStart().startsWith(CHANNEL_WRAPPER2) ? "inbound" : "injected";
39973
+ }
39974
+ if (!Array.isArray(content)) return "injected";
39975
+ for (const block of content) {
39976
+ if (block?.type === "tool_result") return "tool-result";
39977
+ }
39978
+ for (const block of content) {
39979
+ if (block?.type === "text" && typeof block.text === "string") {
39980
+ return block.text.trimStart().startsWith(CHANNEL_WRAPPER2) ? "inbound" : "injected";
39981
+ }
39982
+ }
39983
+ return "injected";
39984
+ }
39893
39985
  function followerPendingMaxMs() {
39894
39986
  return Number(process.env.CHANNEL_PTY_FOLLOWER_PENDING_MAX_MS ?? String(3e5));
39895
39987
  }
@@ -39905,33 +39997,33 @@ function toolResultText(content) {
39905
39997
  }
39906
39998
  function startEmitter(opts) {
39907
39999
  const abort2 = new AbortController();
39908
- const { entry: entry2, tag } = opts;
40000
+ const { entry: entry3, tag } = opts;
39909
40001
  const answers = new AnswerAccumulator();
39910
40002
  let answersEmitted = 0;
39911
40003
  let closeReason = "stream-end";
39912
40004
  const emitAnswer = async (answer) => {
39913
40005
  answersEmitted += 1;
39914
- if (answer.text.trim()) await fanOut2(entry2.subscribers, answer.text, opts.onError, tag);
40006
+ if (answer.text.trim()) await fanOut2(entry3.subscribers, answer.text, opts.onError, tag);
39915
40007
  if (!opts.onAnswer) return;
39916
- const guarded = { ...answer, text: guardOutbound(entry2.senderId, answer.text) };
40008
+ const guarded = { ...answer, text: guardOutbound(entry3.senderId, answer.text) };
39917
40009
  try {
39918
40010
  await opts.onAnswer(guarded);
39919
40011
  } catch (err) {
39920
40012
  console.error(
39921
- `${tag} answer-error sessionId=${entry2.sessionId.slice(0, 8)} messageId=${answer.messageId} message=${err instanceof Error ? err.message : String(err)}`
40013
+ `${tag} answer-error sessionId=${entry3.sessionId.slice(0, 8)} messageId=${answer.messageId} message=${err instanceof Error ? err.message : String(err)}`
39922
40014
  );
39923
40015
  }
39924
40016
  };
39925
40017
  void (async () => {
39926
40018
  try {
39927
- const sid = entry2.sessionId.slice(0, 8);
40019
+ const sid = entry3.sessionId.slice(0, 8);
39928
40020
  const deadline = Date.now() + followerPendingMaxMs();
39929
40021
  const retryMs = followerRetryMs();
39930
40022
  let res;
39931
40023
  let attempt = 0;
39932
40024
  for (; ; ) {
39933
40025
  res = await fetch(
39934
- managerLogFollowUrl(entry2.sessionId, { boundary: opts.suppressResumeReplay === true }),
40026
+ managerLogFollowUrl(entry3.sessionId, { boundary: opts.suppressResumeReplay === true }),
39935
40027
  { signal: abort2.signal }
39936
40028
  );
39937
40029
  console.error(`${tag} follower-connect sessionId=${sid} status=${res.status}`);
@@ -39982,7 +40074,7 @@ function startEmitter(opts) {
39982
40074
  event = JSON.parse(line);
39983
40075
  } catch (err) {
39984
40076
  console.error(
39985
- `${tag} jsonl-parse-skip sessionId=${entry2.sessionId.slice(0, 8)} bytes=${line.length} message=${err instanceof Error ? err.message : String(err)}`
40077
+ `${tag} jsonl-parse-skip sessionId=${entry3.sessionId.slice(0, 8)} bytes=${line.length} message=${err instanceof Error ? err.message : String(err)}`
39986
40078
  );
39987
40079
  continue;
39988
40080
  }
@@ -40035,7 +40127,7 @@ function startEmitter(opts) {
40035
40127
  }
40036
40128
  }
40037
40129
  }
40038
- if (!suppressing) opts.onTurnStart?.();
40130
+ if (!suppressing) opts.onTurnStart?.(classifyTurnStart(event));
40039
40131
  firedFileTools = [];
40040
40132
  continue;
40041
40133
  }
@@ -40100,7 +40192,7 @@ function startEmitter(opts) {
40100
40192
  } finally {
40101
40193
  if (abort2.signal.aborted) closeReason = "aborted";
40102
40194
  console.error(
40103
- `${tag} op=follower-closed sessionId=${entry2.sessionId.slice(0, 8)} reason=${closeReason} answers=${answersEmitted}`
40195
+ `${tag} op=follower-closed sessionId=${entry3.sessionId.slice(0, 8)} reason=${closeReason} answers=${answersEmitted}`
40104
40196
  );
40105
40197
  opts.onClose();
40106
40198
  }
@@ -40155,7 +40247,7 @@ import { resolve as resolve50 } from "path";
40155
40247
  // app/lib/channel-pty-bridge/file-delivery.ts
40156
40248
  var SEND_USER_FILE = "SendUserFile";
40157
40249
  function makeFileDelivery(opts) {
40158
- const { entry: entry2, tag, channel, sendFile, deferUntilVerdict = false } = opts;
40250
+ const { entry: entry3, tag, channel, sendFile, deferUntilVerdict = false } = opts;
40159
40251
  let failedFiles = [];
40160
40252
  let attempts = 0;
40161
40253
  const pending = /* @__PURE__ */ new Map();
@@ -40182,7 +40274,7 @@ function makeFileDelivery(opts) {
40182
40274
  function unreconciledCall(reason, files, detail) {
40183
40275
  const d = detail ? ` detail="${detail.replace(/\s+/g, " ").slice(0, 120)}"` : "";
40184
40276
  console.error(
40185
- `${tag} file-delivery-unreconciled sender=${entry2.senderId} sessionId=${entry2.sessionId.slice(0, 8)} tool=${SEND_USER_FILE} reason=${reason} files=${files}${d}`
40277
+ `${tag} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${entry3.sessionId.slice(0, 8)} tool=${SEND_USER_FILE} reason=${reason} files=${files}${d}`
40186
40278
  );
40187
40279
  }
40188
40280
  const handler = {
@@ -40203,10 +40295,10 @@ function makeFileDelivery(opts) {
40203
40295
  const tried = attempts;
40204
40296
  failedFiles = [];
40205
40297
  attempts = 0;
40206
- const sid = entry2.sessionId.slice(0, 8);
40298
+ const sid = entry3.sessionId.slice(0, 8);
40207
40299
  for (const file of failed2) {
40208
40300
  console.error(
40209
- `${tag} file-delivery-unreconciled sender=${entry2.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
40301
+ `${tag} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
40210
40302
  );
40211
40303
  }
40212
40304
  if (deferUntilVerdict) {
@@ -40216,7 +40308,7 @@ function makeFileDelivery(opts) {
40216
40308
  }
40217
40309
  if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
40218
40310
  console.error(
40219
- `${tag} file-delivery-unreconciled sender=${entry2.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
40311
+ `${tag} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
40220
40312
  );
40221
40313
  }
40222
40314
  }
@@ -40245,43 +40337,43 @@ var TAG66 = "[webchat-adaptor]";
40245
40337
  function platformRoot2() {
40246
40338
  return process.env.MAXY_PLATFORM_ROOT || "";
40247
40339
  }
40248
- function makeWebchatSendFile(entry2) {
40340
+ function makeWebchatSendFile(entry3) {
40249
40341
  return async (filePath) => {
40250
- if (!entry2.accountId) {
40251
- console.error(`${TAG66} file-delivery reject reason=no-account sender=${entry2.senderId}`);
40342
+ if (!entry3.accountId) {
40343
+ console.error(`${TAG66} file-delivery reject reason=no-account sender=${entry3.senderId}`);
40252
40344
  return { ok: false, error: "no-account" };
40253
40345
  }
40254
- const accountDir = resolve50(platformRoot2(), "..", "data/accounts", entry2.accountId);
40346
+ const accountDir = resolve50(platformRoot2(), "..", "data/accounts", entry3.accountId);
40255
40347
  try {
40256
40348
  const resolved = realpathSync9(filePath);
40257
40349
  const accountResolved = realpathSync9(accountDir);
40258
40350
  if (!resolved.startsWith(accountResolved + "/")) {
40259
- console.error(`${TAG66} file-delivery reject reason=outside_account_directory sender=${entry2.senderId}`);
40351
+ console.error(`${TAG66} file-delivery reject reason=outside_account_directory sender=${entry3.senderId}`);
40260
40352
  return { ok: false, error: "outside-account" };
40261
40353
  }
40262
40354
  return { ok: true };
40263
40355
  } catch (err) {
40264
40356
  const code = err.code;
40265
40357
  console.error(
40266
- `${TAG66} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry2.senderId}`
40358
+ `${TAG66} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry3.senderId}`
40267
40359
  );
40268
40360
  return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
40269
40361
  }
40270
40362
  };
40271
40363
  }
40272
- function makeWebchatFileDelivery(entry2) {
40364
+ function makeWebchatFileDelivery(entry3) {
40273
40365
  return makeFileDelivery({
40274
- entry: entry2,
40366
+ entry: entry3,
40275
40367
  tag: TAG66,
40276
40368
  channel: "webchat",
40277
- sendFile: makeWebchatSendFile(entry2),
40369
+ sendFile: makeWebchatSendFile(entry3),
40278
40370
  deferUntilVerdict: true
40279
40371
  });
40280
40372
  }
40281
40373
 
40282
40374
  // app/lib/webchat/gateway/native-file-follower.ts
40283
40375
  function startWebchatNativeFileFollower(input) {
40284
- const entry2 = {
40376
+ const entry3 = {
40285
40377
  sessionId: input.sessionId,
40286
40378
  role: "admin",
40287
40379
  channel: "webchat",
@@ -40300,7 +40392,7 @@ function startWebchatNativeFileFollower(input) {
40300
40392
  replyTarget: null
40301
40393
  };
40302
40394
  return startEmitter({
40303
- entry: entry2,
40395
+ entry: entry3,
40304
40396
  tag: "[webchat-adaptor]",
40305
40397
  // Task 2557 — the emitter has always had this channel and no follower ever
40306
40398
  // passed one, so every cause it reported was discarded. The close line
@@ -40308,7 +40400,7 @@ function startWebchatNativeFileFollower(input) {
40308
40400
  onError: (reason) => {
40309
40401
  console.error(`[webchat-adaptor] op=follower-error sessionId=${input.sessionId.slice(0, 8)} reason=${reason}`);
40310
40402
  },
40311
- fileDelivery: makeWebchatFileDelivery(entry2),
40403
+ fileDelivery: makeWebchatFileDelivery(entry3),
40312
40404
  // A resumed session's JSONL already holds prior SendUserFile tool_uses;
40313
40405
  // suppress replay so historical calls are not re-reconciled on attach.
40314
40406
  suppressResumeReplay: true,
@@ -40357,29 +40449,29 @@ var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
40357
40449
  function platformRoot3() {
40358
40450
  return process.env.MAXY_PLATFORM_ROOT || "";
40359
40451
  }
40360
- function makeWhatsAppSendFile(entry2, maxyAccountId) {
40452
+ function makeWhatsAppSendFile(entry3, maxyAccountId) {
40361
40453
  return async (filePath, caption) => {
40362
40454
  const result = await sendWhatsAppDocument({
40363
- to: entry2.senderId,
40455
+ to: entry3.senderId,
40364
40456
  filePath,
40365
40457
  caption,
40366
- accountId: entry2.accountId,
40458
+ accountId: entry3.accountId,
40367
40459
  maxyAccountId,
40368
40460
  platformRoot: platformRoot3()
40369
40461
  });
40370
40462
  if (result.ok) return { ok: true };
40371
40463
  console.error(
40372
- `${TAG67} file-delivery reject reason=send-failed sender=${entry2.senderId} status=${result.status} message=${result.error}`
40464
+ `${TAG67} file-delivery reject reason=send-failed sender=${entry3.senderId} status=${result.status} message=${result.error}`
40373
40465
  );
40374
40466
  return { ok: false, error: result.error };
40375
40467
  };
40376
40468
  }
40377
- function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
40469
+ function makeWhatsAppFileDelivery(entry3, maxyAccountId) {
40378
40470
  const shared = makeFileDelivery({
40379
- entry: entry2,
40471
+ entry: entry3,
40380
40472
  tag: TAG67,
40381
40473
  channel: "whatsapp",
40382
- sendFile: makeWhatsAppSendFile(entry2, maxyAccountId)
40474
+ sendFile: makeWhatsAppSendFile(entry3, maxyAccountId)
40383
40475
  });
40384
40476
  let turnStartedAt = null;
40385
40477
  let routeCalls = [];
@@ -40404,7 +40496,7 @@ function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
40404
40496
  const routes = routeCalls;
40405
40497
  turnStartedAt = null;
40406
40498
  routeCalls = [];
40407
- const sid = entry2.sessionId.slice(0, 8);
40499
+ const sid = entry3.sessionId.slice(0, 8);
40408
40500
  shared.onTurnEnd(firedTools);
40409
40501
  for (const call2 of routes) {
40410
40502
  const routeAt = call2.to !== void 0 && call2.filePath !== void 0 ? routeDocumentOutboundAt(call2.to, call2.filePath) : void 0;
@@ -40412,7 +40504,7 @@ function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
40412
40504
  if (!delivered) {
40413
40505
  const fileField = call2.filePath !== void 0 ? ` file=${call2.filePath}` : "";
40414
40506
  console.error(
40415
- `${TAG67} file-delivery-unreconciled sender=${entry2.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
40507
+ `${TAG67} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
40416
40508
  );
40417
40509
  }
40418
40510
  }
@@ -40423,7 +40515,7 @@ function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
40423
40515
  // app/lib/whatsapp/gateway/native-file-follower.ts
40424
40516
  var COMPOSING_REFRESH_MS = 1e4;
40425
40517
  function startNativeFileFollower(input) {
40426
- const entry2 = {
40518
+ const entry3 = {
40427
40519
  sessionId: input.sessionId,
40428
40520
  role: input.role,
40429
40521
  channel: "whatsapp",
@@ -40469,7 +40561,7 @@ function startNativeFileFollower(input) {
40469
40561
  void sendPaused(sock, input.senderId);
40470
40562
  };
40471
40563
  return startEmitter({
40472
- entry: entry2,
40564
+ entry: entry3,
40473
40565
  tag: "[whatsapp-adaptor]",
40474
40566
  // Task 2557 — the emitter has always had this channel and no follower ever
40475
40567
  // passed one, so every cause it reported (`follow-status-404`,
@@ -40481,7 +40573,7 @@ function startNativeFileFollower(input) {
40481
40573
  },
40482
40574
  // Task 2521 — admin-only. A public spawn has no tools (Task 2078), so a
40483
40575
  // handler here would reconcile a call that can never fire.
40484
- fileDelivery: input.role === "admin" ? makeWhatsAppFileDelivery(entry2, input.maxyAccountId) : null,
40576
+ fileDelivery: input.role === "admin" ? makeWhatsAppFileDelivery(entry3, input.maxyAccountId) : null,
40485
40577
  // A resumed session's JSONL already holds prior SendUserFile tool_uses;
40486
40578
  // suppress replay so historical files are not re-sent on attach.
40487
40579
  suppressResumeReplay: true,
@@ -41019,24 +41111,24 @@ var TelegramGateway = class {
41019
41111
  let calls = 0;
41020
41112
  for (const h of held) {
41021
41113
  if (this.spawning.has(h.key)) continue;
41022
- const entry2 = this.heldSpawnArgs.get(h.key);
41023
- if (!entry2) {
41114
+ const entry3 = this.heldSpawnArgs.get(h.key);
41115
+ if (!entry3) {
41024
41116
  console.error(
41025
41117
  `[telegram-native] op=spawn-retry-skipped key=${h.key} ageMs=${h.oldestAgeMs} queued=${h.count} reason=no-args`
41026
41118
  );
41027
41119
  continue;
41028
41120
  }
41029
- entry2.attempts++;
41121
+ entry3.attempts++;
41030
41122
  this.spawning.add(h.key);
41031
41123
  calls++;
41032
41124
  try {
41033
- await this.deps.ensureChannelSession(entry2.args);
41125
+ await this.deps.ensureChannelSession(entry3.args);
41034
41126
  console.error(
41035
- `[telegram-native] op=spawn-retry key=${h.key} attempt=${entry2.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=true`
41127
+ `[telegram-native] op=spawn-retry key=${h.key} attempt=${entry3.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=true`
41036
41128
  );
41037
41129
  } catch (err) {
41038
41130
  console.error(
41039
- `[telegram-native] op=spawn-retry key=${h.key} attempt=${entry2.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=false reason=${err instanceof Error ? err.message : String(err)}`
41131
+ `[telegram-native] op=spawn-retry key=${h.key} attempt=${entry3.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=false reason=${err instanceof Error ? err.message : String(err)}`
41040
41132
  );
41041
41133
  } finally {
41042
41134
  this.spawning.delete(h.key);
@@ -41489,33 +41581,33 @@ var TAG71 = "[telegram:outbound]";
41489
41581
  function platformRoot4() {
41490
41582
  return process.env.MAXY_PLATFORM_ROOT || "";
41491
41583
  }
41492
- function makeTelegramSendFile(entry2, botToken) {
41584
+ function makeTelegramSendFile(entry3, botToken) {
41493
41585
  return async (filePath, caption) => {
41494
41586
  if (!botToken) {
41495
- console.error(`${TAG71} file-delivery reject reason=no-bot-token sender=${entry2.senderId} role=${entry2.role}`);
41587
+ console.error(`${TAG71} file-delivery reject reason=no-bot-token sender=${entry3.senderId} role=${entry3.role}`);
41496
41588
  return { ok: false, error: "no-bot-token" };
41497
41589
  }
41498
- if (entry2.replyTarget == null) {
41499
- console.error(`${TAG71} file-delivery reject reason=no-reply-target sender=${entry2.senderId} role=${entry2.role}`);
41590
+ if (entry3.replyTarget == null) {
41591
+ console.error(`${TAG71} file-delivery reject reason=no-reply-target sender=${entry3.senderId} role=${entry3.role}`);
41500
41592
  return { ok: false, error: "no-reply-target" };
41501
41593
  }
41502
41594
  const result = await sendTelegramDocument({
41503
41595
  botToken,
41504
- chatId: Number(entry2.replyTarget),
41596
+ chatId: Number(entry3.replyTarget),
41505
41597
  filePath,
41506
41598
  caption,
41507
- maxyAccountId: entry2.accountId,
41599
+ maxyAccountId: entry3.accountId,
41508
41600
  platformRoot: platformRoot4()
41509
41601
  });
41510
41602
  return result.ok ? { ok: true } : { ok: false, error: result.error };
41511
41603
  };
41512
41604
  }
41513
- function makeTelegramFileDelivery(entry2, botToken) {
41605
+ function makeTelegramFileDelivery(entry3, botToken) {
41514
41606
  return makeFileDelivery({
41515
- entry: entry2,
41607
+ entry: entry3,
41516
41608
  tag: TAG71,
41517
41609
  channel: "telegram",
41518
- sendFile: makeTelegramSendFile(entry2, botToken)
41610
+ sendFile: makeTelegramSendFile(entry3, botToken)
41519
41611
  });
41520
41612
  }
41521
41613
 
@@ -41551,11 +41643,46 @@ function runTelegramPresenceCensus() {
41551
41643
  );
41552
41644
  }
41553
41645
 
41646
+ // app/lib/telegram/gateway/card-census.ts
41647
+ var sessions3 = /* @__PURE__ */ new Map();
41648
+ function entry2(sessionId) {
41649
+ let e = sessions3.get(sessionId);
41650
+ if (!e) {
41651
+ e = { armed: 0, suppressed: 0, delivered: 0 };
41652
+ sessions3.set(sessionId, e);
41653
+ }
41654
+ return e;
41655
+ }
41656
+ function noteCardArmed(sessionId) {
41657
+ entry2(sessionId).armed += 1;
41658
+ }
41659
+ function noteCardSuppressed(sessionId) {
41660
+ entry2(sessionId).suppressed += 1;
41661
+ }
41662
+ function noteCardDelivered(sessionId) {
41663
+ const e = sessions3.get(sessionId);
41664
+ if (e && e.armed > 0) e.delivered += 1;
41665
+ }
41666
+ function cardCensusLines() {
41667
+ let armed2 = 0;
41668
+ let suppressed = 0;
41669
+ let delivered = 0;
41670
+ for (const e of sessions3.values()) {
41671
+ armed2 += e.armed;
41672
+ suppressed += e.suppressed;
41673
+ delivered += e.delivered;
41674
+ }
41675
+ const silent = Math.max(0, armed2 - suppressed - delivered);
41676
+ return [
41677
+ `[telegram-card-suppression-census] sessions=${sessions3.size} armed=${armed2} suppressed=${suppressed} silent-turns=${silent}`
41678
+ ];
41679
+ }
41680
+
41554
41681
  // app/lib/telegram/gateway/native-file-follower.ts
41555
41682
  var TELEGRAM_CARD_TOOL = "mcp__telegram__telegram-card";
41556
41683
  var CHAT_ACTION_REFRESH_MS = 4e3;
41557
41684
  function startTelegramNativeFileFollower(input) {
41558
- const entry2 = {
41685
+ const entry3 = {
41559
41686
  sessionId: input.sessionId,
41560
41687
  role: "admin",
41561
41688
  channel: "telegram",
@@ -41599,7 +41726,8 @@ function startTelegramNativeFileFollower(input) {
41599
41726
  stopHeartbeat();
41600
41727
  lastChatActionAt = Number.NEGATIVE_INFINITY;
41601
41728
  };
41602
- const onTurnStart = () => {
41729
+ const onTurnStart = (kind) => {
41730
+ if (kind === "inbound") cardSentThisTurn = false;
41603
41731
  if (turnOpen) return;
41604
41732
  turnOpen = true;
41605
41733
  stopHeartbeat();
@@ -41612,7 +41740,6 @@ function startTelegramNativeFileFollower(input) {
41612
41740
  };
41613
41741
  const onTurnComplete = () => {
41614
41742
  turnOpen = false;
41615
- cardSentThisTurn = false;
41616
41743
  if (input.backgroundActive?.() === true) return;
41617
41744
  stopHeartbeat();
41618
41745
  lastChatActionAt = Number.NEGATIVE_INFINITY;
@@ -41626,7 +41753,7 @@ function startTelegramNativeFileFollower(input) {
41626
41753
  background: () => input.backgroundActive?.() === true
41627
41754
  });
41628
41755
  return startEmitter({
41629
- entry: entry2,
41756
+ entry: entry3,
41630
41757
  tag: "[telegram-adaptor]",
41631
41758
  // Task 2557 — the emitter has always had this channel and no follower ever
41632
41759
  // passed one, so every cause it reported was discarded. The close line
@@ -41634,7 +41761,7 @@ function startTelegramNativeFileFollower(input) {
41634
41761
  onError: (reason) => {
41635
41762
  console.error(`[telegram-adaptor] op=follower-error sessionId=${input.sessionId.slice(0, 8)} reason=${reason}`);
41636
41763
  },
41637
- fileDelivery: makeTelegramFileDelivery(entry2, input.botToken),
41764
+ fileDelivery: makeTelegramFileDelivery(entry3, input.botToken),
41638
41765
  // A resumed session's JSONL already holds prior SendUserFile tool_uses;
41639
41766
  // suppress replay so historical files are not re-sent on attach.
41640
41767
  suppressResumeReplay: true,
@@ -41652,14 +41779,19 @@ function startTelegramNativeFileFollower(input) {
41652
41779
  // before it reaches the optional answerCallbackQuery, so there is no card
41653
41780
  // call that posts nothing and no bare acknowledgement to keep the text for.
41654
41781
  onToolUse: (name) => {
41655
- if (name === TELEGRAM_CARD_TOOL) cardSentThisTurn = true;
41782
+ if (name !== TELEGRAM_CARD_TOOL) return;
41783
+ cardSentThisTurn = true;
41784
+ noteCardArmed(input.sessionId);
41785
+ console.error(
41786
+ `[telegram-public] op=card-armed sessionId=${input.sessionId.slice(0, 8)} tool=${name}`
41787
+ );
41656
41788
  },
41657
41789
  // Task 2521 — the answer reaches every bound door through the one fan-out.
41658
41790
  // Ungated by design: whatever woke the turn, the reader and the chats show
41659
41791
  // the same thing. Task 2666 adds the one exception above.
41660
41792
  onAnswer: async (answer) => {
41661
41793
  if (cardSentThisTurn) {
41662
- cardSentThisTurn = false;
41794
+ noteCardSuppressed(input.sessionId);
41663
41795
  console.error(
41664
41796
  `[telegram-public] op=text-suppressed sessionId=${input.sessionId.slice(0, 8)} messageId=${answer.messageId} reason=card-sent`
41665
41797
  );
@@ -41670,6 +41802,7 @@ function startTelegramNativeFileFollower(input) {
41670
41802
  noteAnswer(input.sessionId, answer.messageId);
41671
41803
  const { line, reached } = await fanOut({ targets, segments: segmentText(answer.text) });
41672
41804
  noteSent(input.sessionId, answer.messageId, reached);
41805
+ noteCardDelivered(input.sessionId);
41673
41806
  console.error(`${line} messageId=${answer.messageId} sessionId=${input.sessionId.slice(0, 8)}`);
41674
41807
  },
41675
41808
  // Task 2603 — the interval must not outlive the reader. The emitter reaches
@@ -41955,11 +42088,11 @@ async function firePublicSessionEndReview(input) {
41955
42088
  }
41956
42089
 
41957
42090
  // app/lib/whatsapp/inbound/resolve-client-graph-owner.ts
41958
- var import_dist16 = __toESM(require_dist3(), 1);
42091
+ var import_dist17 = __toESM(require_dist3(), 1);
41959
42092
  async function resolveClientOwnerUserId(accountId, deps = {}) {
41960
42093
  const listAccounts = deps.listAccounts ?? listValidAccounts;
41961
42094
  const getSession3 = deps.getSession ?? getSession;
41962
- const resolveOwner2 = deps.resolveOwner ?? import_dist16.resolveOwnerUserId;
42095
+ const resolveOwner2 = deps.resolveOwner ?? import_dist17.resolveOwnerUserId;
41963
42096
  const role = listAccounts().find((a) => a.accountId === accountId)?.config.role;
41964
42097
  if (role !== "client") return null;
41965
42098
  const session = getSession3();
@@ -41982,8 +42115,8 @@ async function resolveClientOwnerUserId(accountId, deps = {}) {
41982
42115
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
41983
42116
  function findAccountManagerDrift(accountManagers, validAccountIds) {
41984
42117
  const drift = [];
41985
- for (const [phone, entry2] of Object.entries(accountManagers)) {
41986
- const managesAccount = typeof entry2 === "string" ? entry2 : entry2.managesAccount;
42118
+ for (const [phone, entry3] of Object.entries(accountManagers)) {
42119
+ const managesAccount = typeof entry3 === "string" ? entry3 : entry3.managesAccount;
41987
42120
  if (!validAccountIds.includes(managesAccount)) {
41988
42121
  drift.push({ phone, managesAccount, reason: "not-in-registry" });
41989
42122
  }
@@ -42089,50 +42222,50 @@ import { join as join71 } from "path";
42089
42222
  function buildTelegramAuditLines(input) {
42090
42223
  const lines = [];
42091
42224
  const entries2 = input.accounts.flatMap(
42092
- (a) => listBotEntries(a.telegram).map((entry2) => ({ accountId: a.accountId, accountDir: a.accountDir, entry: entry2 }))
42225
+ (a) => listBotEntries(a.telegram).map((entry3) => ({ accountId: a.accountId, accountDir: a.accountDir, entry: entry3 }))
42093
42226
  );
42094
42227
  if (entries2.length === 0) return lines;
42095
42228
  const byId = /* @__PURE__ */ new Map();
42096
- for (const { entry: entry2 } of entries2) byId.set(entry2.id, (byId.get(entry2.id) ?? 0) + 1);
42229
+ for (const { entry: entry3 } of entries2) byId.set(entry3.id, (byId.get(entry3.id) ?? 0) + 1);
42097
42230
  for (const [botId, count] of byId) {
42098
42231
  if (count > 1) lines.push(`[telegram-audit] op=duplicate-bot-id botId=${botId} accounts=${count}`);
42099
42232
  }
42100
- for (const { accountId, accountDir, entry: entry2 } of entries2) {
42101
- if (entry2.role === "public" && !input.agentActive(accountDir, entry2.agent)) {
42233
+ for (const { accountId, accountDir, entry: entry3 } of entries2) {
42234
+ if (entry3.role === "public" && !input.agentActive(accountDir, entry3.agent)) {
42102
42235
  lines.push(
42103
- `[telegram-audit] op=entry-agent-inactive accountId=${accountId} botId=${entry2.id} agent=${entry2.agent}`
42236
+ `[telegram-audit] op=entry-agent-inactive accountId=${accountId} botId=${entry3.id} agent=${entry3.agent}`
42104
42237
  );
42105
42238
  }
42106
- if (!input.secretFileExists(entry2.id)) {
42107
- lines.push(`[telegram-audit] op=secret-orphan botId=${entry2.id} side=entry`);
42239
+ if (!input.secretFileExists(entry3.id)) {
42240
+ lines.push(`[telegram-audit] op=secret-orphan botId=${entry3.id} side=entry`);
42108
42241
  }
42109
42242
  }
42110
42243
  const known = new Set(entries2.map((e) => e.entry.id));
42111
42244
  for (const botId of input.secretFilesOnDisk) {
42112
42245
  if (!known.has(botId)) lines.push(`[telegram-audit] op=secret-orphan botId=${botId} side=file`);
42113
42246
  }
42114
- for (const { accountId, entry: entry2 } of entries2) {
42115
- const last = input.lastActivityByBot(entry2.id);
42247
+ for (const { accountId, entry: entry3 } of entries2) {
42248
+ const last = input.lastActivityByBot(entry3.id);
42116
42249
  const spawnAge = last === null ? "never" : String(input.now - last.spawnMs);
42117
42250
  const turnAge = last === null || last.turnMs === null ? "never" : String(input.now - last.turnMs);
42118
- const agent = entryAgentSlug(entry2);
42119
- const secretFile = input.secretFileExists(entry2.id) ? "present" : "absent";
42251
+ const agent = entryAgentSlug(entry3);
42252
+ const secretFile = input.secretFileExists(entry3.id) ? "present" : "absent";
42120
42253
  lines.push(
42121
- `[telegram-audit] op=configured botId=${entry2.id} accountId=${accountId} agent=${agent} secretFile=${secretFile} lastSpawnAgeMs=${spawnAge} lastTurnAgeMs=${turnAge}`
42254
+ `[telegram-audit] op=configured botId=${entry3.id} accountId=${accountId} agent=${agent} secretFile=${secretFile} lastSpawnAgeMs=${spawnAge} lastTurnAgeMs=${turnAge}`
42122
42255
  );
42123
42256
  }
42124
42257
  let bound = 0;
42125
42258
  let rosterOk = 0;
42126
42259
  let rosterMissing = 0;
42127
- for (const { accountId, accountDir, entry: entry2 } of entries2) {
42128
- if (entry2.role !== "specialist") continue;
42260
+ for (const { accountId, accountDir, entry: entry3 } of entries2) {
42261
+ if (entry3.role !== "specialist") continue;
42129
42262
  bound++;
42130
- if (input.specialistCards(accountDir).includes(entry2.specialist)) {
42263
+ if (input.specialistCards(accountDir).includes(entry3.specialist)) {
42131
42264
  rosterOk++;
42132
42265
  } else {
42133
42266
  rosterMissing++;
42134
42267
  lines.push(
42135
- `[telegram-audit] op=specialist-binding-missing accountId=${accountId} botId=${entry2.id} specialist=${entry2.specialist}`
42268
+ `[telegram-audit] op=specialist-binding-missing accountId=${accountId} botId=${entry3.id} specialist=${entry3.specialist}`
42136
42269
  );
42137
42270
  }
42138
42271
  }
@@ -42152,9 +42285,9 @@ function buildManagedBotCensusLine(input) {
42152
42285
  const byId = new Map(input.probes.map((p) => [p.botId, p]));
42153
42286
  let webhookOk = 0;
42154
42287
  let unrestricted = 0;
42155
- for (const entry2 of managed) {
42156
- const probe = byId.get(entry2.id);
42157
- if (probe && probe.webhookUrl && webhookPointsHere(probe.webhookUrl, entry2.id)) webhookOk += 1;
42288
+ for (const entry3 of managed) {
42289
+ const probe = byId.get(entry3.id);
42290
+ if (probe && probe.webhookUrl && webhookPointsHere(probe.webhookUrl, entry3.id)) webhookOk += 1;
42158
42291
  if (probe?.isAccessRestricted === false) unrestricted += 1;
42159
42292
  }
42160
42293
  return `[telegram-audit] op=managed-bot-census bots=${entries2.length} managed=${managed.length} webhookOk=${webhookOk} webhookStale=${managed.length - webhookOk} unrestricted=${unrestricted}`;
@@ -42227,19 +42360,19 @@ function buildGroupCensusLine(c) {
42227
42360
  async function runTelegramGroupCensus(accounts, deps) {
42228
42361
  const counts = { bots: 0, groups: 0, present: 0, absent: 0, privacyOff: 0 };
42229
42362
  for (const account of accounts) {
42230
- for (const entry2 of listBotEntries(account.telegram)) {
42231
- if (entry2.role !== "public") continue;
42232
- if ((entry2.groupPolicy ?? "disabled") !== "allowlist") continue;
42363
+ for (const entry3 of listBotEntries(account.telegram)) {
42364
+ if (entry3.role !== "public") continue;
42365
+ if ((entry3.groupPolicy ?? "disabled") !== "allowlist") continue;
42233
42366
  counts.bots += 1;
42234
42367
  try {
42235
- const me = await deps.getMe(entry2.token);
42368
+ const me = await deps.getMe(entry3.token);
42236
42369
  if (me.can_read_all_group_messages === true) counts.privacyOff += 1;
42237
42370
  } catch {
42238
42371
  }
42239
- for (const chatId of entry2.allowGroups ?? []) {
42372
+ for (const chatId of entry3.allowGroups ?? []) {
42240
42373
  counts.groups += 1;
42241
42374
  try {
42242
- const m = await deps.getChatMember(entry2.token, chatId);
42375
+ const m = await deps.getChatMember(entry3.token, chatId);
42243
42376
  if (typeof m.status === "string" && PRESENT_STATUSES.has(m.status)) counts.present += 1;
42244
42377
  else counts.absent += 1;
42245
42378
  } catch {
@@ -42261,14 +42394,14 @@ function buildChannelCensusLine(c) {
42261
42394
  async function runTelegramChannelCensus(accounts, deps) {
42262
42395
  const counts = { bots: 0, channels: 0, admin: 0, notAdmin: 0, noPostRight: 0 };
42263
42396
  for (const account of accounts) {
42264
- for (const entry2 of listBotEntries(account.telegram)) {
42265
- if (entry2.role !== "public") continue;
42266
- if ((entry2.channelPolicy ?? "disabled") !== "allowlist") continue;
42397
+ for (const entry3 of listBotEntries(account.telegram)) {
42398
+ if (entry3.role !== "public") continue;
42399
+ if ((entry3.channelPolicy ?? "disabled") !== "allowlist") continue;
42267
42400
  counts.bots += 1;
42268
- for (const chatId of entry2.allowChannels ?? []) {
42401
+ for (const chatId of entry3.allowChannels ?? []) {
42269
42402
  counts.channels += 1;
42270
42403
  try {
42271
- const m = await deps.getChatMember(entry2.token, chatId);
42404
+ const m = await deps.getChatMember(entry3.token, chatId);
42272
42405
  if (typeof m.status === "string" && ADMIN_STATUSES.has(m.status)) {
42273
42406
  counts.admin += 1;
42274
42407
  if (m.status === "administrator" && m.can_post_messages !== true) {
@@ -42593,17 +42726,17 @@ function broadcastAdminShutdown(reason) {
42593
42726
  const done = encoder.encode(`data: [DONE]
42594
42727
 
42595
42728
  `);
42596
- for (const entry2 of activeAdminSSEControllers) {
42729
+ for (const entry3 of activeAdminSSEControllers) {
42597
42730
  try {
42598
- entry2.controller.enqueue(frame);
42731
+ entry3.controller.enqueue(frame);
42599
42732
  } catch {
42600
42733
  }
42601
42734
  try {
42602
- entry2.controller.enqueue(done);
42735
+ entry3.controller.enqueue(done);
42603
42736
  } catch {
42604
42737
  }
42605
42738
  try {
42606
- entry2.controller.close();
42739
+ entry3.controller.close();
42607
42740
  } catch {
42608
42741
  }
42609
42742
  }
@@ -42960,8 +43093,8 @@ var webchatFileFollowers = /* @__PURE__ */ new Map();
42960
43093
  async function fetchAccountStandingRules(accountId) {
42961
43094
  const session = getSession();
42962
43095
  try {
42963
- const res = await (0, import_dist17.resolveActiveRules)(session, accountId);
42964
- return { block: (0, import_dist17.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
43096
+ const res = await (0, import_dist18.resolveActiveRules)(session, accountId);
43097
+ return { block: (0, import_dist18.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
42965
43098
  } catch (err) {
42966
43099
  console.error(
42967
43100
  `[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
@@ -42997,6 +43130,7 @@ registerLoop({
42997
43130
  run: () => {
42998
43131
  const lines = parityCensusLines();
42999
43132
  for (const line of lines) console.error(line);
43133
+ for (const line of cardCensusLines()) console.error(line);
43000
43134
  const missing = lines.filter((l) => !l.includes("missing=0")).length;
43001
43135
  return `${lines.length} session(s), ${missing} with a missing answer`;
43002
43136
  }
@@ -43533,15 +43667,15 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
43533
43667
  const entries2 = listBotEntries(a.config.telegram);
43534
43668
  for (const bot of readChannelAdmins(a.accountDir, "telegram").byBot) {
43535
43669
  if (!bot.adminUsers.includes(asNumber)) continue;
43536
- const entry2 = entries2.find((e) => e.id === bot.botId);
43537
- if (!entry2) continue;
43670
+ const entry3 = entries2.find((e) => e.id === bot.botId);
43671
+ if (!entry3) continue;
43538
43672
  hits.push({
43539
43673
  accountId: a.accountId,
43540
43674
  botId: bot.botId,
43541
- botToken: entry2.token,
43675
+ botToken: entry3.token,
43542
43676
  // Task 2617 — read off the entry this resolver just chose, so the
43543
43677
  // outbound gate below decides on the same entry the token came from.
43544
- ...chatSendAllowlists(entry2)
43678
+ ...chatSendAllowlists(entry3)
43545
43679
  });
43546
43680
  }
43547
43681
  }
@@ -44627,24 +44761,24 @@ registerLoop({
44627
44761
  const managed = accounts.flatMap((a) => {
44628
44762
  const bots = listBotEntries(a.telegram);
44629
44763
  const admin = bots.find((b) => b.role === "admin");
44630
- return bots.filter((e) => e.managed === true).map((entry2) => ({ entry: entry2, managingToken: admin?.token ?? null }));
44764
+ return bots.filter((e) => e.managed === true).map((entry3) => ({ entry: entry3, managingToken: admin?.token ?? null }));
44631
44765
  });
44632
44766
  const probes2 = [];
44633
- for (const { entry: entry2, managingToken } of managed) {
44767
+ for (const { entry: entry3, managingToken } of managed) {
44634
44768
  let webhookUrl = null;
44635
44769
  let isAccessRestricted = null;
44636
44770
  try {
44637
- webhookUrl = (await getTelegramWebhookInfo(entry2.token)).url;
44771
+ webhookUrl = (await getTelegramWebhookInfo(entry3.token)).url;
44638
44772
  } catch {
44639
44773
  }
44640
44774
  if (managingToken) {
44641
44775
  try {
44642
- const got = await getManagedBotAccessSettings(managingToken, Number(entry2.id));
44776
+ const got = await getManagedBotAccessSettings(managingToken, Number(entry3.id));
44643
44777
  if (got.ok) isAccessRestricted = got.isAccessRestricted;
44644
44778
  } catch {
44645
44779
  }
44646
44780
  }
44647
- probes2.push({ botId: entry2.id, webhookUrl, isAccessRestricted });
44781
+ probes2.push({ botId: entry3.id, webhookUrl, isAccessRestricted });
44648
44782
  }
44649
44783
  console.error(buildManagedBotCensusLine({ accounts, probes: probes2 }));
44650
44784
  }