@rubytech/create-maxy-code 0.1.613 → 0.1.614
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/lib/agent-dispatch-rule/dist/index.d.ts +34 -0
- package/payload/platform/lib/agent-dispatch-rule/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/agent-dispatch-rule/dist/index.js +39 -0
- package/payload/platform/lib/agent-dispatch-rule/dist/index.js.map +1 -1
- package/payload/platform/lib/agent-dispatch-rule/src/__tests__/carrier-verdict.test.ts +92 -0
- package/payload/platform/lib/agent-dispatch-rule/src/index.ts +59 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +46 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +6 -0
- package/payload/platform/plugins/docs/references/telegram-guide.md +45 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.js +72 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.js +327 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery-reconcile.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.js +130 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js +27 -58
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-run.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts +8 -3
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js +37 -3
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.d.ts +67 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.js +133 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-delivery.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +67 -12
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js +226 -72
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js +66 -4
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/agent-turn-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.js +45 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/reconcile-run-controls.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +3 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +32 -7
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +12 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +15 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +14 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +4 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts +3 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-get.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +14 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.d.ts +2 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.js +237 -0
- package/payload/platform/plugins/telegram/mcp/dist/__tests__/card-row.test.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.d.ts +96 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.js +156 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/card-row.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/card.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/card.js +53 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/card.js.map +1 -1
- package/payload/server/server.js +663 -620
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch-carrier.test.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
/**
|
|
3
|
+
* Task 2776 — write-time validation of a named carrier.
|
|
4
|
+
*
|
|
5
|
+
* Both write paths call `validateAgentDispatch` (schedule-event.ts:503,
|
|
6
|
+
* schedule-update.ts:570), so extending that one function covers the pair and
|
|
7
|
+
* they cannot drift on the rule.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, it, beforeEach, afterEach, vi } from "vitest";
|
|
10
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { validateAgentDispatch } from "../agent-dispatch.js";
|
|
14
|
+
const ACCT = "5bc2c0e2-206c-4fee-b274-d87736b4fce1";
|
|
15
|
+
let root;
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
root = mkdtempSync(join(tmpdir(), "dispatch-"));
|
|
18
|
+
const dir = join(root, "data", "accounts", ACCT);
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
writeFileSync(join(dir, "account.json"), JSON.stringify({
|
|
21
|
+
telegram: {
|
|
22
|
+
bots: [
|
|
23
|
+
{ id: "8711566959", role: "admin", adminUsers: [8992591236] },
|
|
24
|
+
// A SECOND admin bot lists 1311221864, so that destination passes the
|
|
25
|
+
// account-wide registration check and the named-carrier check is what
|
|
26
|
+
// the not-admin / not-listed cases below actually exercise. Without
|
|
27
|
+
// it they would fail one guard earlier, on reason=not-registered.
|
|
28
|
+
{ id: "8700000001", role: "admin", adminUsers: [1311221864] },
|
|
29
|
+
{ id: "8804050655", role: "specialist", adminUsers: [1311221864] },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
vi.stubEnv("PLATFORM_ROOT", join(root, "platform"));
|
|
34
|
+
});
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
vi.unstubAllEnvs();
|
|
37
|
+
rmSync(root, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
const base = { channel: "telegram", destination: "8992591236", prompt: "check" };
|
|
40
|
+
describe("validateAgentDispatch — named carrier (Task 2776)", () => {
|
|
41
|
+
it("accepts a dispatch naming an admin bot that lists the destination", () => {
|
|
42
|
+
expect(() => validateAgentDispatch(ACCT, { ...base, botId: "8711566959" })).not.toThrow();
|
|
43
|
+
});
|
|
44
|
+
it("accepts a dispatch naming no bot, exactly as before", () => {
|
|
45
|
+
expect(() => validateAgentDispatch(ACCT, base)).not.toThrow();
|
|
46
|
+
});
|
|
47
|
+
it("treats an empty botId as naming none rather than as a bot that is absent", () => {
|
|
48
|
+
expect(() => validateAgentDispatch(ACCT, { ...base, botId: "" })).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
it("rejects a bot that is not this account's, naming reason=absent", () => {
|
|
51
|
+
expect(() => validateAgentDispatch(ACCT, { ...base, botId: "9999999999" })).toThrow(/reason=absent/);
|
|
52
|
+
});
|
|
53
|
+
it("rejects a bot demoted off role admin, naming reason=not-admin", () => {
|
|
54
|
+
// The measured case: the carrier is now the driver-relay specialist bot.
|
|
55
|
+
expect(() => validateAgentDispatch(ACCT, { ...base, destination: "1311221864", botId: "8804050655" })).toThrow(/reason=not-admin/);
|
|
56
|
+
});
|
|
57
|
+
it("rejects an admin bot that does not list the destination, naming reason=not-listed", () => {
|
|
58
|
+
expect(() => validateAgentDispatch(ACCT, { ...base, destination: "1311221864", botId: "8711566959" })).toThrow(/reason=not-listed/);
|
|
59
|
+
});
|
|
60
|
+
it("names the offending bot in the refusal, so the operator can act on it", () => {
|
|
61
|
+
expect(() => validateAgentDispatch(ACCT, { ...base, botId: "9999999999" })).toThrow(/9999999999/);
|
|
62
|
+
});
|
|
63
|
+
it("rejects a botId on a whatsapp dispatch — there is no bot to name", () => {
|
|
64
|
+
expect(() => validateAgentDispatch(ACCT, {
|
|
65
|
+
channel: "whatsapp",
|
|
66
|
+
destination: "+447700900000",
|
|
67
|
+
prompt: "check",
|
|
68
|
+
botId: "8711566959",
|
|
69
|
+
})).toThrow(/only a telegram dispatch/);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=agent-dispatch-carrier.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-dispatch-carrier.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/agent-dispatch-carrier.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routine-delivery-reconcile.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/routine-delivery-reconcile.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
/**
|
|
3
|
+
* Task 2776 — delivery established from the outbound store, and the standing
|
|
4
|
+
* reconciliation over it.
|
|
5
|
+
*
|
|
6
|
+
* Delivery is NOT read from the transcript: a routine that legitimately reports
|
|
7
|
+
* nothing and one that failed to send look identical there, which is the
|
|
8
|
+
* conflation this task removes. A store row exists only because a send was
|
|
9
|
+
* confirmed.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, vi } from "vitest";
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { reconcileRoutineDelivery, routineDeliveryCensus, OUTPUT_EXCERPT_CAP, DELIVERY_WINDOW_HOURS, } from "../routine-run.js";
|
|
15
|
+
/** Fixed clock, an hour after the fixture runs, so they sit inside the window. */
|
|
16
|
+
const NOW = Date.parse("2026-08-16T17:00:00Z");
|
|
17
|
+
/** A driver whose FIRST query returns `rows` and whose later queries are
|
|
18
|
+
* recorded as writes — the stub shape recordRunSession's tests already use. */
|
|
19
|
+
function deliveryDriver(rows) {
|
|
20
|
+
const writes = [];
|
|
21
|
+
let first = true;
|
|
22
|
+
return {
|
|
23
|
+
writes,
|
|
24
|
+
session: () => ({
|
|
25
|
+
run: vi.fn(async (_q, params) => {
|
|
26
|
+
if (first) {
|
|
27
|
+
first = false;
|
|
28
|
+
return { records: rows.map((r) => ({ get: (k) => r[k] })) };
|
|
29
|
+
}
|
|
30
|
+
writes.push(params);
|
|
31
|
+
return { records: [] };
|
|
32
|
+
}),
|
|
33
|
+
close: vi.fn(async () => { }),
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const run = (over = {}) => ({
|
|
38
|
+
runId: "r1",
|
|
39
|
+
sessionKey: "s1",
|
|
40
|
+
target: "telegram:8992591236",
|
|
41
|
+
eventId: "evt-1",
|
|
42
|
+
startedAt: "2026-08-16T16:10:00Z",
|
|
43
|
+
deliveredMessageId: null,
|
|
44
|
+
...over,
|
|
45
|
+
});
|
|
46
|
+
const sent = (over = {}) => ({
|
|
47
|
+
messageId: "telegram:acct:8711566959:8992591236:out:2026-08-16T16:10:05Z",
|
|
48
|
+
sessionId: "s1",
|
|
49
|
+
createdAt: "2026-08-16T16:10:05Z",
|
|
50
|
+
body: "the report",
|
|
51
|
+
...over,
|
|
52
|
+
});
|
|
53
|
+
function captureErrors() {
|
|
54
|
+
const lines = [];
|
|
55
|
+
const spy = vi
|
|
56
|
+
.spyOn(console, "error")
|
|
57
|
+
.mockImplementation((...a) => void lines.push(a.join(" ")));
|
|
58
|
+
return { lines, restore: () => spy.mockRestore() };
|
|
59
|
+
}
|
|
60
|
+
describe("reconcileRoutineDelivery", () => {
|
|
61
|
+
it("stamps deliveredMessageId and the excerpt from the run's own store rows", async () => {
|
|
62
|
+
const driver = deliveryDriver([run()]);
|
|
63
|
+
const res = await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
64
|
+
readRows: () => [sent()],
|
|
65
|
+
});
|
|
66
|
+
expect(res.stamped).toBe(1);
|
|
67
|
+
expect(driver.writes[0].deliveredMessageId).toBe(sent().messageId);
|
|
68
|
+
expect(driver.writes[0].outputExcerpt).toBe("the report");
|
|
69
|
+
});
|
|
70
|
+
it("leaves a run with no matching row unstamped", async () => {
|
|
71
|
+
const driver = deliveryDriver([run()]);
|
|
72
|
+
const res = await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
73
|
+
readRows: () => [],
|
|
74
|
+
});
|
|
75
|
+
expect(res.stamped).toBe(0);
|
|
76
|
+
expect(driver.writes).toEqual([]);
|
|
77
|
+
});
|
|
78
|
+
it("skips a run already stamped, so the pass is idempotent", async () => {
|
|
79
|
+
// A delivery arriving after promotion is picked up on a later tick rather
|
|
80
|
+
// than recorded as silence forever; a stamped run is never re-read.
|
|
81
|
+
const driver = deliveryDriver([run({ deliveredMessageId: "already" })]);
|
|
82
|
+
const res = await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
83
|
+
readRows: () => [sent()],
|
|
84
|
+
});
|
|
85
|
+
expect(res.stamped).toBe(0);
|
|
86
|
+
});
|
|
87
|
+
it("joins several rows into one excerpt and stamps the FIRST", async () => {
|
|
88
|
+
const driver = deliveryDriver([run()]);
|
|
89
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
90
|
+
readRows: () => [
|
|
91
|
+
sent({ messageId: "out-1", createdAt: "2026-08-16T16:10:05Z", body: "first" }),
|
|
92
|
+
sent({ messageId: "out-2", createdAt: "2026-08-16T16:10:06Z", body: "second" }),
|
|
93
|
+
],
|
|
94
|
+
});
|
|
95
|
+
expect(driver.writes[0].deliveredMessageId).toBe("out-1");
|
|
96
|
+
expect(driver.writes[0].outputExcerpt).toBe("first\n\nsecond");
|
|
97
|
+
});
|
|
98
|
+
it("caps the excerpt at OUTPUT_EXCERPT_CAP", async () => {
|
|
99
|
+
const driver = deliveryDriver([run()]);
|
|
100
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
101
|
+
readRows: () => [sent({ body: "x".repeat(3000) })],
|
|
102
|
+
});
|
|
103
|
+
expect(driver.writes[0].outputExcerpt.length).toBe(OUTPUT_EXCERPT_CAP);
|
|
104
|
+
});
|
|
105
|
+
it("bounds a run's window by the next run on the SAME session", async () => {
|
|
106
|
+
// Without the ceiling, run 1's window runs to end of time and both runs are
|
|
107
|
+
// credited with one answer.
|
|
108
|
+
const driver = deliveryDriver([
|
|
109
|
+
run({ runId: "r1", startedAt: "2026-08-16T16:10:00Z" }),
|
|
110
|
+
run({ runId: "r2", startedAt: "2026-08-16T16:15:00Z" }),
|
|
111
|
+
]);
|
|
112
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
113
|
+
readRows: () => [sent({ messageId: "late", createdAt: "2026-08-16T16:16:00Z" })],
|
|
114
|
+
});
|
|
115
|
+
expect(driver.writes).toHaveLength(1);
|
|
116
|
+
expect(driver.writes[0].runId).toBe("r2");
|
|
117
|
+
});
|
|
118
|
+
it("does not bound a run by the next run on a DIFFERENT session", async () => {
|
|
119
|
+
const driver = deliveryDriver([
|
|
120
|
+
run({ runId: "r1", sessionKey: "s1", startedAt: "2026-08-16T16:10:00Z" }),
|
|
121
|
+
run({ runId: "r2", sessionKey: "s2", startedAt: "2026-08-16T16:11:00Z" }),
|
|
122
|
+
]);
|
|
123
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
124
|
+
readRows: () => [sent({ sessionId: "s1", createdAt: "2026-08-16T16:12:00Z" })],
|
|
125
|
+
});
|
|
126
|
+
expect(driver.writes.map((w) => w.runId)).toEqual(["r1"]);
|
|
127
|
+
});
|
|
128
|
+
it("reads the store for the run's own destination", async () => {
|
|
129
|
+
const seen = [];
|
|
130
|
+
const driver = deliveryDriver([run()]);
|
|
131
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
132
|
+
readRows: (_root, _acct, dest) => {
|
|
133
|
+
seen.push(dest);
|
|
134
|
+
return [];
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
expect(seen).toEqual(["8992591236"]);
|
|
138
|
+
});
|
|
139
|
+
it("emits op=routine-delivered naming the event, the carrier and the chat", async () => {
|
|
140
|
+
const { lines, restore } = captureErrors();
|
|
141
|
+
const driver = deliveryDriver([run()]);
|
|
142
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, { readRows: () => [sent()] });
|
|
143
|
+
expect(lines.join("\n")).toContain("op=routine-delivered event=evt-1 run=r1");
|
|
144
|
+
// The carrier comes off the row id, so the line names the bot that ACTUALLY
|
|
145
|
+
// carried the message rather than the one the routine named — which is how a
|
|
146
|
+
// moved carrier becomes visible on the delivery itself.
|
|
147
|
+
expect(lines.join("\n")).toContain("botId=8711566959 chatId=8992591236");
|
|
148
|
+
restore();
|
|
149
|
+
});
|
|
150
|
+
it("never throws when the query fails", async () => {
|
|
151
|
+
const { lines, restore } = captureErrors();
|
|
152
|
+
const driver = {
|
|
153
|
+
session: () => ({
|
|
154
|
+
run: async () => {
|
|
155
|
+
throw new Error("down");
|
|
156
|
+
},
|
|
157
|
+
close: async () => { },
|
|
158
|
+
}),
|
|
159
|
+
};
|
|
160
|
+
await expect(reconcileRoutineDelivery(driver, "acct", "/root", NOW)).resolves.toEqual({
|
|
161
|
+
stamped: 0,
|
|
162
|
+
});
|
|
163
|
+
expect(lines.join("\n")).toContain("op=delivery-reconcile-error");
|
|
164
|
+
restore();
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
describe("routineDeliveryCensus", () => {
|
|
168
|
+
function censusDriver(row) {
|
|
169
|
+
return {
|
|
170
|
+
session: () => ({
|
|
171
|
+
run: vi.fn(async () => ({
|
|
172
|
+
records: row === null ? [] : [{ get: (k) => row[k] }],
|
|
173
|
+
})),
|
|
174
|
+
close: vi.fn(async () => { }),
|
|
175
|
+
}),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
it("counts delivered against accepted and names the silent events", async () => {
|
|
179
|
+
// `silent` is not an error on its own — a routine told to report only when
|
|
180
|
+
// something is new is silent by design — so the line names the events rather
|
|
181
|
+
// than alarming on the count.
|
|
182
|
+
const { lines, restore } = captureErrors();
|
|
183
|
+
const driver = censusDriver({ accepted: 3, delivered: 1, silentEvents: ["evt-a", "evt-b"] });
|
|
184
|
+
await routineDeliveryCensus(driver, "acct-1234-rest", 1_000_000_000_000);
|
|
185
|
+
expect(lines.join("\n")).toContain("[routine-delivery-census] accountId=acct-123 accepted=3 delivered=1 silent=2 window=24h");
|
|
186
|
+
expect(lines.join("\n")).toContain("silent-events=[evt-a,evt-b]");
|
|
187
|
+
restore();
|
|
188
|
+
});
|
|
189
|
+
it("omits silent-events when nothing is silent", async () => {
|
|
190
|
+
const { lines, restore } = captureErrors();
|
|
191
|
+
const driver = censusDriver({ accepted: 2, delivered: 2, silentEvents: [] });
|
|
192
|
+
await routineDeliveryCensus(driver, "acct-1234-rest", 1_000_000_000_000);
|
|
193
|
+
expect(lines.join("\n")).toContain("accepted=2 delivered=2 silent=0");
|
|
194
|
+
expect(lines.join("\n")).not.toContain("silent-events=");
|
|
195
|
+
restore();
|
|
196
|
+
});
|
|
197
|
+
it("emits its line on an account with no runs at all", async () => {
|
|
198
|
+
// Emitted every sweep including zero: a check that is silent when healthy
|
|
199
|
+
// cannot be told apart from one that stopped running.
|
|
200
|
+
const { lines, restore } = captureErrors();
|
|
201
|
+
await routineDeliveryCensus(censusDriver(null), "acct-1234-rest", 1_000_000_000_000);
|
|
202
|
+
expect(lines.join("\n")).toContain("accepted=0 delivered=0 silent=0");
|
|
203
|
+
restore();
|
|
204
|
+
});
|
|
205
|
+
it("reports rather than throws when the query fails", async () => {
|
|
206
|
+
const { lines, restore } = captureErrors();
|
|
207
|
+
const driver = {
|
|
208
|
+
session: () => ({
|
|
209
|
+
run: async () => {
|
|
210
|
+
throw new Error("down");
|
|
211
|
+
},
|
|
212
|
+
close: async () => { },
|
|
213
|
+
}),
|
|
214
|
+
};
|
|
215
|
+
await expect(routineDeliveryCensus(driver, "acct-1234-rest", 1_000_000_000_000)).resolves.toBeUndefined();
|
|
216
|
+
expect(lines.join("\n")).toContain("routine-delivery-census-error");
|
|
217
|
+
restore();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
// Task 2776 — the Cypher itself, pinned at source.
|
|
221
|
+
//
|
|
222
|
+
// The stubbed-driver tests above prove the line shape and the silent arithmetic,
|
|
223
|
+
// but they return fixed rows regardless of the query, so they cannot catch a
|
|
224
|
+
// wrong aggregate: replacing `count(r.deliveredMessageId)` with `count(r)` makes
|
|
225
|
+
// the census report every accepted wake as delivered and leaves all fourteen of
|
|
226
|
+
// them green. There is no Neo4j in the unit suite to execute the query against,
|
|
227
|
+
// so the clauses that carry the meaning are asserted here — the same
|
|
228
|
+
// source-assertion idiom `gate-dispatch-wiring.test.ts` uses for wiring it
|
|
229
|
+
// cannot execute.
|
|
230
|
+
describe("routineDeliveryCensus — the query's load-bearing clauses", () => {
|
|
231
|
+
const SOURCE = readFileSync(join(__dirname, "../routine-run.ts"), "utf-8");
|
|
232
|
+
const census = SOURCE.slice(SOURCE.indexOf("export async function routineDeliveryCensus"));
|
|
233
|
+
it("counts the PROPERTY, not the row — count(r) would report every wake delivered", () => {
|
|
234
|
+
expect(census).toContain("count(r.deliveredMessageId) AS delivered");
|
|
235
|
+
});
|
|
236
|
+
it("counts only accepted wakes, so a refused dispatch is never called silent", () => {
|
|
237
|
+
expect(census).toContain("r.dispatchResult = 'accepted'");
|
|
238
|
+
});
|
|
239
|
+
it("is scoped to telegram — a whatsapp run can never be stamped and would read silent forever", () => {
|
|
240
|
+
expect(census).toContain("r.target STARTS WITH 'telegram:'");
|
|
241
|
+
});
|
|
242
|
+
it("bounds by the window, which is what makes the change forward-only", () => {
|
|
243
|
+
expect(census).toContain("r.startedAt >= $since");
|
|
244
|
+
});
|
|
245
|
+
it("collects the silent events, because a bare count names nothing to act on", () => {
|
|
246
|
+
expect(census).toContain("WHEN r.deliveredMessageId IS NULL THEN r.eventId");
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
// Task 2776 — the reconciliation's own query.
|
|
250
|
+
describe("reconcileRoutineDelivery — the query's load-bearing clauses", () => {
|
|
251
|
+
const SOURCE = readFileSync(join(__dirname, "../routine-run.ts"), "utf-8");
|
|
252
|
+
const fn = SOURCE.slice(SOURCE.indexOf("export async function reconcileRoutineDelivery"), SOURCE.indexOf("export async function routineDeliveryCensus"));
|
|
253
|
+
it("orders by session then start, so each run's ceiling is its successor's start", () => {
|
|
254
|
+
expect(fn).toContain("ORDER BY r.sessionKey, r.startedAt");
|
|
255
|
+
});
|
|
256
|
+
it("selects runs of EVERY status — a run can deliver before promotion sees it", () => {
|
|
257
|
+
expect(fn).not.toContain("r.status =");
|
|
258
|
+
});
|
|
259
|
+
it("requires a sessionKey, which is the join key onto the store", () => {
|
|
260
|
+
expect(fn).toContain("r.sessionKey IS NOT NULL");
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
// Task 2776, review findings — the two bounds that keep this pass from being a
|
|
264
|
+
// backfill and from re-reading the store once per run.
|
|
265
|
+
describe("reconcileRoutineDelivery — bounded work", () => {
|
|
266
|
+
it("reads the store ONCE per destination, not once per run", async () => {
|
|
267
|
+
// Every run against one chat shares a destination. Reading inside the loop
|
|
268
|
+
// parsed the same files once per run and discarded all but one result.
|
|
269
|
+
let reads = 0;
|
|
270
|
+
const driver = deliveryDriver([
|
|
271
|
+
run({ runId: "r1", startedAt: "2026-08-16T16:10:00Z" }),
|
|
272
|
+
run({ runId: "r2", startedAt: "2026-08-16T16:15:00Z" }),
|
|
273
|
+
run({ runId: "r3", startedAt: "2026-08-16T16:20:00Z" }),
|
|
274
|
+
]);
|
|
275
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
276
|
+
readRows: () => {
|
|
277
|
+
reads++;
|
|
278
|
+
return [];
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
expect(reads).toBe(1);
|
|
282
|
+
});
|
|
283
|
+
it("reads once per DISTINCT destination when runs span several chats", async () => {
|
|
284
|
+
const seen = [];
|
|
285
|
+
const driver = deliveryDriver([
|
|
286
|
+
run({ runId: "r1", sessionKey: "s1", target: "telegram:111" }),
|
|
287
|
+
run({ runId: "r2", sessionKey: "s2", target: "telegram:222" }),
|
|
288
|
+
run({ runId: "r3", sessionKey: "s3", target: "telegram:111" }),
|
|
289
|
+
]);
|
|
290
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, {
|
|
291
|
+
readRows: (_r, _a, dest) => {
|
|
292
|
+
seen.push(dest);
|
|
293
|
+
return [];
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
expect(seen.sort()).toEqual(["111", "222"]);
|
|
297
|
+
});
|
|
298
|
+
it("bounds the selection by the delivery window, so it is not a backfill", async () => {
|
|
299
|
+
const driver = deliveryDriver([run()]);
|
|
300
|
+
let bound;
|
|
301
|
+
const spy = driver.session;
|
|
302
|
+
driver.session = () => {
|
|
303
|
+
const s = spy();
|
|
304
|
+
const inner = s.run;
|
|
305
|
+
s.run = (async (q, params) => {
|
|
306
|
+
if (typeof params.since === "string")
|
|
307
|
+
bound = params.since;
|
|
308
|
+
return inner(q, params);
|
|
309
|
+
});
|
|
310
|
+
return s;
|
|
311
|
+
};
|
|
312
|
+
await reconcileRoutineDelivery(driver, "acct", "/root", NOW, { readRows: () => [] });
|
|
313
|
+
expect(bound).toBe(new Date(NOW - DELIVERY_WINDOW_HOURS * 3600_000).toISOString());
|
|
314
|
+
});
|
|
315
|
+
it("uses the SAME window as the census, so the census cannot outrun it", () => {
|
|
316
|
+
// A reconciliation window shorter than the census's would report as silent a
|
|
317
|
+
// run this pass had already stopped trying to stamp — an alarm about the
|
|
318
|
+
// checker rather than about the routine.
|
|
319
|
+
const SOURCE = readFileSync(join(__dirname, "../routine-run.ts"), "utf-8");
|
|
320
|
+
const reconcile = SOURCE.slice(SOURCE.indexOf("export async function reconcileRoutineDelivery"), SOURCE.indexOf("export async function routineDeliveryCensus"));
|
|
321
|
+
const census = SOURCE.slice(SOURCE.indexOf("export async function routineDeliveryCensus"));
|
|
322
|
+
expect(reconcile).toContain("DELIVERY_WINDOW_HOURS");
|
|
323
|
+
expect(census).toContain("DELIVERY_WINDOW_HOURS");
|
|
324
|
+
expect(reconcile).toContain("r.startedAt >= $since");
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
//# sourceMappingURL=routine-delivery-reconcile.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routine-delivery-reconcile.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/routine-delivery-reconcile.test.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routine-delivery.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/routine-delivery.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
/**
|
|
3
|
+
* Task 2776 — the store join that establishes a routine's delivery.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, expect, it, beforeEach, afterEach, vi } from "vitest";
|
|
6
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { tmpdir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { selectDeliveredRows, readAgentRowsForDestination, } from "../routine-delivery.js";
|
|
10
|
+
const SK = "session-abc";
|
|
11
|
+
const row = (messageId, sessionId, createdAt, body = "hi") => ({
|
|
12
|
+
messageId,
|
|
13
|
+
sessionId,
|
|
14
|
+
createdAt,
|
|
15
|
+
body,
|
|
16
|
+
});
|
|
17
|
+
describe("selectDeliveredRows", () => {
|
|
18
|
+
it("keeps a row on this session inside the window", () => {
|
|
19
|
+
expect(selectDeliveredRows([row("a", SK, "2026-08-16T16:10:05Z")], SK, "2026-08-16T16:10:00Z", "2026-08-16T16:15:00Z")).toHaveLength(1);
|
|
20
|
+
});
|
|
21
|
+
it("drops a row belonging to another session on the same conversation", () => {
|
|
22
|
+
// The operator's own traffic shares this conversation. Without the session
|
|
23
|
+
// match, a routine that has been dead for a week would be credited with the
|
|
24
|
+
// operator's own answers.
|
|
25
|
+
expect(selectDeliveredRows([row("a", "other", "2026-08-16T16:10:05Z")], SK, "2026-08-16T16:10:00Z", null))
|
|
26
|
+
.toEqual([]);
|
|
27
|
+
});
|
|
28
|
+
it("drops a row written before the run started", () => {
|
|
29
|
+
expect(selectDeliveredRows([row("a", SK, "2026-08-16T16:09:59Z")], SK, "2026-08-16T16:10:00Z", null))
|
|
30
|
+
.toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
it("drops a row at the ceiling — it belongs to the next run", () => {
|
|
33
|
+
// Half-open at the top, for the reason promoteRoutineRuns bounds its own
|
|
34
|
+
// window: without it run N and run N+1 are both credited with one answer.
|
|
35
|
+
expect(selectDeliveredRows([row("a", SK, "2026-08-16T16:15:00Z")], SK, "2026-08-16T16:10:00Z", "2026-08-16T16:15:00Z")).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
it("keeps a row exactly at the run's start instant", () => {
|
|
38
|
+
expect(selectDeliveredRows([row("a", SK, "2026-08-16T16:10:00Z")], SK, "2026-08-16T16:10:00Z", null))
|
|
39
|
+
.toHaveLength(1);
|
|
40
|
+
});
|
|
41
|
+
it("keeps a late row when the window is still open", () => {
|
|
42
|
+
// Null ceiling means this is the newest run on the session: its window runs
|
|
43
|
+
// to now, so a slow turn is still attributed rather than read as silence.
|
|
44
|
+
expect(selectDeliveredRows([row("a", SK, "2026-08-16T23:00:00Z")], SK, "2026-08-16T16:10:00Z", null))
|
|
45
|
+
.toHaveLength(1);
|
|
46
|
+
});
|
|
47
|
+
it("returns rows oldest first, so the first is the delivery", () => {
|
|
48
|
+
const rows = [row("b", SK, "2026-08-16T16:10:09Z"), row("a", SK, "2026-08-16T16:10:05Z")];
|
|
49
|
+
expect(selectDeliveredRows(rows, SK, "2026-08-16T16:10:00Z", null).map((r) => r.messageId))
|
|
50
|
+
.toEqual(["a", "b"]);
|
|
51
|
+
});
|
|
52
|
+
it("returns [] for a run whose session sent nothing", () => {
|
|
53
|
+
expect(selectDeliveredRows([], SK, "2026-08-16T16:10:00Z", null)).toEqual([]);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("readAgentRowsForDestination", () => {
|
|
57
|
+
const ACCT = "acct-1";
|
|
58
|
+
let root;
|
|
59
|
+
let dir;
|
|
60
|
+
const agentRow = (messageId, sessionId) => JSON.stringify({
|
|
61
|
+
messageId,
|
|
62
|
+
sessionId,
|
|
63
|
+
createdAt: "2026-08-16T16:10:05Z",
|
|
64
|
+
body: "sent",
|
|
65
|
+
origin: "agent",
|
|
66
|
+
fromMe: true,
|
|
67
|
+
});
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
root = mkdtempSync(join(tmpdir(), "store-"));
|
|
70
|
+
vi.stubEnv("MAXY_DIR_OVERRIDE", root);
|
|
71
|
+
dir = join(root, "data", "telegram-messages", ACCT);
|
|
72
|
+
mkdirSync(dir, { recursive: true });
|
|
73
|
+
const inbound = JSON.stringify({
|
|
74
|
+
messageId: "in-1",
|
|
75
|
+
sessionId: SK,
|
|
76
|
+
createdAt: "2026-08-16T16:10:01Z",
|
|
77
|
+
body: "asked",
|
|
78
|
+
origin: "inbound",
|
|
79
|
+
fromMe: false,
|
|
80
|
+
});
|
|
81
|
+
writeFileSync(join(dir, "8711566959_8992591236.jsonl"), `${agentRow("out-1", SK)}\n${inbound}\n`);
|
|
82
|
+
writeFileSync(join(dir, "8804050655_8992591236.jsonl"), `${agentRow("out-2", SK)}\n`);
|
|
83
|
+
writeFileSync(join(dir, "8711566959_1311221864.jsonl"), `${agentRow("other-chat", SK)}\n`);
|
|
84
|
+
});
|
|
85
|
+
afterEach(() => {
|
|
86
|
+
vi.unstubAllEnvs();
|
|
87
|
+
rmSync(root, { recursive: true, force: true });
|
|
88
|
+
});
|
|
89
|
+
it("reads agent rows from EVERY bot's file for that destination", () => {
|
|
90
|
+
// Not just the expected carrier's file: a routine whose carrier moved
|
|
91
|
+
// delivered to a real chat on the wrong bot, and reading one file would
|
|
92
|
+
// record that as silence.
|
|
93
|
+
expect(readAgentRowsForDestination("/unused", ACCT, "8992591236").map((r) => r.messageId).sort())
|
|
94
|
+
.toEqual(["out-1", "out-2"]);
|
|
95
|
+
});
|
|
96
|
+
it("excludes inbound rows", () => {
|
|
97
|
+
expect(readAgentRowsForDestination("/unused", ACCT, "8992591236").map((r) => r.messageId))
|
|
98
|
+
.not.toContain("in-1");
|
|
99
|
+
});
|
|
100
|
+
it("excludes another conversation's rows", () => {
|
|
101
|
+
expect(readAgentRowsForDestination("/unused", ACCT, "8992591236").map((r) => r.messageId))
|
|
102
|
+
.not.toContain("other-chat");
|
|
103
|
+
});
|
|
104
|
+
it("does not match a destination that is only a suffix of another chat id", () => {
|
|
105
|
+
// `_1221864.jsonl` must not match the file for chat 1311221864.
|
|
106
|
+
writeFileSync(join(dir, "8711566959_11311221864.jsonl"), `${agentRow("longer", SK)}\n`);
|
|
107
|
+
expect(readAgentRowsForDestination("/unused", ACCT, "1311221864").map((r) => r.messageId))
|
|
108
|
+
.toEqual(["other-chat"]);
|
|
109
|
+
});
|
|
110
|
+
it("returns [] for an account with no store directory", () => {
|
|
111
|
+
expect(readAgentRowsForDestination("/unused", "absent-acct", "8992591236")).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
it("skips a malformed line rather than throwing", () => {
|
|
114
|
+
// The window is a byte range of a file being appended to, so a partial
|
|
115
|
+
// trailing line is normal, not exceptional.
|
|
116
|
+
writeFileSync(join(dir, "8711566959_8992591236.jsonl"), `{not json\n${agentRow("out-1", SK)}\n{"partial`);
|
|
117
|
+
expect(readAgentRowsForDestination("/unused", ACCT, "8992591236").map((r) => r.messageId).sort())
|
|
118
|
+
.toEqual(["out-1", "out-2"]);
|
|
119
|
+
});
|
|
120
|
+
it("skips a row missing the fields the join reads", () => {
|
|
121
|
+
writeFileSync(join(dir, "8711566959_8992591236.jsonl"), `${JSON.stringify({ messageId: "no-session", createdAt: "x", origin: "agent", fromMe: true })}\n`);
|
|
122
|
+
expect(readAgentRowsForDestination("/unused", ACCT, "8992591236").map((r) => r.messageId))
|
|
123
|
+
.toEqual(["out-2"]);
|
|
124
|
+
});
|
|
125
|
+
it("never throws on an unreadable store root", () => {
|
|
126
|
+
vi.stubEnv("MAXY_DIR_OVERRIDE", join(root, "does", "not", "exist"));
|
|
127
|
+
expect(() => readAgentRowsForDestination("/unused", ACCT, "8992591236")).not.toThrow();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=routine-delivery.test.js.map
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/routine-delivery.test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routine-delivery.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/routine-delivery.test.ts"],"names":[],"mappings":"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"}
|