@rubytech/create-maxy-code 0.1.567 → 0.1.569
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/dist/__tests__/brand-agent-root.test.js +91 -0
- package/dist/brand-agent-root.js +58 -0
- package/dist/index.js +25 -28
- package/package.json +1 -1
- package/payload/platform/plugins/admin/PLUGIN.md +4 -2
- package/payload/platform/plugins/admin/hooks/__tests__/quote-path-write-gate.test.sh +152 -0
- package/payload/platform/plugins/admin/hooks/__tests__/quote-pdf-bash-gate.test.sh +95 -0
- package/payload/platform/plugins/admin/hooks/__tests__/quote-render-gate.test.sh +141 -45
- package/payload/platform/plugins/admin/hooks/__tests__/quote-render-pdf-conformance.test.sh +114 -0
- package/payload/platform/plugins/admin/hooks/quote-path-write-gate.sh +137 -0
- package/payload/platform/plugins/admin/hooks/quote-pdf-bash-gate.sh +70 -0
- package/payload/platform/plugins/admin/hooks/quote-render-gate.sh +105 -37
- package/payload/platform/plugins/admin/hooks/quote-render-pdf-conformance.sh +178 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +7 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts +10 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js +52 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/roster-census.js.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts +9 -0
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.d.ts.map +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js +1 -1
- package/payload/platform/plugins/admin/mcp/dist/tools/capabilities-here.js.map +1 -1
- package/payload/platform/plugins/admin/skills/capabilities-here/SKILL.md +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +44 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +15 -0
- package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +43 -0
- package/payload/platform/scripts/__tests__/mailbox-inject-registered.test.sh +5 -5
- package/payload/platform/scripts/__tests__/preference-hooks-registered.test.sh +3 -1
- package/payload/platform/scripts/__tests__/public-surface-registered.test.sh +5 -5
- package/payload/platform/scripts/__tests__/quote-ownership-contract.test.sh +86 -0
- package/payload/platform/scripts/lib/__tests__/account-settings-reconcile.test.sh +73 -0
- package/payload/platform/scripts/lib/account-settings-reconcile.sh +88 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +11 -146
- package/payload/platform/scripts/setup-account.sh +13 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts +39 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js +223 -0
- package/payload/platform/services/claude-session-manager/dist/account-agent-registry.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js +5 -5
- package/payload/platform/services/claude-session-manager/dist/fs-watcher.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +11 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +58 -12
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +112 -13
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +6 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.js +6 -3
- package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +21 -8
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +38 -15
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.d.ts +42 -0
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js +215 -0
- package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +4 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts +3 -0
- package/payload/platform/services/claude-session-manager/dist/reaper.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.js +2 -1
- package/payload/platform/services/claude-session-manager/dist/reaper.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js +5 -2
- package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +2 -0
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +5 -3
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.d.ts +108 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.js +299 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-preference-audit.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts +26 -16
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js +84 -31
- package/payload/platform/services/claude-session-manager/dist/systemd-scope.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts +7 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js +6 -0
- package/payload/platform/services/claude-session-manager/dist/wa-channel-mcp.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.d.ts +10 -2
- package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/notification.js +15 -3
- package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/server.js +35 -3
- package/payload/platform/services/telegram-channel/dist/server.js.map +1 -1
- package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +10 -0
- package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -1
- package/payload/platform/services/telegram-channel/dist/turn-follow.js +38 -1
- package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/server.js +31 -2
- package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +10 -0
- package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.js +38 -1
- package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts +9 -0
- package/payload/platform/services/whatsapp-channel/dist/instructions.d.ts.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/instructions.js +22 -0
- package/payload/platform/services/whatsapp-channel/dist/instructions.js.map +1 -0
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +3 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/notification.js +10 -2
- package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
- package/payload/platform/services/whatsapp-channel/dist/server.js +26 -2
- package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
- package/payload/platform/templates/account-settings.json +153 -0
- package/payload/server/server.js +254 -96
package/payload/server/server.js
CHANGED
|
@@ -4886,9 +4886,9 @@ var idCache = /* @__PURE__ */ new Map();
|
|
|
4886
4886
|
function persistedMsgKeyIds() {
|
|
4887
4887
|
const ids = /* @__PURE__ */ new Set();
|
|
4888
4888
|
for (const accountId of listStoredAccountIds()) {
|
|
4889
|
-
for (const [remoteJid,
|
|
4889
|
+
for (const [remoteJid, records2] of readAllConversationsCached(accountId)) {
|
|
4890
4890
|
void remoteJid;
|
|
4891
|
-
for (const id of
|
|
4891
|
+
for (const id of records2) ids.add(id);
|
|
4892
4892
|
}
|
|
4893
4893
|
}
|
|
4894
4894
|
return ids;
|
|
@@ -6562,9 +6562,9 @@ function readConversation2(accountId, channelKey) {
|
|
|
6562
6562
|
}
|
|
6563
6563
|
}
|
|
6564
6564
|
var summaryCache = /* @__PURE__ */ new Map();
|
|
6565
|
-
function summariseRecords(
|
|
6566
|
-
if (
|
|
6567
|
-
const sorted =
|
|
6565
|
+
function summariseRecords(records2) {
|
|
6566
|
+
if (records2.length === 0) return null;
|
|
6567
|
+
const sorted = records2.slice().sort(byDateSentAsc);
|
|
6568
6568
|
return { first: sorted[0], last: sorted[sorted.length - 1] };
|
|
6569
6569
|
}
|
|
6570
6570
|
function readConversationSummaries2(accountId) {
|
|
@@ -6962,15 +6962,15 @@ var CENSUS_TAG = "[wa-avatar-census]";
|
|
|
6962
6962
|
var AVATAR_STORE_MAX_BYTES = 16 * 1024 * 1024;
|
|
6963
6963
|
function liveJidsForAccount(accountId) {
|
|
6964
6964
|
const live = /* @__PURE__ */ new Set();
|
|
6965
|
-
for (const [remoteJid,
|
|
6966
|
-
for (const jid of avatarJidsForConversation(remoteJid,
|
|
6965
|
+
for (const [remoteJid, records2] of readAllConversations(accountId)) {
|
|
6966
|
+
for (const jid of avatarJidsForConversation(remoteJid, records2)) live.add(jid);
|
|
6967
6967
|
}
|
|
6968
6968
|
return live;
|
|
6969
6969
|
}
|
|
6970
|
-
function avatarJidsForConversation(remoteJid,
|
|
6970
|
+
function avatarJidsForConversation(remoteJid, records2) {
|
|
6971
6971
|
const jids = /* @__PURE__ */ new Set([remoteJid]);
|
|
6972
6972
|
if (!isGroupJid(remoteJid)) return jids;
|
|
6973
|
-
for (const r of
|
|
6973
|
+
for (const r of records2) {
|
|
6974
6974
|
if (r.fromMe) continue;
|
|
6975
6975
|
if (r.senderJid && isUserJid(r.senderJid)) jids.add(r.senderJid);
|
|
6976
6976
|
}
|
|
@@ -7107,10 +7107,10 @@ function auditGroupOutbound() {
|
|
|
7107
7107
|
let unstamped = 0;
|
|
7108
7108
|
const offenders = [];
|
|
7109
7109
|
for (const accountId of listStoredAccountIds()) {
|
|
7110
|
-
for (const [remoteJid,
|
|
7110
|
+
for (const [remoteJid, records2] of readAllConversations(accountId)) {
|
|
7111
7111
|
if (!isGroupJid(remoteJid)) continue;
|
|
7112
7112
|
convos++;
|
|
7113
|
-
for (const r of
|
|
7113
|
+
for (const r of records2) {
|
|
7114
7114
|
if (!r.fromMe) continue;
|
|
7115
7115
|
outbound++;
|
|
7116
7116
|
if (r.origin === "operator-manual") operator++;
|
|
@@ -12532,8 +12532,8 @@ function resolveReplyAppendAccount(args) {
|
|
|
12532
12532
|
if (args.isGroup) return args.sendAccountId;
|
|
12533
12533
|
return args.resolve(args.sendAccountId, args.counterpartyPhone) || args.sendAccountId;
|
|
12534
12534
|
}
|
|
12535
|
-
function warmConversationAvatars(accountId, remoteJid,
|
|
12536
|
-
const jids = avatarJidsForConversation(remoteJid,
|
|
12535
|
+
function warmConversationAvatars(accountId, remoteJid, records2) {
|
|
12536
|
+
const jids = avatarJidsForConversation(remoteJid, records2);
|
|
12537
12537
|
console.log(`[wa-avatar] op=request account=${accountId.slice(0, 8)} convo=${remoteJid.slice(0, 12)} jids=${jids.size} reason=reader-open`);
|
|
12538
12538
|
for (const jid of jids) {
|
|
12539
12539
|
void fetchAvatar(accountId, jid).catch((e) => {
|
|
@@ -12605,7 +12605,7 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
|
|
|
12605
12605
|
return c.json({ turns: [], from: 0, reachedStart: true });
|
|
12606
12606
|
}
|
|
12607
12607
|
const { from, aligned } = backfillRangeStart(file, to, TAIL_WINDOW_BYTES);
|
|
12608
|
-
const
|
|
12608
|
+
const records2 = [];
|
|
12609
12609
|
const reactionRecords = [];
|
|
12610
12610
|
let reactionScanBytes = 0;
|
|
12611
12611
|
try {
|
|
@@ -12619,7 +12619,7 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
|
|
|
12619
12619
|
reactionRecords.push(parsed);
|
|
12620
12620
|
continue;
|
|
12621
12621
|
}
|
|
12622
|
-
|
|
12622
|
+
records2.push(parsed);
|
|
12623
12623
|
} catch {
|
|
12624
12624
|
}
|
|
12625
12625
|
}
|
|
@@ -12638,11 +12638,11 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
|
|
|
12638
12638
|
console.error(`[wa-reader] op=store-backfill remoteJid=${remoteJid} from=${from} to=${to} outcome=read-failed`);
|
|
12639
12639
|
return c.json({ error: "read-failed" }, 500);
|
|
12640
12640
|
}
|
|
12641
|
-
|
|
12641
|
+
records2.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
|
|
12642
12642
|
const byMsgKeyId = /* @__PURE__ */ new Map();
|
|
12643
|
-
for (const m of
|
|
12643
|
+
for (const m of records2) byMsgKeyId.set(m.msgKeyId, m);
|
|
12644
12644
|
const wanted = /* @__PURE__ */ new Set();
|
|
12645
|
-
for (const m of
|
|
12645
|
+
for (const m of records2) {
|
|
12646
12646
|
if (m.quotedId && !byMsgKeyId.has(m.quotedId)) wanted.add(m.quotedId);
|
|
12647
12647
|
}
|
|
12648
12648
|
const q2 = resolveQuoteTargets(file, from, wanted);
|
|
@@ -12658,7 +12658,7 @@ app5.get("/store-backfill", requireAdminSession, (c) => {
|
|
|
12658
12658
|
const uploadsBase = resolve16(DATA_ROOT, "accounts", accountId, "uploads");
|
|
12659
12659
|
const toTurn2 = makeToTurn({ byMsgKeyId, uploadsBase });
|
|
12660
12660
|
const fold = foldReactions(reactionRecords);
|
|
12661
|
-
const turns =
|
|
12661
|
+
const turns = records2.map((m) => toTurn2(m, fold.get(m.msgKeyId)));
|
|
12662
12662
|
const reachedStart = from === 0;
|
|
12663
12663
|
noteBackfill(remoteJid.slice(0, 12));
|
|
12664
12664
|
console.log(
|
|
@@ -12735,7 +12735,7 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
|
|
|
12735
12735
|
|
|
12736
12736
|
`)
|
|
12737
12737
|
);
|
|
12738
|
-
const
|
|
12738
|
+
const records2 = [];
|
|
12739
12739
|
try {
|
|
12740
12740
|
const { buf, end } = readFrom(file, alignedFrom);
|
|
12741
12741
|
offset = end;
|
|
@@ -12747,18 +12747,18 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
|
|
|
12747
12747
|
reactionRecords.push(parsed);
|
|
12748
12748
|
continue;
|
|
12749
12749
|
}
|
|
12750
|
-
|
|
12750
|
+
records2.push(parsed);
|
|
12751
12751
|
} catch {
|
|
12752
12752
|
}
|
|
12753
12753
|
}
|
|
12754
12754
|
} catch {
|
|
12755
12755
|
offset = fileEnd0;
|
|
12756
12756
|
}
|
|
12757
|
-
|
|
12758
|
-
const scope =
|
|
12759
|
-
for (const m of
|
|
12757
|
+
records2.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
|
|
12758
|
+
const scope = records2.length ? records2[records2.length - 1].scope : "none";
|
|
12759
|
+
for (const m of records2) byMsgKeyId.set(m.msgKeyId, m);
|
|
12760
12760
|
const wantedQuotes = /* @__PURE__ */ new Set();
|
|
12761
|
-
for (const m of
|
|
12761
|
+
for (const m of records2) {
|
|
12762
12762
|
if (m.quotedId && !byMsgKeyId.has(m.quotedId)) wantedQuotes.add(m.quotedId);
|
|
12763
12763
|
}
|
|
12764
12764
|
const q2 = resolveQuoteTargets(file, alignedFrom, wantedQuotes);
|
|
@@ -12772,14 +12772,14 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
|
|
|
12772
12772
|
});
|
|
12773
12773
|
}
|
|
12774
12774
|
const backlogFold = foldReactions(reactionRecords);
|
|
12775
|
-
for (const m of
|
|
12775
|
+
for (const m of records2) send(toTurn2(m, backlogFold.get(m.msgKeyId)), offset);
|
|
12776
12776
|
console.log(
|
|
12777
|
-
`[wa-reader] op=open remoteJid=${remoteJid} scope=${scope} messages=${
|
|
12777
|
+
`[wa-reader] op=open remoteJid=${remoteJid} scope=${scope} messages=${records2.length} source=store`
|
|
12778
12778
|
);
|
|
12779
12779
|
console.log(
|
|
12780
|
-
`[wa-reader] op=store-window remoteJid=${remoteJid} fileBytes=${fileEnd0} budget=${TAIL_WINDOW_BYTES} rawFrom=${rawFrom} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"} messages=${
|
|
12780
|
+
`[wa-reader] op=store-window remoteJid=${remoteJid} fileBytes=${fileEnd0} budget=${TAIL_WINDOW_BYTES} rawFrom=${rawFrom} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"} messages=${records2.length} quoteWanted=${wantedQuotes.size} quoteResolved=${q2.found.size} quoteMissing=${wantedQuotes.size - q2.found.size} quoteScanBytes=${q2.scannedBytes} quoteCapped=${q2.capped}`
|
|
12781
12781
|
);
|
|
12782
|
-
warmConversationAvatars(accountId, remoteJid,
|
|
12782
|
+
warmConversationAvatars(accountId, remoteJid, records2);
|
|
12783
12783
|
} else {
|
|
12784
12784
|
reactionRecords.push(...readReactions(accountId, remoteJid));
|
|
12785
12785
|
}
|
|
@@ -13233,16 +13233,16 @@ app6.get("/store-backfill", requireAdminSession, (c) => {
|
|
|
13233
13233
|
const to = Number(c.req.query("before") ?? "0");
|
|
13234
13234
|
if (!Number.isFinite(to) || to <= 0) return c.json({ turns: [], from: 0, done: true });
|
|
13235
13235
|
const { from } = backfillRangeStart(file, to, TAIL_WINDOW_BYTES);
|
|
13236
|
-
let
|
|
13236
|
+
let records2 = [];
|
|
13237
13237
|
try {
|
|
13238
13238
|
const { buf } = readFrom(file, from);
|
|
13239
|
-
|
|
13239
|
+
records2 = parseRecords(buf.subarray(0, to - from)).sort((a, b) => a.dateSent.localeCompare(b.dateSent));
|
|
13240
13240
|
} catch {
|
|
13241
13241
|
}
|
|
13242
13242
|
console.log(
|
|
13243
|
-
`${TAG22} op=backfill channelKey=${v.channelKey} from=${from} to=${to} turns=${
|
|
13243
|
+
`${TAG22} op=backfill channelKey=${v.channelKey} from=${from} to=${to} turns=${records2.length} done=${from === 0}`
|
|
13244
13244
|
);
|
|
13245
|
-
return c.json({ turns:
|
|
13245
|
+
return c.json({ turns: records2.map(toTurn), from, done: from === 0 });
|
|
13246
13246
|
});
|
|
13247
13247
|
app6.get("/store-stream", requireAdminSession, (c) => {
|
|
13248
13248
|
const v = validate(c.req.query("accountId"), c.req.query("channelKey"));
|
|
@@ -13283,18 +13283,18 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
|
|
|
13283
13283
|
|
|
13284
13284
|
`)
|
|
13285
13285
|
);
|
|
13286
|
-
let
|
|
13286
|
+
let records2 = [];
|
|
13287
13287
|
try {
|
|
13288
13288
|
const { buf, end } = readFrom(file, alignedFrom);
|
|
13289
13289
|
offset = end;
|
|
13290
|
-
|
|
13290
|
+
records2 = parseRecords(buf);
|
|
13291
13291
|
} catch {
|
|
13292
13292
|
offset = fileEnd0;
|
|
13293
13293
|
}
|
|
13294
|
-
|
|
13295
|
-
for (const r of
|
|
13294
|
+
records2.sort((a, b) => a.dateSent.localeCompare(b.dateSent));
|
|
13295
|
+
for (const r of records2) send(toTurn(r), offset);
|
|
13296
13296
|
console.log(
|
|
13297
|
-
`${TAG22} op=open channelKey=${channelKey} messages=${
|
|
13297
|
+
`${TAG22} op=open channelKey=${channelKey} messages=${records2.length} fileBytes=${fileEnd0} alignedFrom=${alignedFrom} aligned=${aligned} source=${alignedFrom > 0 ? "windowed" : "full"}`
|
|
13298
13298
|
);
|
|
13299
13299
|
}
|
|
13300
13300
|
const drain = () => {
|
|
@@ -13473,10 +13473,10 @@ function collectTelegramStoreCensus() {
|
|
|
13473
13473
|
const summaries = readConversationSummaries2(accountId);
|
|
13474
13474
|
for (const channelKey of summaries.keys()) {
|
|
13475
13475
|
out.conversations += 1;
|
|
13476
|
-
const
|
|
13477
|
-
if (
|
|
13478
|
-
out.records +=
|
|
13479
|
-
for (const r of
|
|
13476
|
+
const records2 = readConversation2(accountId, channelKey);
|
|
13477
|
+
if (records2.length > 0) out.served += 1;
|
|
13478
|
+
out.records += records2.length;
|
|
13479
|
+
for (const r of records2) {
|
|
13480
13480
|
if (r.origin === "inbound") out.inbound += 1;
|
|
13481
13481
|
else if (r.origin === "agent") out.agent += 1;
|
|
13482
13482
|
else if (r.origin === "operator-manual") out.operatorManual += 1;
|
|
@@ -31580,6 +31580,40 @@ async function fanOut(p) {
|
|
|
31580
31580
|
return { reached, notReached, line };
|
|
31581
31581
|
}
|
|
31582
31582
|
|
|
31583
|
+
// app/lib/channel-delivery/delivery-census.ts
|
|
31584
|
+
var WINDOW_MS2 = 60 * 60 * 1e3;
|
|
31585
|
+
var records = [];
|
|
31586
|
+
function noteTurnEnd(door, role, outcome, now) {
|
|
31587
|
+
records.push({ door, role, outcome, atMs: now });
|
|
31588
|
+
}
|
|
31589
|
+
function readDeliveryCensus(door, now) {
|
|
31590
|
+
records = records.filter((r) => now - r.atMs <= WINDOW_MS2);
|
|
31591
|
+
const mine = records.filter((r) => r.door === door);
|
|
31592
|
+
const undelivered = mine.filter((r) => r.outcome === "undelivered");
|
|
31593
|
+
return {
|
|
31594
|
+
turnEnds: mine.length,
|
|
31595
|
+
record: mine.filter((r) => r.outcome === "record").length,
|
|
31596
|
+
none: mine.filter((r) => r.outcome === "none").length,
|
|
31597
|
+
undelivered: undelivered.length,
|
|
31598
|
+
// The failure field. An admin answer that reaches nobody is Task 2459's
|
|
31599
|
+
// subject; a PUBLIC one is this task's, and the two must be separable on the
|
|
31600
|
+
// line or neither is diagnosable from it.
|
|
31601
|
+
undeliveredPublic: undelivered.filter((r) => r.role === "public").length,
|
|
31602
|
+
undeliveredAdmin: undelivered.filter((r) => r.role === "admin").length,
|
|
31603
|
+
roleAbsent: mine.filter((r) => r.outcome === "role-absent").length
|
|
31604
|
+
};
|
|
31605
|
+
}
|
|
31606
|
+
function buildDeliveryCensusLine(door, c) {
|
|
31607
|
+
return `[${door}-delivery-census] op=census windowMin=60 turnEnds=${c.turnEnds} record=${c.record} none=${c.none} undelivered=${c.undelivered} undeliveredPublic=${c.undeliveredPublic} undeliveredAdmin=${c.undeliveredAdmin} roleAbsent=${c.roleAbsent}`;
|
|
31608
|
+
}
|
|
31609
|
+
function runDeliveryCensus(now) {
|
|
31610
|
+
for (const door of ["telegram", "whatsapp"]) {
|
|
31611
|
+
const c = readDeliveryCensus(door, now);
|
|
31612
|
+
if (c.turnEnds === 0) continue;
|
|
31613
|
+
console.error(buildDeliveryCensusLine(door, c));
|
|
31614
|
+
}
|
|
31615
|
+
}
|
|
31616
|
+
|
|
31583
31617
|
// app/lib/whatsapp/gateway/routes.ts
|
|
31584
31618
|
var PARTY_PHONE_PATTERN = /^[+0-9][0-9 ()\-.]{4,31}$/;
|
|
31585
31619
|
function createWaChannelRoutes(deps) {
|
|
@@ -31679,35 +31713,61 @@ function createWaChannelRoutes(deps) {
|
|
|
31679
31713
|
console.error(
|
|
31680
31714
|
`[whatsapp-native] op=turn-context sessionId=${sid} cacheReadTokens=${num2(body?.cacheReadTokens)} inputTokens=${num2(body?.inputTokens)} outputTokens=${num2(body?.outputTokens)} thinkingBlocks=${num2(body?.thinkingBlocks)} thinkingChars=${num2(body?.thinkingChars)}`
|
|
31681
31715
|
);
|
|
31716
|
+
const role = body?.role;
|
|
31717
|
+
if (role !== "admin" && role !== "public") {
|
|
31718
|
+
const evicted = deps.hub.hasSubscriber(accountId, senderId);
|
|
31719
|
+
if (evicted) deps.hub.detach(accountId, senderId);
|
|
31720
|
+
console.error(
|
|
31721
|
+
`[whatsapp-native] op=turn-role-absent senderId=${senderId} accountId=${accountId} sessionId=${sid} segments=${segmentText(finalText).length} evicted=${evicted ? "yes" : "no"}`
|
|
31722
|
+
);
|
|
31723
|
+
noteTurnEnd("whatsapp", null, "role-absent", Date.now());
|
|
31724
|
+
return c.json({ error: "role must be 'admin' or 'public'" }, 400);
|
|
31725
|
+
}
|
|
31682
31726
|
const segments = segmentText(finalText);
|
|
31683
|
-
const decision = `sessionId=${sid} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} source=${source ?? "unset"} finalBytes=${finalBytes} queueDepth=${queueDepth} pendingTurns=${pendingTurns} consumed=${consumed2} segments=${segments.length}`;
|
|
31727
|
+
const decision = `sessionId=${sid} role=${role} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} source=${source ?? "unset"} finalBytes=${finalBytes} queueDepth=${queueDepth} pendingTurns=${pendingTurns} consumed=${consumed2} segments=${segments.length}`;
|
|
31684
31728
|
if (segments.length === 0) {
|
|
31685
31729
|
console.error(
|
|
31686
31730
|
`[whatsapp-native] op=turn-answer-absent sessionId=${sid} interactive=${interactive ? "yes" : "no"} finalBytes=${finalBytes} queueDepth=${queueDepth}`
|
|
31687
31731
|
);
|
|
31688
31732
|
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=none`);
|
|
31733
|
+
noteTurnEnd("whatsapp", role, "none", Date.now());
|
|
31689
31734
|
return c.json({ ok: true, delivered: "none", segments: 0 });
|
|
31690
31735
|
}
|
|
31691
|
-
|
|
31692
|
-
|
|
31693
|
-
|
|
31694
|
-
|
|
31695
|
-
|
|
31696
|
-
await deps.sendOutbound(accountId, senderId, text);
|
|
31736
|
+
if (role === "public") {
|
|
31737
|
+
let sent = 0;
|
|
31738
|
+
try {
|
|
31739
|
+
for (const segment of segments) {
|
|
31740
|
+
await deps.sendOutbound(accountId, senderId, segment);
|
|
31697
31741
|
deps.onReplyDispatch?.(senderId, Date.now());
|
|
31742
|
+
sent += 1;
|
|
31698
31743
|
}
|
|
31744
|
+
} catch {
|
|
31699
31745
|
}
|
|
31700
|
-
|
|
31746
|
+
if (sent < segments.length) {
|
|
31747
|
+
console.error(
|
|
31748
|
+
`[whatsapp-native] op=turn-strand-anomaly senderId=${senderId} sessionId=${sid} role=public interactive=${interactive ? "yes" : "no"} segments=${segments.length} delivered=undelivered notReached=whatsapp`
|
|
31749
|
+
);
|
|
31750
|
+
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=undelivered reached=none`);
|
|
31751
|
+
noteTurnEnd("whatsapp", role, "undelivered", Date.now());
|
|
31752
|
+
return c.json({ ok: true, delivered: "undelivered", segments: segments.length, reached: [], notReached: ["whatsapp"] });
|
|
31753
|
+
}
|
|
31754
|
+
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=record bytes=${Buffer.byteLength(finalText, "utf8")} reached=whatsapp`);
|
|
31755
|
+
noteTurnEnd("whatsapp", role, "record", Date.now());
|
|
31756
|
+
return c.json({ ok: true, delivered: "record", segments: segments.length, reached: ["whatsapp"], notReached: [] });
|
|
31757
|
+
}
|
|
31758
|
+
const targets = deps.fanTargetsFor(accountId, senderId);
|
|
31701
31759
|
const { reached, notReached, line } = await fanOut({ targets, segments });
|
|
31702
31760
|
console.error(line);
|
|
31703
31761
|
if (reached.length === 0) {
|
|
31704
31762
|
console.error(
|
|
31705
|
-
`[whatsapp-native] op=turn-strand-anomaly senderId=${senderId} sessionId=${sid} interactive=${interactive ? "yes" : "no"} segments=${segments.length} delivered=undelivered notReached=${notReached.join(",") || "none"}`
|
|
31763
|
+
`[whatsapp-native] op=turn-strand-anomaly senderId=${senderId} sessionId=${sid} role=${role} interactive=${interactive ? "yes" : "no"} segments=${segments.length} delivered=undelivered notReached=${notReached.join(",") || "none"}`
|
|
31706
31764
|
);
|
|
31707
31765
|
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=undelivered reached=none`);
|
|
31766
|
+
noteTurnEnd("whatsapp", role, "undelivered", Date.now());
|
|
31708
31767
|
return c.json({ ok: true, delivered: "undelivered", segments: segments.length, reached, notReached });
|
|
31709
31768
|
}
|
|
31710
31769
|
console.error(`[whatsapp-native] op=turn-end ${decision} delivered=record bytes=${Buffer.byteLength(finalText, "utf8")} reached=${reached.join(",")}`);
|
|
31770
|
+
noteTurnEnd("whatsapp", role, "record", Date.now());
|
|
31711
31771
|
return c.json({ ok: true, delivered: "record", segments: segments.length, reached, notReached });
|
|
31712
31772
|
});
|
|
31713
31773
|
app78.post("/wa-channel/send-party", async (c) => {
|
|
@@ -31882,7 +31942,7 @@ var WaGateway = class {
|
|
|
31882
31942
|
return createWaChannelRoutes({
|
|
31883
31943
|
hub: this.hub,
|
|
31884
31944
|
sendOutbound: (accountId, senderId, text) => this.sendOutbound(accountId, senderId, text),
|
|
31885
|
-
|
|
31945
|
+
fanTargetsFor: (accountId, senderId) => this.deps.fanTargetsFor(accountId, senderId),
|
|
31886
31946
|
onReady: (accountId, senderId) => {
|
|
31887
31947
|
const count = this.hub.markReady(accountId, senderId, Date.now());
|
|
31888
31948
|
if (count > 0) {
|
|
@@ -32173,9 +32233,9 @@ var WaGateway = class {
|
|
|
32173
32233
|
|
|
32174
32234
|
// app/lib/whatsapp/gateway/duplicate-sender-audit.ts
|
|
32175
32235
|
init_normalize();
|
|
32176
|
-
function findDuplicateSenderGroups(
|
|
32236
|
+
function findDuplicateSenderGroups(records2) {
|
|
32177
32237
|
const buckets2 = /* @__PURE__ */ new Map();
|
|
32178
|
-
for (const r of
|
|
32238
|
+
for (const r of records2) {
|
|
32179
32239
|
if (r.channel !== "whatsapp" || !r.senderId) continue;
|
|
32180
32240
|
const canonical = normalizeTarget(r.senderId);
|
|
32181
32241
|
if (!canonical) continue;
|
|
@@ -32196,9 +32256,9 @@ function logDuplicateSenderGroups(groups) {
|
|
|
32196
32256
|
);
|
|
32197
32257
|
}
|
|
32198
32258
|
}
|
|
32199
|
-
function findCrossAccountSenderGroups(
|
|
32259
|
+
function findCrossAccountSenderGroups(records2) {
|
|
32200
32260
|
const byCanonical = /* @__PURE__ */ new Map();
|
|
32201
|
-
for (const r of
|
|
32261
|
+
for (const r of records2) {
|
|
32202
32262
|
if (r.channel !== "whatsapp" || !r.senderId || !r.accountId) continue;
|
|
32203
32263
|
const canonical = normalizeTarget(r.senderId);
|
|
32204
32264
|
if (!canonical) continue;
|
|
@@ -33848,28 +33908,60 @@ function createTelegramChannelRoutes(deps) {
|
|
|
33848
33908
|
console.error(
|
|
33849
33909
|
`[telegram-native] op=turn-context sessionId=${sid} cacheReadTokens=${num2(body?.cacheReadTokens)} inputTokens=${num2(body?.inputTokens)} outputTokens=${num2(body?.outputTokens)} thinkingBlocks=${num2(body?.thinkingBlocks)} thinkingChars=${num2(body?.thinkingChars)}`
|
|
33850
33910
|
);
|
|
33911
|
+
const role = body?.role;
|
|
33912
|
+
if (role !== "admin" && role !== "public") {
|
|
33913
|
+
const evicted = deps.hub.hasSubscriber(key2);
|
|
33914
|
+
if (evicted) deps.hub.detach(key2);
|
|
33915
|
+
console.error(
|
|
33916
|
+
`[telegram-native] op=turn-role-absent key=${key2} sessionId=${sid} segments=${segmentText(finalText).length} evicted=${evicted ? "yes" : "no"}`
|
|
33917
|
+
);
|
|
33918
|
+
noteTurnEnd("telegram", null, "role-absent", Date.now());
|
|
33919
|
+
return c.json({ error: "role must be 'admin' or 'public'" }, 400);
|
|
33920
|
+
}
|
|
33851
33921
|
const segments = segmentText(finalText);
|
|
33852
|
-
const decision = `key=${key2} sessionId=${sid} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} segments=${segments.length}`;
|
|
33922
|
+
const decision = `key=${key2} sessionId=${sid} role=${role} replied=${replied ? "yes" : "no"} interactive=${interactive ? "yes" : "no"} segments=${segments.length}`;
|
|
33853
33923
|
if (segments.length === 0) {
|
|
33854
33924
|
console.error(
|
|
33855
33925
|
`[telegram-native] op=turn-answer-absent key=${key2} sessionId=${sid} interactive=${interactive ? "yes" : "no"}`
|
|
33856
33926
|
);
|
|
33857
33927
|
console.error(`[telegram-native] op=turn-end ${decision} delivered=none`);
|
|
33928
|
+
noteTurnEnd("telegram", role, "none", Date.now());
|
|
33858
33929
|
return c.json({ ok: true, delivered: "none", segments: 0 });
|
|
33859
33930
|
}
|
|
33860
|
-
|
|
33861
|
-
|
|
33862
|
-
|
|
33931
|
+
if (role === "public") {
|
|
33932
|
+
let sent = 0;
|
|
33933
|
+
try {
|
|
33934
|
+
for (const segment of segments) {
|
|
33935
|
+
await deps.sendOutbound(key2, segment);
|
|
33936
|
+
sent += 1;
|
|
33937
|
+
}
|
|
33938
|
+
} catch {
|
|
33939
|
+
}
|
|
33940
|
+
if (sent < segments.length) {
|
|
33941
|
+
console.error(
|
|
33942
|
+
`[telegram-native] op=turn-strand-anomaly key=${key2} sessionId=${sid} role=public interactive=${interactive ? "yes" : "no"} segments=${segments.length} delivered=undelivered notReached=telegram`
|
|
33943
|
+
);
|
|
33944
|
+
console.error(`[telegram-native] op=turn-end ${decision} delivered=undelivered reached=none`);
|
|
33945
|
+
noteTurnEnd("telegram", role, "undelivered", Date.now());
|
|
33946
|
+
return c.json({ ok: true, delivered: "undelivered", segments: segments.length, reached: [], notReached: ["telegram"] });
|
|
33947
|
+
}
|
|
33948
|
+
console.error(`[telegram-native] op=turn-end ${decision} delivered=record bytes=${Buffer.byteLength(finalText, "utf8")} reached=telegram`);
|
|
33949
|
+
noteTurnEnd("telegram", role, "record", Date.now());
|
|
33950
|
+
return c.json({ ok: true, delivered: "record", segments: segments.length, reached: ["telegram"], notReached: [] });
|
|
33951
|
+
}
|
|
33952
|
+
const targets = deps.fanTargetsFor(key2);
|
|
33863
33953
|
const { reached, notReached, line } = await fanOut({ targets, segments });
|
|
33864
33954
|
console.error(line);
|
|
33865
33955
|
if (reached.length === 0) {
|
|
33866
33956
|
console.error(
|
|
33867
|
-
`[telegram-native] op=turn-strand-anomaly key=${key2} sessionId=${sid} interactive=${interactive ? "yes" : "no"} segments=${segments.length} delivered=undelivered notReached=${notReached.join(",") || "none"}`
|
|
33957
|
+
`[telegram-native] op=turn-strand-anomaly key=${key2} sessionId=${sid} role=${role} interactive=${interactive ? "yes" : "no"} segments=${segments.length} delivered=undelivered notReached=${notReached.join(",") || "none"}`
|
|
33868
33958
|
);
|
|
33869
33959
|
console.error(`[telegram-native] op=turn-end ${decision} delivered=undelivered reached=none`);
|
|
33960
|
+
noteTurnEnd("telegram", role, "undelivered", Date.now());
|
|
33870
33961
|
return c.json({ ok: true, delivered: "undelivered", segments: segments.length, reached, notReached });
|
|
33871
33962
|
}
|
|
33872
33963
|
console.error(`[telegram-native] op=turn-end ${decision} delivered=record bytes=${Buffer.byteLength(finalText, "utf8")} reached=${reached.join(",")}`);
|
|
33964
|
+
noteTurnEnd("telegram", role, "record", Date.now());
|
|
33873
33965
|
return c.json({ ok: true, delivered: "record", segments: segments.length, reached, notReached });
|
|
33874
33966
|
});
|
|
33875
33967
|
return app78;
|
|
@@ -33994,7 +34086,7 @@ var TelegramGateway = class {
|
|
|
33994
34086
|
return createTelegramChannelRoutes({
|
|
33995
34087
|
hub: this.hub,
|
|
33996
34088
|
sendOutbound: (key2, text) => this.sendOutbound(key2, text),
|
|
33997
|
-
|
|
34089
|
+
fanTargetsFor: (key2) => this.deps.fanTargetsFor(key2),
|
|
33998
34090
|
onReady: (key2) => {
|
|
33999
34091
|
if (this.spawning.has(key2)) {
|
|
34000
34092
|
if (!this.readyWhileSpawning.has(key2)) {
|
|
@@ -34397,6 +34489,48 @@ function startTelegramNativeFileFollower(input) {
|
|
|
34397
34489
|
|
|
34398
34490
|
// server/index.ts
|
|
34399
34491
|
init_admin_session_id();
|
|
34492
|
+
|
|
34493
|
+
// app/lib/channel-delivery/admin-fan-targets.ts
|
|
34494
|
+
function noTarget(origin, accountId, reason) {
|
|
34495
|
+
console.error(`[channel-fanout] op=no-target origin=${origin} accountId=${accountId} reason=${reason}`);
|
|
34496
|
+
}
|
|
34497
|
+
function adminFanTargets(p) {
|
|
34498
|
+
const send = p.deps.originLiveTarget(p.originKey);
|
|
34499
|
+
const origin = send ? { channel: p.origin, live: true, send } : { channel: p.origin, live: false };
|
|
34500
|
+
if (!send) noTarget(p.origin, p.accountId, "origin-not-live");
|
|
34501
|
+
if (!p.adminUserId) {
|
|
34502
|
+
noTarget(p.origin, p.accountId, "no-admin-user");
|
|
34503
|
+
return [origin];
|
|
34504
|
+
}
|
|
34505
|
+
let users;
|
|
34506
|
+
try {
|
|
34507
|
+
users = p.deps.readUsers();
|
|
34508
|
+
} catch {
|
|
34509
|
+
noTarget(p.origin, p.accountId, "users-read-failed");
|
|
34510
|
+
return [origin];
|
|
34511
|
+
}
|
|
34512
|
+
const entry = (users ?? []).find((u) => u.userId === p.adminUserId);
|
|
34513
|
+
if (!entry) {
|
|
34514
|
+
noTarget(p.origin, p.accountId, "no-entry");
|
|
34515
|
+
return [origin];
|
|
34516
|
+
}
|
|
34517
|
+
if (!entry.phone && !entry.telegram) {
|
|
34518
|
+
noTarget(p.origin, p.accountId, "no-bindings");
|
|
34519
|
+
return [origin];
|
|
34520
|
+
}
|
|
34521
|
+
const bound = [];
|
|
34522
|
+
if (entry.phone) {
|
|
34523
|
+
const wa = p.deps.whatsappLiveTarget(entry.phone);
|
|
34524
|
+
bound.push(wa ? { channel: "whatsapp", live: true, send: wa } : { channel: "whatsapp", live: false });
|
|
34525
|
+
}
|
|
34526
|
+
if (entry.telegram) {
|
|
34527
|
+
const tg = p.deps.telegramLiveTarget(entry.telegram);
|
|
34528
|
+
bound.push(tg ? { channel: "telegram", live: true, send: tg } : { channel: "telegram", live: false });
|
|
34529
|
+
}
|
|
34530
|
+
return [origin, ...bound.filter((t) => t.channel !== p.origin)];
|
|
34531
|
+
}
|
|
34532
|
+
|
|
34533
|
+
// server/index.ts
|
|
34400
34534
|
init_pin_validator();
|
|
34401
34535
|
init_webchat();
|
|
34402
34536
|
|
|
@@ -35335,7 +35469,15 @@ var waGateway = new WaGateway({
|
|
|
35335
35469
|
sendToParty: (accountId, phone, text) => sendWhatsAppTextForRelay(accountId, phone, text),
|
|
35336
35470
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
35337
35471
|
// Task 2078 — one answer reaches every live door for this admin person.
|
|
35338
|
-
|
|
35472
|
+
// Task 2459 — the origin door is resolved from `senderId`, so it is present
|
|
35473
|
+
// even when the sender resolves to no admin at all.
|
|
35474
|
+
fanTargetsFor: (accountId, senderId) => adminFanTargets({
|
|
35475
|
+
origin: "whatsapp",
|
|
35476
|
+
originKey: senderId,
|
|
35477
|
+
accountId,
|
|
35478
|
+
adminUserId: resolveAdminUserId({ accountId, senderPhone: senderId }),
|
|
35479
|
+
deps: adminFanDeps("whatsapp", accountId)
|
|
35480
|
+
}),
|
|
35339
35481
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
35340
35482
|
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve49(process.env.MAXY_PLATFORM_ROOT ?? join59(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
35341
35483
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
@@ -35520,16 +35662,16 @@ function runDuplicateSenderAudit() {
|
|
|
35520
35662
|
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
35521
35663
|
if (!configDir) return;
|
|
35522
35664
|
const projectsRoot = join59(configDir, "projects");
|
|
35523
|
-
const
|
|
35665
|
+
const records2 = [];
|
|
35524
35666
|
for (const { path: jsonlPath, isSubagent, archived } of enumerateJsonls(projectsRoot)) {
|
|
35525
35667
|
if (isSubagent || archived) continue;
|
|
35526
35668
|
const sessionId = jsonlPath.slice(jsonlPath.lastIndexOf("/") + 1).replace(/\.jsonl$/, "");
|
|
35527
35669
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
35528
35670
|
const meta = readSidecarMeta(metaPath);
|
|
35529
|
-
|
|
35671
|
+
records2.push({ sessionId, channel: meta.channel, senderId: meta.senderId, accountId: meta.accountId });
|
|
35530
35672
|
}
|
|
35531
|
-
logDuplicateSenderGroups(findDuplicateSenderGroups(
|
|
35532
|
-
logCrossAccountSenderGroups(findCrossAccountSenderGroups(
|
|
35673
|
+
logDuplicateSenderGroups(findDuplicateSenderGroups(records2));
|
|
35674
|
+
logCrossAccountSenderGroups(findCrossAccountSenderGroups(records2));
|
|
35533
35675
|
const outbound = waGateway.takeOutboundAccountCensus();
|
|
35534
35676
|
console.error(
|
|
35535
35677
|
`[whatsapp-native] op=outbound-account-mismatch sends=${outbound.sends} mismatched=${outbound.mismatched}`
|
|
@@ -35687,8 +35829,18 @@ var telegramGateway = new TelegramGateway({
|
|
|
35687
35829
|
const account = listValidAccounts().find(
|
|
35688
35830
|
(a) => listBotEntries(a.config.telegram).some((e) => e.id === key2.slice(0, key2.indexOf(":")))
|
|
35689
35831
|
);
|
|
35690
|
-
if (!account)
|
|
35691
|
-
|
|
35832
|
+
if (!account) {
|
|
35833
|
+
console.error(`[channel-fanout] op=no-target origin=telegram key=${key2} accountId=none reason=no-account`);
|
|
35834
|
+
const send = telegramOriginTarget(key2);
|
|
35835
|
+
return [send ? { channel: "telegram", live: true, send } : { channel: "telegram", live: false }];
|
|
35836
|
+
}
|
|
35837
|
+
return adminFanTargets({
|
|
35838
|
+
origin: "telegram",
|
|
35839
|
+
originKey: key2,
|
|
35840
|
+
accountId: account.accountId,
|
|
35841
|
+
adminUserId: resolveTelegramAdminUserId({ accountId: account.accountId, chatId }),
|
|
35842
|
+
deps: adminFanDeps("telegram", account.accountId)
|
|
35843
|
+
});
|
|
35692
35844
|
},
|
|
35693
35845
|
gatewayUrl: telegramGatewayUrl(),
|
|
35694
35846
|
serverPath: telegramServerPath(),
|
|
@@ -36792,6 +36944,14 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
36792
36944
|
runTelegramStoreCensus();
|
|
36793
36945
|
}
|
|
36794
36946
|
});
|
|
36947
|
+
registerLoop({
|
|
36948
|
+
name: "channel-delivery-census",
|
|
36949
|
+
intervalMs: 6e4,
|
|
36950
|
+
firstRunDelayMs: 95e3,
|
|
36951
|
+
run: async () => {
|
|
36952
|
+
runDeliveryCensus(Date.now());
|
|
36953
|
+
}
|
|
36954
|
+
});
|
|
36795
36955
|
registerLoop({
|
|
36796
36956
|
name: "portal-uploads-pull",
|
|
36797
36957
|
intervalMs: PORTAL_UPLOADS_PULL_INTERVAL_MS,
|
|
@@ -37002,12 +37162,12 @@ startAuthHealthHeartbeat();
|
|
|
37002
37162
|
const loopHist = monitorEventLoopDelay({ resolution: 50 });
|
|
37003
37163
|
loopHist.enable();
|
|
37004
37164
|
const LOUD_THRESHOLD_MS = 1e3;
|
|
37005
|
-
const
|
|
37165
|
+
const WINDOW_MS3 = 3e4;
|
|
37006
37166
|
registerLoop({
|
|
37007
37167
|
name: "event-loop-lag",
|
|
37008
|
-
intervalMs:
|
|
37168
|
+
intervalMs: WINDOW_MS3,
|
|
37009
37169
|
run: () => {
|
|
37010
|
-
const w = readAndResetWindow(loopHist,
|
|
37170
|
+
const w = readAndResetWindow(loopHist, WINDOW_MS3);
|
|
37011
37171
|
const line = formatLagWindow(w);
|
|
37012
37172
|
if (w.maxMs >= LOUD_THRESHOLD_MS) console.error(line);
|
|
37013
37173
|
else console.log(line);
|
|
@@ -37304,30 +37464,28 @@ try {
|
|
|
37304
37464
|
console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
|
|
37305
37465
|
}
|
|
37306
37466
|
var adminAdoptionSeen = /* @__PURE__ */ new Set();
|
|
37307
|
-
function
|
|
37308
|
-
|
|
37309
|
-
|
|
37310
|
-
|
|
37311
|
-
|
|
37312
|
-
|
|
37313
|
-
|
|
37314
|
-
|
|
37315
|
-
|
|
37316
|
-
|
|
37317
|
-
|
|
37318
|
-
|
|
37319
|
-
|
|
37320
|
-
|
|
37321
|
-
|
|
37322
|
-
|
|
37323
|
-
|
|
37324
|
-
);
|
|
37325
|
-
}
|
|
37326
|
-
|
|
37327
|
-
|
|
37328
|
-
|
|
37329
|
-
}
|
|
37330
|
-
return targets.filter((t) => t.channel !== origin || t.live);
|
|
37467
|
+
function adminFanDeps(origin, accountId) {
|
|
37468
|
+
return {
|
|
37469
|
+
readUsers: () => readUsersFile(USERS_FILE),
|
|
37470
|
+
originLiveTarget: (originKey) => origin === "whatsapp" ? whatsappSender(accountId, originKey) : telegramOriginTarget(originKey),
|
|
37471
|
+
whatsappLiveTarget: (phone) => whatsappSender(accountId, phone),
|
|
37472
|
+
// The telegram reply map is keyed on the `<botId>:<chatId>` composite, and
|
|
37473
|
+
// the bot is not derivable from the person. Every bot this account runs is
|
|
37474
|
+
// tried, and the first live one wins: a person reachable on two of this
|
|
37475
|
+
// account's bots hears once, on whichever is attached.
|
|
37476
|
+
telegramLiveTarget: (chatId) => telegramBotIdsFor(accountId).map((botId) => telegramGateway.liveReplyTarget(`${botId}:${chatId}`)).find((t) => t !== null) ?? null
|
|
37477
|
+
};
|
|
37478
|
+
}
|
|
37479
|
+
function whatsappSender(accountId, key2) {
|
|
37480
|
+
const send = waGateway.liveReplyTarget(accountId, key2);
|
|
37481
|
+
if (!send) return null;
|
|
37482
|
+
return async (text) => {
|
|
37483
|
+
await send(text);
|
|
37484
|
+
waGateway.noteReplyDispatch(key2, Date.now());
|
|
37485
|
+
};
|
|
37486
|
+
}
|
|
37487
|
+
function telegramOriginTarget(key2) {
|
|
37488
|
+
return telegramGateway.liveReplyTarget(key2);
|
|
37331
37489
|
}
|
|
37332
37490
|
function telegramBotIdsFor(accountId) {
|
|
37333
37491
|
try {
|