@rubytech/create-realagent-code 0.1.577 → 0.1.578
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/routine-templates/dist/index.d.ts +4 -4
- package/payload/platform/lib/routine-templates/dist/index.js +4 -4
- package/payload/platform/lib/routine-templates/src/index.ts +4 -4
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +9 -0
- package/payload/platform/plugins/docs/references/platform.md +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +0 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/server/public/activity.html +4 -4
- package/payload/server/public/agents.html +3 -3
- package/payload/server/public/assets/{AdminLoginScreens-BLGZEaKU.js → AdminLoginScreens-CAZ3DSwA.js} +1 -1
- package/payload/server/public/assets/AdminLoginScreens-CAZ3DSwA.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-CAZ3DSwA.js.gz +0 -0
- package/payload/server/public/assets/{AdminShell-C_M7YE5Z.js → AdminShell-BLs5v0qF.js} +1 -1
- package/payload/server/public/assets/AdminShell-BLs5v0qF.js.br +0 -0
- package/payload/server/public/assets/AdminShell-BLs5v0qF.js.gz +0 -0
- package/payload/server/public/assets/{activity-BL6qbB9a.js → activity-BwPck2yT.js} +1 -1
- package/payload/server/public/assets/activity-BwPck2yT.js.br +0 -0
- package/payload/server/public/assets/activity-BwPck2yT.js.gz +0 -0
- package/payload/server/public/assets/{admin-jnJ3P2u4.js → admin-DjtOoabA.js} +1 -1
- package/payload/server/public/assets/admin-DjtOoabA.js.br +0 -0
- package/payload/server/public/assets/admin-DjtOoabA.js.gz +0 -0
- package/payload/server/public/assets/{agents-BzhfKZDN.js → agents-IQYaRvoh.js} +1 -1
- package/payload/server/public/assets/agents-IQYaRvoh.js.br +0 -0
- package/payload/server/public/assets/agents-IQYaRvoh.js.gz +0 -0
- package/payload/server/public/assets/{browser-C-6io2B9.js → browser-CPG8w8rQ.js} +1 -1
- package/payload/server/public/assets/browser-CPG8w8rQ.js.br +0 -0
- package/payload/server/public/assets/browser-CPG8w8rQ.js.gz +0 -0
- package/payload/server/public/assets/{calendar-Dt3gS88s.js → calendar-CRwrgNdq.js} +1 -1
- package/payload/server/public/assets/calendar-CRwrgNdq.js.br +0 -0
- package/payload/server/public/assets/calendar-CRwrgNdq.js.gz +0 -0
- package/payload/server/public/assets/chat-B7yZ4KLl.js +1 -0
- package/payload/server/public/assets/chat-B7yZ4KLl.js.br +3 -0
- package/payload/server/public/assets/chat-B7yZ4KLl.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-BzKUQqLy.js +1 -0
- package/payload/server/public/assets/chevron-left-BzKUQqLy.js.br +0 -0
- package/payload/server/public/assets/clock-BafWFNGo.js +1 -0
- package/payload/server/public/assets/clock-BafWFNGo.js.br +0 -0
- package/payload/server/public/assets/{copy-TkPJgllS.js → copy-JyiMTV0k.js} +1 -1
- package/payload/server/public/assets/copy-JyiMTV0k.js.br +0 -0
- package/payload/server/public/assets/copy-JyiMTV0k.js.gz +0 -0
- package/payload/server/public/assets/data-DHtGCVrl.js +1 -0
- package/payload/server/public/assets/data-DHtGCVrl.js.br +0 -0
- package/payload/server/public/assets/data-DHtGCVrl.js.gz +0 -0
- package/payload/server/public/assets/{file-text-DyAL2DCh.js → file-text-cnHv9ZS6.js} +1 -1
- package/payload/server/public/assets/file-text-cnHv9ZS6.js.br +0 -0
- package/payload/server/public/assets/file-text-cnHv9ZS6.js.gz +0 -0
- package/payload/server/public/assets/{graph-PiNIcCfX.js → graph-lFwEUDSH.js} +1 -1
- package/payload/server/public/assets/graph-lFwEUDSH.js.br +0 -0
- package/payload/server/public/assets/graph-lFwEUDSH.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-8jKFrLHn.js → graph-labels-C6qUA0vO.js} +1 -1
- package/payload/server/public/assets/graph-labels-C6qUA0vO.js.br +0 -0
- package/payload/server/public/assets/graph-labels-C6qUA0vO.js.gz +0 -0
- package/payload/server/public/assets/{operator-DKGKGzrb.js → operator-BdoEy5mh.js} +1 -1
- package/payload/server/public/assets/operator-BdoEy5mh.js.br +0 -0
- package/payload/server/public/assets/operator-BdoEy5mh.js.gz +0 -0
- package/payload/server/public/assets/{page-DXz_K10G.js → page-DMrnnXNA.js} +1 -1
- package/payload/server/public/assets/page-DMrnnXNA.js.br +0 -0
- package/payload/server/public/assets/page-DMrnnXNA.js.gz +0 -0
- package/payload/server/public/assets/{page-BU5vlY9Y.js → page-DeMikGYL.js} +1 -1
- package/payload/server/public/assets/page-DeMikGYL.js.br +0 -0
- package/payload/server/public/assets/page-DeMikGYL.js.gz +0 -0
- package/payload/server/public/assets/play-D6OAQVJ_.js +1 -0
- package/payload/server/public/assets/play-D6OAQVJ_.js.br +0 -0
- package/payload/server/public/assets/play-D6OAQVJ_.js.gz +0 -0
- package/payload/server/public/assets/{public-BmA3-jLj.js → public-B-X1Cxfw.js} +1 -1
- package/payload/server/public/assets/public-B-X1Cxfw.js.br +0 -0
- package/payload/server/public/assets/public-B-X1Cxfw.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-fCH0LSMq.js → rotate-ccw-DVFvQAHG.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-DVFvQAHG.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-DVFvQAHG.js.gz +0 -0
- package/payload/server/public/assets/{routines-BeFrmkls.js → routines-CgkkGJaS.js} +1 -1
- package/payload/server/public/assets/routines-CgkkGJaS.js.br +0 -0
- package/payload/server/public/assets/routines-CgkkGJaS.js.gz +0 -0
- package/payload/server/public/assets/{skills-mjls5HBa.js → skills-CW7gVcaG.js} +1 -1
- package/payload/server/public/assets/skills-CW7gVcaG.js.br +0 -0
- package/payload/server/public/assets/skills-CW7gVcaG.js.gz +0 -0
- package/payload/server/public/assets/{tasks-p6puHHD0.js → tasks-Ddjp0Rwh.js} +1 -1
- package/payload/server/public/assets/tasks-Ddjp0Rwh.js.br +0 -0
- package/payload/server/public/assets/tasks-Ddjp0Rwh.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-CrZ1ewbd.js → triangle-alert-DUrUutX4.js} +1 -1
- package/payload/server/public/assets/triangle-alert-DUrUutX4.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-DUrUutX4.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-zOQNrIwd.js → useCopyFeedback-ClVINDrQ.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-ClVINDrQ.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-ClVINDrQ.js.gz +0 -0
- package/payload/server/public/assets/{useMediaQuery-BmG090FR.js → useMediaQuery-BUFkZYRs.js} +1 -1
- package/payload/server/public/assets/useMediaQuery-BUFkZYRs.js.br +0 -0
- package/payload/server/public/assets/{useMediaQuery-BmG090FR.js.gz → useMediaQuery-BUFkZYRs.js.gz} +0 -0
- package/payload/server/public/assets/{useVoiceRecorder-9pf6FFJ-.js → useVoiceRecorder-DnyqQFet.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-DnyqQFet.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-DnyqQFet.js.gz +0 -0
- package/payload/server/public/assets/{wrench-k4GYG5rc.js → wrench-BW28s_d0.js} +1 -1
- package/payload/server/public/assets/wrench-BW28s_d0.js.br +0 -0
- package/payload/server/public/assets/wrench-BW28s_d0.js.gz +0 -0
- package/payload/server/public/browser.html +3 -3
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +10 -10
- package/payload/server/public/graph.html +8 -8
- package/payload/server/public/index.html +12 -12
- package/payload/server/public/operator.html +13 -13
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/routines.html +6 -6
- package/payload/server/public/skills.html +4 -4
- package/payload/server/public/tasks.html +5 -5
- package/payload/server/server.js +520 -272
- package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.d.ts +0 -6
- package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.d.ts.map +0 -1
- package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.js +0 -102
- package/payload/platform/plugins/dispatch/mcp/dist/scripts/converge-visit-instants.js.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.d.ts +0 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.d.ts.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.js +0 -87
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-routine-description.test.js.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.d.ts +0 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.d.ts.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.js +0 -79
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/converge-startdate-utc.test.js.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.d.ts +0 -35
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.d.ts.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.js +0 -56
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-routine-description.js.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.d.ts +0 -24
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.d.ts.map +0 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.js +0 -87
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/converge-startdate-utc.js.map +0 -1
- package/payload/server/public/assets/AdminLoginScreens-BLGZEaKU.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-BLGZEaKU.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-C_M7YE5Z.js.br +0 -0
- package/payload/server/public/assets/AdminShell-C_M7YE5Z.js.gz +0 -0
- package/payload/server/public/assets/activity-BL6qbB9a.js.br +0 -0
- package/payload/server/public/assets/activity-BL6qbB9a.js.gz +0 -0
- package/payload/server/public/assets/admin-jnJ3P2u4.js.br +0 -0
- package/payload/server/public/assets/admin-jnJ3P2u4.js.gz +0 -0
- package/payload/server/public/assets/agents-BzhfKZDN.js.br +0 -0
- package/payload/server/public/assets/agents-BzhfKZDN.js.gz +0 -0
- package/payload/server/public/assets/browser-C-6io2B9.js.br +0 -0
- package/payload/server/public/assets/browser-C-6io2B9.js.gz +0 -0
- package/payload/server/public/assets/calendar-Dt3gS88s.js.br +0 -0
- package/payload/server/public/assets/calendar-Dt3gS88s.js.gz +0 -0
- package/payload/server/public/assets/chat-IUOvVkL-.js +0 -1
- package/payload/server/public/assets/chat-IUOvVkL-.js.br +0 -1
- package/payload/server/public/assets/chat-IUOvVkL-.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-DSzR9zR7.js +0 -1
- package/payload/server/public/assets/chevron-left-DSzR9zR7.js.br +0 -0
- package/payload/server/public/assets/clock-C5xE5p2I.js +0 -1
- package/payload/server/public/assets/clock-C5xE5p2I.js.br +0 -0
- package/payload/server/public/assets/copy-TkPJgllS.js.br +0 -0
- package/payload/server/public/assets/copy-TkPJgllS.js.gz +0 -0
- package/payload/server/public/assets/data-Bhv0Pvfl.js +0 -1
- package/payload/server/public/assets/data-Bhv0Pvfl.js.br +0 -0
- package/payload/server/public/assets/data-Bhv0Pvfl.js.gz +0 -0
- package/payload/server/public/assets/file-text-DyAL2DCh.js.br +0 -0
- package/payload/server/public/assets/file-text-DyAL2DCh.js.gz +0 -0
- package/payload/server/public/assets/graph-PiNIcCfX.js.br +0 -0
- package/payload/server/public/assets/graph-PiNIcCfX.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-8jKFrLHn.js.br +0 -0
- package/payload/server/public/assets/graph-labels-8jKFrLHn.js.gz +0 -0
- package/payload/server/public/assets/operator-DKGKGzrb.js.br +0 -0
- package/payload/server/public/assets/operator-DKGKGzrb.js.gz +0 -0
- package/payload/server/public/assets/page-BU5vlY9Y.js.br +0 -0
- package/payload/server/public/assets/page-BU5vlY9Y.js.gz +0 -0
- package/payload/server/public/assets/page-DXz_K10G.js.br +0 -0
- package/payload/server/public/assets/page-DXz_K10G.js.gz +0 -0
- package/payload/server/public/assets/play-CmSyYrB6.js +0 -1
- package/payload/server/public/assets/play-CmSyYrB6.js.br +0 -0
- package/payload/server/public/assets/play-CmSyYrB6.js.gz +0 -0
- package/payload/server/public/assets/public-BmA3-jLj.js.br +0 -0
- package/payload/server/public/assets/public-BmA3-jLj.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-fCH0LSMq.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-fCH0LSMq.js.gz +0 -0
- package/payload/server/public/assets/routines-BeFrmkls.js.br +0 -0
- package/payload/server/public/assets/routines-BeFrmkls.js.gz +0 -0
- package/payload/server/public/assets/skills-mjls5HBa.js.br +0 -0
- package/payload/server/public/assets/skills-mjls5HBa.js.gz +0 -0
- package/payload/server/public/assets/tasks-p6puHHD0.js.br +0 -0
- package/payload/server/public/assets/tasks-p6puHHD0.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-CrZ1ewbd.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-CrZ1ewbd.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-zOQNrIwd.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-zOQNrIwd.js.gz +0 -0
- package/payload/server/public/assets/useMediaQuery-BmG090FR.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-9pf6FFJ-.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-9pf6FFJ-.js.gz +0 -0
- package/payload/server/public/assets/wrench-k4GYG5rc.js.br +0 -0
- package/payload/server/public/assets/wrench-k4GYG5rc.js.gz +0 -0
package/payload/server/server.js
CHANGED
|
@@ -471,6 +471,16 @@ var init_agent_errors = __esm({
|
|
|
471
471
|
});
|
|
472
472
|
|
|
473
473
|
// app/lib/reader/parse-transcript.ts
|
|
474
|
+
function emptyEnvelopeCensus() {
|
|
475
|
+
return {
|
|
476
|
+
rows: 0,
|
|
477
|
+
wrapped: 0,
|
|
478
|
+
nested: 0,
|
|
479
|
+
stripped: 0,
|
|
480
|
+
unstripped: 0,
|
|
481
|
+
sources: { whatsapp: 0, webchat: 0, telegram: 0, none: 0 }
|
|
482
|
+
};
|
|
483
|
+
}
|
|
474
484
|
function isCompactSummaryRow(row, content) {
|
|
475
485
|
return row.isCompactSummary === true || content !== null && content.startsWith(COMPACT_SUMMARY_PREFIX);
|
|
476
486
|
}
|
|
@@ -481,10 +491,11 @@ function isScheduleSource(text) {
|
|
|
481
491
|
}
|
|
482
492
|
function unwrapChannel(text) {
|
|
483
493
|
const m = text.match(CHANNEL_WRAPPER);
|
|
484
|
-
if (!m) return { text };
|
|
494
|
+
if (!m) return { text, wrapped: false, nested: false };
|
|
485
495
|
let source = SOURCE_MAP[m[1].match(SOURCE_ATTR)?.[1] ?? ""];
|
|
486
496
|
let inner = m[2];
|
|
487
497
|
const im = inner.match(CHANNEL_WRAPPER);
|
|
498
|
+
const nested = im !== null;
|
|
488
499
|
if (im) {
|
|
489
500
|
const innerSource = SOURCE_MAP[im[1].match(SOURCE_ATTR)?.[1] ?? ""];
|
|
490
501
|
if (innerSource) {
|
|
@@ -492,7 +503,7 @@ function unwrapChannel(text) {
|
|
|
492
503
|
inner = im[2];
|
|
493
504
|
}
|
|
494
505
|
}
|
|
495
|
-
return source ? { text: inner, source } : { text: inner };
|
|
506
|
+
return source ? { text: inner, source, wrapped: true, nested } : { text: inner, wrapped: true, nested };
|
|
496
507
|
}
|
|
497
508
|
function splitReplyNote(payload) {
|
|
498
509
|
const m = payload.match(REPLY_NOTE);
|
|
@@ -508,7 +519,17 @@ function uploadCountOf(text) {
|
|
|
508
519
|
const n = Number(text.match(UPLOAD_COUNT)?.[1]);
|
|
509
520
|
return Number.isInteger(n) && n > 0 ? n : void 0;
|
|
510
521
|
}
|
|
511
|
-
function
|
|
522
|
+
function noteEnvelope(census, u) {
|
|
523
|
+
if (!census) return;
|
|
524
|
+
census.rows++;
|
|
525
|
+
if (u.wrapped) census.wrapped++;
|
|
526
|
+
if (u.nested) census.nested++;
|
|
527
|
+
if (u.text.startsWith("<channel ")) census.unstripped++;
|
|
528
|
+
else if (u.wrapped) census.stripped++;
|
|
529
|
+
census.sources[u.source ?? "none"]++;
|
|
530
|
+
}
|
|
531
|
+
function operatorInboundTurn(u, ts, uuid, census) {
|
|
532
|
+
noteEnvelope(census, u);
|
|
512
533
|
const framed = rawFromComposedAdminFrame(u.text);
|
|
513
534
|
const split = framed === void 0 ? void 0 : splitReplyNote(framed);
|
|
514
535
|
const rawBody = split?.body;
|
|
@@ -635,12 +656,12 @@ function lastTurnTs(turns) {
|
|
|
635
656
|
function isModelGated(turns) {
|
|
636
657
|
return turns.some((t) => t.kind === "agent-error" && t.code === "model_unavailable");
|
|
637
658
|
}
|
|
638
|
-
function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, uuid) {
|
|
659
|
+
function renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, uuid, census) {
|
|
639
660
|
const armed2 = queuedPendingSuppress.get(u.text) ?? 0;
|
|
640
661
|
if (armed2 > 0) {
|
|
641
662
|
queuedPendingSuppress.set(u.text, armed2 - 1);
|
|
642
663
|
} else {
|
|
643
|
-
out.push(operatorInboundTurn(u, ts, uuid));
|
|
664
|
+
out.push(operatorInboundTurn(u, ts, uuid, census));
|
|
644
665
|
}
|
|
645
666
|
}
|
|
646
667
|
function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(), turnCtx = {}) {
|
|
@@ -678,7 +699,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
678
699
|
const u = unwrapChannel(content);
|
|
679
700
|
turnCtx.lastChannelSource = u.source;
|
|
680
701
|
if (!isScheduleSource(content)) {
|
|
681
|
-
out.push(operatorInboundTurn(u, ts));
|
|
702
|
+
out.push(operatorInboundTurn(u, ts, void 0, turnCtx.envelopeCensus));
|
|
682
703
|
queuedPendingSuppress.set(u.text, (queuedPendingSuppress.get(u.text) ?? 0) + 1);
|
|
683
704
|
}
|
|
684
705
|
}
|
|
@@ -691,7 +712,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
691
712
|
const u = unwrapChannel(att.prompt);
|
|
692
713
|
turnCtx.lastChannelSource = u.source;
|
|
693
714
|
if (!isScheduleSource(att.prompt)) {
|
|
694
|
-
renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, asString(row.uuid) ?? void 0);
|
|
715
|
+
renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, asString(row.uuid) ?? void 0, turnCtx.envelopeCensus);
|
|
695
716
|
}
|
|
696
717
|
}
|
|
697
718
|
continue;
|
|
@@ -720,7 +741,7 @@ function parseTranscript(lines, queuedPendingSuppress = /* @__PURE__ */ new Map(
|
|
|
720
741
|
const u = unwrapChannel(text);
|
|
721
742
|
turnCtx.lastChannelSource = u.source;
|
|
722
743
|
if (!isScheduleSource(text)) {
|
|
723
|
-
renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, asString(row.uuid) ?? void 0);
|
|
744
|
+
renderOrSuppressChannelInbound(u, ts, out, queuedPendingSuppress, asString(row.uuid) ?? void 0, turnCtx.envelopeCensus);
|
|
724
745
|
}
|
|
725
746
|
} else if (row.isMeta === true) {
|
|
726
747
|
out.push({ kind: "harness-internal", text, ts });
|
|
@@ -747,7 +768,7 @@ var init_parse_transcript = __esm({
|
|
|
747
768
|
HARNESS_LIFECYCLE_PREFIXES = ["<task-notification>"];
|
|
748
769
|
CC_IMAGE_VISION_HINT = /^\[Image:[^\]]*Multiply coordinates by [\d.]+ to map to original image\.\]$/;
|
|
749
770
|
COMPACT_SUMMARY_PREFIX = "This session is being continued from a previous conversation that ran out of context.";
|
|
750
|
-
CHANNEL_WRAPPER = /^<channel\b([^>]*)>\n?([\s\S]*?)\n
|
|
771
|
+
CHANNEL_WRAPPER = /^<channel\b([^>]*)>\n?([\s\S]*?)\n?<\\?\/channel>$/;
|
|
751
772
|
SOURCE_ATTR = /\bsource="([^"]+)"/;
|
|
752
773
|
SOURCE_MAP = {
|
|
753
774
|
whatsapp: "whatsapp",
|
|
@@ -815,7 +836,7 @@ var init_audio_mime = __esm({
|
|
|
815
836
|
// app/lib/stt/voice-note.ts
|
|
816
837
|
import { writeFile, mkdtemp, rm } from "fs/promises";
|
|
817
838
|
import { tmpdir as tmpdir2 } from "os";
|
|
818
|
-
import { join as
|
|
839
|
+
import { join as join7 } from "path";
|
|
819
840
|
async function transcribeVoiceNote(file, source) {
|
|
820
841
|
const startMs = Date.now();
|
|
821
842
|
const bytes = file.size;
|
|
@@ -826,9 +847,9 @@ async function transcribeVoiceNote(file, source) {
|
|
|
826
847
|
let tempDir;
|
|
827
848
|
let tempPath;
|
|
828
849
|
try {
|
|
829
|
-
tempDir = await mkdtemp(
|
|
850
|
+
tempDir = await mkdtemp(join7(tmpdir2(), "voice-"));
|
|
830
851
|
const ext = audioExtension(mimeType);
|
|
831
|
-
tempPath =
|
|
852
|
+
tempPath = join7(tempDir, `recording${ext}`);
|
|
832
853
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
833
854
|
await writeFile(tempPath, buffer);
|
|
834
855
|
} catch (err) {
|
|
@@ -1239,10 +1260,56 @@ function buildStoreConversationRows(accountId, summaries, chatMeta) {
|
|
|
1239
1260
|
}
|
|
1240
1261
|
return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
|
|
1241
1262
|
}
|
|
1263
|
+
function buildTelegramStoreConversationRows(accountId, summaries) {
|
|
1264
|
+
const rows = [];
|
|
1265
|
+
for (const [channelKey, { first, last }] of summaries) {
|
|
1266
|
+
rows.push({
|
|
1267
|
+
sessionId: "",
|
|
1268
|
+
projectDir: "",
|
|
1269
|
+
title: channelKey,
|
|
1270
|
+
senderId: last.senderId,
|
|
1271
|
+
startedAt: first.dateSent,
|
|
1272
|
+
channel: "telegram",
|
|
1273
|
+
role: last.scope === "admin" ? "admin" : "public",
|
|
1274
|
+
operatorName: null,
|
|
1275
|
+
whatsappName: null,
|
|
1276
|
+
// The name is what it is NOW, matching how `scope` is taken from `last`.
|
|
1277
|
+
// The fallback covers an outbound last record: the outbound write points
|
|
1278
|
+
// have no sender to name, so they store null.
|
|
1279
|
+
telegramName: last.senderName ?? first.senderName ?? null,
|
|
1280
|
+
lastMessageAt: last.dateSent,
|
|
1281
|
+
modelGated: false,
|
|
1282
|
+
model: null,
|
|
1283
|
+
source: "store",
|
|
1284
|
+
accountId,
|
|
1285
|
+
channelKey,
|
|
1286
|
+
scope: last.scope,
|
|
1287
|
+
preview: last.body
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
|
|
1291
|
+
}
|
|
1292
|
+
function dropTelegramSessionDuplicates(rows, telegramStoreRows) {
|
|
1293
|
+
const coveredKeys = /* @__PURE__ */ new Set();
|
|
1294
|
+
const coveredSenders = /* @__PURE__ */ new Set();
|
|
1295
|
+
for (const r of telegramStoreRows) {
|
|
1296
|
+
const parsed = r.channelKey ? parseTelegramChannelKey(r.channelKey) : null;
|
|
1297
|
+
if (!parsed) continue;
|
|
1298
|
+
coveredKeys.add(r.channelKey);
|
|
1299
|
+
coveredSenders.add(parsed.senderId);
|
|
1300
|
+
}
|
|
1301
|
+
if (coveredKeys.size === 0) return rows;
|
|
1302
|
+
return rows.filter((r) => {
|
|
1303
|
+
if (r.source !== "session" || r.channel !== "telegram" || r.senderId == null) return true;
|
|
1304
|
+
const parsed = parseTelegramChannelKey(r.senderId);
|
|
1305
|
+
return parsed ? !coveredKeys.has(r.senderId) : !coveredSenders.has(r.senderId);
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1242
1308
|
var init_select_sessions = __esm({
|
|
1243
1309
|
"app/lib/reader/select-sessions.ts"() {
|
|
1244
1310
|
"use strict";
|
|
1245
1311
|
init_normalize();
|
|
1312
|
+
init_channel_key();
|
|
1246
1313
|
init_conversation_preview();
|
|
1247
1314
|
}
|
|
1248
1315
|
});
|
|
@@ -5460,9 +5527,186 @@ import { homedir as homedir5 } from "os";
|
|
|
5460
5527
|
init_route_cache();
|
|
5461
5528
|
init_account();
|
|
5462
5529
|
init_message_store();
|
|
5463
|
-
init_src2();
|
|
5464
5530
|
import { watch } from "fs";
|
|
5465
|
-
import { resolve } from "path";
|
|
5531
|
+
import { resolve as resolve2 } from "path";
|
|
5532
|
+
|
|
5533
|
+
// app/lib/telegram/message-store.ts
|
|
5534
|
+
import { existsSync, mkdirSync, readFileSync, appendFileSync, readdirSync, statSync } from "fs";
|
|
5535
|
+
import { homedir } from "os";
|
|
5536
|
+
import { dirname, join, resolve } from "path";
|
|
5537
|
+
function configDirName() {
|
|
5538
|
+
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve(process.cwd(), "..");
|
|
5539
|
+
const brandPath = join(platformRoot5, "config", "brand.json");
|
|
5540
|
+
if (existsSync(brandPath)) {
|
|
5541
|
+
try {
|
|
5542
|
+
return JSON.parse(readFileSync(brandPath, "utf-8")).configDir ?? ".maxy";
|
|
5543
|
+
} catch {
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
return ".maxy";
|
|
5547
|
+
}
|
|
5548
|
+
var STORE_ROOT2 = () => join(process.env.MAXY_DIR_OVERRIDE ?? resolve(homedir(), configDirName()), "data", "telegram-messages");
|
|
5549
|
+
function isStorableChannelKey(channelKey) {
|
|
5550
|
+
return /^-?\d+:-?\d+$/.test(channelKey);
|
|
5551
|
+
}
|
|
5552
|
+
function channelKeyToFileStem(channelKey) {
|
|
5553
|
+
if (!isStorableChannelKey(channelKey)) {
|
|
5554
|
+
throw new Error(`telegram-message-store: refusing unstorable channelKey ${JSON.stringify(channelKey)}`);
|
|
5555
|
+
}
|
|
5556
|
+
return channelKey.replace(":", "_");
|
|
5557
|
+
}
|
|
5558
|
+
function fileStemToChannelKey(stem) {
|
|
5559
|
+
const i = stem.indexOf("_");
|
|
5560
|
+
return i <= 0 ? stem : `${stem.slice(0, i)}:${stem.slice(i + 1)}`;
|
|
5561
|
+
}
|
|
5562
|
+
function conversationFile(accountId, channelKey) {
|
|
5563
|
+
return join(STORE_ROOT2(), accountId, `${channelKeyToFileStem(channelKey)}.jsonl`);
|
|
5564
|
+
}
|
|
5565
|
+
function conversationFilePath2(accountId, channelKey) {
|
|
5566
|
+
return conversationFile(accountId, channelKey);
|
|
5567
|
+
}
|
|
5568
|
+
function isStorableStem(stem) {
|
|
5569
|
+
return isStorableChannelKey(fileStemToChannelKey(stem));
|
|
5570
|
+
}
|
|
5571
|
+
function listStoredAccountIds2() {
|
|
5572
|
+
const root = STORE_ROOT2();
|
|
5573
|
+
if (!existsSync(root)) return [];
|
|
5574
|
+
return readdirSync(root).filter((name) => {
|
|
5575
|
+
try {
|
|
5576
|
+
return statSync(join(root, name)).isDirectory();
|
|
5577
|
+
} catch {
|
|
5578
|
+
return false;
|
|
5579
|
+
}
|
|
5580
|
+
});
|
|
5581
|
+
}
|
|
5582
|
+
function parseLines(raw) {
|
|
5583
|
+
const out = [];
|
|
5584
|
+
for (const line of raw.split("\n")) {
|
|
5585
|
+
if (!line.trim()) continue;
|
|
5586
|
+
try {
|
|
5587
|
+
out.push(JSON.parse(line));
|
|
5588
|
+
} catch {
|
|
5589
|
+
console.error(`[telegram-message-store] skip reason=corrupt-line`);
|
|
5590
|
+
}
|
|
5591
|
+
}
|
|
5592
|
+
return out;
|
|
5593
|
+
}
|
|
5594
|
+
var byDateSentAsc = (a, b) => a.dateSent.localeCompare(b.dateSent);
|
|
5595
|
+
function appendMessage2(accountId, record) {
|
|
5596
|
+
if (!isStorableChannelKey(record.channelKey)) {
|
|
5597
|
+
console.error(
|
|
5598
|
+
`[telegram-message-store] FAIL op=append reason=unstorable-channel-key messageId=${record.messageId}`
|
|
5599
|
+
);
|
|
5600
|
+
return { created: false };
|
|
5601
|
+
}
|
|
5602
|
+
const file = conversationFile(accountId, record.channelKey);
|
|
5603
|
+
try {
|
|
5604
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
5605
|
+
} catch (err) {
|
|
5606
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5607
|
+
console.error(
|
|
5608
|
+
`[telegram-message-store] FAIL op=append reason=${reason.slice(0, 100)} messageId=${record.messageId}`
|
|
5609
|
+
);
|
|
5610
|
+
return { created: false };
|
|
5611
|
+
}
|
|
5612
|
+
try {
|
|
5613
|
+
if (existsSync(file)) {
|
|
5614
|
+
const existing = parseLines(readFileSync(file, "utf8"));
|
|
5615
|
+
if (existing.some((r) => r.messageId === record.messageId)) {
|
|
5616
|
+
console.error(
|
|
5617
|
+
`[telegram-message-store] op=append channelKey=${record.channelKey} origin=${record.origin} created=false`
|
|
5618
|
+
);
|
|
5619
|
+
return { created: false };
|
|
5620
|
+
}
|
|
5621
|
+
}
|
|
5622
|
+
appendFileSync(file, `${JSON.stringify(record)}
|
|
5623
|
+
`);
|
|
5624
|
+
console.error(
|
|
5625
|
+
`[telegram-message-store] op=append channelKey=${record.channelKey} origin=${record.origin} created=true`
|
|
5626
|
+
);
|
|
5627
|
+
return { created: true };
|
|
5628
|
+
} catch (err) {
|
|
5629
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5630
|
+
console.error(
|
|
5631
|
+
`[telegram-message-store] FAIL op=append reason=${reason.slice(0, 100)} messageId=${record.messageId}`
|
|
5632
|
+
);
|
|
5633
|
+
return { created: false };
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
function readConversation2(accountId, channelKey) {
|
|
5637
|
+
if (!isStorableChannelKey(channelKey)) return [];
|
|
5638
|
+
const file = conversationFile(accountId, channelKey);
|
|
5639
|
+
try {
|
|
5640
|
+
if (!existsSync(file)) return [];
|
|
5641
|
+
return parseLines(readFileSync(file, "utf8")).sort(byDateSentAsc);
|
|
5642
|
+
} catch (err) {
|
|
5643
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5644
|
+
console.error(
|
|
5645
|
+
`[telegram-message-store] FAIL op=read reason=${reason.slice(0, 100)} channelKey=${channelKey}`
|
|
5646
|
+
);
|
|
5647
|
+
return [];
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
var summaryCache = /* @__PURE__ */ new Map();
|
|
5651
|
+
function summariseRecords(records2) {
|
|
5652
|
+
if (records2.length === 0) return null;
|
|
5653
|
+
const sorted = records2.slice().sort(byDateSentAsc);
|
|
5654
|
+
return { first: sorted[0], last: sorted[sorted.length - 1] };
|
|
5655
|
+
}
|
|
5656
|
+
function readConversationSummaries2(accountId) {
|
|
5657
|
+
const dir = join(STORE_ROOT2(), accountId);
|
|
5658
|
+
const out = /* @__PURE__ */ new Map();
|
|
5659
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5660
|
+
try {
|
|
5661
|
+
if (!existsSync(dir)) return out;
|
|
5662
|
+
for (const entry2 of readdirSync(dir)) {
|
|
5663
|
+
if (!entry2.endsWith(".jsonl")) continue;
|
|
5664
|
+
const stem = entry2.slice(0, -".jsonl".length);
|
|
5665
|
+
if (!isStorableStem(stem)) continue;
|
|
5666
|
+
const channelKey = fileStemToChannelKey(stem);
|
|
5667
|
+
const file = join(dir, entry2);
|
|
5668
|
+
seen.add(file);
|
|
5669
|
+
let size;
|
|
5670
|
+
let mtimeMs;
|
|
5671
|
+
try {
|
|
5672
|
+
const st = statSync(file);
|
|
5673
|
+
size = st.size;
|
|
5674
|
+
mtimeMs = st.mtimeMs;
|
|
5675
|
+
} catch {
|
|
5676
|
+
continue;
|
|
5677
|
+
}
|
|
5678
|
+
const cached3 = summaryCache.get(file);
|
|
5679
|
+
if (cached3 && cached3.size === size && cached3.mtimeMs === mtimeMs) {
|
|
5680
|
+
out.set(channelKey, cached3.summary);
|
|
5681
|
+
continue;
|
|
5682
|
+
}
|
|
5683
|
+
let summary;
|
|
5684
|
+
try {
|
|
5685
|
+
summary = summariseRecords(parseLines(readFileSync(file, "utf8")));
|
|
5686
|
+
} catch (err) {
|
|
5687
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5688
|
+
console.error(
|
|
5689
|
+
`[telegram-message-store] FAIL op=summarise reason=${reason.slice(0, 100)} channelKey=${channelKey}`
|
|
5690
|
+
);
|
|
5691
|
+
continue;
|
|
5692
|
+
}
|
|
5693
|
+
if (!summary) continue;
|
|
5694
|
+
summaryCache.set(file, { size, mtimeMs, summary });
|
|
5695
|
+
out.set(channelKey, summary);
|
|
5696
|
+
}
|
|
5697
|
+
} catch (err) {
|
|
5698
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
5699
|
+
console.error(`[telegram-message-store] FAIL op=summarise-dir reason=${reason.slice(0, 100)}`);
|
|
5700
|
+
return out;
|
|
5701
|
+
}
|
|
5702
|
+
for (const key2 of [...summaryCache.keys()]) {
|
|
5703
|
+
if (key2.startsWith(dir) && !seen.has(key2)) summaryCache.delete(key2);
|
|
5704
|
+
}
|
|
5705
|
+
return out;
|
|
5706
|
+
}
|
|
5707
|
+
|
|
5708
|
+
// server/lib/route-cache-watchers.ts
|
|
5709
|
+
init_src2();
|
|
5466
5710
|
function watchDirInto(dir, resource, accountId) {
|
|
5467
5711
|
try {
|
|
5468
5712
|
const w = watch(dir, { recursive: true }, () => {
|
|
@@ -5483,9 +5727,9 @@ function watchDirInto(dir, resource, accountId) {
|
|
|
5483
5727
|
var perAccountAttached = /* @__PURE__ */ new Set();
|
|
5484
5728
|
function ensureAccountWatchers(accountId, accountDir) {
|
|
5485
5729
|
for (const [dir, resource] of [
|
|
5486
|
-
[
|
|
5487
|
-
[
|
|
5488
|
-
[
|
|
5730
|
+
[resolve2(accountDir, "plugins"), "skills"],
|
|
5731
|
+
[resolve2(accountDir, "agents", "admin"), "sidebar-artefacts"],
|
|
5732
|
+
[resolve2(accountDir, "specialists", "agents"), "sidebar-artefacts"]
|
|
5489
5733
|
]) {
|
|
5490
5734
|
if (perAccountAttached.has(dir)) continue;
|
|
5491
5735
|
const { attached: attached2 } = watchDirInto(dir, resource, accountId);
|
|
@@ -5498,11 +5742,11 @@ function attachRouteCacheWatchers() {
|
|
|
5498
5742
|
attached = true;
|
|
5499
5743
|
const configDir = process.env.CLAUDE_CONFIG_DIR;
|
|
5500
5744
|
if (configDir) {
|
|
5501
|
-
watchDirInto(
|
|
5745
|
+
watchDirInto(resolve2(configDir, "projects"), "sidebar-sessions", null);
|
|
5502
5746
|
} else {
|
|
5503
5747
|
console.error("[admin-events] op=watch-failed resource=sidebar-sessions reason=CLAUDE_CONFIG_DIR-unset");
|
|
5504
5748
|
}
|
|
5505
|
-
watchDirInto(
|
|
5749
|
+
watchDirInto(resolve2(PLATFORM_ROOT, "templates", "specialists", "agents"), "sidebar-artefacts", null);
|
|
5506
5750
|
let houseId = null;
|
|
5507
5751
|
try {
|
|
5508
5752
|
houseId = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
|
|
@@ -5511,6 +5755,7 @@ function attachRouteCacheWatchers() {
|
|
|
5511
5755
|
}
|
|
5512
5756
|
if (houseId) {
|
|
5513
5757
|
watchDirInto(STORE_ROOT(), "conversations-all", houseId);
|
|
5758
|
+
watchDirInto(STORE_ROOT2(), "conversations-all", houseId);
|
|
5514
5759
|
} else {
|
|
5515
5760
|
console.error("[admin-events] op=watch-failed resource=conversations-all reason=house-account-unresolved");
|
|
5516
5761
|
}
|
|
@@ -5707,8 +5952,8 @@ var _baseMimes = {
|
|
|
5707
5952
|
var baseMimes = _baseMimes;
|
|
5708
5953
|
|
|
5709
5954
|
// node_modules/@hono/node-server/dist/serve-static.mjs
|
|
5710
|
-
import { createReadStream, statSync, existsSync } from "fs";
|
|
5711
|
-
import { join } from "path";
|
|
5955
|
+
import { createReadStream, statSync as statSync2, existsSync as existsSync2 } from "fs";
|
|
5956
|
+
import { join as join2 } from "path";
|
|
5712
5957
|
import { versions } from "process";
|
|
5713
5958
|
import { Readable } from "stream";
|
|
5714
5959
|
var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
|
|
@@ -5748,7 +5993,7 @@ var createStreamBody = (stream2) => {
|
|
|
5748
5993
|
var getStats = (path) => {
|
|
5749
5994
|
let stats;
|
|
5750
5995
|
try {
|
|
5751
|
-
stats =
|
|
5996
|
+
stats = statSync2(path);
|
|
5752
5997
|
} catch {
|
|
5753
5998
|
}
|
|
5754
5999
|
return stats;
|
|
@@ -5770,7 +6015,7 @@ var tryDecodeURI = (str2) => tryDecode(str2, decodeURI);
|
|
|
5770
6015
|
var serveStatic = (options = { root: "" }) => {
|
|
5771
6016
|
const root = options.root || "";
|
|
5772
6017
|
const optionPath = options.path;
|
|
5773
|
-
if (root !== "" && !
|
|
6018
|
+
if (root !== "" && !existsSync2(root)) {
|
|
5774
6019
|
console.error(`serveStatic: root path '${root}' is not found, are you sure it's correct?`);
|
|
5775
6020
|
}
|
|
5776
6021
|
return async (c, next) => {
|
|
@@ -5791,14 +6036,14 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
5791
6036
|
return next();
|
|
5792
6037
|
}
|
|
5793
6038
|
}
|
|
5794
|
-
let path =
|
|
6039
|
+
let path = join2(
|
|
5795
6040
|
root,
|
|
5796
6041
|
!optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c) : filename
|
|
5797
6042
|
);
|
|
5798
6043
|
let stats = getStats(path);
|
|
5799
6044
|
if (stats && stats.isDirectory()) {
|
|
5800
6045
|
const indexFile2 = options.index ?? "index.html";
|
|
5801
|
-
path =
|
|
6046
|
+
path = join2(path, indexFile2);
|
|
5802
6047
|
stats = getStats(path);
|
|
5803
6048
|
}
|
|
5804
6049
|
if (!stats) {
|
|
@@ -5856,13 +6101,13 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
5856
6101
|
};
|
|
5857
6102
|
|
|
5858
6103
|
// server/lib/static-cache.ts
|
|
5859
|
-
import { statSync as
|
|
5860
|
-
import { join as
|
|
6104
|
+
import { statSync as statSync3 } from "fs";
|
|
6105
|
+
import { join as join3 } from "path";
|
|
5861
6106
|
var IMMUTABLE = "public, max-age=31536000, immutable";
|
|
5862
6107
|
var REVALIDATE = "public, no-cache";
|
|
5863
6108
|
function statOrNull(path) {
|
|
5864
6109
|
try {
|
|
5865
|
-
return
|
|
6110
|
+
return statSync3(path);
|
|
5866
6111
|
} catch {
|
|
5867
6112
|
return null;
|
|
5868
6113
|
}
|
|
@@ -5883,10 +6128,10 @@ function tryDecodeURI2(str2) {
|
|
|
5883
6128
|
function resolveStaticFile(root, reqPath) {
|
|
5884
6129
|
const filename = tryDecodeURI2(reqPath);
|
|
5885
6130
|
if (/(?:^|[/\\])\.\.(?:$|[/\\])/.test(filename)) return null;
|
|
5886
|
-
let path =
|
|
6131
|
+
let path = join3(root, filename);
|
|
5887
6132
|
let stats = statOrNull(path);
|
|
5888
6133
|
if (stats?.isDirectory()) {
|
|
5889
|
-
path =
|
|
6134
|
+
path = join3(path, "index.html");
|
|
5890
6135
|
stats = statOrNull(path);
|
|
5891
6136
|
}
|
|
5892
6137
|
if (!stats?.isFile()) return null;
|
|
@@ -5961,8 +6206,8 @@ ${clientErrorReporterBody}
|
|
|
5961
6206
|
</script>`;
|
|
5962
6207
|
|
|
5963
6208
|
// app/lib/whatsapp/ingest-audit.ts
|
|
5964
|
-
import { readdirSync, readFileSync, statSync as
|
|
5965
|
-
import { join as
|
|
6209
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync4 } from "fs";
|
|
6210
|
+
import { join as join4 } from "path";
|
|
5966
6211
|
init_message_store();
|
|
5967
6212
|
var TAG2 = "[wa-ingest-audit]";
|
|
5968
6213
|
var NOTIFY_TAG = "[wa-notify]";
|
|
@@ -5983,16 +6228,16 @@ function readAllConversationsCached(accountId) {
|
|
|
5983
6228
|
const out = /* @__PURE__ */ new Map();
|
|
5984
6229
|
let dir;
|
|
5985
6230
|
try {
|
|
5986
|
-
dir =
|
|
6231
|
+
dir = readdirSync2(join4(STORE_ROOT(), accountId));
|
|
5987
6232
|
} catch {
|
|
5988
6233
|
return out;
|
|
5989
6234
|
}
|
|
5990
6235
|
for (const file of dir) {
|
|
5991
6236
|
if (!file.endsWith(".jsonl")) continue;
|
|
5992
|
-
const path =
|
|
6237
|
+
const path = join4(STORE_ROOT(), accountId, file);
|
|
5993
6238
|
let stat11;
|
|
5994
6239
|
try {
|
|
5995
|
-
stat11 =
|
|
6240
|
+
stat11 = statSync4(path);
|
|
5996
6241
|
} catch {
|
|
5997
6242
|
continue;
|
|
5998
6243
|
}
|
|
@@ -6003,7 +6248,7 @@ function readAllConversationsCached(accountId) {
|
|
|
6003
6248
|
}
|
|
6004
6249
|
const parsed = [];
|
|
6005
6250
|
try {
|
|
6006
|
-
for (const line of
|
|
6251
|
+
for (const line of readFileSync2(path, "utf8").split("\n")) {
|
|
6007
6252
|
if (!line.trim()) continue;
|
|
6008
6253
|
try {
|
|
6009
6254
|
const row = JSON.parse(line);
|
|
@@ -6119,17 +6364,17 @@ function runNotifyReconcile(opts) {
|
|
|
6119
6364
|
init_parse_transcript();
|
|
6120
6365
|
import { mkdtempSync, rmSync, writeFileSync } from "fs";
|
|
6121
6366
|
import { tmpdir } from "os";
|
|
6122
|
-
import { join as
|
|
6367
|
+
import { join as join5 } from "path";
|
|
6123
6368
|
|
|
6124
6369
|
// server/lib/tail-window.ts
|
|
6125
|
-
import { openSync, readSync, closeSync, statSync as
|
|
6370
|
+
import { openSync, readSync, closeSync, statSync as statSync5 } from "fs";
|
|
6126
6371
|
var TAIL_WINDOW_BYTES = 1024 * 1024;
|
|
6127
6372
|
var ALIGN_SCAN_MAX_BYTES = 4 * 1024 * 1024;
|
|
6128
6373
|
function tailWindowStart(fileEnd, budget = TAIL_WINDOW_BYTES) {
|
|
6129
6374
|
return Math.max(0, fileEnd - budget);
|
|
6130
6375
|
}
|
|
6131
6376
|
function readRange(path, from, to) {
|
|
6132
|
-
const size =
|
|
6377
|
+
const size = statSync5(path).size;
|
|
6133
6378
|
const start = Math.max(0, from);
|
|
6134
6379
|
const end = Math.min(to, size);
|
|
6135
6380
|
if (end <= start) return Buffer.alloc(0);
|
|
@@ -6251,8 +6496,62 @@ function censusSnapshot() {
|
|
|
6251
6496
|
return { windowed: windowedOpens.size, withBackfill };
|
|
6252
6497
|
}
|
|
6253
6498
|
|
|
6499
|
+
// server/lib/frame-strip-census.ts
|
|
6500
|
+
var MAX_ACCOUNTS = 50;
|
|
6501
|
+
var MAX_UNSTRIPPED_SESSIONS = 3;
|
|
6502
|
+
var byAccount = /* @__PURE__ */ new Map();
|
|
6503
|
+
function rowFor(accountId) {
|
|
6504
|
+
const existing = byAccount.get(accountId);
|
|
6505
|
+
if (existing) return existing;
|
|
6506
|
+
if (byAccount.size >= MAX_ACCOUNTS) {
|
|
6507
|
+
const oldest = byAccount.keys().next();
|
|
6508
|
+
if (!oldest.done) byAccount.delete(oldest.value);
|
|
6509
|
+
}
|
|
6510
|
+
const fresh = {
|
|
6511
|
+
accountId,
|
|
6512
|
+
rows: 0,
|
|
6513
|
+
wrapped: 0,
|
|
6514
|
+
nested: 0,
|
|
6515
|
+
stripped: 0,
|
|
6516
|
+
unstripped: 0,
|
|
6517
|
+
sources: { whatsapp: 0, webchat: 0, telegram: 0, none: 0 },
|
|
6518
|
+
unstrippedSessions: []
|
|
6519
|
+
};
|
|
6520
|
+
byAccount.set(accountId, fresh);
|
|
6521
|
+
return fresh;
|
|
6522
|
+
}
|
|
6523
|
+
function foldFrameCensus(accountId, sessionId, acc) {
|
|
6524
|
+
if (acc.rows === 0) return;
|
|
6525
|
+
const row = rowFor(accountId);
|
|
6526
|
+
row.rows += acc.rows;
|
|
6527
|
+
row.wrapped += acc.wrapped;
|
|
6528
|
+
row.nested += acc.nested;
|
|
6529
|
+
row.stripped += acc.stripped;
|
|
6530
|
+
row.unstripped += acc.unstripped;
|
|
6531
|
+
row.sources.whatsapp += acc.sources.whatsapp;
|
|
6532
|
+
row.sources.webchat += acc.sources.webchat;
|
|
6533
|
+
row.sources.telegram += acc.sources.telegram;
|
|
6534
|
+
row.sources.none += acc.sources.none;
|
|
6535
|
+
if (acc.unstripped > 0 && row.unstrippedSessions.length < MAX_UNSTRIPPED_SESSIONS && !row.unstrippedSessions.includes(sessionId)) {
|
|
6536
|
+
row.unstrippedSessions.push(sessionId);
|
|
6537
|
+
}
|
|
6538
|
+
acc.rows = 0;
|
|
6539
|
+
acc.wrapped = 0;
|
|
6540
|
+
acc.nested = 0;
|
|
6541
|
+
acc.stripped = 0;
|
|
6542
|
+
acc.unstripped = 0;
|
|
6543
|
+
acc.sources.whatsapp = 0;
|
|
6544
|
+
acc.sources.webchat = 0;
|
|
6545
|
+
acc.sources.telegram = 0;
|
|
6546
|
+
acc.sources.none = 0;
|
|
6547
|
+
}
|
|
6548
|
+
function frameCensusSnapshot() {
|
|
6549
|
+
return [...byAccount.values()];
|
|
6550
|
+
}
|
|
6551
|
+
|
|
6254
6552
|
// server/lib/reader-window-audit.ts
|
|
6255
6553
|
var TAG3 = "[reader-audit]";
|
|
6554
|
+
var FRAME_TAG = "[chat-reader]";
|
|
6256
6555
|
var FIXTURE_ROWS = 60;
|
|
6257
6556
|
var FIXTURE_BUDGET = 2e3;
|
|
6258
6557
|
function fixtureBody() {
|
|
@@ -6286,8 +6585,25 @@ var key = (t) => JSON.stringify(t);
|
|
|
6286
6585
|
function runReaderWindowAudit() {
|
|
6287
6586
|
const census = censusSnapshot();
|
|
6288
6587
|
console.log(`${TAG3} op=backfill-reach windowed=${census.windowed} withBackfill=${census.withBackfill}`);
|
|
6289
|
-
const
|
|
6290
|
-
|
|
6588
|
+
const frames = frameCensusSnapshot();
|
|
6589
|
+
let frameRows = 0;
|
|
6590
|
+
let frameUnstripped = 0;
|
|
6591
|
+
if (frames.length === 0) {
|
|
6592
|
+
console.log(`${FRAME_TAG} op=frame-census accounts=0`);
|
|
6593
|
+
}
|
|
6594
|
+
for (const f of frames) {
|
|
6595
|
+
frameRows += f.rows;
|
|
6596
|
+
frameUnstripped += f.unstripped;
|
|
6597
|
+
console.log(
|
|
6598
|
+
`${FRAME_TAG} op=frame-census accountId=${f.accountId} rows=${f.rows} wrapped=${f.wrapped} nested=${f.nested} stripped=${f.stripped} sources=whatsapp:${f.sources.whatsapp},webchat:${f.sources.webchat},telegram:${f.sources.telegram},none:${f.sources.none}`
|
|
6599
|
+
);
|
|
6600
|
+
console.log(
|
|
6601
|
+
`${FRAME_TAG} op=frame-unstripped accountId=${f.accountId} rows=${f.unstripped} sessions=${f.unstrippedSessions.join(",")}`
|
|
6602
|
+
);
|
|
6603
|
+
}
|
|
6604
|
+
const frameSummary = `frames=${frameRows} unstripped=${frameUnstripped}`;
|
|
6605
|
+
const dir = mkdtempSync(join5(tmpdir(), "reader-audit-"));
|
|
6606
|
+
const path = join5(dir, "fixture.jsonl");
|
|
6291
6607
|
try {
|
|
6292
6608
|
const body = fixtureBody();
|
|
6293
6609
|
writeFileSync(path, body);
|
|
@@ -6332,11 +6648,11 @@ function runReaderWindowAudit() {
|
|
|
6332
6648
|
`${TAG3} op=window-kinds fixture=synthetic windowTurns=${windowTurns.length} mismatched=${mismatched}`
|
|
6333
6649
|
);
|
|
6334
6650
|
const ok2 = duplicates === 0 && gaps === 0 && ordered && walkTerminated && mismatched === 0;
|
|
6335
|
-
return `continuity=${ok2 ? "ok" : "FAILED"} full=${full.length} assembled=${assembled.length} walk=${walkTerminated ? "ok" : "STUCK"} reach=${census.withBackfill}/${census.windowed}`;
|
|
6651
|
+
return `continuity=${ok2 ? "ok" : "FAILED"} full=${full.length} assembled=${assembled.length} walk=${walkTerminated ? "ok" : "STUCK"} reach=${census.withBackfill}/${census.windowed} ${frameSummary}`;
|
|
6336
6652
|
} catch (err) {
|
|
6337
6653
|
const reason = err instanceof Error ? err.message : String(err);
|
|
6338
6654
|
console.error(`${TAG3} op=audit-failed reason=${reason.slice(0, 120)}`);
|
|
6339
|
-
return `continuity=ERROR ${reason.slice(0, 60)}`;
|
|
6655
|
+
return `continuity=ERROR ${reason.slice(0, 60)} ${frameSummary}`;
|
|
6340
6656
|
} finally {
|
|
6341
6657
|
try {
|
|
6342
6658
|
rmSync(dir, { recursive: true, force: true });
|
|
@@ -6398,12 +6714,12 @@ init_src();
|
|
|
6398
6714
|
init_pwa();
|
|
6399
6715
|
|
|
6400
6716
|
// server/webchat-descriptor.ts
|
|
6401
|
-
import { resolve as
|
|
6717
|
+
import { resolve as resolve3, join as join6 } from "path";
|
|
6402
6718
|
function webchatGatewayUrl() {
|
|
6403
6719
|
return `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`;
|
|
6404
6720
|
}
|
|
6405
6721
|
function webchatServerPath() {
|
|
6406
|
-
return process.env.MAXY_WEBCHAT_CHANNEL_SERVER_PATH ??
|
|
6722
|
+
return process.env.MAXY_WEBCHAT_CHANNEL_SERVER_PATH ?? resolve3(process.env.MAXY_PLATFORM_ROOT ?? join6(__dirname, ".."), "services/webchat-channel/dist/server.js");
|
|
6407
6723
|
}
|
|
6408
6724
|
function webchatChannelDescriptor(key2) {
|
|
6409
6725
|
return { key: key2, gatewayUrl: webchatGatewayUrl(), serverPath: webchatServerPath() };
|
|
@@ -6421,7 +6737,7 @@ init_paths();
|
|
|
6421
6737
|
// app/lib/claude-auth.ts
|
|
6422
6738
|
init_paths();
|
|
6423
6739
|
init_loop_registry();
|
|
6424
|
-
import { readFileSync as
|
|
6740
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync, unlinkSync } from "fs";
|
|
6425
6741
|
import { sep } from "path";
|
|
6426
6742
|
import { lock } from "proper-lockfile";
|
|
6427
6743
|
if (!CLAUDE_CREDENTIALS_FILE.startsWith(MAXY_DIR + sep)) {
|
|
@@ -6438,7 +6754,7 @@ var REFRESH_NOT_FIRING_THRESHOLD = 2;
|
|
|
6438
6754
|
function readCredentials() {
|
|
6439
6755
|
let raw;
|
|
6440
6756
|
try {
|
|
6441
|
-
raw =
|
|
6757
|
+
raw = readFileSync3(CLAUDE_CREDENTIALS_FILE, "utf-8");
|
|
6442
6758
|
} catch {
|
|
6443
6759
|
return null;
|
|
6444
6760
|
}
|
|
@@ -6466,7 +6782,7 @@ function writeOAuthCredentials(tokens) {
|
|
|
6466
6782
|
try {
|
|
6467
6783
|
let fileData = {};
|
|
6468
6784
|
try {
|
|
6469
|
-
const rawFile =
|
|
6785
|
+
const rawFile = readFileSync3(CLAUDE_CREDENTIALS_FILE, "utf-8");
|
|
6470
6786
|
fileData = JSON.parse(rawFile);
|
|
6471
6787
|
} catch {
|
|
6472
6788
|
}
|
|
@@ -6694,7 +7010,7 @@ init_helpers();
|
|
|
6694
7010
|
// server/routes/health.ts
|
|
6695
7011
|
init_dist();
|
|
6696
7012
|
init_paths();
|
|
6697
|
-
import { existsSync as
|
|
7013
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5 } from "fs";
|
|
6698
7014
|
import { createConnection as createConnection2 } from "net";
|
|
6699
7015
|
|
|
6700
7016
|
// app/lib/network.ts
|
|
@@ -6719,11 +7035,11 @@ function getLanIp() {
|
|
|
6719
7035
|
init_paths();
|
|
6720
7036
|
import { spawnSync, execFileSync } from "child_process";
|
|
6721
7037
|
import { createConnection } from "net";
|
|
6722
|
-
import { mkdirSync, readFileSync as
|
|
6723
|
-
import { resolve as
|
|
7038
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
7039
|
+
import { resolve as resolve4 } from "path";
|
|
6724
7040
|
init_remote_auth();
|
|
6725
|
-
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ??
|
|
6726
|
-
var VNC_SCRIPT =
|
|
7041
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
7042
|
+
var VNC_SCRIPT = resolve4(PLATFORM_ROOT2, "scripts/vnc.sh");
|
|
6727
7043
|
var displayMode = process.env.DISPLAY_MODE ?? "virtual";
|
|
6728
7044
|
if (displayMode === "native") {
|
|
6729
7045
|
console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
|
|
@@ -6778,7 +7094,7 @@ function discoverNativeDisplay() {
|
|
|
6778
7094
|
const leaderPid = leaderResult.stdout?.trim();
|
|
6779
7095
|
if (leaderPid) {
|
|
6780
7096
|
try {
|
|
6781
|
-
const environ =
|
|
7097
|
+
const environ = readFileSync4(`/proc/${leaderPid}/environ`, "utf8");
|
|
6782
7098
|
const match = environ.split("\0").find((e) => e.startsWith("WAYLAND_DISPLAY="));
|
|
6783
7099
|
if (match) waylandDisplay = match.split("=")[1];
|
|
6784
7100
|
} catch {
|
|
@@ -6833,10 +7149,10 @@ async function waitForPort(port2, timeoutMs = 12e3) {
|
|
|
6833
7149
|
return false;
|
|
6834
7150
|
}
|
|
6835
7151
|
function ensureLogDir() {
|
|
6836
|
-
|
|
7152
|
+
mkdirSync2(LOG_DIR, { recursive: true });
|
|
6837
7153
|
}
|
|
6838
7154
|
function logPath(name) {
|
|
6839
|
-
return
|
|
7155
|
+
return resolve4(LOG_DIR, `${name}.log`);
|
|
6840
7156
|
}
|
|
6841
7157
|
async function ensureVnc() {
|
|
6842
7158
|
if (process.platform !== "linux") {
|
|
@@ -6921,19 +7237,19 @@ async function ensureCdp(transport = "vnc") {
|
|
|
6921
7237
|
function killChromium() {
|
|
6922
7238
|
spawnSync("pkill", ["-f", "chromium"], { stdio: "pipe" });
|
|
6923
7239
|
}
|
|
6924
|
-
var AUTH_TARGET_ID_FILE =
|
|
7240
|
+
var AUTH_TARGET_ID_FILE = resolve4(LOG_DIR, "claude-auth-target.id");
|
|
6925
7241
|
function readAuthTargetId() {
|
|
6926
7242
|
try {
|
|
6927
|
-
const id =
|
|
7243
|
+
const id = readFileSync4(AUTH_TARGET_ID_FILE, "utf8").trim();
|
|
6928
7244
|
return id.length > 0 ? id : null;
|
|
6929
7245
|
} catch {
|
|
6930
7246
|
return null;
|
|
6931
7247
|
}
|
|
6932
7248
|
}
|
|
6933
7249
|
function writeChromiumWrapper() {
|
|
6934
|
-
|
|
7250
|
+
mkdirSync2(BIN_DIR, { recursive: true });
|
|
6935
7251
|
rmSync2(AUTH_TARGET_ID_FILE, { force: true });
|
|
6936
|
-
const wrapperPath =
|
|
7252
|
+
const wrapperPath = resolve4(BIN_DIR, "chromium");
|
|
6937
7253
|
writeFileSync3(wrapperPath, `#!/bin/bash
|
|
6938
7254
|
# Generated by writeChromiumWrapper (app/lib/vnc.ts) \u2014 Task 1562.
|
|
6939
7255
|
# Hands its URL argument to the brand's running Chromium over CDP.
|
|
@@ -7035,8 +7351,8 @@ app.get("/", async (c) => {
|
|
|
7035
7351
|
const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
|
|
7036
7352
|
let pinConfigured = false;
|
|
7037
7353
|
try {
|
|
7038
|
-
if (
|
|
7039
|
-
const raw =
|
|
7354
|
+
if (existsSync3(USERS_FILE)) {
|
|
7355
|
+
const raw = readFileSync5(USERS_FILE, "utf-8").trim();
|
|
7040
7356
|
if (raw) {
|
|
7041
7357
|
const users = JSON.parse(raw);
|
|
7042
7358
|
pinConfigured = Array.isArray(users) && users.length > 0;
|
|
@@ -7529,183 +7845,6 @@ init_dist();
|
|
|
7529
7845
|
init_channel_key();
|
|
7530
7846
|
import { statSync as statSync7 } from "fs";
|
|
7531
7847
|
import { join as join12 } from "path";
|
|
7532
|
-
|
|
7533
|
-
// app/lib/telegram/message-store.ts
|
|
7534
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync5, appendFileSync, readdirSync as readdirSync2, statSync as statSync5 } from "fs";
|
|
7535
|
-
import { homedir } from "os";
|
|
7536
|
-
import { dirname, join as join7, resolve as resolve4 } from "path";
|
|
7537
|
-
function configDirName() {
|
|
7538
|
-
const platformRoot5 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
7539
|
-
const brandPath = join7(platformRoot5, "config", "brand.json");
|
|
7540
|
-
if (existsSync3(brandPath)) {
|
|
7541
|
-
try {
|
|
7542
|
-
return JSON.parse(readFileSync5(brandPath, "utf-8")).configDir ?? ".maxy";
|
|
7543
|
-
} catch {
|
|
7544
|
-
}
|
|
7545
|
-
}
|
|
7546
|
-
return ".maxy";
|
|
7547
|
-
}
|
|
7548
|
-
var STORE_ROOT2 = () => join7(process.env.MAXY_DIR_OVERRIDE ?? resolve4(homedir(), configDirName()), "data", "telegram-messages");
|
|
7549
|
-
function isStorableChannelKey(channelKey) {
|
|
7550
|
-
return /^-?\d+:-?\d+$/.test(channelKey);
|
|
7551
|
-
}
|
|
7552
|
-
function channelKeyToFileStem(channelKey) {
|
|
7553
|
-
if (!isStorableChannelKey(channelKey)) {
|
|
7554
|
-
throw new Error(`telegram-message-store: refusing unstorable channelKey ${JSON.stringify(channelKey)}`);
|
|
7555
|
-
}
|
|
7556
|
-
return channelKey.replace(":", "_");
|
|
7557
|
-
}
|
|
7558
|
-
function fileStemToChannelKey(stem) {
|
|
7559
|
-
const i = stem.indexOf("_");
|
|
7560
|
-
return i <= 0 ? stem : `${stem.slice(0, i)}:${stem.slice(i + 1)}`;
|
|
7561
|
-
}
|
|
7562
|
-
function conversationFile(accountId, channelKey) {
|
|
7563
|
-
return join7(STORE_ROOT2(), accountId, `${channelKeyToFileStem(channelKey)}.jsonl`);
|
|
7564
|
-
}
|
|
7565
|
-
function conversationFilePath2(accountId, channelKey) {
|
|
7566
|
-
return conversationFile(accountId, channelKey);
|
|
7567
|
-
}
|
|
7568
|
-
function isStorableStem(stem) {
|
|
7569
|
-
return isStorableChannelKey(fileStemToChannelKey(stem));
|
|
7570
|
-
}
|
|
7571
|
-
function listStoredAccountIds2() {
|
|
7572
|
-
const root = STORE_ROOT2();
|
|
7573
|
-
if (!existsSync3(root)) return [];
|
|
7574
|
-
return readdirSync2(root).filter((name) => {
|
|
7575
|
-
try {
|
|
7576
|
-
return statSync5(join7(root, name)).isDirectory();
|
|
7577
|
-
} catch {
|
|
7578
|
-
return false;
|
|
7579
|
-
}
|
|
7580
|
-
});
|
|
7581
|
-
}
|
|
7582
|
-
function parseLines(raw) {
|
|
7583
|
-
const out = [];
|
|
7584
|
-
for (const line of raw.split("\n")) {
|
|
7585
|
-
if (!line.trim()) continue;
|
|
7586
|
-
try {
|
|
7587
|
-
out.push(JSON.parse(line));
|
|
7588
|
-
} catch {
|
|
7589
|
-
console.error(`[telegram-message-store] skip reason=corrupt-line`);
|
|
7590
|
-
}
|
|
7591
|
-
}
|
|
7592
|
-
return out;
|
|
7593
|
-
}
|
|
7594
|
-
var byDateSentAsc = (a, b) => a.dateSent.localeCompare(b.dateSent);
|
|
7595
|
-
function appendMessage2(accountId, record) {
|
|
7596
|
-
if (!isStorableChannelKey(record.channelKey)) {
|
|
7597
|
-
console.error(
|
|
7598
|
-
`[telegram-message-store] FAIL op=append reason=unstorable-channel-key messageId=${record.messageId}`
|
|
7599
|
-
);
|
|
7600
|
-
return { created: false };
|
|
7601
|
-
}
|
|
7602
|
-
const file = conversationFile(accountId, record.channelKey);
|
|
7603
|
-
try {
|
|
7604
|
-
mkdirSync2(dirname(file), { recursive: true });
|
|
7605
|
-
} catch (err) {
|
|
7606
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
7607
|
-
console.error(
|
|
7608
|
-
`[telegram-message-store] FAIL op=append reason=${reason.slice(0, 100)} messageId=${record.messageId}`
|
|
7609
|
-
);
|
|
7610
|
-
return { created: false };
|
|
7611
|
-
}
|
|
7612
|
-
try {
|
|
7613
|
-
if (existsSync3(file)) {
|
|
7614
|
-
const existing = parseLines(readFileSync5(file, "utf8"));
|
|
7615
|
-
if (existing.some((r) => r.messageId === record.messageId)) {
|
|
7616
|
-
console.error(
|
|
7617
|
-
`[telegram-message-store] op=append channelKey=${record.channelKey} origin=${record.origin} created=false`
|
|
7618
|
-
);
|
|
7619
|
-
return { created: false };
|
|
7620
|
-
}
|
|
7621
|
-
}
|
|
7622
|
-
appendFileSync(file, `${JSON.stringify(record)}
|
|
7623
|
-
`);
|
|
7624
|
-
console.error(
|
|
7625
|
-
`[telegram-message-store] op=append channelKey=${record.channelKey} origin=${record.origin} created=true`
|
|
7626
|
-
);
|
|
7627
|
-
return { created: true };
|
|
7628
|
-
} catch (err) {
|
|
7629
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
7630
|
-
console.error(
|
|
7631
|
-
`[telegram-message-store] FAIL op=append reason=${reason.slice(0, 100)} messageId=${record.messageId}`
|
|
7632
|
-
);
|
|
7633
|
-
return { created: false };
|
|
7634
|
-
}
|
|
7635
|
-
}
|
|
7636
|
-
function readConversation2(accountId, channelKey) {
|
|
7637
|
-
if (!isStorableChannelKey(channelKey)) return [];
|
|
7638
|
-
const file = conversationFile(accountId, channelKey);
|
|
7639
|
-
try {
|
|
7640
|
-
if (!existsSync3(file)) return [];
|
|
7641
|
-
return parseLines(readFileSync5(file, "utf8")).sort(byDateSentAsc);
|
|
7642
|
-
} catch (err) {
|
|
7643
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
7644
|
-
console.error(
|
|
7645
|
-
`[telegram-message-store] FAIL op=read reason=${reason.slice(0, 100)} channelKey=${channelKey}`
|
|
7646
|
-
);
|
|
7647
|
-
return [];
|
|
7648
|
-
}
|
|
7649
|
-
}
|
|
7650
|
-
var summaryCache = /* @__PURE__ */ new Map();
|
|
7651
|
-
function summariseRecords(records2) {
|
|
7652
|
-
if (records2.length === 0) return null;
|
|
7653
|
-
const sorted = records2.slice().sort(byDateSentAsc);
|
|
7654
|
-
return { first: sorted[0], last: sorted[sorted.length - 1] };
|
|
7655
|
-
}
|
|
7656
|
-
function readConversationSummaries2(accountId) {
|
|
7657
|
-
const dir = join7(STORE_ROOT2(), accountId);
|
|
7658
|
-
const out = /* @__PURE__ */ new Map();
|
|
7659
|
-
const seen = /* @__PURE__ */ new Set();
|
|
7660
|
-
try {
|
|
7661
|
-
if (!existsSync3(dir)) return out;
|
|
7662
|
-
for (const entry2 of readdirSync2(dir)) {
|
|
7663
|
-
if (!entry2.endsWith(".jsonl")) continue;
|
|
7664
|
-
const stem = entry2.slice(0, -".jsonl".length);
|
|
7665
|
-
if (!isStorableStem(stem)) continue;
|
|
7666
|
-
const channelKey = fileStemToChannelKey(stem);
|
|
7667
|
-
const file = join7(dir, entry2);
|
|
7668
|
-
seen.add(file);
|
|
7669
|
-
let size;
|
|
7670
|
-
let mtimeMs;
|
|
7671
|
-
try {
|
|
7672
|
-
const st = statSync5(file);
|
|
7673
|
-
size = st.size;
|
|
7674
|
-
mtimeMs = st.mtimeMs;
|
|
7675
|
-
} catch {
|
|
7676
|
-
continue;
|
|
7677
|
-
}
|
|
7678
|
-
const cached3 = summaryCache.get(file);
|
|
7679
|
-
if (cached3 && cached3.size === size && cached3.mtimeMs === mtimeMs) {
|
|
7680
|
-
out.set(channelKey, cached3.summary);
|
|
7681
|
-
continue;
|
|
7682
|
-
}
|
|
7683
|
-
let summary;
|
|
7684
|
-
try {
|
|
7685
|
-
summary = summariseRecords(parseLines(readFileSync5(file, "utf8")));
|
|
7686
|
-
} catch (err) {
|
|
7687
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
7688
|
-
console.error(
|
|
7689
|
-
`[telegram-message-store] FAIL op=summarise reason=${reason.slice(0, 100)} channelKey=${channelKey}`
|
|
7690
|
-
);
|
|
7691
|
-
continue;
|
|
7692
|
-
}
|
|
7693
|
-
if (!summary) continue;
|
|
7694
|
-
summaryCache.set(file, { size, mtimeMs, summary });
|
|
7695
|
-
out.set(channelKey, summary);
|
|
7696
|
-
}
|
|
7697
|
-
} catch (err) {
|
|
7698
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
7699
|
-
console.error(`[telegram-message-store] FAIL op=summarise-dir reason=${reason.slice(0, 100)}`);
|
|
7700
|
-
return out;
|
|
7701
|
-
}
|
|
7702
|
-
for (const key2 of [...summaryCache.keys()]) {
|
|
7703
|
-
if (key2.startsWith(dir) && !seen.has(key2)) summaryCache.delete(key2);
|
|
7704
|
-
}
|
|
7705
|
-
return out;
|
|
7706
|
-
}
|
|
7707
|
-
|
|
7708
|
-
// server/routes/channel/schedule-inject.ts
|
|
7709
7848
|
init_admin_session_id();
|
|
7710
7849
|
init_normalize();
|
|
7711
7850
|
|
|
@@ -8288,6 +8427,32 @@ async function sendTelegramText(botToken, chatId, text) {
|
|
|
8288
8427
|
}
|
|
8289
8428
|
}
|
|
8290
8429
|
|
|
8430
|
+
// app/lib/telegram/outbound/send-chat-action.ts
|
|
8431
|
+
async function sendTelegramChatAction(botToken, chatId, key2, site) {
|
|
8432
|
+
const report = (ok2, reason) => console.error(
|
|
8433
|
+
`[telegram-native] op=chat-action key=${key2} site=${site} ok=${ok2}${ok2 ? "" : ` reason=${reason}`}`
|
|
8434
|
+
);
|
|
8435
|
+
try {
|
|
8436
|
+
const res = await fetch(`https://api.telegram.org/bot${botToken}/sendChatAction`, {
|
|
8437
|
+
method: "POST",
|
|
8438
|
+
headers: { "Content-Type": "application/json" },
|
|
8439
|
+
body: JSON.stringify({ chat_id: chatId, action: "typing" })
|
|
8440
|
+
});
|
|
8441
|
+
const data = await res.json();
|
|
8442
|
+
if (data.ok) {
|
|
8443
|
+
report(true);
|
|
8444
|
+
return { ok: true };
|
|
8445
|
+
}
|
|
8446
|
+
const error = data.description ?? "chat action failed";
|
|
8447
|
+
report(false, error);
|
|
8448
|
+
return { ok: false, error };
|
|
8449
|
+
} catch (e) {
|
|
8450
|
+
const error = e instanceof Error ? e.message : String(e);
|
|
8451
|
+
report(false, error);
|
|
8452
|
+
return { ok: false, error };
|
|
8453
|
+
}
|
|
8454
|
+
}
|
|
8455
|
+
|
|
8291
8456
|
// server/routes/whatsapp.ts
|
|
8292
8457
|
init_dist();
|
|
8293
8458
|
import { basename as basename3, join as join17, resolve as resolve8 } from "path";
|
|
@@ -15549,6 +15714,7 @@ function pickActivity(entries2, grown, nowMs, concurrencyWindowMs) {
|
|
|
15549
15714
|
}
|
|
15550
15715
|
|
|
15551
15716
|
// server/routes/session-reader.ts
|
|
15717
|
+
init_session_store();
|
|
15552
15718
|
init_dist();
|
|
15553
15719
|
var app7 = new Hono();
|
|
15554
15720
|
function probeDataRef(path) {
|
|
@@ -15724,6 +15890,7 @@ data: ${JSON.stringify(turn)}
|
|
|
15724
15890
|
|
|
15725
15891
|
`));
|
|
15726
15892
|
const connId = `${sessionId.slice(0, 8)}-${process.hrtime.bigint().toString(36).slice(-6)}`;
|
|
15893
|
+
const censusAccountId = getAccountIdForSession(c.get("cacheKey") ?? "") ?? "unresolved";
|
|
15727
15894
|
const startedAt = Date.now();
|
|
15728
15895
|
const lastEventId = c.req.header("Last-Event-ID");
|
|
15729
15896
|
let teardown = () => {
|
|
@@ -15759,7 +15926,8 @@ data: ${JSON.stringify(obj)}
|
|
|
15759
15926
|
log: (line) => console.log(line)
|
|
15760
15927
|
});
|
|
15761
15928
|
const queuedPendingSuppress = /* @__PURE__ */ new Map();
|
|
15762
|
-
const
|
|
15929
|
+
const envelopeCensus = emptyEnvelopeCensus();
|
|
15930
|
+
const turnCtx = { envelopeCensus };
|
|
15763
15931
|
const drain = () => {
|
|
15764
15932
|
try {
|
|
15765
15933
|
const { buf, end } = readFrom(jsonlPath, offset);
|
|
@@ -15776,6 +15944,7 @@ data: ${JSON.stringify(obj)}
|
|
|
15776
15944
|
n++;
|
|
15777
15945
|
}
|
|
15778
15946
|
if (n > 0) {
|
|
15947
|
+
foldFrameCensus(censusAccountId, sessionId, envelopeCensus);
|
|
15779
15948
|
actx.parentGrewThisTick = true;
|
|
15780
15949
|
console.log(`[wa-stream] op=drain conn=${connId} turns=${n}`);
|
|
15781
15950
|
}
|
|
@@ -15822,6 +15991,7 @@ data: ${JSON.stringify({ from: alignedFrom, fileEnd: fileEnd0 })}
|
|
|
15822
15991
|
}
|
|
15823
15992
|
n++;
|
|
15824
15993
|
}
|
|
15994
|
+
foldFrameCensus(censusAccountId, sessionId, envelopeCensus);
|
|
15825
15995
|
console.log(
|
|
15826
15996
|
`[wa-stream] op=backlog conn=${connId} turns=${n} bytes=${end - alignedFrom} from=${alignedFrom}`
|
|
15827
15997
|
);
|
|
@@ -16005,7 +16175,7 @@ import { basename as basename8, dirname as dirname9, join as join26 } from "path
|
|
|
16005
16175
|
// app/lib/reader/operator-conversations.ts
|
|
16006
16176
|
function operatorConvName(row) {
|
|
16007
16177
|
if (row.isGroup === true) return row.chatName ?? row.title;
|
|
16008
|
-
const name = row.operatorName ?? row.chatName ?? row.whatsappName;
|
|
16178
|
+
const name = row.operatorName ?? row.chatName ?? row.whatsappName ?? row.telegramName;
|
|
16009
16179
|
const senderId = row.senderId != null && row.channel === "whatsapp" ? formatSenderPhone(row.senderId) : row.senderId;
|
|
16010
16180
|
if (name) return senderId ? `${name} ${senderId}` : name;
|
|
16011
16181
|
return senderId ?? row.title;
|
|
@@ -16015,6 +16185,7 @@ function operatorConvNameSource(row) {
|
|
|
16015
16185
|
if (row.operatorName) return "operator";
|
|
16016
16186
|
if (row.chatName) return "chatmeta";
|
|
16017
16187
|
if (row.whatsappName) return "inbound-pushname";
|
|
16188
|
+
if (row.telegramName) return "telegram-sender-name";
|
|
16018
16189
|
if (row.senderId != null) return "counterparty-number";
|
|
16019
16190
|
return "jid";
|
|
16020
16191
|
}
|
|
@@ -16234,6 +16405,14 @@ app8.get("/conversations", requireAdminSession, async (c) => {
|
|
|
16234
16405
|
);
|
|
16235
16406
|
const adminScope = allStoreRows.filter((r) => r.scope === "admin").length;
|
|
16236
16407
|
const publicScope = allStoreRows.length - adminScope;
|
|
16408
|
+
const telegramStoreRows = account ? buildTelegramStoreConversationRows(account.accountId, readConversationSummaries2(account.accountId)) : [];
|
|
16409
|
+
const dedupedSessionRows = dropTelegramSessionDuplicates(sessionConversations, telegramStoreRows);
|
|
16410
|
+
const telegramRows = [
|
|
16411
|
+
...dedupedSessionRows.filter((r) => r.channel === "telegram"),
|
|
16412
|
+
...telegramStoreRows
|
|
16413
|
+
].sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
|
|
16414
|
+
const otherSessionRows = dedupedSessionRows.filter((r) => r.channel !== "telegram");
|
|
16415
|
+
const conversations = [...otherSessionRows, ...allStoreRows, ...telegramRows];
|
|
16237
16416
|
const census = buildLabelCensus(allStoreRows, selfPushNames(summaries));
|
|
16238
16417
|
for (const f of fallbackLabelSources(allStoreRows)) {
|
|
16239
16418
|
console.log(`[wa-label] op=row-label jid=${f.jid} source=${f.source} route=conversations`);
|
|
@@ -16241,11 +16420,11 @@ app8.get("/conversations", requireAdminSession, async (c) => {
|
|
|
16241
16420
|
console.log(
|
|
16242
16421
|
`[wa-label-census] route=conversations chats=${census.chats} withMeta=${census.withMeta} groups=${census.groups} groupsWithSubject=${census.groupsWithSubject} labelledByMember=${census.labelledByMember} dms=${census.dms} namedDms=${census.namedDms} labelledBySelfPushName=${census.labelledBySelfPushName}`
|
|
16243
16422
|
);
|
|
16244
|
-
const sessionRowCount =
|
|
16423
|
+
const sessionRowCount = dedupedSessionRows.length;
|
|
16245
16424
|
console.log(
|
|
16246
|
-
`[wa-reader] op=list pinnedAccount=${scopeAccountId?.slice(0, 8) ?? "null"} resolvedAccount=${account?.accountId.slice(0, 8) ?? "null"} multiAccount=${multiAccount} conversations=${
|
|
16425
|
+
`[wa-reader] op=list pinnedAccount=${scopeAccountId?.slice(0, 8) ?? "null"} resolvedAccount=${account?.accountId.slice(0, 8) ?? "null"} multiAccount=${multiAccount} conversations=${conversations.length} sessionRows=${sessionRowCount} storeRows=${allStoreRows.length} storeRowsByChannel=whatsapp:${allStoreRows.length},telegram:${telegramStoreRows.length} adminScope=${adminScope} publicScope=${publicScope} sessionRowsExcludedUntagged=${sessionRowsExcludedUntagged} houseResolved=${houseId ? houseId.slice(0, 8) : "null"} managerPhonesBound=${managerPhonesBound} managedStoreRows=${managedRows.length}`
|
|
16247
16426
|
);
|
|
16248
|
-
return c.json({ conversations
|
|
16427
|
+
return c.json({ conversations });
|
|
16249
16428
|
});
|
|
16250
16429
|
function tagAndMergeInstallConversations(perAccount) {
|
|
16251
16430
|
const all = [];
|
|
@@ -16256,7 +16435,7 @@ function tagAndMergeInstallConversations(perAccount) {
|
|
|
16256
16435
|
}
|
|
16257
16436
|
function dropHouseRowsOwnedBySubAccount(rows, subManagerPhones) {
|
|
16258
16437
|
return rows.filter((r) => {
|
|
16259
|
-
if (r.isHouse !== true || r.source !== "store" || r.senderId == null) return true;
|
|
16438
|
+
if (r.channel !== "whatsapp" || r.isHouse !== true || r.source !== "store" || r.senderId == null) return true;
|
|
16260
16439
|
let owner = null;
|
|
16261
16440
|
for (const [subId, phones] of subManagerPhones) {
|
|
16262
16441
|
if (counterpartyBoundToSubAccount(phones, r.senderId)) {
|
|
@@ -16396,7 +16575,14 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
16396
16575
|
readConversationSummaries(account.accountId),
|
|
16397
16576
|
readChatMetadata(account.accountId)
|
|
16398
16577
|
);
|
|
16399
|
-
|
|
16578
|
+
const telegramStoreRows = buildTelegramStoreConversationRows(
|
|
16579
|
+
account.accountId,
|
|
16580
|
+
readConversationSummaries2(account.accountId)
|
|
16581
|
+
);
|
|
16582
|
+
perAccount.push({
|
|
16583
|
+
isHouse,
|
|
16584
|
+
rows: [...dropTelegramSessionDuplicates(named, telegramStoreRows), ...storeRows, ...telegramStoreRows]
|
|
16585
|
+
});
|
|
16400
16586
|
}
|
|
16401
16587
|
const houseAccount = validAccounts.find((a) => a.accountId === houseId) ?? null;
|
|
16402
16588
|
const subManagerPhones = /* @__PURE__ */ new Map();
|
|
@@ -16414,16 +16600,19 @@ app8.get("/conversations-all", requireAdminSession, async (c) => {
|
|
|
16414
16600
|
for (const account of validAccounts) {
|
|
16415
16601
|
for (const [jid, s] of readConversationSummaries(account.accountId)) allSummaries.set(`${account.accountId}:${jid}`, s);
|
|
16416
16602
|
}
|
|
16417
|
-
const
|
|
16418
|
-
|
|
16603
|
+
const whatsappRows = conversations.filter((r) => r.channel === "whatsapp");
|
|
16604
|
+
const census = buildLabelCensus(whatsappRows, selfPushNames(allSummaries));
|
|
16605
|
+
for (const f of fallbackLabelSources(whatsappRows)) {
|
|
16419
16606
|
console.log(`[wa-label] op=row-label jid=${f.jid} source=${f.source} route=conversations-all`);
|
|
16420
16607
|
}
|
|
16421
16608
|
console.log(
|
|
16422
16609
|
`[wa-label-census] route=conversations-all chats=${census.chats} withMeta=${census.withMeta} groups=${census.groups} groupsWithSubject=${census.groupsWithSubject} labelledByMember=${census.labelledByMember} dms=${census.dms} namedDms=${census.namedDms} labelledBySelfPushName=${census.labelledBySelfPushName}`
|
|
16423
16610
|
);
|
|
16424
16611
|
const houseRows = conversations.filter((r) => r.isHouse).length;
|
|
16612
|
+
const storeByChannel = conversations.filter((r) => r.source === "store");
|
|
16613
|
+
const waStoreRows = storeByChannel.filter((r) => r.channel === "whatsapp").length;
|
|
16425
16614
|
console.log(
|
|
16426
|
-
`[wa-reader] op=conversations-all accounts=${validAccounts.length} rows=${conversations.length} house=${houseRows} sub=${conversations.length - houseRows} deduped=${merged.length - conversations.length}`
|
|
16615
|
+
`[wa-reader] op=conversations-all accounts=${validAccounts.length} rows=${conversations.length} house=${houseRows} sub=${conversations.length - houseRows} deduped=${merged.length - conversations.length} storeRowsByChannel=whatsapp:${waStoreRows},telegram:${storeByChannel.length - waStoreRows}`
|
|
16427
16616
|
);
|
|
16428
16617
|
return { conversations };
|
|
16429
16618
|
});
|
|
@@ -38129,21 +38318,22 @@ function startNativeFileFollower(input) {
|
|
|
38129
38318
|
}
|
|
38130
38319
|
|
|
38131
38320
|
// app/lib/channel-delivery/follower-registry.ts
|
|
38132
|
-
function createFollowerRegistry(start) {
|
|
38321
|
+
function createFollowerRegistry(start, keyOf2 = (p) => p.senderId) {
|
|
38133
38322
|
const controllers = /* @__PURE__ */ new Map();
|
|
38134
38323
|
const params = /* @__PURE__ */ new Map();
|
|
38135
38324
|
return {
|
|
38136
38325
|
attach(p) {
|
|
38137
|
-
|
|
38138
|
-
|
|
38326
|
+
const doorId = keyOf2(p);
|
|
38327
|
+
controllers.get(doorId)?.abort();
|
|
38328
|
+
params.set(doorId, p);
|
|
38139
38329
|
let ac;
|
|
38140
38330
|
ac = start(p, () => {
|
|
38141
|
-
if (controllers.get(
|
|
38331
|
+
if (controllers.get(doorId) === ac) controllers.delete(doorId);
|
|
38142
38332
|
});
|
|
38143
|
-
controllers.set(
|
|
38333
|
+
controllers.set(doorId, ac);
|
|
38144
38334
|
},
|
|
38145
38335
|
live: () => [...controllers.keys()],
|
|
38146
|
-
paramsFor: (
|
|
38336
|
+
paramsFor: (doorId) => params.get(doorId) ?? null
|
|
38147
38337
|
};
|
|
38148
38338
|
}
|
|
38149
38339
|
|
|
@@ -38156,41 +38346,51 @@ var id82 = (s) => s.slice(0, 8);
|
|
|
38156
38346
|
function followerCensusTick(deps, state = makeCensusState()) {
|
|
38157
38347
|
const bound = deps.bound();
|
|
38158
38348
|
const live = new Set(deps.live());
|
|
38159
|
-
|
|
38160
|
-
const
|
|
38349
|
+
const label = deps.label ?? id82;
|
|
38350
|
+
for (const doorId of live) state.attempts.delete(doorId);
|
|
38351
|
+
const missing = bound.filter((doorId) => !live.has(doorId));
|
|
38161
38352
|
console.error(
|
|
38162
|
-
`[channel-follower] op=follower-census channel=${deps.channel} bound=${bound.length} attached=${live.size} missing=${missing.length} missingSenders=${missing.map(
|
|
38353
|
+
`[channel-follower] op=follower-census channel=${deps.channel} bound=${bound.length} attached=${live.size} missing=${missing.length} missingSenders=${missing.map(label).join(",") || "none"}`
|
|
38163
38354
|
);
|
|
38164
|
-
for (const
|
|
38165
|
-
const used = state.attempts.get(
|
|
38355
|
+
for (const doorId of missing) {
|
|
38356
|
+
const used = state.attempts.get(doorId) ?? 0;
|
|
38166
38357
|
if (used >= CENSUS_MAX_REATTEMPTS) {
|
|
38167
38358
|
console.error(
|
|
38168
|
-
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=${id82(deps.paramsFor(
|
|
38359
|
+
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=${id82(deps.paramsFor(doorId)?.sessionId ?? "none")} senderId=${label(doorId)} outcome=skipped reason=reattach-exhausted attempts=${used}`
|
|
38169
38360
|
);
|
|
38170
38361
|
continue;
|
|
38171
38362
|
}
|
|
38172
|
-
const p = deps.paramsFor(
|
|
38363
|
+
const p = deps.paramsFor(doorId);
|
|
38173
38364
|
if (!p) {
|
|
38174
38365
|
console.error(
|
|
38175
|
-
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=none senderId=${
|
|
38366
|
+
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=none senderId=${label(doorId)} outcome=failed reason=no-attach-params`
|
|
38176
38367
|
);
|
|
38177
38368
|
continue;
|
|
38178
38369
|
}
|
|
38179
38370
|
try {
|
|
38180
|
-
state.attempts.set(
|
|
38371
|
+
state.attempts.set(doorId, used + 1);
|
|
38181
38372
|
deps.attach(p);
|
|
38182
38373
|
console.error(
|
|
38183
|
-
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=${id82(p.sessionId)} senderId=${
|
|
38374
|
+
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=${id82(p.sessionId)} senderId=${label(doorId)} outcome=attached reason=none`
|
|
38184
38375
|
);
|
|
38185
38376
|
} catch (err) {
|
|
38186
38377
|
console.error(
|
|
38187
|
-
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=${id82(p.sessionId)} senderId=${
|
|
38378
|
+
`[channel-follower] op=follower-reattach channel=${deps.channel} sessionId=${id82(p.sessionId)} senderId=${label(doorId)} outcome=failed reason=${err instanceof Error ? err.message : String(err)}`
|
|
38188
38379
|
);
|
|
38189
38380
|
}
|
|
38190
38381
|
}
|
|
38191
38382
|
return `${deps.channel}: ${bound.length} door(s), ${missing.length} without a reader`;
|
|
38192
38383
|
}
|
|
38193
38384
|
|
|
38385
|
+
// app/lib/channel-delivery/whatsapp-door.ts
|
|
38386
|
+
function whatsappDoorId(p) {
|
|
38387
|
+
return hubKey(p.effectiveAccountId, p.senderId);
|
|
38388
|
+
}
|
|
38389
|
+
function whatsappDoorLabel(doorId) {
|
|
38390
|
+
const [decoded] = decodeHubKeys([doorId]);
|
|
38391
|
+
return decoded ? `${id82(decoded.accountId)}:${id82(decoded.senderId)}` : id82(doorId);
|
|
38392
|
+
}
|
|
38393
|
+
|
|
38194
38394
|
// app/lib/telegram/gateway/inbound-hub.ts
|
|
38195
38395
|
var InboundHub3 = class {
|
|
38196
38396
|
keys = /* @__PURE__ */ new Map();
|
|
@@ -38634,6 +38834,15 @@ var TelegramGateway = class {
|
|
|
38634
38834
|
console.error(
|
|
38635
38835
|
`[telegram-native] op=inbound key=${key2} botId=${input.botId} accountId=${input.accountId} role=${input.role} agent=${input.agentSlug} bytes=${bytes} source=${source} delivery=${willDeliverNow ? "immediate" : "queued"} sessionId=${resolvedSessionId}`
|
|
38636
38836
|
);
|
|
38837
|
+
try {
|
|
38838
|
+
if (source === "user") {
|
|
38839
|
+
this.deps.sendChatAction?.({ botToken: input.botToken, chatId: input.chatId, key: key2 });
|
|
38840
|
+
}
|
|
38841
|
+
} catch (err) {
|
|
38842
|
+
console.error(
|
|
38843
|
+
`[telegram-native] op=chat-action key=${key2} site=inbound ok=false reason=${err instanceof Error ? err.message : String(err)}`
|
|
38844
|
+
);
|
|
38845
|
+
}
|
|
38637
38846
|
if (!hadSubscriber && !this.spawning.has(key2)) {
|
|
38638
38847
|
this.spawning.add(key2);
|
|
38639
38848
|
console.error(`[telegram-native] op=spawn-trigger key=${key2} botId=${input.botId} role=${input.role}`);
|
|
@@ -38898,6 +39107,7 @@ function makeTelegramFileDelivery(entry2, botToken) {
|
|
|
38898
39107
|
}
|
|
38899
39108
|
|
|
38900
39109
|
// app/lib/telegram/gateway/native-file-follower.ts
|
|
39110
|
+
var CHAT_ACTION_REFRESH_MS = 4e3;
|
|
38901
39111
|
function startTelegramNativeFileFollower(input) {
|
|
38902
39112
|
const entry2 = {
|
|
38903
39113
|
sessionId: input.sessionId,
|
|
@@ -38917,6 +39127,16 @@ function startTelegramNativeFileFollower(input) {
|
|
|
38917
39127
|
// The chat id the document is addressed to (set per spawn from the inbound).
|
|
38918
39128
|
replyTarget: input.chatId
|
|
38919
39129
|
};
|
|
39130
|
+
let lastChatActionAt = Number.NEGATIVE_INFINITY;
|
|
39131
|
+
const onActivity = () => {
|
|
39132
|
+
const now = Date.now();
|
|
39133
|
+
if (now - lastChatActionAt < CHAT_ACTION_REFRESH_MS) return;
|
|
39134
|
+
lastChatActionAt = now;
|
|
39135
|
+
void sendTelegramChatAction(input.botToken, input.chatId, input.senderId, "activity");
|
|
39136
|
+
};
|
|
39137
|
+
const onTurnComplete = () => {
|
|
39138
|
+
lastChatActionAt = Number.NEGATIVE_INFINITY;
|
|
39139
|
+
};
|
|
38920
39140
|
return startEmitter({
|
|
38921
39141
|
entry: entry2,
|
|
38922
39142
|
tag: "[telegram-adaptor]",
|
|
@@ -38930,6 +39150,8 @@ function startTelegramNativeFileFollower(input) {
|
|
|
38930
39150
|
// A resumed session's JSONL already holds prior SendUserFile tool_uses;
|
|
38931
39151
|
// suppress replay so historical files are not re-sent on attach.
|
|
38932
39152
|
suppressResumeReplay: true,
|
|
39153
|
+
onActivity,
|
|
39154
|
+
onTurnComplete,
|
|
38933
39155
|
// Task 2521 — the answer reaches every bound door through the one fan-out.
|
|
38934
39156
|
// Ungated by design: whatever woke the turn, the reader and the chats show
|
|
38935
39157
|
// the same thing.
|
|
@@ -39363,6 +39585,15 @@ function* walkTelegramSidecars() {
|
|
|
39363
39585
|
}
|
|
39364
39586
|
}
|
|
39365
39587
|
|
|
39588
|
+
// app/lib/telegram/gateway/origin-target.ts
|
|
39589
|
+
function answeringOriginTarget(send, key2, noteAnswered3) {
|
|
39590
|
+
if (!send) return null;
|
|
39591
|
+
return async (text) => {
|
|
39592
|
+
await send(text);
|
|
39593
|
+
noteAnswered3(key2);
|
|
39594
|
+
};
|
|
39595
|
+
}
|
|
39596
|
+
|
|
39366
39597
|
// server/telegram-migrate.ts
|
|
39367
39598
|
import { readFileSync as readFileSync53, writeFileSync as writeFileSync19, existsSync as existsSync53, renameSync as renameSync11, mkdirSync as mkdirSync11 } from "fs";
|
|
39368
39599
|
import { dirname as dirname18, join as join63 } from "path";
|
|
@@ -39863,7 +40094,12 @@ var nativeFileFollowers = createFollowerRegistry(
|
|
|
39863
40094
|
// delivered session 8e5bc4e8's answer to nothing.
|
|
39864
40095
|
fanTargets: () => whatsappFanTargets(p.effectiveAccountId, p.senderId),
|
|
39865
40096
|
onClose
|
|
39866
|
-
})
|
|
40097
|
+
}),
|
|
40098
|
+
// Task 2569 — the door is (effective account, sender), the same pair the
|
|
40099
|
+
// reply map and the session id key on. Keyed on the sender alone, a second
|
|
40100
|
+
// account's spawn for the same phone aborted the first account's live reader
|
|
40101
|
+
// and overwrote its recorded parameters, and the census called that healthy.
|
|
40102
|
+
whatsappDoorId
|
|
39867
40103
|
);
|
|
39868
40104
|
var webchatFileFollowers = /* @__PURE__ */ new Map();
|
|
39869
40105
|
async function fetchAccountStandingRules(accountId) {
|
|
@@ -40311,6 +40547,12 @@ var telegramGateway = new TelegramGateway({
|
|
|
40311
40547
|
resolveSessionOverride: (accountId, channelKey) => resolveChannelOverride(accountId, "telegram", channelKey),
|
|
40312
40548
|
// Task 2521 — hoisted above; see `telegramFanTargets`.
|
|
40313
40549
|
fanTargetsFor: telegramFanTargets,
|
|
40550
|
+
// Task 2576 — the typing indicator. Injected here because the gateway is
|
|
40551
|
+
// contracted to hold no Bot API import; see the dep comment in
|
|
40552
|
+
// telegram-gateway.ts. Not awaited: an indicator never paces a turn.
|
|
40553
|
+
sendChatAction: ({ botToken, chatId, key: key2 }) => {
|
|
40554
|
+
void sendTelegramChatAction(botToken, chatId, key2, "inbound");
|
|
40555
|
+
},
|
|
40314
40556
|
gatewayUrl: telegramGatewayUrl(),
|
|
40315
40557
|
serverPath: telegramServerPath(),
|
|
40316
40558
|
// Task 2368 — the role branch lives in its own module so it has a test that
|
|
@@ -40334,10 +40576,16 @@ registerLoop({
|
|
|
40334
40576
|
followerCensusTick(
|
|
40335
40577
|
{
|
|
40336
40578
|
channel: "whatsapp",
|
|
40337
|
-
|
|
40579
|
+
// Task 2569 — the whole hub key, not its sender half. `boundTargets()`
|
|
40580
|
+
// decodes the reply-map keys and this re-encodes them through the same
|
|
40581
|
+
// helper pair, so the census and the gateway compare one fact. Halving
|
|
40582
|
+
// it here is what let one phone's two accounts collapse to one door.
|
|
40583
|
+
bound: () => waGateway.boundTargets().map((t) => hubKey(t.accountId, t.senderId)),
|
|
40338
40584
|
live: () => nativeFileFollowers.live(),
|
|
40339
|
-
paramsFor: (
|
|
40340
|
-
attach: (p) => nativeFileFollowers.attach(p)
|
|
40585
|
+
paramsFor: (doorId) => nativeFileFollowers.paramsFor(doorId),
|
|
40586
|
+
attach: (p) => nativeFileFollowers.attach(p),
|
|
40587
|
+
// The hub key carries a NUL, which must never reach server.log.
|
|
40588
|
+
label: whatsappDoorLabel
|
|
40341
40589
|
},
|
|
40342
40590
|
followerCensusState.whatsapp
|
|
40343
40591
|
),
|
|
@@ -40346,7 +40594,7 @@ registerLoop({
|
|
|
40346
40594
|
channel: "telegram",
|
|
40347
40595
|
bound: () => telegramGateway.boundTargets(),
|
|
40348
40596
|
live: () => telegramFileFollowers.live(),
|
|
40349
|
-
paramsFor: (
|
|
40597
|
+
paramsFor: (doorId) => telegramFileFollowers.paramsFor(doorId),
|
|
40350
40598
|
attach: (p) => telegramFileFollowers.attach(p)
|
|
40351
40599
|
},
|
|
40352
40600
|
followerCensusState.telegram
|
|
@@ -41979,7 +42227,7 @@ function whatsappSender(accountId, key2) {
|
|
|
41979
42227
|
};
|
|
41980
42228
|
}
|
|
41981
42229
|
function telegramOriginTarget(key2) {
|
|
41982
|
-
return telegramGateway.liveReplyTarget(key2);
|
|
42230
|
+
return answeringOriginTarget(telegramGateway.liveReplyTarget(key2), key2, noteAnswered);
|
|
41983
42231
|
}
|
|
41984
42232
|
var configDirForWhatsApp = basename17(MAXY_DIR) || ".maxy";
|
|
41985
42233
|
var bootAccount = resolveAccount();
|