switchroom 0.14.39 → 0.14.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth-broker/index.js +294 -46
- package/dist/cli/drive-write-pretool.mjs +25 -1
- package/dist/cli/switchroom.js +63 -6
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +103 -11
- package/telegram-plugin/gateway/gateway.ts +81 -3
- package/telegram-plugin/gateway/inbound-delivery-confirm.ts +96 -0
- package/telegram-plugin/tests/inbound-delivery-confirm.test.ts +109 -0
- package/telegram-plugin/uat/scenarios/inbound-no-drop-rapid-fire-dm.test.ts +64 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* E2E regression — inbound is NEVER dropped under rapid fire (the drop-wedge).
|
|
3
|
+
*
|
|
4
|
+
* The bug: a Telegram inbound reaches claude as an MCP channel notification
|
|
5
|
+
* the unmodified CLI appends to its composer and auto-submits only when the
|
|
6
|
+
* composer is empty + idle. A message arriving the instant the prior turn
|
|
7
|
+
* completes races that auto-submit and strands unsubmitted — claude never
|
|
8
|
+
* starts the turn, the gateway sits "typing…", and the 300s silence-poke
|
|
9
|
+
* DROPS the message. Observed recurring on `marko` (supergroup topics + DMs).
|
|
10
|
+
*
|
|
11
|
+
* This scenario drives the exact failure timing: it fires each message the
|
|
12
|
+
* instant the prior reply lands (i.e. right at turn-completion, the strand
|
|
13
|
+
* window) and asserts EVERY message gets its own unique token back. With the
|
|
14
|
+
* deliver-until-acked queue (inbound-delivery-confirm.ts) a strand self-heals
|
|
15
|
+
* via re-delivery; without it, a stranded message yields NO reply within the
|
|
16
|
+
* timeout and this test fails on exactly the message that was swallowed.
|
|
17
|
+
*
|
|
18
|
+
* Each message carries a random token and the assertion matches THAT token,
|
|
19
|
+
* so a reply to message N-1 can never be mistaken for message N's reply — the
|
|
20
|
+
* test proves every distinct message was actually processed, not merely that
|
|
21
|
+
* "some replies came back".
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { describe, it, expect } from "vitest";
|
|
25
|
+
import { spinUp } from "../harness.js";
|
|
26
|
+
|
|
27
|
+
describe("uat: rapid-fire inbound — no message is ever dropped (drop-wedge)", () => {
|
|
28
|
+
it(
|
|
29
|
+
"every back-to-back message gets its own reply (delivery never strands)",
|
|
30
|
+
async () => {
|
|
31
|
+
const sc = await spinUp({ agent: "test-harness" });
|
|
32
|
+
try {
|
|
33
|
+
const N = 8;
|
|
34
|
+
const dropped: number[] = [];
|
|
35
|
+
for (let i = 1; i <= N; i++) {
|
|
36
|
+
const token = `acktok-${i}-${Math.random().toString(36).slice(2, 8)}`;
|
|
37
|
+
await sc.sendDM(
|
|
38
|
+
`Reply with exactly this token and nothing else: ${token}`,
|
|
39
|
+
);
|
|
40
|
+
try {
|
|
41
|
+
const reply = await sc.expectMessage((m) => m.text.includes(token), {
|
|
42
|
+
from: "bot",
|
|
43
|
+
timeout: 75_000,
|
|
44
|
+
});
|
|
45
|
+
expect(reply.text).toContain(token);
|
|
46
|
+
} catch {
|
|
47
|
+
// The message stranded — no reply carrying its token arrived.
|
|
48
|
+
dropped.push(i);
|
|
49
|
+
}
|
|
50
|
+
// Deliberately NO delay: fire the next message the instant this
|
|
51
|
+
// reply lands, so it arrives in the turn-completion strand window.
|
|
52
|
+
}
|
|
53
|
+
expect(
|
|
54
|
+
dropped,
|
|
55
|
+
`messages dropped (no reply within timeout): ${dropped.join(", ")} of ${N}`,
|
|
56
|
+
).toEqual([]);
|
|
57
|
+
} finally {
|
|
58
|
+
await sc.tearDown();
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
// N messages × (turn + up to one ~15-20s strand recovery) — generous.
|
|
62
|
+
900_000,
|
|
63
|
+
);
|
|
64
|
+
});
|