@rubytech/create-realagent-code 0.1.594 → 0.1.596
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.
- package/package.json +1 -1
- package/payload/platform/lib/telegram-button-labels/dist/index.d.ts +19 -0
- package/payload/platform/lib/telegram-button-labels/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/telegram-button-labels/dist/index.js +84 -0
- package/payload/platform/lib/telegram-button-labels/dist/index.js.map +1 -0
- package/payload/platform/lib/telegram-button-labels/src/__tests__/store.test.ts +67 -0
- package/payload/platform/lib/telegram-button-labels/src/index.ts +88 -0
- package/payload/platform/lib/telegram-button-labels/tsconfig.json +9 -0
- package/payload/platform/lib/telegram-button-labels/vitest.config.ts +9 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +8 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +10 -0
- package/payload/platform/plugins/docs/references/telegram-guide.md +7 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +74 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +0 -11
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +103 -3
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/skills/routine-control/SKILL.md +4 -0
- package/payload/server/public/operator.html +1 -1
- package/payload/server/server.js +564 -411
package/payload/server/server.js
CHANGED
|
@@ -363,10 +363,10 @@ var require_dist2 = __commonJS({
|
|
|
363
363
|
} catch {
|
|
364
364
|
return { newest, storeRows };
|
|
365
365
|
}
|
|
366
|
-
for (const
|
|
367
|
-
if (!
|
|
366
|
+
for (const entry3 of entries2) {
|
|
367
|
+
if (!entry3.endsWith(".jsonl"))
|
|
368
368
|
continue;
|
|
369
|
-
const stem =
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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(
|
|
3233
|
+
function recomputeFloorMs(entry3) {
|
|
3173
3234
|
return Math.min(
|
|
3174
|
-
Math.max(
|
|
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
|
|
3181
|
-
if (
|
|
3182
|
-
const age = Date.now() -
|
|
3183
|
-
if (!
|
|
3184
|
-
return { value:
|
|
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() -
|
|
3187
|
-
if (
|
|
3188
|
-
return { value:
|
|
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 =
|
|
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
|
|
3237
|
-
if (
|
|
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
|
|
3431
|
-
if (!
|
|
3432
|
-
const stem =
|
|
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
|
|
3462
|
-
if (!
|
|
3463
|
-
const stem =
|
|
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,
|
|
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
|
|
5129
|
-
if (
|
|
5130
|
-
if (
|
|
5131
|
-
return
|
|
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 =
|
|
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(
|
|
6096
|
-
if (!
|
|
6097
|
-
return { allowChannels:
|
|
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,
|
|
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,
|
|
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
|
|
6657
|
-
if (
|
|
6658
|
-
out.push({ path: join11(slugDir,
|
|
6659
|
-
} else if (
|
|
6660
|
-
const subDir = join11(slugDir,
|
|
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 (
|
|
6676
|
-
const archiveDir = join11(slugDir,
|
|
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 });
|
|
@@ -7092,6 +7153,20 @@ function transcriptBoundary(sessionKey) {
|
|
|
7092
7153
|
return { transcriptOffset: null, bounded: false };
|
|
7093
7154
|
}
|
|
7094
7155
|
}
|
|
7156
|
+
function composeDeliveredText(prompt, deliveredMessages) {
|
|
7157
|
+
if (!Array.isArray(deliveredMessages) || deliveredMessages.length === 0) return prompt;
|
|
7158
|
+
const lines = deliveredMessages.map((m) => {
|
|
7159
|
+
const r = m ?? {};
|
|
7160
|
+
const who = typeof r.senderName === "string" && r.senderName.trim() !== "" ? r.senderName : String(r.senderId ?? "unknown");
|
|
7161
|
+
const when = typeof r.createdAt === "string" ? r.createdAt : "";
|
|
7162
|
+
const text = typeof r.body === "string" ? r.body : "";
|
|
7163
|
+
return `- ${who} (${when}): ${text}`;
|
|
7164
|
+
});
|
|
7165
|
+
return `${prompt}
|
|
7166
|
+
|
|
7167
|
+
## Messages since the last check
|
|
7168
|
+
${lines.join("\n")}`;
|
|
7169
|
+
}
|
|
7095
7170
|
function createScheduleInjectRoutes(deps) {
|
|
7096
7171
|
const app81 = new Hono();
|
|
7097
7172
|
app81.post("/", async (c) => {
|
|
@@ -7120,6 +7195,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7120
7195
|
const eventAccountId = typeof body.accountId === "string" && body.accountId.length > 0 ? body.accountId : null;
|
|
7121
7196
|
const destination = typeof body.destination === "string" ? body.destination : "";
|
|
7122
7197
|
const prompt = typeof body.prompt === "string" ? body.prompt : "";
|
|
7198
|
+
const injectedText = composeDeliveredText(prompt, body.deliveredMessages);
|
|
7123
7199
|
const eventId = typeof body.eventId === "string" ? body.eventId : "";
|
|
7124
7200
|
const scheduleProvenance = parseScheduleProvenance(body.scheduleProvenance);
|
|
7125
7201
|
if (channel !== "whatsapp" && channel !== "telegram" || !destination || !prompt) {
|
|
@@ -7262,7 +7338,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7262
7338
|
// Task 2619 — a scheduled dispatch is always role:'admin' (see above),
|
|
7263
7339
|
// so it names no specialist card.
|
|
7264
7340
|
specialist: null,
|
|
7265
|
-
text:
|
|
7341
|
+
text: injectedText,
|
|
7266
7342
|
// Task 2489 — a scheduled inject carries no inbound file by
|
|
7267
7343
|
// construction: there is no message to have attached one.
|
|
7268
7344
|
media: [],
|
|
@@ -7281,7 +7357,7 @@ function createScheduleInjectRoutes(deps) {
|
|
|
7281
7357
|
}
|
|
7282
7358
|
|
|
7283
7359
|
// server/index.ts
|
|
7284
|
-
var
|
|
7360
|
+
var import_dist18 = __toESM(require_dist3(), 1);
|
|
7285
7361
|
|
|
7286
7362
|
// app/lib/whatsapp/avatar-store.ts
|
|
7287
7363
|
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 +7594,12 @@ async function sweepAvatars() {
|
|
|
7518
7594
|
var TAG9 = "[wa-chatmeta]";
|
|
7519
7595
|
function diffStoredAgainstLive(stored, live) {
|
|
7520
7596
|
const out = [];
|
|
7521
|
-
for (const
|
|
7522
|
-
if (!
|
|
7523
|
-
const seen = live.get(
|
|
7597
|
+
for (const entry3 of stored.values()) {
|
|
7598
|
+
if (!entry3.isGroup) continue;
|
|
7599
|
+
const seen = live.get(entry3.jid);
|
|
7524
7600
|
if (seen === void 0) continue;
|
|
7525
|
-
if (seen.name !==
|
|
7526
|
-
out.push({ jid:
|
|
7601
|
+
if (seen.name !== entry3.name) {
|
|
7602
|
+
out.push({ jid: entry3.jid, stored: entry3.name, live: seen.name, observedAt: seen.observedAt });
|
|
7527
7603
|
}
|
|
7528
7604
|
}
|
|
7529
7605
|
return out;
|
|
@@ -7539,13 +7615,13 @@ async function sweepChatMetadata() {
|
|
|
7539
7615
|
const sock = conn.sock;
|
|
7540
7616
|
const stored = readChatMetadata(conn.platformAccountId);
|
|
7541
7617
|
const live = /* @__PURE__ */ new Map();
|
|
7542
|
-
for (const
|
|
7543
|
-
if (!
|
|
7618
|
+
for (const entry3 of stored.values()) {
|
|
7619
|
+
if (!entry3.isGroup) continue;
|
|
7544
7620
|
groups += 1;
|
|
7545
7621
|
try {
|
|
7546
|
-
const meta = await sock.groupMetadata(
|
|
7622
|
+
const meta = await sock.groupMetadata(entry3.jid);
|
|
7547
7623
|
if (typeof meta.subject === "string" && meta.subject.length > 0) {
|
|
7548
|
-
live.set(
|
|
7624
|
+
live.set(entry3.jid, { name: meta.subject, observedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7549
7625
|
checked2 += 1;
|
|
7550
7626
|
}
|
|
7551
7627
|
} catch {
|
|
@@ -8208,9 +8284,9 @@ function purgeNonSocketAccountManagers(accountDir, socketOwnerId) {
|
|
|
8208
8284
|
if (targetId === socketOwnerId) return { removed: [] };
|
|
8209
8285
|
const map = wa.accountManagers;
|
|
8210
8286
|
if (!map || typeof map !== "object" || Array.isArray(map)) return { removed: [] };
|
|
8211
|
-
const removed = Object.entries(map).map(([phone,
|
|
8287
|
+
const removed = Object.entries(map).map(([phone, entry3]) => ({
|
|
8212
8288
|
phone,
|
|
8213
|
-
managesAccount: typeof
|
|
8289
|
+
managesAccount: typeof entry3 === "string" ? entry3 : entry3 && typeof entry3 === "object" && typeof entry3.managesAccount === "string" ? entry3.managesAccount : ""
|
|
8214
8290
|
}));
|
|
8215
8291
|
if (removed.length === 0) return { removed: [] };
|
|
8216
8292
|
delete wa.accountManagers;
|
|
@@ -8513,10 +8589,10 @@ function purgeNonSocketRegisteredParties(accountDir, socketOwnerId) {
|
|
|
8513
8589
|
if (targetId === socketOwnerId) return { removed: [] };
|
|
8514
8590
|
const map = wa.registeredParties;
|
|
8515
8591
|
if (!map || typeof map !== "object" || Array.isArray(map)) return { removed: [] };
|
|
8516
|
-
const removed = Object.entries(map).map(([phone,
|
|
8592
|
+
const removed = Object.entries(map).map(([phone, entry3]) => ({
|
|
8517
8593
|
phone,
|
|
8518
|
-
account:
|
|
8519
|
-
party:
|
|
8594
|
+
account: entry3 && typeof entry3 === "object" && typeof entry3.account === "string" ? entry3.account : "",
|
|
8595
|
+
party: entry3 && typeof entry3 === "object" && typeof entry3.party === "string" ? entry3.party : ""
|
|
8520
8596
|
}));
|
|
8521
8597
|
if (removed.length === 0) return { removed: [] };
|
|
8522
8598
|
delete wa.registeredParties;
|
|
@@ -9350,27 +9426,27 @@ function bare(value) {
|
|
|
9350
9426
|
function reconcilePersonIdentities(rows) {
|
|
9351
9427
|
const perAccount = /* @__PURE__ */ new Map();
|
|
9352
9428
|
for (const row of rows) {
|
|
9353
|
-
let
|
|
9354
|
-
if (!
|
|
9355
|
-
|
|
9356
|
-
perAccount.set(row.accountId,
|
|
9429
|
+
let entry3 = perAccount.get(row.accountId);
|
|
9430
|
+
if (!entry3) {
|
|
9431
|
+
entry3 = { seen: /* @__PURE__ */ new Map(), divergent: 0 };
|
|
9432
|
+
perAccount.set(row.accountId, entry3);
|
|
9357
9433
|
}
|
|
9358
9434
|
const p = row.phone ? bare(row.phone) : null;
|
|
9359
9435
|
const t = row.telephone ? bare(row.telephone) : null;
|
|
9360
|
-
if (p && t && p !== t)
|
|
9436
|
+
if (p && t && p !== t) entry3.divergent += 1;
|
|
9361
9437
|
const key2 = p ?? t;
|
|
9362
|
-
if (key2)
|
|
9438
|
+
if (key2) entry3.seen.set(key2, (entry3.seen.get(key2) ?? 0) + 1);
|
|
9363
9439
|
}
|
|
9364
9440
|
const accounts = [];
|
|
9365
9441
|
let duplicates = 0;
|
|
9366
9442
|
let divergent = 0;
|
|
9367
|
-
for (const [accountId,
|
|
9443
|
+
for (const [accountId, entry3] of perAccount) {
|
|
9368
9444
|
let dup = 0;
|
|
9369
|
-
for (const count of
|
|
9445
|
+
for (const count of entry3.seen.values()) if (count > 1) dup += count - 1;
|
|
9370
9446
|
duplicates += dup;
|
|
9371
|
-
divergent +=
|
|
9372
|
-
if (dup > 0 ||
|
|
9373
|
-
accounts.push({ accountId, duplicates: dup, divergent:
|
|
9447
|
+
divergent += entry3.divergent;
|
|
9448
|
+
if (dup > 0 || entry3.divergent > 0) {
|
|
9449
|
+
accounts.push({ accountId, duplicates: dup, divergent: entry3.divergent });
|
|
9374
9450
|
}
|
|
9375
9451
|
}
|
|
9376
9452
|
return {
|
|
@@ -9392,7 +9468,7 @@ function reconcileSelfPhoneAdmins(input) {
|
|
|
9392
9468
|
for (const cred of nonHouse) {
|
|
9393
9469
|
const selfPhone = cred.selfPhone;
|
|
9394
9470
|
if (!selfPhone) continue;
|
|
9395
|
-
if (input.adminPhones.some((
|
|
9471
|
+
if (input.adminPhones.some((entry3) => phonesMatch(entry3, selfPhone))) {
|
|
9396
9472
|
matches.push({ accountId: cred.accountId, phone: selfPhone });
|
|
9397
9473
|
}
|
|
9398
9474
|
}
|
|
@@ -9773,11 +9849,11 @@ function authorizeRecallRead(input) {
|
|
|
9773
9849
|
function buildRegisteredPartyReconcile(params) {
|
|
9774
9850
|
const rows = [];
|
|
9775
9851
|
const entries2 = Object.entries(params.registeredParties);
|
|
9776
|
-
for (const [phone,
|
|
9852
|
+
for (const [phone, entry3] of entries2) {
|
|
9777
9853
|
const shadowedBy = isAdminPhone(phone, params.adminPhones) ? "adminPhones" : managedAccountFor(params.accountManagers, phone) ? "accountManagers" : null;
|
|
9778
|
-
const unresolved2 = !params.isValidAccount(
|
|
9854
|
+
const unresolved2 = !params.isValidAccount(entry3.account);
|
|
9779
9855
|
if (shadowedBy || unresolved2) {
|
|
9780
|
-
rows.push({ phone, account:
|
|
9856
|
+
rows.push({ phone, account: entry3.account, party: entry3.party, shadowedBy, unresolved: unresolved2 });
|
|
9781
9857
|
}
|
|
9782
9858
|
}
|
|
9783
9859
|
const shadowed = rows.filter((r) => r.shadowedBy !== null).length;
|
|
@@ -10187,15 +10263,15 @@ app3.post("/config", async (c) => {
|
|
|
10187
10263
|
const agents = [];
|
|
10188
10264
|
if (existsSync9(agentsDir)) {
|
|
10189
10265
|
try {
|
|
10190
|
-
for (const
|
|
10191
|
-
if (!
|
|
10192
|
-
const configPath3 = resolve8(agentsDir,
|
|
10266
|
+
for (const entry3 of readdirSync8(agentsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
10267
|
+
if (!entry3.isDirectory() || entry3.name === "admin") continue;
|
|
10268
|
+
const configPath3 = resolve8(agentsDir, entry3.name, "config.json");
|
|
10193
10269
|
if (!existsSync9(configPath3)) continue;
|
|
10194
10270
|
try {
|
|
10195
10271
|
const parsed = JSON.parse(readFileSync11(configPath3, "utf-8"));
|
|
10196
|
-
agents.push({ slug:
|
|
10272
|
+
agents.push({ slug: entry3.name, displayName: parsed.displayName ?? entry3.name });
|
|
10197
10273
|
} catch {
|
|
10198
|
-
console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${
|
|
10274
|
+
console.error(`${TAG17} config action=list-public-agents error="failed to parse config.json for agent ${entry3.name}" \u2014 skipping`);
|
|
10199
10275
|
}
|
|
10200
10276
|
}
|
|
10201
10277
|
} catch (err) {
|
|
@@ -10215,8 +10291,8 @@ app3.post("/config", async (c) => {
|
|
|
10215
10291
|
const listDenial = bindingScopeDenial(c, callerAccountId, "list-account-managers");
|
|
10216
10292
|
if (listDenial) return listDenial;
|
|
10217
10293
|
const all = readAccountManagers(acct.accountDir);
|
|
10218
|
-
const boundAccountOf = (
|
|
10219
|
-
const accountManagers = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([,
|
|
10294
|
+
const boundAccountOf = (entry3) => typeof entry3 === "string" ? entry3 : entry3.managesAccount;
|
|
10295
|
+
const accountManagers = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry3]) => boundAccountOf(entry3) === callerAccountId));
|
|
10220
10296
|
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
10297
|
return c.json({ ok: true, accountManagers });
|
|
10222
10298
|
}
|
|
@@ -10254,7 +10330,7 @@ app3.post("/config", async (c) => {
|
|
|
10254
10330
|
const listDenial = bindingScopeDenial(c, callerAccountId, "list-registered-parties");
|
|
10255
10331
|
if (listDenial) return listDenial;
|
|
10256
10332
|
const all = readRegisteredParties(acct.accountDir);
|
|
10257
|
-
const registeredParties = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([,
|
|
10333
|
+
const registeredParties = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry3]) => entry3.account === callerAccountId));
|
|
10258
10334
|
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
10335
|
return c.json({ ok: true, registeredParties });
|
|
10260
10336
|
}
|
|
@@ -12159,10 +12235,10 @@ function readState(platformRoot5) {
|
|
|
12159
12235
|
return {};
|
|
12160
12236
|
}
|
|
12161
12237
|
}
|
|
12162
|
-
function recordSuccess(platformRoot5, accountId,
|
|
12238
|
+
function recordSuccess(platformRoot5, accountId, entry3) {
|
|
12163
12239
|
const path = stateFilePath(platformRoot5);
|
|
12164
12240
|
const state = readState(platformRoot5);
|
|
12165
|
-
state[accountId] =
|
|
12241
|
+
state[accountId] = entry3;
|
|
12166
12242
|
try {
|
|
12167
12243
|
writeFileSync6(path, JSON.stringify(state, null, 1));
|
|
12168
12244
|
} catch (err) {
|
|
@@ -13259,10 +13335,10 @@ function readVisitsPushState(platformRoot5) {
|
|
|
13259
13335
|
return {};
|
|
13260
13336
|
}
|
|
13261
13337
|
}
|
|
13262
|
-
function recordVisitsPush(platformRoot5, accountId,
|
|
13338
|
+
function recordVisitsPush(platformRoot5, accountId, entry3) {
|
|
13263
13339
|
const path = visitsStateFilePath(platformRoot5);
|
|
13264
13340
|
const state = readVisitsPushState(platformRoot5);
|
|
13265
|
-
state[accountId] =
|
|
13341
|
+
state[accountId] = entry3;
|
|
13266
13342
|
try {
|
|
13267
13343
|
writeFileSync7(path, JSON.stringify(state, null, 1));
|
|
13268
13344
|
} catch (err) {
|
|
@@ -14906,20 +14982,20 @@ function listBotEntries(config) {
|
|
|
14906
14982
|
}
|
|
14907
14983
|
return out;
|
|
14908
14984
|
}
|
|
14909
|
-
function entryAgentSlug(
|
|
14910
|
-
if (
|
|
14911
|
-
if (
|
|
14912
|
-
if (
|
|
14913
|
-
return
|
|
14985
|
+
function entryAgentSlug(entry3) {
|
|
14986
|
+
if (entry3.role === "admin") return "admin";
|
|
14987
|
+
if (entry3.role === "unbound") return "unbound";
|
|
14988
|
+
if (entry3.role === "specialist") return entry3.specialist;
|
|
14989
|
+
return entry3.agent;
|
|
14914
14990
|
}
|
|
14915
|
-
function isBindableEntry(
|
|
14916
|
-
return
|
|
14991
|
+
function isBindableEntry(entry3) {
|
|
14992
|
+
return entry3.role !== "unbound";
|
|
14917
14993
|
}
|
|
14918
14994
|
function resolveBotEntry(accounts, botId) {
|
|
14919
14995
|
const hits = [];
|
|
14920
14996
|
for (const a of accounts) {
|
|
14921
|
-
for (const
|
|
14922
|
-
if (
|
|
14997
|
+
for (const entry3 of listBotEntries(a.config.telegram)) {
|
|
14998
|
+
if (entry3.id === botId) hits.push({ accountId: a.accountId, accountDir: a.accountDir, entry: entry3 });
|
|
14923
14999
|
}
|
|
14924
15000
|
}
|
|
14925
15001
|
if (hits.length === 0) return { kind: "none" };
|
|
@@ -15023,20 +15099,24 @@ app6.get("/gate-probe", (c) => {
|
|
|
15023
15099
|
return resolution === "scope" ? c.json({ error: "that telegram bot belongs to another account", reason: "account-scope" }, 403) : c.json({ error: "no such telegram bot", reason: "unknown-bot" }, 404);
|
|
15024
15100
|
}
|
|
15025
15101
|
}
|
|
15102
|
+
const since = c.req.query("since")?.trim() || null;
|
|
15026
15103
|
const all = [...readConversationSummaries2(accountId).keys()];
|
|
15027
15104
|
const keys = botId === null ? all : all.filter((k) => parseTelegramChannelKey(k)?.botId === botId);
|
|
15028
15105
|
let newestInboundAt = null;
|
|
15106
|
+
const messages = [];
|
|
15029
15107
|
for (const channelKey of keys) {
|
|
15030
15108
|
for (const row of readRows(accountId, channelKey)) {
|
|
15031
15109
|
if (isUpdateRecord(row)) continue;
|
|
15032
15110
|
if (row.origin !== "inbound") continue;
|
|
15033
15111
|
if (newestInboundAt === null || row.createdAt > newestInboundAt) newestInboundAt = row.createdAt;
|
|
15112
|
+
if (since !== null && row.createdAt > since) messages.push(row);
|
|
15034
15113
|
}
|
|
15035
15114
|
}
|
|
15115
|
+
messages.sort((a, b) => a.createdAt < b.createdAt ? -1 : a.createdAt > b.createdAt ? 1 : 0);
|
|
15036
15116
|
console.log(
|
|
15037
15117
|
`${TAG24} op=gate-probe accountId=${accountId.slice(0, 8)} botId=${botId ?? "all"} resolution=${resolution} conversations=${keys.length} ofAccount=${all.length} newestInboundAt=${newestInboundAt ?? "null"}`
|
|
15038
15118
|
);
|
|
15039
|
-
return c.json({ newestInboundAt });
|
|
15119
|
+
return c.json(since === null ? { newestInboundAt } : { newestInboundAt, messages });
|
|
15040
15120
|
});
|
|
15041
15121
|
app6.get("/store-backfill", requireAdminSession, (c) => {
|
|
15042
15122
|
const v = validate(c.req.query("accountId"), c.req.query("channelKey"));
|
|
@@ -16964,7 +17044,7 @@ function createSidecarStore(config) {
|
|
|
16964
17044
|
function readAll3(sessionsDir, onSkip) {
|
|
16965
17045
|
let names;
|
|
16966
17046
|
try {
|
|
16967
|
-
names = readdirSync17(sessionsDir, { withFileTypes: true }).filter((
|
|
17047
|
+
names = readdirSync17(sessionsDir, { withFileTypes: true }).filter((entry3) => entry3.isFile()).map((entry3) => entry3.name);
|
|
16968
17048
|
} catch {
|
|
16969
17049
|
return [];
|
|
16970
17050
|
}
|
|
@@ -17897,7 +17977,7 @@ async function setTelegramWebhook(botToken, webhookUrl, secretFilePath, fetchImp
|
|
|
17897
17977
|
}
|
|
17898
17978
|
return { ok: true, secret };
|
|
17899
17979
|
}
|
|
17900
|
-
function writeTelegramBotEntry(accountDir,
|
|
17980
|
+
function writeTelegramBotEntry(accountDir, entry3) {
|
|
17901
17981
|
const configPath3 = join36(accountDir, "account.json");
|
|
17902
17982
|
if (!existsSync25(configPath3)) {
|
|
17903
17983
|
return { ok: false, error: `account.json not found at ${configPath3}` };
|
|
@@ -17910,9 +17990,9 @@ function writeTelegramBotEntry(accountDir, entry2) {
|
|
|
17910
17990
|
}
|
|
17911
17991
|
const telegram = config.telegram ?? {};
|
|
17912
17992
|
const bots = Array.isArray(telegram.bots) ? telegram.bots : [];
|
|
17913
|
-
const at = bots.findIndex((b) => b && typeof b === "object" && b.id ===
|
|
17914
|
-
if (at >= 0) bots[at] =
|
|
17915
|
-
else bots.push(
|
|
17993
|
+
const at = bots.findIndex((b) => b && typeof b === "object" && b.id === entry3.id);
|
|
17994
|
+
if (at >= 0) bots[at] = entry3;
|
|
17995
|
+
else bots.push(entry3);
|
|
17916
17996
|
telegram.bots = bots;
|
|
17917
17997
|
config.telegram = telegram;
|
|
17918
17998
|
writeFileSync12(configPath3, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
@@ -17998,34 +18078,34 @@ function getTelegramGateway() {
|
|
|
17998
18078
|
|
|
17999
18079
|
// app/lib/telegram/access-control.ts
|
|
18000
18080
|
function checkTelegramAccess(params) {
|
|
18001
|
-
const { senderId, entry:
|
|
18081
|
+
const { senderId, entry: entry3, chatId, chatType } = params;
|
|
18002
18082
|
const isGroup = chatType === "group" || chatType === "supergroup";
|
|
18003
18083
|
const isChannel = chatType === "channel";
|
|
18004
|
-
if (isChannel &&
|
|
18084
|
+
if (isChannel && entry3.role !== "public") {
|
|
18005
18085
|
return {
|
|
18006
18086
|
allowed: false,
|
|
18007
|
-
reason:
|
|
18008
|
-
agentType:
|
|
18087
|
+
reason: entry3.role === "admin" ? "admin-entry-no-channels" : entry3.role === "specialist" ? "specialist-entry-no-channels" : "unbound-entry-no-channels",
|
|
18088
|
+
agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
|
|
18009
18089
|
};
|
|
18010
18090
|
}
|
|
18011
|
-
if (isGroup &&
|
|
18091
|
+
if (isGroup && entry3.role !== "public") {
|
|
18012
18092
|
return {
|
|
18013
18093
|
allowed: false,
|
|
18014
|
-
reason:
|
|
18015
|
-
agentType:
|
|
18094
|
+
reason: entry3.role === "admin" ? "admin-entry-no-groups" : entry3.role === "specialist" ? "specialist-entry-no-groups" : "unbound-entry-no-groups",
|
|
18095
|
+
agentType: entry3.role === "admin" ? "admin" : entry3.role === "specialist" ? "specialist" : "public"
|
|
18016
18096
|
};
|
|
18017
18097
|
}
|
|
18018
|
-
if (
|
|
18019
|
-
const adminUsers =
|
|
18098
|
+
if (entry3.role === "admin") {
|
|
18099
|
+
const adminUsers = entry3.adminUsers ?? [];
|
|
18020
18100
|
if (adminUsers.includes(senderId)) {
|
|
18021
18101
|
return { allowed: true, reason: "admin-binding", agentType: "admin" };
|
|
18022
18102
|
}
|
|
18023
18103
|
return { allowed: false, reason: "not-admin-user", agentType: "admin" };
|
|
18024
18104
|
}
|
|
18025
|
-
if (isGroup &&
|
|
18026
|
-
const groupPolicy =
|
|
18105
|
+
if (isGroup && entry3.role === "public") {
|
|
18106
|
+
const groupPolicy = entry3.groupPolicy ?? "disabled";
|
|
18027
18107
|
if (groupPolicy === "allowlist") {
|
|
18028
|
-
const allowGroups =
|
|
18108
|
+
const allowGroups = entry3.allowGroups ?? [];
|
|
18029
18109
|
if (allowGroups.includes(chatId)) {
|
|
18030
18110
|
return { allowed: true, reason: "group-allowlist-match", agentType: "public" };
|
|
18031
18111
|
}
|
|
@@ -18033,10 +18113,10 @@ function checkTelegramAccess(params) {
|
|
|
18033
18113
|
}
|
|
18034
18114
|
return { allowed: false, reason: "group-policy-disabled", agentType: "public" };
|
|
18035
18115
|
}
|
|
18036
|
-
if (isChannel &&
|
|
18037
|
-
const channelPolicy =
|
|
18116
|
+
if (isChannel && entry3.role === "public") {
|
|
18117
|
+
const channelPolicy = entry3.channelPolicy ?? "disabled";
|
|
18038
18118
|
if (channelPolicy === "allowlist") {
|
|
18039
|
-
const allowChannels =
|
|
18119
|
+
const allowChannels = entry3.allowChannels ?? [];
|
|
18040
18120
|
if (allowChannels.includes(chatId)) {
|
|
18041
18121
|
return { allowed: true, reason: "channel-allowlist-match", agentType: "public" };
|
|
18042
18122
|
}
|
|
@@ -18044,22 +18124,22 @@ function checkTelegramAccess(params) {
|
|
|
18044
18124
|
}
|
|
18045
18125
|
return { allowed: false, reason: "channel-policy-disabled", agentType: "public" };
|
|
18046
18126
|
}
|
|
18047
|
-
if (
|
|
18127
|
+
if (entry3.role === "unbound") {
|
|
18048
18128
|
return { allowed: false, reason: "bot-unbound", agentType: "public" };
|
|
18049
18129
|
}
|
|
18050
|
-
if (
|
|
18051
|
-
const allowFrom =
|
|
18130
|
+
if (entry3.role === "specialist") {
|
|
18131
|
+
const allowFrom = entry3.allowFrom ?? [];
|
|
18052
18132
|
if (allowFrom.includes(senderId)) {
|
|
18053
18133
|
return { allowed: true, reason: "specialist-allowlist-match", agentType: "specialist" };
|
|
18054
18134
|
}
|
|
18055
18135
|
return { allowed: false, reason: "not-in-allowlist", agentType: "specialist" };
|
|
18056
18136
|
}
|
|
18057
|
-
const policy =
|
|
18137
|
+
const policy = entry3.dmPolicy ?? "disabled";
|
|
18058
18138
|
switch (policy) {
|
|
18059
18139
|
case "open":
|
|
18060
18140
|
return { allowed: true, reason: "dm-policy-open", agentType: "public" };
|
|
18061
18141
|
case "allowlist": {
|
|
18062
|
-
const allowFrom =
|
|
18142
|
+
const allowFrom = entry3.allowFrom ?? [];
|
|
18063
18143
|
if (allowFrom.includes(senderId)) {
|
|
18064
18144
|
return { allowed: true, reason: "allowlist-match", agentType: "public" };
|
|
18065
18145
|
}
|
|
@@ -19082,6 +19162,9 @@ function runTelegramNotifyReconcile(now) {
|
|
|
19082
19162
|
console.error(buildTelegramKindCensusLine(readTelegramKindCensus()));
|
|
19083
19163
|
}
|
|
19084
19164
|
|
|
19165
|
+
// server/routes/telegram.ts
|
|
19166
|
+
var import_dist8 = __toESM(require_dist5(), 1);
|
|
19167
|
+
|
|
19085
19168
|
// app/lib/telegram/outbound/answer-callback.ts
|
|
19086
19169
|
async function answerTelegramCallback(botToken, callbackQueryId, text) {
|
|
19087
19170
|
try {
|
|
@@ -19114,8 +19197,8 @@ var NOTIFY_MAX = 5e3;
|
|
|
19114
19197
|
var cache = /* @__PURE__ */ new Map();
|
|
19115
19198
|
function isTelegramNotifyDuplicate(notifyId) {
|
|
19116
19199
|
const now = Date.now();
|
|
19117
|
-
const
|
|
19118
|
-
if (
|
|
19200
|
+
const entry3 = cache.get(notifyId);
|
|
19201
|
+
if (entry3 && now - entry3.ts <= NOTIFY_TTL_MS) return true;
|
|
19119
19202
|
for (const [key2, e] of cache) {
|
|
19120
19203
|
if (now - e.ts > NOTIFY_TTL_MS) cache.delete(key2);
|
|
19121
19204
|
}
|
|
@@ -19174,7 +19257,7 @@ function runTelegramNotifyPublishCensus(now) {
|
|
|
19174
19257
|
}
|
|
19175
19258
|
|
|
19176
19259
|
// ../lib/dispatch-write/src/lifecycle.ts
|
|
19177
|
-
var import_dist6 = __toESM(
|
|
19260
|
+
var import_dist6 = __toESM(require_dist6());
|
|
19178
19261
|
function checkTransition(kind, from, to, cfg) {
|
|
19179
19262
|
const edges = kind === "job" ? cfg.statuses.jobTransitions : cfg.statuses.visitTransitions;
|
|
19180
19263
|
const legal = edges.some(([f, t]) => f === from && t === to);
|
|
@@ -19216,7 +19299,7 @@ function formatTransitionRefused(p) {
|
|
|
19216
19299
|
}
|
|
19217
19300
|
|
|
19218
19301
|
// ../lib/dispatch-write/src/write.ts
|
|
19219
|
-
var import_dist7 = __toESM(
|
|
19302
|
+
var import_dist7 = __toESM(require_dist6());
|
|
19220
19303
|
function refuse(reason, message) {
|
|
19221
19304
|
return { ok: false, reason, message };
|
|
19222
19305
|
}
|
|
@@ -19793,8 +19876,8 @@ function liveLocationCensusLine(accountsRoot, now) {
|
|
|
19793
19876
|
let expired = 0;
|
|
19794
19877
|
let oldestAgeMs = 0;
|
|
19795
19878
|
try {
|
|
19796
|
-
for (const
|
|
19797
|
-
const path = join38(accountsRoot,
|
|
19879
|
+
for (const entry3 of readdirSync20(accountsRoot)) {
|
|
19880
|
+
const path = join38(accountsRoot, entry3, LIVE_LOCATION_FILE);
|
|
19798
19881
|
if (!existsSync26(path) || !statSync19(path).isFile()) continue;
|
|
19799
19882
|
for (const rec of Object.values(readAll(path))) {
|
|
19800
19883
|
records3 += 1;
|
|
@@ -19930,20 +20013,20 @@ async function editTelegramMessage(botToken, chatId, messageId, text, keyboard)
|
|
|
19930
20013
|
// server/routes/telegram.ts
|
|
19931
20014
|
var TAG34 = "[telegram-inbound]";
|
|
19932
20015
|
var PROVISION_TAG = "[telegram-provision]";
|
|
19933
|
-
function entryActive(
|
|
19934
|
-
if (
|
|
19935
|
-
if (
|
|
20016
|
+
function entryActive(entry3, accountDir, botId, noteSpecialist) {
|
|
20017
|
+
if (entry3.role === "admin") return true;
|
|
20018
|
+
if (entry3.role === "specialist") {
|
|
19936
20019
|
const cards = listSpecialistCardNames(join40(accountDir, "specialists", "agents"));
|
|
19937
|
-
const rosterValid = cards.includes(
|
|
20020
|
+
const rosterValid = cards.includes(entry3.specialist);
|
|
19938
20021
|
noteSpecialist?.(rosterValid);
|
|
19939
20022
|
if (rosterValid) return true;
|
|
19940
20023
|
console.error(
|
|
19941
|
-
`${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(
|
|
20024
|
+
`${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=specialist-unknown`
|
|
19942
20025
|
);
|
|
19943
20026
|
return false;
|
|
19944
20027
|
}
|
|
19945
|
-
if (
|
|
19946
|
-
return isActiveAgentSlug(accountDir,
|
|
20028
|
+
if (entry3.role === "unbound") return false;
|
|
20029
|
+
return isActiveAgentSlug(accountDir, entry3.agent);
|
|
19947
20030
|
}
|
|
19948
20031
|
function logValue2(v) {
|
|
19949
20032
|
return v.replace(/[\r\n]+/g, " ");
|
|
@@ -20055,9 +20138,9 @@ app11.post("/", async (c) => {
|
|
|
20055
20138
|
console.error(`${TAG34} op=reject reason=duplicate-bot-id botId=${botId} accounts=${resolved.count}`);
|
|
20056
20139
|
return c.json({ ok: false }, 401);
|
|
20057
20140
|
}
|
|
20058
|
-
const { accountId, accountDir, entry:
|
|
20059
|
-
const agentSlug = entryAgentSlug(
|
|
20060
|
-
console.error(`${TAG34} op=entry botId=${botId} accountId=${accountId} role=${
|
|
20141
|
+
const { accountId, accountDir, entry: entry3 } = resolved;
|
|
20142
|
+
const agentSlug = entryAgentSlug(entry3);
|
|
20143
|
+
console.error(`${TAG34} op=entry botId=${botId} accountId=${accountId} role=${entry3.role} agent=${agentSlug}`);
|
|
20061
20144
|
const sp = secretPath(botId);
|
|
20062
20145
|
if (!existsSync28(sp)) {
|
|
20063
20146
|
console.error(`${TAG34} op=secret botId=${botId} result=missing-file`);
|
|
@@ -20090,13 +20173,13 @@ app11.post("/", async (c) => {
|
|
|
20090
20173
|
});
|
|
20091
20174
|
}
|
|
20092
20175
|
if (shape.chatType === "group" || shape.chatType === "supergroup") {
|
|
20093
|
-
const allowlisted =
|
|
20176
|
+
const allowlisted = entry3.role === "public" && (entry3.groupPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry3.allowGroups ?? []).includes(shape.chatId);
|
|
20094
20177
|
console.error(
|
|
20095
20178
|
`${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
20179
|
);
|
|
20097
20180
|
}
|
|
20098
20181
|
if (shape.chatType === "channel") {
|
|
20099
|
-
const allowlisted =
|
|
20182
|
+
const allowlisted = entry3.role === "public" && (entry3.channelPolicy ?? "disabled") === "allowlist" && shape.chatId !== null && (entry3.allowChannels ?? []).includes(shape.chatId);
|
|
20100
20183
|
console.error(
|
|
20101
20184
|
`${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
20185
|
);
|
|
@@ -20104,7 +20187,7 @@ app11.post("/", async (c) => {
|
|
|
20104
20187
|
const hostBots = update.managed_bot ? listBotEntries(accountConfig(accountDir).telegram) : [];
|
|
20105
20188
|
const decision = routeTelegramUpdate({
|
|
20106
20189
|
update,
|
|
20107
|
-
entry:
|
|
20190
|
+
entry: entry3,
|
|
20108
20191
|
...update.managed_bot ? { knownBotIds: hostBots.map((b) => b.id) } : {}
|
|
20109
20192
|
});
|
|
20110
20193
|
const raisesTurn = decision.kind === "dispatch" || // Task 2615 — a customer asking a question raises one. `business-deleted`
|
|
@@ -20134,14 +20217,14 @@ app11.post("/", async (c) => {
|
|
|
20134
20217
|
);
|
|
20135
20218
|
}
|
|
20136
20219
|
console.error(`${TAG34} op=access botId=${botId} senderId=- allowed=false reason=${decision.reason}`);
|
|
20137
|
-
if (
|
|
20220
|
+
if (entry3.role === "specialist") {
|
|
20138
20221
|
console.error(
|
|
20139
|
-
`${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(
|
|
20222
|
+
`${TAG34} op=specialist-refused botId=${botId} specialist=${logValue2(entry3.specialist)} reason=${decision.reason}`
|
|
20140
20223
|
);
|
|
20141
20224
|
}
|
|
20142
20225
|
return c.json({ ok: true }, 200);
|
|
20143
20226
|
}
|
|
20144
|
-
if (!isBindableEntry(
|
|
20227
|
+
if (!isBindableEntry(entry3)) {
|
|
20145
20228
|
console.error(`${TAG34} op=ignore botId=${botId} reason=bot-unbound`);
|
|
20146
20229
|
return c.json({ ok: true }, 200);
|
|
20147
20230
|
}
|
|
@@ -20174,19 +20257,30 @@ app11.post("/", async (c) => {
|
|
|
20174
20257
|
if (decision.allowed) {
|
|
20175
20258
|
const cbKey = telegramChannelKey(botId, cbSubject);
|
|
20176
20259
|
const cbAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20260
|
+
let cbBody = `[button: ${decision.data ?? "no-data"}]`;
|
|
20261
|
+
if (decision.data !== null) {
|
|
20262
|
+
const label = (0, import_dist8.readButtonLabel)(accountDir, (0, import_dist8.buttonLabelKey)(botId, decision.chatId, decision.data));
|
|
20263
|
+
if (label !== null) {
|
|
20264
|
+
cbBody = label;
|
|
20265
|
+
} else {
|
|
20266
|
+
console.error(
|
|
20267
|
+
`${TAG34} op=button-label-miss botId=${botId} chatId=${decision.chatId} data=${logValue2(decision.data)}`
|
|
20268
|
+
);
|
|
20269
|
+
}
|
|
20270
|
+
}
|
|
20177
20271
|
appendMessage2(accountId, {
|
|
20178
20272
|
messageId: `telegram:${accountId}:${cbKey}:cb:${decision.callbackId}`,
|
|
20179
20273
|
// Task 2598 — the one shared derivation, so this row names the session
|
|
20180
20274
|
// the press actually reaches.
|
|
20181
20275
|
sessionId: telegramAdminSessionId(accountId, cbSubject, botId),
|
|
20182
20276
|
dateSent: cbAt,
|
|
20183
|
-
body:
|
|
20277
|
+
body: cbBody,
|
|
20184
20278
|
fromMe: false,
|
|
20185
20279
|
senderId: decision.senderId,
|
|
20186
20280
|
senderName: decision.senderDisplay,
|
|
20187
20281
|
chatId: String(decision.chatId),
|
|
20188
20282
|
channelKey: cbKey,
|
|
20189
|
-
scope:
|
|
20283
|
+
scope: entry3.role === "admin" ? "admin" : "public",
|
|
20190
20284
|
origin: "inbound",
|
|
20191
20285
|
createdAt: cbAt
|
|
20192
20286
|
});
|
|
@@ -20202,7 +20296,7 @@ app11.post("/", async (c) => {
|
|
|
20202
20296
|
session,
|
|
20203
20297
|
accountId,
|
|
20204
20298
|
cfg: load.value,
|
|
20205
|
-
token:
|
|
20299
|
+
token: entry3.token,
|
|
20206
20300
|
botId,
|
|
20207
20301
|
updateId: decision.updateId,
|
|
20208
20302
|
chatId: decision.chatId,
|
|
@@ -20228,22 +20322,22 @@ app11.post("/", async (c) => {
|
|
|
20228
20322
|
}
|
|
20229
20323
|
}
|
|
20230
20324
|
const answerStartedAt = Date.now();
|
|
20231
|
-
const answered4 = await answerTelegramCallback(
|
|
20325
|
+
const answered4 = await answerTelegramCallback(entry3.token, decision.callbackId, answerText);
|
|
20232
20326
|
const outcome = !answered4.ok ? "error" : decision.allowed ? "stored" : "refused";
|
|
20233
20327
|
console.error(
|
|
20234
20328
|
`${TAG34} op=callback-answered botId=${botId} callbackId=${decision.callbackId} ms=${Date.now() - answerStartedAt} outcome=${outcome}${answered4.error ? ` error=${logValue2(answered4.error)}` : ""}`
|
|
20235
20329
|
);
|
|
20236
20330
|
if (decision.allowed && parseDispatchCallback(decision.data) === null) {
|
|
20237
|
-
const cbActive = entryActive(
|
|
20331
|
+
const cbActive = entryActive(entry3, accountDir, botId);
|
|
20238
20332
|
console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${cbActive}`);
|
|
20239
20333
|
raiseChannelTurn({
|
|
20240
20334
|
accountId,
|
|
20241
20335
|
accountDir,
|
|
20242
20336
|
botId,
|
|
20243
|
-
botToken:
|
|
20244
|
-
role:
|
|
20337
|
+
botToken: entry3.token,
|
|
20338
|
+
role: entry3.role,
|
|
20245
20339
|
agentSlug,
|
|
20246
|
-
specialist:
|
|
20340
|
+
specialist: entry3.role === "specialist" ? entry3.specialist : null,
|
|
20247
20341
|
active: cbActive,
|
|
20248
20342
|
senderId: decision.senderId,
|
|
20249
20343
|
senderDisplay: decision.senderDisplay,
|
|
@@ -20253,7 +20347,7 @@ app11.post("/", async (c) => {
|
|
|
20253
20347
|
text: `[button: ${decision.data ?? "no-data"}]`,
|
|
20254
20348
|
channelKey: telegramChannelKey(botId, cbSubject),
|
|
20255
20349
|
conversationSubject: cbSubject,
|
|
20256
|
-
scope:
|
|
20350
|
+
scope: entry3.role === "admin" ? "admin" : "public",
|
|
20257
20351
|
sessionId: telegramAdminSessionId(accountId, cbSubject, botId)
|
|
20258
20352
|
});
|
|
20259
20353
|
}
|
|
@@ -20269,7 +20363,7 @@ app11.post("/", async (c) => {
|
|
|
20269
20363
|
);
|
|
20270
20364
|
if (!decision.allowed) return c.json({ ok: true }, 200);
|
|
20271
20365
|
const pollKey = telegramChannelKey(botId, decision.senderId);
|
|
20272
|
-
const pollScope =
|
|
20366
|
+
const pollScope = entry3.role === "admin" ? "admin" : "public";
|
|
20273
20367
|
const pollSession = telegramAdminSessionId(accountId, decision.senderId, botId);
|
|
20274
20368
|
const pollAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20275
20369
|
const pollBody = `[poll vote: poll=${decision.pollId} options=${options}]`;
|
|
@@ -20287,16 +20381,16 @@ app11.post("/", async (c) => {
|
|
|
20287
20381
|
origin: "inbound",
|
|
20288
20382
|
createdAt: pollAt
|
|
20289
20383
|
});
|
|
20290
|
-
const pollActive = entryActive(
|
|
20384
|
+
const pollActive = entryActive(entry3, accountDir, botId);
|
|
20291
20385
|
console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${pollActive}`);
|
|
20292
20386
|
raiseChannelTurn({
|
|
20293
20387
|
accountId,
|
|
20294
20388
|
accountDir,
|
|
20295
20389
|
botId,
|
|
20296
|
-
botToken:
|
|
20297
|
-
role:
|
|
20390
|
+
botToken: entry3.token,
|
|
20391
|
+
role: entry3.role,
|
|
20298
20392
|
agentSlug,
|
|
20299
|
-
specialist:
|
|
20393
|
+
specialist: entry3.role === "specialist" ? entry3.specialist : null,
|
|
20300
20394
|
active: pollActive,
|
|
20301
20395
|
senderId: decision.senderId,
|
|
20302
20396
|
senderDisplay: decision.senderDisplay,
|
|
@@ -20320,7 +20414,7 @@ app11.post("/", async (c) => {
|
|
|
20320
20414
|
`${TAG34} op=reaction-authorised botId=${botId} senderId=${decision.senderId} allowed=${decision.allowed} reason=${decision.reason}`
|
|
20321
20415
|
);
|
|
20322
20416
|
if (!decision.allowed) return c.json({ ok: true }, 200);
|
|
20323
|
-
const rScope =
|
|
20417
|
+
const rScope = entry3.role === "admin" ? "admin" : "public";
|
|
20324
20418
|
const rSession = telegramAdminSessionId(accountId, rSubject, botId);
|
|
20325
20419
|
const rAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20326
20420
|
const rTarget = quotedBody === null ? `message ${decision.messageId} \u2014 not held in this conversation` : `"${quotedBody}"`;
|
|
@@ -20339,16 +20433,16 @@ app11.post("/", async (c) => {
|
|
|
20339
20433
|
origin: "inbound",
|
|
20340
20434
|
createdAt: rAt
|
|
20341
20435
|
});
|
|
20342
|
-
const rActive = entryActive(
|
|
20436
|
+
const rActive = entryActive(entry3, accountDir, botId);
|
|
20343
20437
|
console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${rActive}`);
|
|
20344
20438
|
raiseChannelTurn({
|
|
20345
20439
|
accountId,
|
|
20346
20440
|
accountDir,
|
|
20347
20441
|
botId,
|
|
20348
|
-
botToken:
|
|
20349
|
-
role:
|
|
20442
|
+
botToken: entry3.token,
|
|
20443
|
+
role: entry3.role,
|
|
20350
20444
|
agentSlug,
|
|
20351
|
-
specialist:
|
|
20445
|
+
specialist: entry3.role === "specialist" ? entry3.specialist : null,
|
|
20352
20446
|
active: rActive,
|
|
20353
20447
|
senderId: decision.senderId,
|
|
20354
20448
|
senderDisplay: decision.senderDisplay,
|
|
@@ -20386,7 +20480,7 @@ app11.post("/", async (c) => {
|
|
|
20386
20480
|
);
|
|
20387
20481
|
if (!decision.allowed) return c.json({ ok: true }, 200);
|
|
20388
20482
|
const sKey = telegramChannelKey(botId, decision.senderId);
|
|
20389
|
-
const sScope =
|
|
20483
|
+
const sScope = entry3.role === "admin" ? "admin" : "public";
|
|
20390
20484
|
const sSession = telegramAdminSessionId(accountId, decision.senderId, botId);
|
|
20391
20485
|
const sAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20392
20486
|
const sBody = `[subscription ${decision.state}: payload=${decision.payload}]`;
|
|
@@ -20404,16 +20498,16 @@ app11.post("/", async (c) => {
|
|
|
20404
20498
|
origin: "inbound",
|
|
20405
20499
|
createdAt: sAt
|
|
20406
20500
|
});
|
|
20407
|
-
const sActive = entryActive(
|
|
20501
|
+
const sActive = entryActive(entry3, accountDir, botId);
|
|
20408
20502
|
console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${sActive}`);
|
|
20409
20503
|
raiseChannelTurn({
|
|
20410
20504
|
accountId,
|
|
20411
20505
|
accountDir,
|
|
20412
20506
|
botId,
|
|
20413
|
-
botToken:
|
|
20414
|
-
role:
|
|
20507
|
+
botToken: entry3.token,
|
|
20508
|
+
role: entry3.role,
|
|
20415
20509
|
agentSlug,
|
|
20416
|
-
specialist:
|
|
20510
|
+
specialist: entry3.role === "specialist" ? entry3.specialist : null,
|
|
20417
20511
|
active: sActive,
|
|
20418
20512
|
senderId: decision.senderId,
|
|
20419
20513
|
senderDisplay: decision.senderDisplay,
|
|
@@ -20432,7 +20526,7 @@ app11.post("/", async (c) => {
|
|
|
20432
20526
|
`${TAG34} op=poll-closed botId=${botId} pollId=${logValue2(decision.pollId)} options=${decision.options.length} totalVotes=${decision.totalVotes} targetHeld=${where === null ? "no" : "yes"}`
|
|
20433
20527
|
);
|
|
20434
20528
|
if (where === null) return c.json({ ok: true }, 200);
|
|
20435
|
-
const plScope =
|
|
20529
|
+
const plScope = entry3.role === "admin" ? "admin" : "public";
|
|
20436
20530
|
const plSession = telegramAdminSessionId(accountId, where.senderId, botId);
|
|
20437
20531
|
const plAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20438
20532
|
const tally = decision.options.map((o) => `${o.text}:${o.votes}`).join(", ") || "no options";
|
|
@@ -20451,16 +20545,16 @@ app11.post("/", async (c) => {
|
|
|
20451
20545
|
origin: "inbound",
|
|
20452
20546
|
createdAt: plAt
|
|
20453
20547
|
});
|
|
20454
|
-
const plActive = entryActive(
|
|
20548
|
+
const plActive = entryActive(entry3, accountDir, botId);
|
|
20455
20549
|
console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${plActive}`);
|
|
20456
20550
|
raiseChannelTurn({
|
|
20457
20551
|
accountId,
|
|
20458
20552
|
accountDir,
|
|
20459
20553
|
botId,
|
|
20460
|
-
botToken:
|
|
20461
|
-
role:
|
|
20554
|
+
botToken: entry3.token,
|
|
20555
|
+
role: entry3.role,
|
|
20462
20556
|
agentSlug,
|
|
20463
|
-
specialist:
|
|
20557
|
+
specialist: entry3.role === "specialist" ? entry3.specialist : null,
|
|
20464
20558
|
active: plActive,
|
|
20465
20559
|
senderId: where.senderId,
|
|
20466
20560
|
chatId: Number(where.chatId),
|
|
@@ -20489,7 +20583,7 @@ app11.post("/", async (c) => {
|
|
|
20489
20583
|
console.error(
|
|
20490
20584
|
`${TAG34} op=managed-bot botId=${botId} event=${event} newBotId=${newBotId} creator=${decision.creatorId ?? "none"}`
|
|
20491
20585
|
);
|
|
20492
|
-
const fetched = await getManagedBotToken(
|
|
20586
|
+
const fetched = await getManagedBotToken(entry3.token, Number(newBotId));
|
|
20493
20587
|
console.error(
|
|
20494
20588
|
`${PROVISION_TAG} op=provision-token botId=${botId} newBotId=${newBotId} fetched=${fetched.ok ? "yes" : "no"} reason=${fetched.ok ? "ok" : "api-error"}`
|
|
20495
20589
|
);
|
|
@@ -20499,7 +20593,7 @@ app11.post("/", async (c) => {
|
|
|
20499
20593
|
accountDir,
|
|
20500
20594
|
existing ? { ...existing, token: fetched.token, managed: true } : { id: newBotId, token: fetched.token, role: "unbound", managed: true }
|
|
20501
20595
|
);
|
|
20502
|
-
const restricted = await setManagedBotAccessSettings(
|
|
20596
|
+
const restricted = await setManagedBotAccessSettings(entry3.token, Number(newBotId), true, void 0);
|
|
20503
20597
|
console.error(
|
|
20504
20598
|
`${PROVISION_TAG} op=provision-persist botId=${botId} newBotId=${newBotId} written=${persisted.ok ? "yes" : "no"} restricted=${restricted.ok ? "yes" : "no"}`
|
|
20505
20599
|
);
|
|
@@ -20515,7 +20609,7 @@ app11.post("/", async (c) => {
|
|
|
20515
20609
|
accountId,
|
|
20516
20610
|
accountDir,
|
|
20517
20611
|
botId,
|
|
20518
|
-
botToken:
|
|
20612
|
+
botToken: entry3.token,
|
|
20519
20613
|
// `authorised` above already established this entry is role 'admin', so
|
|
20520
20614
|
// the admin agent is the one that runs and it is active by construction.
|
|
20521
20615
|
role: "admin",
|
|
@@ -20588,7 +20682,7 @@ app11.post("/", async (c) => {
|
|
|
20588
20682
|
});
|
|
20589
20683
|
}
|
|
20590
20684
|
const bizMedia = await downloadTelegramMedia({
|
|
20591
|
-
token:
|
|
20685
|
+
token: entry3.token,
|
|
20592
20686
|
botId,
|
|
20593
20687
|
items: decision.media
|
|
20594
20688
|
});
|
|
@@ -20628,7 +20722,7 @@ app11.post("/", async (c) => {
|
|
|
20628
20722
|
return;
|
|
20629
20723
|
}
|
|
20630
20724
|
const sent = await sendTelegramText(
|
|
20631
|
-
|
|
20725
|
+
entry3.token,
|
|
20632
20726
|
decision.chatId,
|
|
20633
20727
|
replyText,
|
|
20634
20728
|
void 0,
|
|
@@ -20657,7 +20751,7 @@ app11.post("/", async (c) => {
|
|
|
20657
20751
|
accountId,
|
|
20658
20752
|
accountDir,
|
|
20659
20753
|
botId,
|
|
20660
|
-
botToken:
|
|
20754
|
+
botToken: entry3.token,
|
|
20661
20755
|
agentSlug,
|
|
20662
20756
|
senderId: decision.senderId,
|
|
20663
20757
|
// Task 2615 — load-bearing. The gateway keys the hub, the reply closure,
|
|
@@ -20707,7 +20801,7 @@ app11.post("/", async (c) => {
|
|
|
20707
20801
|
console.error(`${TAG34} op=access botId=${botId} senderId=${senderId} allowed=true reason=${decision.reason}`);
|
|
20708
20802
|
const subject = telegramConversationSubject(decision.chatType, chatId, senderId);
|
|
20709
20803
|
const channelKey = telegramChannelKey(botId, subject);
|
|
20710
|
-
const scope =
|
|
20804
|
+
const scope = entry3.role === "public" ? "public" : "admin";
|
|
20711
20805
|
const sessionId = telegramAdminSessionId(accountId, subject, botId);
|
|
20712
20806
|
const inboundAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20713
20807
|
const editSuffix = decision.isEdit ? `:edit:${decision.editDate}` : "";
|
|
@@ -20788,10 +20882,10 @@ app11.post("/", async (c) => {
|
|
|
20788
20882
|
});
|
|
20789
20883
|
}
|
|
20790
20884
|
}
|
|
20791
|
-
const active = entryActive(
|
|
20792
|
-
if (
|
|
20885
|
+
const active = entryActive(entry3, accountDir, botId, (rosterValid) => {
|
|
20886
|
+
if (entry3.role !== "specialist") return;
|
|
20793
20887
|
console.error(
|
|
20794
|
-
`${TAG34} op=specialist-inbound botId=${botId} specialist=${logValue2(
|
|
20888
|
+
`${TAG34} op=specialist-inbound botId=${botId} specialist=${logValue2(entry3.specialist)} senderId=${senderId} sessionId=${sessionId} rosterValid=${rosterValid ? "yes" : "no"}`
|
|
20795
20889
|
);
|
|
20796
20890
|
});
|
|
20797
20891
|
console.error(`${TAG34} op=agent botId=${botId} slug=${agentSlug} active=${active}`);
|
|
@@ -20806,7 +20900,7 @@ app11.post("/", async (c) => {
|
|
|
20806
20900
|
}
|
|
20807
20901
|
const replyThreadId = decision.threadId ?? void 0;
|
|
20808
20902
|
const reply = async (replyText) => {
|
|
20809
|
-
const sent = await sendTelegramText(
|
|
20903
|
+
const sent = await sendTelegramText(entry3.token, chatId, replyText, replyThreadId);
|
|
20810
20904
|
if (sent.ok) {
|
|
20811
20905
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
20812
20906
|
appendMessage2(accountId, {
|
|
@@ -20826,7 +20920,7 @@ app11.post("/", async (c) => {
|
|
|
20826
20920
|
console.error(`${TAG34} op=reply-sent botId=${botId} ok=true`);
|
|
20827
20921
|
} else console.error(`${TAG34} op=reply-dropped botId=${botId} reason=${sent.error}`);
|
|
20828
20922
|
};
|
|
20829
|
-
const media = await downloadTelegramMedia({ token:
|
|
20923
|
+
const media = await downloadTelegramMedia({ token: entry3.token, botId, items: decision.media });
|
|
20830
20924
|
if (media.length > 0) {
|
|
20831
20925
|
const attachmentIds = await storeTelegramServable({
|
|
20832
20926
|
accountId,
|
|
@@ -20952,7 +21046,7 @@ app11.post("/", async (c) => {
|
|
|
20952
21046
|
accountId,
|
|
20953
21047
|
accountDir,
|
|
20954
21048
|
botId,
|
|
20955
|
-
botToken:
|
|
21049
|
+
botToken: entry3.token,
|
|
20956
21050
|
agentSlug,
|
|
20957
21051
|
senderId,
|
|
20958
21052
|
// Task 2616 — the name beside the id on the turn's provenance line.
|
|
@@ -20962,10 +21056,10 @@ app11.post("/", async (c) => {
|
|
|
20962
21056
|
// room reach one channel server rather than opening five sessions whose
|
|
20963
21057
|
// reply closures all target the same chat. A DM subject IS the sender id.
|
|
20964
21058
|
conversationSubject: subject,
|
|
20965
|
-
role:
|
|
21059
|
+
role: entry3.role,
|
|
20966
21060
|
personId: null,
|
|
20967
21061
|
// Task 2619 — the card this bot runs as, null on every other role.
|
|
20968
|
-
specialist:
|
|
21062
|
+
specialist: entry3.role === "specialist" ? entry3.specialist : null,
|
|
20969
21063
|
chatId: String(chatId),
|
|
20970
21064
|
text: turnText,
|
|
20971
21065
|
media: heard.media,
|
|
@@ -21369,9 +21463,9 @@ function computeAdminStoreDivergence(input) {
|
|
|
21369
21463
|
result.errors.push({ source: input.accountsDir, detail: err instanceof Error ? err.message : String(err) });
|
|
21370
21464
|
return result;
|
|
21371
21465
|
}
|
|
21372
|
-
for (const
|
|
21373
|
-
if (
|
|
21374
|
-
const accountDir = join42(input.accountsDir,
|
|
21466
|
+
for (const entry3 of entries2) {
|
|
21467
|
+
if (entry3.startsWith(".")) continue;
|
|
21468
|
+
const accountDir = join42(input.accountsDir, entry3);
|
|
21375
21469
|
try {
|
|
21376
21470
|
if (!statSync20(accountDir).isDirectory()) continue;
|
|
21377
21471
|
} catch {
|
|
@@ -23508,12 +23602,12 @@ function authoredAgentFiles(accountDir) {
|
|
|
23508
23602
|
const dir = resolve28(pluginsDir, plugin, "agents");
|
|
23509
23603
|
if (!existsSync38(dir)) continue;
|
|
23510
23604
|
try {
|
|
23511
|
-
for (const
|
|
23512
|
-
if (!
|
|
23513
|
-
const path = resolve28(dir,
|
|
23514
|
-
const paths = byName.get(
|
|
23605
|
+
for (const entry3 of readdirSync24(dir)) {
|
|
23606
|
+
if (!entry3.endsWith(".md")) continue;
|
|
23607
|
+
const path = resolve28(dir, entry3);
|
|
23608
|
+
const paths = byName.get(entry3);
|
|
23515
23609
|
if (paths) paths.push(path);
|
|
23516
|
-
else byName.set(
|
|
23610
|
+
else byName.set(entry3, [path]);
|
|
23517
23611
|
}
|
|
23518
23612
|
} catch (err) {
|
|
23519
23613
|
console.error(`[admin/agents] op=authored-scan read-failed plugin=${plugin} error="${err}"`);
|
|
@@ -23582,7 +23676,7 @@ function listShipped(accountDir, riskByTool, disabled, riskSurfaceFailed, author
|
|
|
23582
23676
|
for (const dir of dirs) {
|
|
23583
23677
|
if (!existsSync38(dir)) continue;
|
|
23584
23678
|
try {
|
|
23585
|
-
for (const
|
|
23679
|
+
for (const entry3 of readdirSync24(dir)) if (entry3.endsWith(".md")) names.add(entry3);
|
|
23586
23680
|
} catch (err) {
|
|
23587
23681
|
console.error(`[admin/agents] op=list-shipped read-failed dir=${dir} error="${err}"`);
|
|
23588
23682
|
}
|
|
@@ -23676,19 +23770,19 @@ function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
|
|
|
23676
23770
|
} catch {
|
|
23677
23771
|
continue;
|
|
23678
23772
|
}
|
|
23679
|
-
for (const
|
|
23680
|
-
if (!
|
|
23773
|
+
for (const entry3 of entries2) {
|
|
23774
|
+
if (!entry3.isFile() || !entry3.name.endsWith(".md")) continue;
|
|
23681
23775
|
try {
|
|
23682
|
-
const { fm } = splitFrontmatter(readFileSync38(resolve28(agentsDirP,
|
|
23776
|
+
const { fm } = splitFrontmatter(readFileSync38(resolve28(agentsDirP, entry3.name), "utf-8"));
|
|
23683
23777
|
if (!fm.name) {
|
|
23684
23778
|
specialistsSkipped++;
|
|
23685
|
-
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${
|
|
23779
|
+
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry3.name}`);
|
|
23686
23780
|
continue;
|
|
23687
23781
|
}
|
|
23688
23782
|
const specialistTools = parseToolsLine(fm.tools);
|
|
23689
23783
|
const r = classify(specialistTools, riskByTool, riskSurfaceFailed);
|
|
23690
23784
|
specialists.push({
|
|
23691
|
-
slug:
|
|
23785
|
+
slug: entry3.name.replace(/\.md$/, ""),
|
|
23692
23786
|
displayName: fm.name,
|
|
23693
23787
|
kind: "specialist",
|
|
23694
23788
|
origin: "specialist",
|
|
@@ -23711,11 +23805,11 @@ function listSpecialists(accountDir, riskByTool, riskSurfaceFailed) {
|
|
|
23711
23805
|
// have the surface claim an agent is stopped while it is still being
|
|
23712
23806
|
// spawned. Absence from the live dir alone is also not the test: a
|
|
23713
23807
|
// specialist that was never activated was never switched off either.
|
|
23714
|
-
disabled: existsSync38(resolve28(accountDir, ...QUARANTINE_DIR,
|
|
23808
|
+
disabled: existsSync38(resolve28(accountDir, ...QUARANTINE_DIR, entry3.name)) && !existsSync38(resolve28(accountDir, ...SPECIALISTS_DIR, entry3.name))
|
|
23715
23809
|
});
|
|
23716
23810
|
} catch {
|
|
23717
23811
|
specialistsSkipped++;
|
|
23718
|
-
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${
|
|
23812
|
+
console.error(`[admin/agents] op=list-specialist-skip plugin=${plugin} file=${entry3.name}`);
|
|
23719
23813
|
}
|
|
23720
23814
|
}
|
|
23721
23815
|
}
|
|
@@ -23742,16 +23836,16 @@ app22.get("/", requireAdminSession, (c) => {
|
|
|
23742
23836
|
if (existsSync38(agentsDir)) {
|
|
23743
23837
|
try {
|
|
23744
23838
|
const entries2 = readdirSync24(agentsDir, { withFileTypes: true });
|
|
23745
|
-
for (const
|
|
23746
|
-
if (!
|
|
23747
|
-
if (
|
|
23748
|
-
const configPath3 = resolve28(agentsDir,
|
|
23839
|
+
for (const entry3 of entries2.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
23840
|
+
if (!entry3.isDirectory()) continue;
|
|
23841
|
+
if (entry3.name === "admin") continue;
|
|
23842
|
+
const configPath3 = resolve28(agentsDir, entry3.name, "config.json");
|
|
23749
23843
|
if (!existsSync38(configPath3)) continue;
|
|
23750
23844
|
try {
|
|
23751
23845
|
const config = JSON.parse(readFileSync38(configPath3, "utf-8"));
|
|
23752
23846
|
agents.push({
|
|
23753
|
-
slug:
|
|
23754
|
-
displayName: config.displayName ??
|
|
23847
|
+
slug: entry3.name,
|
|
23848
|
+
displayName: config.displayName ?? entry3.name,
|
|
23755
23849
|
status: config.status ?? "unknown",
|
|
23756
23850
|
kind: "public",
|
|
23757
23851
|
origin: "public",
|
|
@@ -23764,7 +23858,7 @@ app22.get("/", requireAdminSession, (c) => {
|
|
|
23764
23858
|
});
|
|
23765
23859
|
} catch {
|
|
23766
23860
|
skipped++;
|
|
23767
|
-
console.error(`[admin/agents] failed to parse config.json for agent "${
|
|
23861
|
+
console.error(`[admin/agents] failed to parse config.json for agent "${entry3.name}" \u2014 skipping`);
|
|
23768
23862
|
}
|
|
23769
23863
|
}
|
|
23770
23864
|
} catch (err) {
|
|
@@ -24418,7 +24512,7 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
24418
24512
|
if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
|
|
24419
24513
|
try {
|
|
24420
24514
|
const flushed = await listAdminSessions(accountId, userId, 20);
|
|
24421
|
-
const
|
|
24515
|
+
const sessions4 = flushed.map((r) => ({
|
|
24422
24516
|
sessionId: r.sessionId,
|
|
24423
24517
|
cacheKey: null,
|
|
24424
24518
|
name: r.name,
|
|
@@ -24426,15 +24520,15 @@ app23.get("/", requireAdminSession, async (c) => {
|
|
|
24426
24520
|
phase: "flushed",
|
|
24427
24521
|
channel: r.channel
|
|
24428
24522
|
})).sort((a, b) => a.updatedAt < b.updatedAt ? 1 : a.updatedAt > b.updatedAt ? -1 : 0).slice(0, 20);
|
|
24429
|
-
const channelCounts =
|
|
24523
|
+
const channelCounts = sessions4.reduce((acc, s) => {
|
|
24430
24524
|
const k = s.channel ?? "unknown";
|
|
24431
24525
|
acc[k] = (acc[k] ?? 0) + 1;
|
|
24432
24526
|
return acc;
|
|
24433
24527
|
}, {});
|
|
24434
24528
|
console.error(
|
|
24435
|
-
`[conversations-list] render rows=${
|
|
24529
|
+
`[conversations-list] render rows=${sessions4.length} channels=${JSON.stringify(channelCounts)}`
|
|
24436
24530
|
);
|
|
24437
|
-
return c.json({ sessions:
|
|
24531
|
+
return c.json({ sessions: sessions4 });
|
|
24438
24532
|
} catch (err) {
|
|
24439
24533
|
console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
24440
24534
|
return c.json({ error: "Failed to fetch sessions" }, 500);
|
|
@@ -26099,14 +26193,14 @@ async function walkSubtree(root, dataRoot, out) {
|
|
|
26099
26193
|
return { clean: false };
|
|
26100
26194
|
}
|
|
26101
26195
|
let clean = true;
|
|
26102
|
-
for (const
|
|
26103
|
-
if (
|
|
26104
|
-
if (
|
|
26105
|
-
const abs = join49(root,
|
|
26106
|
-
if (
|
|
26196
|
+
for (const entry3 of entries2) {
|
|
26197
|
+
if (entry3.isSymbolicLink()) continue;
|
|
26198
|
+
if (entry3.isDirectory() && entry3.name === ".uploads-tmp") continue;
|
|
26199
|
+
const abs = join49(root, entry3.name);
|
|
26200
|
+
if (entry3.isDirectory()) {
|
|
26107
26201
|
const sub = await walkSubtree(abs, dataRoot, out);
|
|
26108
26202
|
if (!sub.clean) clean = false;
|
|
26109
|
-
} else if (
|
|
26203
|
+
} else if (entry3.isFile()) {
|
|
26110
26204
|
try {
|
|
26111
26205
|
const st = await fsp.stat(abs);
|
|
26112
26206
|
out.push({
|
|
@@ -26395,14 +26489,14 @@ function buildZip(entries2) {
|
|
|
26395
26489
|
const localChunks = [];
|
|
26396
26490
|
const centralChunks = [];
|
|
26397
26491
|
let offset = 0;
|
|
26398
|
-
for (const
|
|
26399
|
-
const nameBytes = Buffer.from(
|
|
26400
|
-
const crc = crc32(
|
|
26401
|
-
const uncompressedSize =
|
|
26402
|
-
const deflated = deflateRawSync(
|
|
26492
|
+
for (const entry3 of entries2) {
|
|
26493
|
+
const nameBytes = Buffer.from(entry3.name, "utf8");
|
|
26494
|
+
const crc = crc32(entry3.data);
|
|
26495
|
+
const uncompressedSize = entry3.data.length;
|
|
26496
|
+
const deflated = deflateRawSync(entry3.data);
|
|
26403
26497
|
const useDeflate = deflated.length < uncompressedSize;
|
|
26404
26498
|
const method = useDeflate ? 8 : 0;
|
|
26405
|
-
const payload = useDeflate ? deflated :
|
|
26499
|
+
const payload = useDeflate ? deflated : entry3.data;
|
|
26406
26500
|
const compressedSize = payload.length;
|
|
26407
26501
|
const localHeader = Buffer.alloc(30);
|
|
26408
26502
|
localHeader.writeUInt32LE(LOCAL_SIG, 0);
|
|
@@ -26454,13 +26548,13 @@ function buildZip(entries2) {
|
|
|
26454
26548
|
}
|
|
26455
26549
|
|
|
26456
26550
|
// server/lib/account-root-groups.ts
|
|
26457
|
-
var
|
|
26551
|
+
var import_dist9 = __toESM(require_dist7(), 1);
|
|
26458
26552
|
function classifyAccountRoot(schemaMd, rootEntries) {
|
|
26459
|
-
const regions = (0,
|
|
26553
|
+
const regions = (0, import_dist9.parseSchemaRegions)(schemaMd);
|
|
26460
26554
|
if (!regions.parsed) {
|
|
26461
26555
|
return { home: [], system: [...rootEntries], unknown: [], parsed: false, reason: regions.reason };
|
|
26462
26556
|
}
|
|
26463
|
-
const homeOrder = [...regions.ontologyRoots, ...
|
|
26557
|
+
const homeOrder = [...regions.ontologyRoots, ...import_dist9.HOME_FIXED].filter((d, i, a) => a.indexOf(d) === i);
|
|
26464
26558
|
const homeSet = new Set(homeOrder);
|
|
26465
26559
|
const present = new Set(rootEntries);
|
|
26466
26560
|
const home = homeOrder.filter((d) => present.has(d));
|
|
@@ -26528,47 +26622,47 @@ async function servableFilesIn(dirAbs, uuid) {
|
|
|
26528
26622
|
}
|
|
26529
26623
|
return out;
|
|
26530
26624
|
}
|
|
26531
|
-
async function enrich(absolute,
|
|
26532
|
-
if (
|
|
26533
|
-
const dirAbs = join50(absolute,
|
|
26534
|
-
const meta = await readMeta2(dirAbs,
|
|
26625
|
+
async function enrich(absolute, entry3, accountNames) {
|
|
26626
|
+
if (entry3.kind === "directory" && UUID_RE3.test(entry3.name)) {
|
|
26627
|
+
const dirAbs = join50(absolute, entry3.name);
|
|
26628
|
+
const meta = await readMeta2(dirAbs, entry3.name);
|
|
26535
26629
|
if (meta?.filename) {
|
|
26536
|
-
const servable = await servableFilesIn(dirAbs,
|
|
26630
|
+
const servable = await servableFilesIn(dirAbs, entry3.name);
|
|
26537
26631
|
if (servable.length === 1) {
|
|
26538
26632
|
const innerName = servable[0];
|
|
26539
26633
|
let size = null;
|
|
26540
|
-
let modifiedAt =
|
|
26634
|
+
let modifiedAt = entry3.modifiedAt;
|
|
26541
26635
|
try {
|
|
26542
26636
|
const st = await stat6(join50(dirAbs, innerName));
|
|
26543
26637
|
size = st.size;
|
|
26544
26638
|
modifiedAt = st.mtime.toISOString();
|
|
26545
26639
|
} catch {
|
|
26546
26640
|
}
|
|
26547
|
-
|
|
26548
|
-
|
|
26549
|
-
|
|
26550
|
-
|
|
26551
|
-
|
|
26552
|
-
|
|
26641
|
+
entry3.kind = "file";
|
|
26642
|
+
entry3.displayName = meta.filename;
|
|
26643
|
+
entry3.mimeType = meta.mimeType;
|
|
26644
|
+
entry3.sizeBytes = size;
|
|
26645
|
+
entry3.modifiedAt = modifiedAt;
|
|
26646
|
+
entry3.entryPath = `${entry3.name}/${innerName}`;
|
|
26553
26647
|
return "flattened";
|
|
26554
26648
|
}
|
|
26555
|
-
|
|
26649
|
+
entry3.displayName = meta.filename;
|
|
26556
26650
|
return "kept-as-dir";
|
|
26557
26651
|
}
|
|
26558
|
-
const accountName = accountNames.get(
|
|
26652
|
+
const accountName = accountNames.get(entry3.name);
|
|
26559
26653
|
if (accountName) {
|
|
26560
|
-
|
|
26654
|
+
entry3.displayName = accountName;
|
|
26561
26655
|
}
|
|
26562
26656
|
return null;
|
|
26563
26657
|
}
|
|
26564
|
-
if (
|
|
26565
|
-
const dot =
|
|
26566
|
-
const base = dot === -1 ?
|
|
26658
|
+
if (entry3.kind === "file") {
|
|
26659
|
+
const dot = entry3.name.lastIndexOf(".");
|
|
26660
|
+
const base = dot === -1 ? entry3.name : entry3.name.slice(0, dot);
|
|
26567
26661
|
if (UUID_RE3.test(base)) {
|
|
26568
26662
|
const meta = await readMeta2(absolute, base);
|
|
26569
26663
|
if (meta?.filename) {
|
|
26570
|
-
|
|
26571
|
-
|
|
26664
|
+
entry3.displayName = meta.filename;
|
|
26665
|
+
entry3.mimeType = meta.mimeType;
|
|
26572
26666
|
}
|
|
26573
26667
|
}
|
|
26574
26668
|
}
|
|
@@ -27603,7 +27697,7 @@ app28.post("/rename", requireAdminSession, async (c) => {
|
|
|
27603
27697
|
var files_default = app28;
|
|
27604
27698
|
|
|
27605
27699
|
// ../lib/graph-search/src/index.ts
|
|
27606
|
-
var
|
|
27700
|
+
var import_dist10 = __toESM(require_dist8());
|
|
27607
27701
|
import { int } from "neo4j-driver";
|
|
27608
27702
|
|
|
27609
27703
|
// ../lib/graph-search/src/rrf-fusion.ts
|
|
@@ -27853,7 +27947,7 @@ async function bm25Only(session, params) {
|
|
|
27853
27947
|
${scopeClause}
|
|
27854
27948
|
${agentClause}
|
|
27855
27949
|
${labelClause}
|
|
27856
|
-
AND ${(0,
|
|
27950
|
+
AND ${(0, import_dist10.notTrashed)("node")}
|
|
27857
27951
|
${kwClause}
|
|
27858
27952
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
27859
27953
|
ORDER BY score DESC
|
|
@@ -28015,7 +28109,7 @@ async function hybrid(session, embed2, params) {
|
|
|
28015
28109
|
WHERE node.accountId = $accountId
|
|
28016
28110
|
${scopeClause}
|
|
28017
28111
|
${agentClause}
|
|
28018
|
-
AND ${(0,
|
|
28112
|
+
AND ${(0, import_dist10.notTrashed)("node")}
|
|
28019
28113
|
${keywordClause}
|
|
28020
28114
|
RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
|
|
28021
28115
|
ORDER BY score DESC
|
|
@@ -28114,7 +28208,7 @@ async function hybrid(session, embed2, params) {
|
|
|
28114
28208
|
const propResult = await session.run(
|
|
28115
28209
|
`MATCH (node)
|
|
28116
28210
|
WHERE node.accountId = $accountId
|
|
28117
|
-
AND ${(0,
|
|
28211
|
+
AND ${(0, import_dist10.notTrashed)("node")}
|
|
28118
28212
|
AND node.keywords IS NOT NULL
|
|
28119
28213
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
28120
28214
|
${propScope.clause}
|
|
@@ -28166,7 +28260,7 @@ async function hybrid(session, embed2, params) {
|
|
|
28166
28260
|
const propResult = await session.run(
|
|
28167
28261
|
`MATCH (node)
|
|
28168
28262
|
WHERE node.accountId = $accountId
|
|
28169
|
-
AND ${(0,
|
|
28263
|
+
AND ${(0, import_dist10.notTrashed)("node")}
|
|
28170
28264
|
AND node.keywords IS NOT NULL
|
|
28171
28265
|
AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
|
|
28172
28266
|
${propScope.clause}
|
|
@@ -28273,7 +28367,7 @@ async function hybrid(session, embed2, params) {
|
|
|
28273
28367
|
`UNWIND $nodeIds AS nid
|
|
28274
28368
|
MATCH (n)-[r]-(related)
|
|
28275
28369
|
WHERE elementId(n) = nid
|
|
28276
|
-
AND ${(0,
|
|
28370
|
+
AND ${(0, import_dist10.notTrashed)("related")}
|
|
28277
28371
|
${expandScopeClause}
|
|
28278
28372
|
${expandAgentClause}
|
|
28279
28373
|
WITH nid, n, r, related
|
|
@@ -28501,8 +28595,8 @@ var graph_search_default = app29;
|
|
|
28501
28595
|
import neo4j2 from "neo4j-driver";
|
|
28502
28596
|
|
|
28503
28597
|
// app/lib/graph-labels.ts
|
|
28504
|
-
var
|
|
28505
|
-
var
|
|
28598
|
+
var import_dist11 = __toESM(require_dist9(), 1);
|
|
28599
|
+
var import_dist12 = __toESM(require_dist9(), 1);
|
|
28506
28600
|
var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
|
|
28507
28601
|
/* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
|
|
28508
28602
|
);
|
|
@@ -28586,14 +28680,14 @@ var EXCLUDED_EDGE_TYPES = Object.freeze(
|
|
|
28586
28680
|
])
|
|
28587
28681
|
);
|
|
28588
28682
|
function isKnownLabel(label) {
|
|
28589
|
-
return Object.prototype.hasOwnProperty.call(
|
|
28683
|
+
return Object.prototype.hasOwnProperty.call(import_dist12.GRAPH_LABEL_COLOURS, label);
|
|
28590
28684
|
}
|
|
28591
28685
|
function isHiddenByDefault(label) {
|
|
28592
28686
|
return HIDDEN_BY_DEFAULT_LABELS.has(label);
|
|
28593
28687
|
}
|
|
28594
28688
|
|
|
28595
28689
|
// server/lib/top-level-labels.ts
|
|
28596
|
-
var
|
|
28690
|
+
var import_dist13 = __toESM(require_dist9(), 1);
|
|
28597
28691
|
import { readdirSync as readdirSync26, readFileSync as readFileSync41 } from "fs";
|
|
28598
28692
|
import { join as join51, resolve as resolve33 } from "path";
|
|
28599
28693
|
var STATIC_TOP_LEVEL_LABELS = Object.freeze(
|
|
@@ -28730,7 +28824,7 @@ function getTopLevelLabelAllowlist(opts = {}) {
|
|
|
28730
28824
|
const referencesDir = opts.referencesDir ?? resolveReferencesDir();
|
|
28731
28825
|
const { labels: derived, contributingFiles } = parseTableTopLevelLabels(referencesDir);
|
|
28732
28826
|
const excluded = [];
|
|
28733
|
-
for (const label of
|
|
28827
|
+
for (const label of import_dist13.ADDITIONAL_BASE_LABELS) {
|
|
28734
28828
|
if (derived.delete(label)) excluded.push(label);
|
|
28735
28829
|
}
|
|
28736
28830
|
const derivedCount = derived.size;
|
|
@@ -29982,8 +30076,8 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
|
|
|
29982
30076
|
if (!existsSync43(dir)) continue;
|
|
29983
30077
|
try {
|
|
29984
30078
|
const entries2 = await readdir5(dir);
|
|
29985
|
-
for (const
|
|
29986
|
-
if (
|
|
30079
|
+
for (const entry3 of entries2) {
|
|
30080
|
+
if (entry3.endsWith(".md")) names.add(entry3);
|
|
29987
30081
|
}
|
|
29988
30082
|
} catch (err) {
|
|
29989
30083
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -34247,8 +34341,8 @@ function readHouseAdminUserIds(accountsDir = ACCOUNTS_DIR) {
|
|
|
34247
34341
|
} catch {
|
|
34248
34342
|
return [];
|
|
34249
34343
|
}
|
|
34250
|
-
for (const
|
|
34251
|
-
const cfgPath = resolve42(accountsDir,
|
|
34344
|
+
for (const entry3 of entries2) {
|
|
34345
|
+
const cfgPath = resolve42(accountsDir, entry3, "account.json");
|
|
34252
34346
|
if (!existsSync50(cfgPath)) continue;
|
|
34253
34347
|
try {
|
|
34254
34348
|
const cfg = JSON.parse(readFileSync51(cfgPath, "utf-8"));
|
|
@@ -35596,9 +35690,9 @@ function originAllowed(origin, allowlist) {
|
|
|
35596
35690
|
try {
|
|
35597
35691
|
const u = new URL(origin);
|
|
35598
35692
|
const host = u.host;
|
|
35599
|
-
return allowlist.some((
|
|
35600
|
-
if (
|
|
35601
|
-
if (
|
|
35693
|
+
return allowlist.some((entry3) => {
|
|
35694
|
+
if (entry3 === host) return true;
|
|
35695
|
+
if (entry3.startsWith("*.")) return host.endsWith(entry3.slice(1));
|
|
35602
35696
|
return false;
|
|
35603
35697
|
});
|
|
35604
35698
|
} catch {
|
|
@@ -36875,7 +36969,7 @@ async function runConversationAudit() {
|
|
|
36875
36969
|
}
|
|
36876
36970
|
|
|
36877
36971
|
// app/lib/graph-health.ts
|
|
36878
|
-
var
|
|
36972
|
+
var import_dist15 = __toESM(require_dist10(), 1);
|
|
36879
36973
|
var HOUR_MS2 = 60 * 60 * 1e3;
|
|
36880
36974
|
function renderLabelTop(rows) {
|
|
36881
36975
|
return rows.map((b) => {
|
|
@@ -36920,7 +37014,7 @@ async function runGraphHealthTick() {
|
|
|
36920
37014
|
console.error(
|
|
36921
37015
|
`[graph-health] userprofile-multi accounts=${upAccounts} top=${upTop.length > 0 ? upTop.join(",") : "none"}`
|
|
36922
37016
|
);
|
|
36923
|
-
const indexed = [...
|
|
37017
|
+
const indexed = [...import_dist15.VECTOR_INDEXED_LABELS];
|
|
36924
37018
|
const embCount = await session.run(
|
|
36925
37019
|
`MATCH (n) WHERE n.embedding IS NULL AND any(l IN labels(n) WHERE l IN $indexed)
|
|
36926
37020
|
RETURN count(n) AS total`,
|
|
@@ -36994,7 +37088,7 @@ function startGraphHealthTimer() {
|
|
|
36994
37088
|
// app/lib/shared-folder-census.ts
|
|
36995
37089
|
import { existsSync as existsSync54, statSync as statSync29, readdirSync as readdirSync32 } from "fs";
|
|
36996
37090
|
import { join as join65 } from "path";
|
|
36997
|
-
var
|
|
37091
|
+
var import_dist16 = __toESM(require_dist(), 1);
|
|
36998
37092
|
var TAG64 = "[shared-census]";
|
|
36999
37093
|
var WIRED_SURFACES = 3;
|
|
37000
37094
|
function countFiles(dir) {
|
|
@@ -37012,7 +37106,7 @@ function countFiles(dir) {
|
|
|
37012
37106
|
return n;
|
|
37013
37107
|
}
|
|
37014
37108
|
function runSharedFolderCensus(dataRoot = DATA_ROOT) {
|
|
37015
|
-
const root = (0,
|
|
37109
|
+
const root = (0, import_dist16.sharedRoot)(dataRoot);
|
|
37016
37110
|
const exists = existsSync54(root);
|
|
37017
37111
|
let isDir = false;
|
|
37018
37112
|
if (exists) {
|
|
@@ -37060,15 +37154,15 @@ function surveyBrandCredentials(home) {
|
|
|
37060
37154
|
return [];
|
|
37061
37155
|
}
|
|
37062
37156
|
const rows = [];
|
|
37063
|
-
for (const
|
|
37064
|
-
if (!
|
|
37157
|
+
for (const entry3 of entries2.sort()) {
|
|
37158
|
+
if (!entry3.startsWith(".")) continue;
|
|
37065
37159
|
let raw;
|
|
37066
37160
|
try {
|
|
37067
|
-
raw = readFileSync57(join66(home,
|
|
37161
|
+
raw = readFileSync57(join66(home, entry3, ".claude", ".credentials.json"), "utf-8");
|
|
37068
37162
|
} catch {
|
|
37069
37163
|
continue;
|
|
37070
37164
|
}
|
|
37071
|
-
rows.push({ brand:
|
|
37165
|
+
rows.push({ brand: entry3.slice(1), refreshTokenLen: refreshTokenLength(raw) });
|
|
37072
37166
|
}
|
|
37073
37167
|
return rows;
|
|
37074
37168
|
}
|
|
@@ -37277,8 +37371,8 @@ async function migrateUploads(opts = {}) {
|
|
|
37277
37371
|
let moved = 0;
|
|
37278
37372
|
let skipped = 0;
|
|
37279
37373
|
try {
|
|
37280
|
-
for (const
|
|
37281
|
-
const name =
|
|
37374
|
+
for (const entry3 of topEntries) {
|
|
37375
|
+
const name = entry3.name;
|
|
37282
37376
|
if (ACCOUNT_UUID_RE4.test(name)) {
|
|
37283
37377
|
moved += await relocateTree(
|
|
37284
37378
|
resolve49(oldRoot, name),
|
|
@@ -37378,11 +37472,11 @@ async function collectLiveSidecars(projectsRoot) {
|
|
|
37378
37472
|
} catch {
|
|
37379
37473
|
continue;
|
|
37380
37474
|
}
|
|
37381
|
-
for (const
|
|
37382
|
-
if (
|
|
37383
|
-
out.push(join67(slugDir,
|
|
37384
|
-
} else if (
|
|
37385
|
-
const subDir = join67(slugDir,
|
|
37475
|
+
for (const entry3 of entries2) {
|
|
37476
|
+
if (entry3.isFile() && SESSION_META_RE.test(entry3.name)) {
|
|
37477
|
+
out.push(join67(slugDir, entry3.name));
|
|
37478
|
+
} else if (entry3.isDirectory() && entry3.name === "subagents") {
|
|
37479
|
+
const subDir = join67(slugDir, entry3.name);
|
|
37386
37480
|
let subs;
|
|
37387
37481
|
try {
|
|
37388
37482
|
subs = await readdir7(subDir, { withFileTypes: true });
|
|
@@ -37532,11 +37626,11 @@ async function collectSidecars(projectsRoot) {
|
|
|
37532
37626
|
} catch {
|
|
37533
37627
|
continue;
|
|
37534
37628
|
}
|
|
37535
|
-
for (const
|
|
37536
|
-
if (
|
|
37537
|
-
out.push({ path: join68(slugDir,
|
|
37538
|
-
} else if (
|
|
37539
|
-
const subDir = join68(slugDir,
|
|
37629
|
+
for (const entry3 of entries2) {
|
|
37630
|
+
if (entry3.isFile() && SESSION_META_RE2.test(entry3.name)) {
|
|
37631
|
+
out.push({ path: join68(slugDir, entry3.name), slug: slug.name });
|
|
37632
|
+
} else if (entry3.isDirectory() && (entry3.name === "subagents" || entry3.name === "archive")) {
|
|
37633
|
+
const subDir = join68(slugDir, entry3.name);
|
|
37540
37634
|
let subs;
|
|
37541
37635
|
try {
|
|
37542
37636
|
subs = await readdir8(subDir, { withFileTypes: true });
|
|
@@ -38465,24 +38559,24 @@ var WaGateway = class {
|
|
|
38465
38559
|
for (const h of held) {
|
|
38466
38560
|
const key2 = hubKey(h.accountId, h.senderId);
|
|
38467
38561
|
if (this.spawning.has(key2)) continue;
|
|
38468
|
-
const
|
|
38469
|
-
if (!
|
|
38562
|
+
const entry3 = this.heldSpawnArgs.get(key2);
|
|
38563
|
+
if (!entry3) {
|
|
38470
38564
|
console.error(
|
|
38471
38565
|
`[whatsapp-native] op=spawn-retry-skipped senderId=${h.senderId} accountId=${h.accountId} ageMs=${h.oldestAgeMs} queued=${h.count} reason=no-args`
|
|
38472
38566
|
);
|
|
38473
38567
|
continue;
|
|
38474
38568
|
}
|
|
38475
|
-
|
|
38569
|
+
entry3.attempts++;
|
|
38476
38570
|
this.spawning.add(key2);
|
|
38477
38571
|
calls++;
|
|
38478
38572
|
try {
|
|
38479
|
-
await this.deps.ensureChannelSession(
|
|
38573
|
+
await this.deps.ensureChannelSession(entry3.args);
|
|
38480
38574
|
console.error(
|
|
38481
|
-
`[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${
|
|
38575
|
+
`[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${entry3.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=true`
|
|
38482
38576
|
);
|
|
38483
38577
|
} catch (err) {
|
|
38484
38578
|
console.error(
|
|
38485
|
-
`[whatsapp-native] op=spawn-retry senderId=${h.senderId} accountId=${h.accountId} attempt=${
|
|
38579
|
+
`[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
38580
|
);
|
|
38487
38581
|
} finally {
|
|
38488
38582
|
this.spawning.delete(key2);
|
|
@@ -39027,8 +39121,8 @@ var InboundHub2 = class {
|
|
|
39027
39121
|
if (!s) return null;
|
|
39028
39122
|
const i = s.inFlight.findIndex((e) => e.payload.messageId === messageId && e.state === "queued");
|
|
39029
39123
|
if (i < 0) return null;
|
|
39030
|
-
const [
|
|
39031
|
-
return
|
|
39124
|
+
const [entry3] = s.inFlight.splice(i, 1);
|
|
39125
|
+
return entry3.payload;
|
|
39032
39126
|
}
|
|
39033
39127
|
/** Whether a key currently has a live channel server attached. The gateway
|
|
39034
39128
|
* uses this to decide whether an inbound needs a cold-start spawn/resume. */
|
|
@@ -39360,9 +39454,9 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
39360
39454
|
* false (and is a no-op) when no prompt with this key+id is open — a late or
|
|
39361
39455
|
* duplicate click. */
|
|
39362
39456
|
resolvePermissionVerdict(key2, requestId, behavior) {
|
|
39363
|
-
const
|
|
39364
|
-
if (!
|
|
39365
|
-
|
|
39457
|
+
const entry3 = this.pendingPrompts.get(_WebchatGateway.promptKey(key2, requestId));
|
|
39458
|
+
if (!entry3) return false;
|
|
39459
|
+
entry3.resolve({ behavior });
|
|
39366
39460
|
console.error(`[webchat:perm] op=verdict key=${keyDisplay(key2)} id=${requestId} behavior=${behavior}`);
|
|
39367
39461
|
return true;
|
|
39368
39462
|
}
|
|
@@ -39890,6 +39984,23 @@ var AnswerAccumulator = class {
|
|
|
39890
39984
|
};
|
|
39891
39985
|
|
|
39892
39986
|
// app/lib/channel-delivery/emitter.ts
|
|
39987
|
+
var CHANNEL_WRAPPER2 = "<channel source=";
|
|
39988
|
+
function classifyTurnStart(event) {
|
|
39989
|
+
const content = event.message?.content;
|
|
39990
|
+
if (typeof content === "string") {
|
|
39991
|
+
return content.trimStart().startsWith(CHANNEL_WRAPPER2) ? "inbound" : "injected";
|
|
39992
|
+
}
|
|
39993
|
+
if (!Array.isArray(content)) return "injected";
|
|
39994
|
+
for (const block of content) {
|
|
39995
|
+
if (block?.type === "tool_result") return "tool-result";
|
|
39996
|
+
}
|
|
39997
|
+
for (const block of content) {
|
|
39998
|
+
if (block?.type === "text" && typeof block.text === "string") {
|
|
39999
|
+
return block.text.trimStart().startsWith(CHANNEL_WRAPPER2) ? "inbound" : "injected";
|
|
40000
|
+
}
|
|
40001
|
+
}
|
|
40002
|
+
return "injected";
|
|
40003
|
+
}
|
|
39893
40004
|
function followerPendingMaxMs() {
|
|
39894
40005
|
return Number(process.env.CHANNEL_PTY_FOLLOWER_PENDING_MAX_MS ?? String(3e5));
|
|
39895
40006
|
}
|
|
@@ -39905,33 +40016,33 @@ function toolResultText(content) {
|
|
|
39905
40016
|
}
|
|
39906
40017
|
function startEmitter(opts) {
|
|
39907
40018
|
const abort2 = new AbortController();
|
|
39908
|
-
const { entry:
|
|
40019
|
+
const { entry: entry3, tag } = opts;
|
|
39909
40020
|
const answers = new AnswerAccumulator();
|
|
39910
40021
|
let answersEmitted = 0;
|
|
39911
40022
|
let closeReason = "stream-end";
|
|
39912
40023
|
const emitAnswer = async (answer) => {
|
|
39913
40024
|
answersEmitted += 1;
|
|
39914
|
-
if (answer.text.trim()) await fanOut2(
|
|
40025
|
+
if (answer.text.trim()) await fanOut2(entry3.subscribers, answer.text, opts.onError, tag);
|
|
39915
40026
|
if (!opts.onAnswer) return;
|
|
39916
|
-
const guarded = { ...answer, text: guardOutbound(
|
|
40027
|
+
const guarded = { ...answer, text: guardOutbound(entry3.senderId, answer.text) };
|
|
39917
40028
|
try {
|
|
39918
40029
|
await opts.onAnswer(guarded);
|
|
39919
40030
|
} catch (err) {
|
|
39920
40031
|
console.error(
|
|
39921
|
-
`${tag} answer-error sessionId=${
|
|
40032
|
+
`${tag} answer-error sessionId=${entry3.sessionId.slice(0, 8)} messageId=${answer.messageId} message=${err instanceof Error ? err.message : String(err)}`
|
|
39922
40033
|
);
|
|
39923
40034
|
}
|
|
39924
40035
|
};
|
|
39925
40036
|
void (async () => {
|
|
39926
40037
|
try {
|
|
39927
|
-
const sid =
|
|
40038
|
+
const sid = entry3.sessionId.slice(0, 8);
|
|
39928
40039
|
const deadline = Date.now() + followerPendingMaxMs();
|
|
39929
40040
|
const retryMs = followerRetryMs();
|
|
39930
40041
|
let res;
|
|
39931
40042
|
let attempt = 0;
|
|
39932
40043
|
for (; ; ) {
|
|
39933
40044
|
res = await fetch(
|
|
39934
|
-
managerLogFollowUrl(
|
|
40045
|
+
managerLogFollowUrl(entry3.sessionId, { boundary: opts.suppressResumeReplay === true }),
|
|
39935
40046
|
{ signal: abort2.signal }
|
|
39936
40047
|
);
|
|
39937
40048
|
console.error(`${tag} follower-connect sessionId=${sid} status=${res.status}`);
|
|
@@ -39982,7 +40093,7 @@ function startEmitter(opts) {
|
|
|
39982
40093
|
event = JSON.parse(line);
|
|
39983
40094
|
} catch (err) {
|
|
39984
40095
|
console.error(
|
|
39985
|
-
`${tag} jsonl-parse-skip sessionId=${
|
|
40096
|
+
`${tag} jsonl-parse-skip sessionId=${entry3.sessionId.slice(0, 8)} bytes=${line.length} message=${err instanceof Error ? err.message : String(err)}`
|
|
39986
40097
|
);
|
|
39987
40098
|
continue;
|
|
39988
40099
|
}
|
|
@@ -40035,7 +40146,7 @@ function startEmitter(opts) {
|
|
|
40035
40146
|
}
|
|
40036
40147
|
}
|
|
40037
40148
|
}
|
|
40038
|
-
if (!suppressing) opts.onTurnStart?.();
|
|
40149
|
+
if (!suppressing) opts.onTurnStart?.(classifyTurnStart(event));
|
|
40039
40150
|
firedFileTools = [];
|
|
40040
40151
|
continue;
|
|
40041
40152
|
}
|
|
@@ -40100,7 +40211,7 @@ function startEmitter(opts) {
|
|
|
40100
40211
|
} finally {
|
|
40101
40212
|
if (abort2.signal.aborted) closeReason = "aborted";
|
|
40102
40213
|
console.error(
|
|
40103
|
-
`${tag} op=follower-closed sessionId=${
|
|
40214
|
+
`${tag} op=follower-closed sessionId=${entry3.sessionId.slice(0, 8)} reason=${closeReason} answers=${answersEmitted}`
|
|
40104
40215
|
);
|
|
40105
40216
|
opts.onClose();
|
|
40106
40217
|
}
|
|
@@ -40155,7 +40266,7 @@ import { resolve as resolve50 } from "path";
|
|
|
40155
40266
|
// app/lib/channel-pty-bridge/file-delivery.ts
|
|
40156
40267
|
var SEND_USER_FILE = "SendUserFile";
|
|
40157
40268
|
function makeFileDelivery(opts) {
|
|
40158
|
-
const { entry:
|
|
40269
|
+
const { entry: entry3, tag, channel, sendFile, deferUntilVerdict = false } = opts;
|
|
40159
40270
|
let failedFiles = [];
|
|
40160
40271
|
let attempts = 0;
|
|
40161
40272
|
const pending = /* @__PURE__ */ new Map();
|
|
@@ -40182,7 +40293,7 @@ function makeFileDelivery(opts) {
|
|
|
40182
40293
|
function unreconciledCall(reason, files, detail) {
|
|
40183
40294
|
const d = detail ? ` detail="${detail.replace(/\s+/g, " ").slice(0, 120)}"` : "";
|
|
40184
40295
|
console.error(
|
|
40185
|
-
`${tag} file-delivery-unreconciled sender=${
|
|
40296
|
+
`${tag} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${entry3.sessionId.slice(0, 8)} tool=${SEND_USER_FILE} reason=${reason} files=${files}${d}`
|
|
40186
40297
|
);
|
|
40187
40298
|
}
|
|
40188
40299
|
const handler = {
|
|
@@ -40203,10 +40314,10 @@ function makeFileDelivery(opts) {
|
|
|
40203
40314
|
const tried = attempts;
|
|
40204
40315
|
failedFiles = [];
|
|
40205
40316
|
attempts = 0;
|
|
40206
|
-
const sid =
|
|
40317
|
+
const sid = entry3.sessionId.slice(0, 8);
|
|
40207
40318
|
for (const file of failed2) {
|
|
40208
40319
|
console.error(
|
|
40209
|
-
`${tag} file-delivery-unreconciled sender=${
|
|
40320
|
+
`${tag} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${SEND_USER_FILE} file=${file}`
|
|
40210
40321
|
);
|
|
40211
40322
|
}
|
|
40212
40323
|
if (deferUntilVerdict) {
|
|
@@ -40216,7 +40327,7 @@ function makeFileDelivery(opts) {
|
|
|
40216
40327
|
}
|
|
40217
40328
|
if (firedTools.includes(SEND_USER_FILE) && tried === 0) {
|
|
40218
40329
|
console.error(
|
|
40219
|
-
`${tag} file-delivery-unreconciled sender=${
|
|
40330
|
+
`${tag} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${SEND_USER_FILE}`
|
|
40220
40331
|
);
|
|
40221
40332
|
}
|
|
40222
40333
|
}
|
|
@@ -40245,43 +40356,43 @@ var TAG66 = "[webchat-adaptor]";
|
|
|
40245
40356
|
function platformRoot2() {
|
|
40246
40357
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
40247
40358
|
}
|
|
40248
|
-
function makeWebchatSendFile(
|
|
40359
|
+
function makeWebchatSendFile(entry3) {
|
|
40249
40360
|
return async (filePath) => {
|
|
40250
|
-
if (!
|
|
40251
|
-
console.error(`${TAG66} file-delivery reject reason=no-account sender=${
|
|
40361
|
+
if (!entry3.accountId) {
|
|
40362
|
+
console.error(`${TAG66} file-delivery reject reason=no-account sender=${entry3.senderId}`);
|
|
40252
40363
|
return { ok: false, error: "no-account" };
|
|
40253
40364
|
}
|
|
40254
|
-
const accountDir = resolve50(platformRoot2(), "..", "data/accounts",
|
|
40365
|
+
const accountDir = resolve50(platformRoot2(), "..", "data/accounts", entry3.accountId);
|
|
40255
40366
|
try {
|
|
40256
40367
|
const resolved = realpathSync9(filePath);
|
|
40257
40368
|
const accountResolved = realpathSync9(accountDir);
|
|
40258
40369
|
if (!resolved.startsWith(accountResolved + "/")) {
|
|
40259
|
-
console.error(`${TAG66} file-delivery reject reason=outside_account_directory sender=${
|
|
40370
|
+
console.error(`${TAG66} file-delivery reject reason=outside_account_directory sender=${entry3.senderId}`);
|
|
40260
40371
|
return { ok: false, error: "outside-account" };
|
|
40261
40372
|
}
|
|
40262
40373
|
return { ok: true };
|
|
40263
40374
|
} catch (err) {
|
|
40264
40375
|
const code = err.code;
|
|
40265
40376
|
console.error(
|
|
40266
|
-
`${TAG66} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${
|
|
40377
|
+
`${TAG66} file-delivery reject reason=${code === "ENOENT" ? "not-found" : "path-error"} sender=${entry3.senderId}`
|
|
40267
40378
|
);
|
|
40268
40379
|
return { ok: false, error: code === "ENOENT" ? "not-found" : "path-error" };
|
|
40269
40380
|
}
|
|
40270
40381
|
};
|
|
40271
40382
|
}
|
|
40272
|
-
function makeWebchatFileDelivery(
|
|
40383
|
+
function makeWebchatFileDelivery(entry3) {
|
|
40273
40384
|
return makeFileDelivery({
|
|
40274
|
-
entry:
|
|
40385
|
+
entry: entry3,
|
|
40275
40386
|
tag: TAG66,
|
|
40276
40387
|
channel: "webchat",
|
|
40277
|
-
sendFile: makeWebchatSendFile(
|
|
40388
|
+
sendFile: makeWebchatSendFile(entry3),
|
|
40278
40389
|
deferUntilVerdict: true
|
|
40279
40390
|
});
|
|
40280
40391
|
}
|
|
40281
40392
|
|
|
40282
40393
|
// app/lib/webchat/gateway/native-file-follower.ts
|
|
40283
40394
|
function startWebchatNativeFileFollower(input) {
|
|
40284
|
-
const
|
|
40395
|
+
const entry3 = {
|
|
40285
40396
|
sessionId: input.sessionId,
|
|
40286
40397
|
role: "admin",
|
|
40287
40398
|
channel: "webchat",
|
|
@@ -40300,7 +40411,7 @@ function startWebchatNativeFileFollower(input) {
|
|
|
40300
40411
|
replyTarget: null
|
|
40301
40412
|
};
|
|
40302
40413
|
return startEmitter({
|
|
40303
|
-
entry:
|
|
40414
|
+
entry: entry3,
|
|
40304
40415
|
tag: "[webchat-adaptor]",
|
|
40305
40416
|
// Task 2557 — the emitter has always had this channel and no follower ever
|
|
40306
40417
|
// passed one, so every cause it reported was discarded. The close line
|
|
@@ -40308,7 +40419,7 @@ function startWebchatNativeFileFollower(input) {
|
|
|
40308
40419
|
onError: (reason) => {
|
|
40309
40420
|
console.error(`[webchat-adaptor] op=follower-error sessionId=${input.sessionId.slice(0, 8)} reason=${reason}`);
|
|
40310
40421
|
},
|
|
40311
|
-
fileDelivery: makeWebchatFileDelivery(
|
|
40422
|
+
fileDelivery: makeWebchatFileDelivery(entry3),
|
|
40312
40423
|
// A resumed session's JSONL already holds prior SendUserFile tool_uses;
|
|
40313
40424
|
// suppress replay so historical calls are not re-reconciled on attach.
|
|
40314
40425
|
suppressResumeReplay: true,
|
|
@@ -40357,29 +40468,29 @@ var WHATSAPP_SEND_DOCUMENT = "whatsapp-send-document";
|
|
|
40357
40468
|
function platformRoot3() {
|
|
40358
40469
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
40359
40470
|
}
|
|
40360
|
-
function makeWhatsAppSendFile(
|
|
40471
|
+
function makeWhatsAppSendFile(entry3, maxyAccountId) {
|
|
40361
40472
|
return async (filePath, caption) => {
|
|
40362
40473
|
const result = await sendWhatsAppDocument({
|
|
40363
|
-
to:
|
|
40474
|
+
to: entry3.senderId,
|
|
40364
40475
|
filePath,
|
|
40365
40476
|
caption,
|
|
40366
|
-
accountId:
|
|
40477
|
+
accountId: entry3.accountId,
|
|
40367
40478
|
maxyAccountId,
|
|
40368
40479
|
platformRoot: platformRoot3()
|
|
40369
40480
|
});
|
|
40370
40481
|
if (result.ok) return { ok: true };
|
|
40371
40482
|
console.error(
|
|
40372
|
-
`${TAG67} file-delivery reject reason=send-failed sender=${
|
|
40483
|
+
`${TAG67} file-delivery reject reason=send-failed sender=${entry3.senderId} status=${result.status} message=${result.error}`
|
|
40373
40484
|
);
|
|
40374
40485
|
return { ok: false, error: result.error };
|
|
40375
40486
|
};
|
|
40376
40487
|
}
|
|
40377
|
-
function makeWhatsAppFileDelivery(
|
|
40488
|
+
function makeWhatsAppFileDelivery(entry3, maxyAccountId) {
|
|
40378
40489
|
const shared = makeFileDelivery({
|
|
40379
|
-
entry:
|
|
40490
|
+
entry: entry3,
|
|
40380
40491
|
tag: TAG67,
|
|
40381
40492
|
channel: "whatsapp",
|
|
40382
|
-
sendFile: makeWhatsAppSendFile(
|
|
40493
|
+
sendFile: makeWhatsAppSendFile(entry3, maxyAccountId)
|
|
40383
40494
|
});
|
|
40384
40495
|
let turnStartedAt = null;
|
|
40385
40496
|
let routeCalls = [];
|
|
@@ -40404,7 +40515,7 @@ function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
|
|
|
40404
40515
|
const routes = routeCalls;
|
|
40405
40516
|
turnStartedAt = null;
|
|
40406
40517
|
routeCalls = [];
|
|
40407
|
-
const sid =
|
|
40518
|
+
const sid = entry3.sessionId.slice(0, 8);
|
|
40408
40519
|
shared.onTurnEnd(firedTools);
|
|
40409
40520
|
for (const call2 of routes) {
|
|
40410
40521
|
const routeAt = call2.to !== void 0 && call2.filePath !== void 0 ? routeDocumentOutboundAt(call2.to, call2.filePath) : void 0;
|
|
@@ -40412,7 +40523,7 @@ function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
|
|
|
40412
40523
|
if (!delivered) {
|
|
40413
40524
|
const fileField = call2.filePath !== void 0 ? ` file=${call2.filePath}` : "";
|
|
40414
40525
|
console.error(
|
|
40415
|
-
`${TAG67} file-delivery-unreconciled sender=${
|
|
40526
|
+
`${TAG67} file-delivery-unreconciled sender=${entry3.senderId} sessionId=${sid} tool=${WHATSAPP_SEND_DOCUMENT}${fileField}`
|
|
40416
40527
|
);
|
|
40417
40528
|
}
|
|
40418
40529
|
}
|
|
@@ -40423,7 +40534,7 @@ function makeWhatsAppFileDelivery(entry2, maxyAccountId) {
|
|
|
40423
40534
|
// app/lib/whatsapp/gateway/native-file-follower.ts
|
|
40424
40535
|
var COMPOSING_REFRESH_MS = 1e4;
|
|
40425
40536
|
function startNativeFileFollower(input) {
|
|
40426
|
-
const
|
|
40537
|
+
const entry3 = {
|
|
40427
40538
|
sessionId: input.sessionId,
|
|
40428
40539
|
role: input.role,
|
|
40429
40540
|
channel: "whatsapp",
|
|
@@ -40469,7 +40580,7 @@ function startNativeFileFollower(input) {
|
|
|
40469
40580
|
void sendPaused(sock, input.senderId);
|
|
40470
40581
|
};
|
|
40471
40582
|
return startEmitter({
|
|
40472
|
-
entry:
|
|
40583
|
+
entry: entry3,
|
|
40473
40584
|
tag: "[whatsapp-adaptor]",
|
|
40474
40585
|
// Task 2557 — the emitter has always had this channel and no follower ever
|
|
40475
40586
|
// passed one, so every cause it reported (`follow-status-404`,
|
|
@@ -40481,7 +40592,7 @@ function startNativeFileFollower(input) {
|
|
|
40481
40592
|
},
|
|
40482
40593
|
// Task 2521 — admin-only. A public spawn has no tools (Task 2078), so a
|
|
40483
40594
|
// handler here would reconcile a call that can never fire.
|
|
40484
|
-
fileDelivery: input.role === "admin" ? makeWhatsAppFileDelivery(
|
|
40595
|
+
fileDelivery: input.role === "admin" ? makeWhatsAppFileDelivery(entry3, input.maxyAccountId) : null,
|
|
40485
40596
|
// A resumed session's JSONL already holds prior SendUserFile tool_uses;
|
|
40486
40597
|
// suppress replay so historical files are not re-sent on attach.
|
|
40487
40598
|
suppressResumeReplay: true,
|
|
@@ -41019,24 +41130,24 @@ var TelegramGateway = class {
|
|
|
41019
41130
|
let calls = 0;
|
|
41020
41131
|
for (const h of held) {
|
|
41021
41132
|
if (this.spawning.has(h.key)) continue;
|
|
41022
|
-
const
|
|
41023
|
-
if (!
|
|
41133
|
+
const entry3 = this.heldSpawnArgs.get(h.key);
|
|
41134
|
+
if (!entry3) {
|
|
41024
41135
|
console.error(
|
|
41025
41136
|
`[telegram-native] op=spawn-retry-skipped key=${h.key} ageMs=${h.oldestAgeMs} queued=${h.count} reason=no-args`
|
|
41026
41137
|
);
|
|
41027
41138
|
continue;
|
|
41028
41139
|
}
|
|
41029
|
-
|
|
41140
|
+
entry3.attempts++;
|
|
41030
41141
|
this.spawning.add(h.key);
|
|
41031
41142
|
calls++;
|
|
41032
41143
|
try {
|
|
41033
|
-
await this.deps.ensureChannelSession(
|
|
41144
|
+
await this.deps.ensureChannelSession(entry3.args);
|
|
41034
41145
|
console.error(
|
|
41035
|
-
`[telegram-native] op=spawn-retry key=${h.key} attempt=${
|
|
41146
|
+
`[telegram-native] op=spawn-retry key=${h.key} attempt=${entry3.attempts} ageMs=${h.oldestAgeMs} queued=${h.count} ok=true`
|
|
41036
41147
|
);
|
|
41037
41148
|
} catch (err) {
|
|
41038
41149
|
console.error(
|
|
41039
|
-
`[telegram-native] op=spawn-retry key=${h.key} attempt=${
|
|
41150
|
+
`[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
41151
|
);
|
|
41041
41152
|
} finally {
|
|
41042
41153
|
this.spawning.delete(h.key);
|
|
@@ -41489,33 +41600,33 @@ var TAG71 = "[telegram:outbound]";
|
|
|
41489
41600
|
function platformRoot4() {
|
|
41490
41601
|
return process.env.MAXY_PLATFORM_ROOT || "";
|
|
41491
41602
|
}
|
|
41492
|
-
function makeTelegramSendFile(
|
|
41603
|
+
function makeTelegramSendFile(entry3, botToken) {
|
|
41493
41604
|
return async (filePath, caption) => {
|
|
41494
41605
|
if (!botToken) {
|
|
41495
|
-
console.error(`${TAG71} file-delivery reject reason=no-bot-token sender=${
|
|
41606
|
+
console.error(`${TAG71} file-delivery reject reason=no-bot-token sender=${entry3.senderId} role=${entry3.role}`);
|
|
41496
41607
|
return { ok: false, error: "no-bot-token" };
|
|
41497
41608
|
}
|
|
41498
|
-
if (
|
|
41499
|
-
console.error(`${TAG71} file-delivery reject reason=no-reply-target sender=${
|
|
41609
|
+
if (entry3.replyTarget == null) {
|
|
41610
|
+
console.error(`${TAG71} file-delivery reject reason=no-reply-target sender=${entry3.senderId} role=${entry3.role}`);
|
|
41500
41611
|
return { ok: false, error: "no-reply-target" };
|
|
41501
41612
|
}
|
|
41502
41613
|
const result = await sendTelegramDocument({
|
|
41503
41614
|
botToken,
|
|
41504
|
-
chatId: Number(
|
|
41615
|
+
chatId: Number(entry3.replyTarget),
|
|
41505
41616
|
filePath,
|
|
41506
41617
|
caption,
|
|
41507
|
-
maxyAccountId:
|
|
41618
|
+
maxyAccountId: entry3.accountId,
|
|
41508
41619
|
platformRoot: platformRoot4()
|
|
41509
41620
|
});
|
|
41510
41621
|
return result.ok ? { ok: true } : { ok: false, error: result.error };
|
|
41511
41622
|
};
|
|
41512
41623
|
}
|
|
41513
|
-
function makeTelegramFileDelivery(
|
|
41624
|
+
function makeTelegramFileDelivery(entry3, botToken) {
|
|
41514
41625
|
return makeFileDelivery({
|
|
41515
|
-
entry:
|
|
41626
|
+
entry: entry3,
|
|
41516
41627
|
tag: TAG71,
|
|
41517
41628
|
channel: "telegram",
|
|
41518
|
-
sendFile: makeTelegramSendFile(
|
|
41629
|
+
sendFile: makeTelegramSendFile(entry3, botToken)
|
|
41519
41630
|
});
|
|
41520
41631
|
}
|
|
41521
41632
|
|
|
@@ -41551,11 +41662,46 @@ function runTelegramPresenceCensus() {
|
|
|
41551
41662
|
);
|
|
41552
41663
|
}
|
|
41553
41664
|
|
|
41665
|
+
// app/lib/telegram/gateway/card-census.ts
|
|
41666
|
+
var sessions3 = /* @__PURE__ */ new Map();
|
|
41667
|
+
function entry2(sessionId) {
|
|
41668
|
+
let e = sessions3.get(sessionId);
|
|
41669
|
+
if (!e) {
|
|
41670
|
+
e = { armed: 0, suppressed: 0, delivered: 0 };
|
|
41671
|
+
sessions3.set(sessionId, e);
|
|
41672
|
+
}
|
|
41673
|
+
return e;
|
|
41674
|
+
}
|
|
41675
|
+
function noteCardArmed(sessionId) {
|
|
41676
|
+
entry2(sessionId).armed += 1;
|
|
41677
|
+
}
|
|
41678
|
+
function noteCardSuppressed(sessionId) {
|
|
41679
|
+
entry2(sessionId).suppressed += 1;
|
|
41680
|
+
}
|
|
41681
|
+
function noteCardDelivered(sessionId) {
|
|
41682
|
+
const e = sessions3.get(sessionId);
|
|
41683
|
+
if (e && e.armed > 0) e.delivered += 1;
|
|
41684
|
+
}
|
|
41685
|
+
function cardCensusLines() {
|
|
41686
|
+
let armed2 = 0;
|
|
41687
|
+
let suppressed = 0;
|
|
41688
|
+
let delivered = 0;
|
|
41689
|
+
for (const e of sessions3.values()) {
|
|
41690
|
+
armed2 += e.armed;
|
|
41691
|
+
suppressed += e.suppressed;
|
|
41692
|
+
delivered += e.delivered;
|
|
41693
|
+
}
|
|
41694
|
+
const silent = Math.max(0, armed2 - suppressed - delivered);
|
|
41695
|
+
return [
|
|
41696
|
+
`[telegram-card-suppression-census] sessions=${sessions3.size} armed=${armed2} suppressed=${suppressed} silent-turns=${silent}`
|
|
41697
|
+
];
|
|
41698
|
+
}
|
|
41699
|
+
|
|
41554
41700
|
// app/lib/telegram/gateway/native-file-follower.ts
|
|
41555
41701
|
var TELEGRAM_CARD_TOOL = "mcp__telegram__telegram-card";
|
|
41556
41702
|
var CHAT_ACTION_REFRESH_MS = 4e3;
|
|
41557
41703
|
function startTelegramNativeFileFollower(input) {
|
|
41558
|
-
const
|
|
41704
|
+
const entry3 = {
|
|
41559
41705
|
sessionId: input.sessionId,
|
|
41560
41706
|
role: "admin",
|
|
41561
41707
|
channel: "telegram",
|
|
@@ -41599,7 +41745,8 @@ function startTelegramNativeFileFollower(input) {
|
|
|
41599
41745
|
stopHeartbeat();
|
|
41600
41746
|
lastChatActionAt = Number.NEGATIVE_INFINITY;
|
|
41601
41747
|
};
|
|
41602
|
-
const onTurnStart = () => {
|
|
41748
|
+
const onTurnStart = (kind) => {
|
|
41749
|
+
if (kind === "inbound") cardSentThisTurn = false;
|
|
41603
41750
|
if (turnOpen) return;
|
|
41604
41751
|
turnOpen = true;
|
|
41605
41752
|
stopHeartbeat();
|
|
@@ -41612,7 +41759,6 @@ function startTelegramNativeFileFollower(input) {
|
|
|
41612
41759
|
};
|
|
41613
41760
|
const onTurnComplete = () => {
|
|
41614
41761
|
turnOpen = false;
|
|
41615
|
-
cardSentThisTurn = false;
|
|
41616
41762
|
if (input.backgroundActive?.() === true) return;
|
|
41617
41763
|
stopHeartbeat();
|
|
41618
41764
|
lastChatActionAt = Number.NEGATIVE_INFINITY;
|
|
@@ -41626,7 +41772,7 @@ function startTelegramNativeFileFollower(input) {
|
|
|
41626
41772
|
background: () => input.backgroundActive?.() === true
|
|
41627
41773
|
});
|
|
41628
41774
|
return startEmitter({
|
|
41629
|
-
entry:
|
|
41775
|
+
entry: entry3,
|
|
41630
41776
|
tag: "[telegram-adaptor]",
|
|
41631
41777
|
// Task 2557 — the emitter has always had this channel and no follower ever
|
|
41632
41778
|
// passed one, so every cause it reported was discarded. The close line
|
|
@@ -41634,7 +41780,7 @@ function startTelegramNativeFileFollower(input) {
|
|
|
41634
41780
|
onError: (reason) => {
|
|
41635
41781
|
console.error(`[telegram-adaptor] op=follower-error sessionId=${input.sessionId.slice(0, 8)} reason=${reason}`);
|
|
41636
41782
|
},
|
|
41637
|
-
fileDelivery: makeTelegramFileDelivery(
|
|
41783
|
+
fileDelivery: makeTelegramFileDelivery(entry3, input.botToken),
|
|
41638
41784
|
// A resumed session's JSONL already holds prior SendUserFile tool_uses;
|
|
41639
41785
|
// suppress replay so historical files are not re-sent on attach.
|
|
41640
41786
|
suppressResumeReplay: true,
|
|
@@ -41652,14 +41798,19 @@ function startTelegramNativeFileFollower(input) {
|
|
|
41652
41798
|
// before it reaches the optional answerCallbackQuery, so there is no card
|
|
41653
41799
|
// call that posts nothing and no bare acknowledgement to keep the text for.
|
|
41654
41800
|
onToolUse: (name) => {
|
|
41655
|
-
if (name
|
|
41801
|
+
if (name !== TELEGRAM_CARD_TOOL) return;
|
|
41802
|
+
cardSentThisTurn = true;
|
|
41803
|
+
noteCardArmed(input.sessionId);
|
|
41804
|
+
console.error(
|
|
41805
|
+
`[telegram-public] op=card-armed sessionId=${input.sessionId.slice(0, 8)} tool=${name}`
|
|
41806
|
+
);
|
|
41656
41807
|
},
|
|
41657
41808
|
// Task 2521 — the answer reaches every bound door through the one fan-out.
|
|
41658
41809
|
// Ungated by design: whatever woke the turn, the reader and the chats show
|
|
41659
41810
|
// the same thing. Task 2666 adds the one exception above.
|
|
41660
41811
|
onAnswer: async (answer) => {
|
|
41661
41812
|
if (cardSentThisTurn) {
|
|
41662
|
-
|
|
41813
|
+
noteCardSuppressed(input.sessionId);
|
|
41663
41814
|
console.error(
|
|
41664
41815
|
`[telegram-public] op=text-suppressed sessionId=${input.sessionId.slice(0, 8)} messageId=${answer.messageId} reason=card-sent`
|
|
41665
41816
|
);
|
|
@@ -41670,6 +41821,7 @@ function startTelegramNativeFileFollower(input) {
|
|
|
41670
41821
|
noteAnswer(input.sessionId, answer.messageId);
|
|
41671
41822
|
const { line, reached } = await fanOut({ targets, segments: segmentText(answer.text) });
|
|
41672
41823
|
noteSent(input.sessionId, answer.messageId, reached);
|
|
41824
|
+
noteCardDelivered(input.sessionId);
|
|
41673
41825
|
console.error(`${line} messageId=${answer.messageId} sessionId=${input.sessionId.slice(0, 8)}`);
|
|
41674
41826
|
},
|
|
41675
41827
|
// Task 2603 — the interval must not outlive the reader. The emitter reaches
|
|
@@ -41955,11 +42107,11 @@ async function firePublicSessionEndReview(input) {
|
|
|
41955
42107
|
}
|
|
41956
42108
|
|
|
41957
42109
|
// app/lib/whatsapp/inbound/resolve-client-graph-owner.ts
|
|
41958
|
-
var
|
|
42110
|
+
var import_dist17 = __toESM(require_dist3(), 1);
|
|
41959
42111
|
async function resolveClientOwnerUserId(accountId, deps = {}) {
|
|
41960
42112
|
const listAccounts = deps.listAccounts ?? listValidAccounts;
|
|
41961
42113
|
const getSession3 = deps.getSession ?? getSession;
|
|
41962
|
-
const resolveOwner2 = deps.resolveOwner ??
|
|
42114
|
+
const resolveOwner2 = deps.resolveOwner ?? import_dist17.resolveOwnerUserId;
|
|
41963
42115
|
const role = listAccounts().find((a) => a.accountId === accountId)?.config.role;
|
|
41964
42116
|
if (role !== "client") return null;
|
|
41965
42117
|
const session = getSession3();
|
|
@@ -41982,8 +42134,8 @@ async function resolveClientOwnerUserId(accountId, deps = {}) {
|
|
|
41982
42134
|
// app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
|
|
41983
42135
|
function findAccountManagerDrift(accountManagers, validAccountIds) {
|
|
41984
42136
|
const drift = [];
|
|
41985
|
-
for (const [phone,
|
|
41986
|
-
const managesAccount = typeof
|
|
42137
|
+
for (const [phone, entry3] of Object.entries(accountManagers)) {
|
|
42138
|
+
const managesAccount = typeof entry3 === "string" ? entry3 : entry3.managesAccount;
|
|
41987
42139
|
if (!validAccountIds.includes(managesAccount)) {
|
|
41988
42140
|
drift.push({ phone, managesAccount, reason: "not-in-registry" });
|
|
41989
42141
|
}
|
|
@@ -42089,50 +42241,50 @@ import { join as join71 } from "path";
|
|
|
42089
42241
|
function buildTelegramAuditLines(input) {
|
|
42090
42242
|
const lines = [];
|
|
42091
42243
|
const entries2 = input.accounts.flatMap(
|
|
42092
|
-
(a) => listBotEntries(a.telegram).map((
|
|
42244
|
+
(a) => listBotEntries(a.telegram).map((entry3) => ({ accountId: a.accountId, accountDir: a.accountDir, entry: entry3 }))
|
|
42093
42245
|
);
|
|
42094
42246
|
if (entries2.length === 0) return lines;
|
|
42095
42247
|
const byId = /* @__PURE__ */ new Map();
|
|
42096
|
-
for (const { entry:
|
|
42248
|
+
for (const { entry: entry3 } of entries2) byId.set(entry3.id, (byId.get(entry3.id) ?? 0) + 1);
|
|
42097
42249
|
for (const [botId, count] of byId) {
|
|
42098
42250
|
if (count > 1) lines.push(`[telegram-audit] op=duplicate-bot-id botId=${botId} accounts=${count}`);
|
|
42099
42251
|
}
|
|
42100
|
-
for (const { accountId, accountDir, entry:
|
|
42101
|
-
if (
|
|
42252
|
+
for (const { accountId, accountDir, entry: entry3 } of entries2) {
|
|
42253
|
+
if (entry3.role === "public" && !input.agentActive(accountDir, entry3.agent)) {
|
|
42102
42254
|
lines.push(
|
|
42103
|
-
`[telegram-audit] op=entry-agent-inactive accountId=${accountId} botId=${
|
|
42255
|
+
`[telegram-audit] op=entry-agent-inactive accountId=${accountId} botId=${entry3.id} agent=${entry3.agent}`
|
|
42104
42256
|
);
|
|
42105
42257
|
}
|
|
42106
|
-
if (!input.secretFileExists(
|
|
42107
|
-
lines.push(`[telegram-audit] op=secret-orphan botId=${
|
|
42258
|
+
if (!input.secretFileExists(entry3.id)) {
|
|
42259
|
+
lines.push(`[telegram-audit] op=secret-orphan botId=${entry3.id} side=entry`);
|
|
42108
42260
|
}
|
|
42109
42261
|
}
|
|
42110
42262
|
const known = new Set(entries2.map((e) => e.entry.id));
|
|
42111
42263
|
for (const botId of input.secretFilesOnDisk) {
|
|
42112
42264
|
if (!known.has(botId)) lines.push(`[telegram-audit] op=secret-orphan botId=${botId} side=file`);
|
|
42113
42265
|
}
|
|
42114
|
-
for (const { accountId, entry:
|
|
42115
|
-
const last = input.lastActivityByBot(
|
|
42266
|
+
for (const { accountId, entry: entry3 } of entries2) {
|
|
42267
|
+
const last = input.lastActivityByBot(entry3.id);
|
|
42116
42268
|
const spawnAge = last === null ? "never" : String(input.now - last.spawnMs);
|
|
42117
42269
|
const turnAge = last === null || last.turnMs === null ? "never" : String(input.now - last.turnMs);
|
|
42118
|
-
const agent = entryAgentSlug(
|
|
42119
|
-
const secretFile = input.secretFileExists(
|
|
42270
|
+
const agent = entryAgentSlug(entry3);
|
|
42271
|
+
const secretFile = input.secretFileExists(entry3.id) ? "present" : "absent";
|
|
42120
42272
|
lines.push(
|
|
42121
|
-
`[telegram-audit] op=configured botId=${
|
|
42273
|
+
`[telegram-audit] op=configured botId=${entry3.id} accountId=${accountId} agent=${agent} secretFile=${secretFile} lastSpawnAgeMs=${spawnAge} lastTurnAgeMs=${turnAge}`
|
|
42122
42274
|
);
|
|
42123
42275
|
}
|
|
42124
42276
|
let bound = 0;
|
|
42125
42277
|
let rosterOk = 0;
|
|
42126
42278
|
let rosterMissing = 0;
|
|
42127
|
-
for (const { accountId, accountDir, entry:
|
|
42128
|
-
if (
|
|
42279
|
+
for (const { accountId, accountDir, entry: entry3 } of entries2) {
|
|
42280
|
+
if (entry3.role !== "specialist") continue;
|
|
42129
42281
|
bound++;
|
|
42130
|
-
if (input.specialistCards(accountDir).includes(
|
|
42282
|
+
if (input.specialistCards(accountDir).includes(entry3.specialist)) {
|
|
42131
42283
|
rosterOk++;
|
|
42132
42284
|
} else {
|
|
42133
42285
|
rosterMissing++;
|
|
42134
42286
|
lines.push(
|
|
42135
|
-
`[telegram-audit] op=specialist-binding-missing accountId=${accountId} botId=${
|
|
42287
|
+
`[telegram-audit] op=specialist-binding-missing accountId=${accountId} botId=${entry3.id} specialist=${entry3.specialist}`
|
|
42136
42288
|
);
|
|
42137
42289
|
}
|
|
42138
42290
|
}
|
|
@@ -42152,9 +42304,9 @@ function buildManagedBotCensusLine(input) {
|
|
|
42152
42304
|
const byId = new Map(input.probes.map((p) => [p.botId, p]));
|
|
42153
42305
|
let webhookOk = 0;
|
|
42154
42306
|
let unrestricted = 0;
|
|
42155
|
-
for (const
|
|
42156
|
-
const probe = byId.get(
|
|
42157
|
-
if (probe && probe.webhookUrl && webhookPointsHere(probe.webhookUrl,
|
|
42307
|
+
for (const entry3 of managed) {
|
|
42308
|
+
const probe = byId.get(entry3.id);
|
|
42309
|
+
if (probe && probe.webhookUrl && webhookPointsHere(probe.webhookUrl, entry3.id)) webhookOk += 1;
|
|
42158
42310
|
if (probe?.isAccessRestricted === false) unrestricted += 1;
|
|
42159
42311
|
}
|
|
42160
42312
|
return `[telegram-audit] op=managed-bot-census bots=${entries2.length} managed=${managed.length} webhookOk=${webhookOk} webhookStale=${managed.length - webhookOk} unrestricted=${unrestricted}`;
|
|
@@ -42227,19 +42379,19 @@ function buildGroupCensusLine(c) {
|
|
|
42227
42379
|
async function runTelegramGroupCensus(accounts, deps) {
|
|
42228
42380
|
const counts = { bots: 0, groups: 0, present: 0, absent: 0, privacyOff: 0 };
|
|
42229
42381
|
for (const account of accounts) {
|
|
42230
|
-
for (const
|
|
42231
|
-
if (
|
|
42232
|
-
if ((
|
|
42382
|
+
for (const entry3 of listBotEntries(account.telegram)) {
|
|
42383
|
+
if (entry3.role !== "public") continue;
|
|
42384
|
+
if ((entry3.groupPolicy ?? "disabled") !== "allowlist") continue;
|
|
42233
42385
|
counts.bots += 1;
|
|
42234
42386
|
try {
|
|
42235
|
-
const me = await deps.getMe(
|
|
42387
|
+
const me = await deps.getMe(entry3.token);
|
|
42236
42388
|
if (me.can_read_all_group_messages === true) counts.privacyOff += 1;
|
|
42237
42389
|
} catch {
|
|
42238
42390
|
}
|
|
42239
|
-
for (const chatId of
|
|
42391
|
+
for (const chatId of entry3.allowGroups ?? []) {
|
|
42240
42392
|
counts.groups += 1;
|
|
42241
42393
|
try {
|
|
42242
|
-
const m = await deps.getChatMember(
|
|
42394
|
+
const m = await deps.getChatMember(entry3.token, chatId);
|
|
42243
42395
|
if (typeof m.status === "string" && PRESENT_STATUSES.has(m.status)) counts.present += 1;
|
|
42244
42396
|
else counts.absent += 1;
|
|
42245
42397
|
} catch {
|
|
@@ -42261,14 +42413,14 @@ function buildChannelCensusLine(c) {
|
|
|
42261
42413
|
async function runTelegramChannelCensus(accounts, deps) {
|
|
42262
42414
|
const counts = { bots: 0, channels: 0, admin: 0, notAdmin: 0, noPostRight: 0 };
|
|
42263
42415
|
for (const account of accounts) {
|
|
42264
|
-
for (const
|
|
42265
|
-
if (
|
|
42266
|
-
if ((
|
|
42416
|
+
for (const entry3 of listBotEntries(account.telegram)) {
|
|
42417
|
+
if (entry3.role !== "public") continue;
|
|
42418
|
+
if ((entry3.channelPolicy ?? "disabled") !== "allowlist") continue;
|
|
42267
42419
|
counts.bots += 1;
|
|
42268
|
-
for (const chatId of
|
|
42420
|
+
for (const chatId of entry3.allowChannels ?? []) {
|
|
42269
42421
|
counts.channels += 1;
|
|
42270
42422
|
try {
|
|
42271
|
-
const m = await deps.getChatMember(
|
|
42423
|
+
const m = await deps.getChatMember(entry3.token, chatId);
|
|
42272
42424
|
if (typeof m.status === "string" && ADMIN_STATUSES.has(m.status)) {
|
|
42273
42425
|
counts.admin += 1;
|
|
42274
42426
|
if (m.status === "administrator" && m.can_post_messages !== true) {
|
|
@@ -42593,17 +42745,17 @@ function broadcastAdminShutdown(reason) {
|
|
|
42593
42745
|
const done = encoder.encode(`data: [DONE]
|
|
42594
42746
|
|
|
42595
42747
|
`);
|
|
42596
|
-
for (const
|
|
42748
|
+
for (const entry3 of activeAdminSSEControllers) {
|
|
42597
42749
|
try {
|
|
42598
|
-
|
|
42750
|
+
entry3.controller.enqueue(frame);
|
|
42599
42751
|
} catch {
|
|
42600
42752
|
}
|
|
42601
42753
|
try {
|
|
42602
|
-
|
|
42754
|
+
entry3.controller.enqueue(done);
|
|
42603
42755
|
} catch {
|
|
42604
42756
|
}
|
|
42605
42757
|
try {
|
|
42606
|
-
|
|
42758
|
+
entry3.controller.close();
|
|
42607
42759
|
} catch {
|
|
42608
42760
|
}
|
|
42609
42761
|
}
|
|
@@ -42960,8 +43112,8 @@ var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
|
42960
43112
|
async function fetchAccountStandingRules(accountId) {
|
|
42961
43113
|
const session = getSession();
|
|
42962
43114
|
try {
|
|
42963
|
-
const res = await (0,
|
|
42964
|
-
return { block: (0,
|
|
43115
|
+
const res = await (0, import_dist18.resolveActiveRules)(session, accountId);
|
|
43116
|
+
return { block: (0, import_dist18.formatStandingRulesBlock)(res.rules), ownerUserId: res.ownerUserId, source: res.source };
|
|
42965
43117
|
} catch (err) {
|
|
42966
43118
|
console.error(
|
|
42967
43119
|
`[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
|
|
@@ -42997,6 +43149,7 @@ registerLoop({
|
|
|
42997
43149
|
run: () => {
|
|
42998
43150
|
const lines = parityCensusLines();
|
|
42999
43151
|
for (const line of lines) console.error(line);
|
|
43152
|
+
for (const line of cardCensusLines()) console.error(line);
|
|
43000
43153
|
const missing = lines.filter((l) => !l.includes("missing=0")).length;
|
|
43001
43154
|
return `${lines.length} session(s), ${missing} with a missing answer`;
|
|
43002
43155
|
}
|
|
@@ -43533,15 +43686,15 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
|
|
|
43533
43686
|
const entries2 = listBotEntries(a.config.telegram);
|
|
43534
43687
|
for (const bot of readChannelAdmins(a.accountDir, "telegram").byBot) {
|
|
43535
43688
|
if (!bot.adminUsers.includes(asNumber)) continue;
|
|
43536
|
-
const
|
|
43537
|
-
if (!
|
|
43689
|
+
const entry3 = entries2.find((e) => e.id === bot.botId);
|
|
43690
|
+
if (!entry3) continue;
|
|
43538
43691
|
hits.push({
|
|
43539
43692
|
accountId: a.accountId,
|
|
43540
43693
|
botId: bot.botId,
|
|
43541
|
-
botToken:
|
|
43694
|
+
botToken: entry3.token,
|
|
43542
43695
|
// Task 2617 — read off the entry this resolver just chose, so the
|
|
43543
43696
|
// outbound gate below decides on the same entry the token came from.
|
|
43544
|
-
...chatSendAllowlists(
|
|
43697
|
+
...chatSendAllowlists(entry3)
|
|
43545
43698
|
});
|
|
43546
43699
|
}
|
|
43547
43700
|
}
|
|
@@ -44627,24 +44780,24 @@ registerLoop({
|
|
|
44627
44780
|
const managed = accounts.flatMap((a) => {
|
|
44628
44781
|
const bots = listBotEntries(a.telegram);
|
|
44629
44782
|
const admin = bots.find((b) => b.role === "admin");
|
|
44630
|
-
return bots.filter((e) => e.managed === true).map((
|
|
44783
|
+
return bots.filter((e) => e.managed === true).map((entry3) => ({ entry: entry3, managingToken: admin?.token ?? null }));
|
|
44631
44784
|
});
|
|
44632
44785
|
const probes2 = [];
|
|
44633
|
-
for (const { entry:
|
|
44786
|
+
for (const { entry: entry3, managingToken } of managed) {
|
|
44634
44787
|
let webhookUrl = null;
|
|
44635
44788
|
let isAccessRestricted = null;
|
|
44636
44789
|
try {
|
|
44637
|
-
webhookUrl = (await getTelegramWebhookInfo(
|
|
44790
|
+
webhookUrl = (await getTelegramWebhookInfo(entry3.token)).url;
|
|
44638
44791
|
} catch {
|
|
44639
44792
|
}
|
|
44640
44793
|
if (managingToken) {
|
|
44641
44794
|
try {
|
|
44642
|
-
const got = await getManagedBotAccessSettings(managingToken, Number(
|
|
44795
|
+
const got = await getManagedBotAccessSettings(managingToken, Number(entry3.id));
|
|
44643
44796
|
if (got.ok) isAccessRestricted = got.isAccessRestricted;
|
|
44644
44797
|
} catch {
|
|
44645
44798
|
}
|
|
44646
44799
|
}
|
|
44647
|
-
probes2.push({ botId:
|
|
44800
|
+
probes2.push({ botId: entry3.id, webhookUrl, isAccessRestricted });
|
|
44648
44801
|
}
|
|
44649
44802
|
console.error(buildManagedBotCensusLine({ accounts, probes: probes2 }));
|
|
44650
44803
|
}
|