switchroom 0.13.63 → 0.13.64
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/agent-scheduler/index.js +80 -80
- package/dist/auth-broker/index.js +80 -80
- package/dist/cli/drive-write-pretool.mjs +10 -10
- package/dist/cli/notion-write-pretool.mjs +82 -82
- package/dist/cli/skill-validate-pretool.mjs +72 -72
- package/dist/cli/switchroom.js +363 -365
- package/dist/host-control/main.js +148 -148
- package/dist/vault/approvals/kernel-server.js +82 -82
- package/dist/vault/broker/server.js +83 -83
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +112 -112
- package/telegram-plugin/dist/gateway/gateway.js +200 -193
- package/telegram-plugin/dist/server.js +160 -160
- package/telegram-plugin/gateway/gateway.ts +41 -8
- package/telegram-plugin/uat/scenarios/greeting-reply-dm.test.ts +48 -0
|
@@ -3192,6 +3192,26 @@ const ANSWER_STREAM_VISIBLE_ENABLED = (() => {
|
|
|
3192
3192
|
if (v === '0' || v === 'false' || v === 'off' || v === 'no') return false
|
|
3193
3193
|
return true
|
|
3194
3194
|
})()
|
|
3195
|
+
|
|
3196
|
+
// Draft-mirror preview (RFC docs/rfcs/draft-mirror-preview.md), Phase 1.
|
|
3197
|
+
// When enabled, the model's prose narration streams into the ephemeral
|
|
3198
|
+
// compose-area draft (sendMessageDraft) instead of a visible real
|
|
3199
|
+
// message — a live "what's it doing" preview that clears when the
|
|
3200
|
+
// reply lands. Default OFF (canary flag). When on it (a) forces the
|
|
3201
|
+
// answer-stream onto draft transport regardless of
|
|
3202
|
+
// ANSWER_STREAM_VISIBLE_ENABLED, and (b) suppresses the activity-summary
|
|
3203
|
+
// tool-count draft so the two don't collide on the single per-chat
|
|
3204
|
+
// draft slot. Delivery on a no-reply turn is owned by turn-flush
|
|
3205
|
+
// (decideTurnFlush → capturedText fresh send), NOT answer-stream
|
|
3206
|
+
// materialize() — which is dead on the draft-only path (streamMsgId
|
|
3207
|
+
// stays null, so its turn-end gate is false). Kill switch:
|
|
3208
|
+
// SWITCHROOM_DRAFT_MIRROR unset/0/false/off/no.
|
|
3209
|
+
const DRAFT_MIRROR_ENABLED = (() => {
|
|
3210
|
+
const raw = process.env.SWITCHROOM_DRAFT_MIRROR
|
|
3211
|
+
if (raw == null) return false
|
|
3212
|
+
const v = raw.trim().toLowerCase()
|
|
3213
|
+
return !(v === '0' || v === 'false' || v === 'off' || v === 'no')
|
|
3214
|
+
})()
|
|
3195
3215
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3196
3216
|
const progressDriver: any = null
|
|
3197
3217
|
const unpinProgressCardForChat: ((chatId: string, threadId: number | undefined) => void) | null = null
|
|
@@ -7110,7 +7130,13 @@ function handleSessionEvent(ev: SessionEvent): void {
|
|
|
7110
7130
|
// exactly once at a time and re-running until pending matches
|
|
7111
7131
|
// the last-sent. Captures `turn` so a late drain after turn-swap
|
|
7112
7132
|
// can't corrupt the next turn's atom.
|
|
7113
|
-
|
|
7133
|
+
// DRAFT_MIRROR (RFC draft-mirror-preview, Phase 1): the model's
|
|
7134
|
+
// prose narration owns the single per-chat draft slot. Suppress
|
|
7135
|
+
// the activity-summary tool-count draft so the two don't collide
|
|
7136
|
+
// (Telegram shows one draft per chat — the later write clobbers
|
|
7137
|
+
// the earlier). The activity-summary code stays intact for the
|
|
7138
|
+
// kill-switch path; it's retired for good only in Phase 4.
|
|
7139
|
+
if (!DRAFT_MIRROR_ENABLED && !turn.replyCalled && !isTelegramSurfaceTool(name)) {
|
|
7114
7140
|
const rendered = registerAndRender(turn.toolActivity, name)
|
|
7115
7141
|
if (rendered != null) {
|
|
7116
7142
|
turn.activityPendingRender = rendered
|
|
@@ -7158,13 +7184,20 @@ function handleSessionEvent(ev: SessionEvent): void {
|
|
|
7158
7184
|
chatId: turn.sessionChatId,
|
|
7159
7185
|
isPrivateChat: turn.isDm,
|
|
7160
7186
|
threadId: turn.sessionThreadId,
|
|
7161
|
-
//
|
|
7162
|
-
//
|
|
7163
|
-
//
|
|
7164
|
-
//
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7187
|
+
// Transport selection:
|
|
7188
|
+
// - DRAFT_MIRROR (RFC draft-mirror-preview, Phase 1): force
|
|
7189
|
+
// the ephemeral compose-area draft so narration is a
|
|
7190
|
+
// clears-on-reply preview. Wins over visible-answer-stream.
|
|
7191
|
+
// No-reply delivery is owned by turn-flush, not materialize.
|
|
7192
|
+
// - else #869-Phase1 visible-answer-stream: omit the draft
|
|
7193
|
+
// API so the lane edits a user-visible chat-timeline
|
|
7194
|
+
// message (minInitialChars:1 opens it on the first chunk).
|
|
7195
|
+
// - else legacy: draft transport.
|
|
7196
|
+
...(DRAFT_MIRROR_ENABLED
|
|
7197
|
+
? { sendMessageDraft: sendMessageDraftFn }
|
|
7198
|
+
: ANSWER_STREAM_VISIBLE_ENABLED
|
|
7199
|
+
? { minInitialChars: 1 }
|
|
7200
|
+
: { sendMessageDraft: sendMessageDraftFn }),
|
|
7168
7201
|
// #1075: route through robustApiCall so flood-wait,
|
|
7169
7202
|
// benign-400, and THREAD_NOT_FOUND are handled uniformly
|
|
7170
7203
|
// instead of crashing the answer-stream loop on a deleted
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Greeting reply scenario — driver DMs the test bot a bare "hi", bot
|
|
3
|
+
* MUST reply (not NO_REPLY).
|
|
4
|
+
*
|
|
5
|
+
* Regression gate for the v0.13.61 turn-pacing v3 over-correction: the
|
|
6
|
+
* "don't ack" directive made the model classify a bare greeting as
|
|
7
|
+
* "not substantive" and end the turn with NO_REPLY, leaving the user
|
|
8
|
+
* staring at silence. v4 (this fix) re-asserts "always reply to a
|
|
9
|
+
* direct message; a greeting gets a greeting."
|
|
10
|
+
*
|
|
11
|
+
* Runs against real Telegram. Same env requirements as
|
|
12
|
+
* smoke-dm-reply.test.ts. Invoke via `bun run test:uat greeting`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect } from "vitest";
|
|
16
|
+
import { spinUp } from "../harness.js";
|
|
17
|
+
|
|
18
|
+
describe("uat: greeting gets a reply (v4 turn-pacing regression gate)", () => {
|
|
19
|
+
it(
|
|
20
|
+
"driver DMs a bare 'hi' and the bot replies within 60s (not NO_REPLY)",
|
|
21
|
+
async () => {
|
|
22
|
+
const sc = await spinUp({ agent: "test-harness" });
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const started = Date.now();
|
|
26
|
+
await sc.sendDM("hi");
|
|
27
|
+
|
|
28
|
+
// A greeting should come back fast — well under the silence-poke
|
|
29
|
+
// soft window. 60s budget tolerates a cold turn but a NO_REPLY
|
|
30
|
+
// (the bug) would blow past it via the 300s framework fallback.
|
|
31
|
+
const reply = await sc.expectMessage(/.+/, {
|
|
32
|
+
from: "bot",
|
|
33
|
+
timeout: 60_000,
|
|
34
|
+
});
|
|
35
|
+
const elapsed = Date.now() - started;
|
|
36
|
+
|
|
37
|
+
expect(reply.text.length).toBeGreaterThan(0);
|
|
38
|
+
expect(reply.senderUserId).toBe(sc.botUserId);
|
|
39
|
+
// The bug path replies only after the 300s framework fallback.
|
|
40
|
+
// A real greeting reply lands fast; assert it beat the fallback.
|
|
41
|
+
expect(elapsed).toBeLessThan(60_000);
|
|
42
|
+
} finally {
|
|
43
|
+
await sc.tearDown();
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
90_000,
|
|
47
|
+
);
|
|
48
|
+
});
|