switchroom 0.21.10 → 0.21.12
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/cli/switchroom.js +143 -27
- package/dist/host-control/main.js +1 -1
- package/package.json +5 -2
- package/skills/dev-protocol/SKILL.md +14 -10
- package/skills/switchroom-release/SKILL.md +11 -5
- package/telegram-plugin/dist/gateway/gateway.js +385 -35
- package/telegram-plugin/format.ts +56 -0
- package/telegram-plugin/gateway/answer-route-overrides.ts +163 -0
- package/telegram-plugin/gateway/answer-thread-resolve.test.ts +175 -1
- package/telegram-plugin/gateway/answer-thread-resolve.ts +58 -6
- package/telegram-plugin/gateway/escalation-staleness.ts +526 -0
- package/telegram-plugin/gateway/gateway.ts +61 -68
- package/telegram-plugin/gateway/obligation-wiring.ts +91 -3
- package/telegram-plugin/gateway/outbound-send-path.ts +55 -1
- package/telegram-plugin/gateway/reply-route-log.test.ts +134 -0
- package/telegram-plugin/gateway/reply-route-log.ts +118 -0
- package/telegram-plugin/gateway/stream-render.ts +1 -1
- package/telegram-plugin/history.ts +21 -0
- package/telegram-plugin/registry/subagents-bugs.test.ts +3 -3
- package/telegram-plugin/render/html-fold.ts +22 -4
- package/telegram-plugin/render/parse.ts +9 -1
- package/telegram-plugin/render/render.ts +14 -13
- package/telegram-plugin/tests/answer-route-side-effect.test.ts +111 -0
- package/telegram-plugin/tests/catch-all-forwarded-history.test.ts +3 -3
- package/telegram-plugin/tests/escalation-staleness.test.ts +1275 -0
- package/telegram-plugin/tests/forwarded-rich-message-coalesce.test.ts +6 -6
- package/telegram-plugin/tests/forwarded-rich-message.test.ts +8 -8
- package/telegram-plugin/tests/history.test.ts +78 -0
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +29 -1
- package/telegram-plugin/tests/orphaned-db-sweep.test.ts +17 -1
- package/telegram-plugin/tests/render/html-dialect-content-loss.test.ts +76 -3
- package/telegram-plugin/tests/send-reply-golden.test.ts +192 -3
- package/telegram-plugin/tests/status-pin.test.ts +2 -2
- package/telegram-plugin/tests/subagent-handback-inbound-builder.test.ts +2 -2
- package/telegram-plugin/tests/subagent-progress-inbound-builder.test.ts +2 -2
- package/telegram-plugin/tests/telegram-format.test.ts +52 -0
- package/telegram-plugin/tests/turn-supersede-finalizes-prior-card.test.ts +1 -1
- package/telegram-plugin/tests/worker-origin-gap-dispatch.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-supergroup-reply-channel.test.ts +1 -1
- package/telegram-plugin/worker-activity-feed.ts +2 -2
|
@@ -20,10 +20,62 @@ import {
|
|
|
20
20
|
escapeMarkdown,
|
|
21
21
|
splitMarkdownChunks,
|
|
22
22
|
addParagraphSpacers,
|
|
23
|
+
codeFenceFor,
|
|
23
24
|
PARAGRAPH_SPACER,
|
|
24
25
|
RICH_MESSAGE_MAX_CHARS,
|
|
25
26
|
} from '../format.js'
|
|
26
27
|
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// codeFenceFor — the single shared fence-width rule (#4702 follow-up)
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
describe('codeFenceFor', () => {
|
|
33
|
+
test('defaults to three backticks when the body carries no long run', () => {
|
|
34
|
+
expect(codeFenceFor('')).toBe('```')
|
|
35
|
+
expect(codeFenceFor('plain body')).toBe('```')
|
|
36
|
+
expect(codeFenceFor('a ` b `` c')).toBe('```')
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('widens to one longer than the longest run in the body', () => {
|
|
40
|
+
expect(codeFenceFor('a\n```\nb')).toBe('````')
|
|
41
|
+
expect(codeFenceFor('a\n````\nb')).toBe('`````')
|
|
42
|
+
expect(codeFenceFor('` `` ``` ````')).toBe('`````')
|
|
43
|
+
expect(codeFenceFor('x```y')).toBe('````')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('the delimiter is strictly longer than every run in the body', () => {
|
|
47
|
+
// The property the wire depends on: with the open/close pair the only
|
|
48
|
+
// runs of that width, nothing in the body can close the fence early or
|
|
49
|
+
// leave one open.
|
|
50
|
+
for (const body of ['no ticks', '`', '```', '``````', 'a ``` b ````` c']) {
|
|
51
|
+
const fence = codeFenceFor(body)
|
|
52
|
+
for (const run of body.match(/`+/g) ?? []) {
|
|
53
|
+
expect(run.length).toBeLessThan(fence.length)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('does not blow the stack on a body with very many backtick runs', () => {
|
|
59
|
+
// The former `Math.max(0, ...runs.map(…))` form passed one argument per
|
|
60
|
+
// run and threw `RangeError: Maximum call stack size exceeded` past the
|
|
61
|
+
// engine's argument limit, from inside the render path.
|
|
62
|
+
//
|
|
63
|
+
// THE COUNT IS LOAD-BEARING and has to clear the argument limit of EVERY
|
|
64
|
+
// engine that runs this file, because the limit is engine-dependent and
|
|
65
|
+
// this file is collected by BOTH runners: `bun test` (the shipping
|
|
66
|
+
// runtime, via the `tests/` positional in scripts/bun-test-ci.sh) and
|
|
67
|
+
// vitest/Node. Measured on the pinned toolchain by bisecting the former
|
|
68
|
+
// spread form on this exact body shape — highest surviving run count is
|
|
69
|
+
// 638,621 on Bun 1.3.13 and 125,289 on Node 22.23.2. So the 200_000 this
|
|
70
|
+
// started at passed on Bun WITH the bug reinstated: it asserted nothing
|
|
71
|
+
// in the shipping runtime. 1M is ~1.57x the higher (Bun) limit, and the
|
|
72
|
+
// shipping loop scans the resulting 2.9 MB body in single-digit
|
|
73
|
+
// milliseconds, so the margin is nearly free.
|
|
74
|
+
const body = 'x `'.repeat(1_000_000)
|
|
75
|
+
expect(codeFenceFor(body)).toBe('```')
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
27
79
|
// ---------------------------------------------------------------------------
|
|
28
80
|
// escapeMarkdown — narrowed inline-special escaper (#2669)
|
|
29
81
|
// ---------------------------------------------------------------------------
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* card was never finalized, never unpinned (`fg:<statusKey>` stayed claimed
|
|
8
8
|
* forever), froze on its last landed edit, and left NO `turn-lifecycle clear`
|
|
9
9
|
* line to explain it. Proof from the field: carrie's turn
|
|
10
|
-
* `-
|
|
10
|
+
* `-1004444444444:_#1078` has a `turn-lifecycle set reason=enqueue` at
|
|
11
11
|
* 2026-07-28T18:13:37.554Z and no `clear` anywhere in the log; its stale pinned
|
|
12
12
|
* card was still on screen in the operator's screenshot.
|
|
13
13
|
*
|
|
@@ -47,7 +47,7 @@ import { decideSubagentHandback } from '../gateway/subagent-handback-inbound-bui
|
|
|
47
47
|
|
|
48
48
|
type Db = ReturnType<typeof openTurnsDbInMemory>
|
|
49
49
|
|
|
50
|
-
const TOPIC_CHAT = '-
|
|
50
|
+
const TOPIC_CHAT = '-1004444444444'
|
|
51
51
|
const TOPIC_THREAD = '77'
|
|
52
52
|
const OWNER_DM = '555'
|
|
53
53
|
|
|
@@ -35,7 +35,7 @@ import { expectMessage } from "../assertions.js";
|
|
|
35
35
|
|
|
36
36
|
const AGENT = "test-harness";
|
|
37
37
|
|
|
38
|
-
/** Bot API marked id of the test supergroup, e.g. -
|
|
38
|
+
/** Bot API marked id of the test supergroup, e.g. -1006666666666. */
|
|
39
39
|
const SUPERGROUP_ID = Number.parseInt(process.env.SWITCHROOM_UAT_CHAT_ID ?? "", 10);
|
|
40
40
|
|
|
41
41
|
/** A supergroup turn is a full inbound→claude→outbound round-trip; give
|
|
@@ -850,8 +850,8 @@ export function createWorkerActivityFeed(opts: WorkerActivityFeedOpts): WorkerAc
|
|
|
850
850
|
*
|
|
851
851
|
* The separator and the topic-less sentinel are both load-bearing. This used
|
|
852
852
|
* to be `` `${chatId} ${threadId ?? ''}` ``, which for a topic-less group
|
|
853
|
-
* rendered as `-
|
|
854
|
-
* became `"pinKey": "wk:group:-
|
|
853
|
+
* rendered as `-1004444444444 ` — a key ending in a bare space. On disk that
|
|
854
|
+
* became `"pinKey": "wk:group:-1004444444444 "`: trailing whitespace that
|
|
855
855
|
* survives JSON round-trips, is invisible in every log line and grep, and
|
|
856
856
|
* cannot be distinguished by eye from the same chat WITH a topic. Any operator
|
|
857
857
|
* or tool reconciling rows by chat would silently treat the two as one.
|