switchroom 0.16.28 → 0.16.38
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 +13 -2
- package/dist/auth-broker/index.js +13 -2
- package/dist/cli/notion-write-pretool.mjs +13 -2
- package/dist/cli/switchroom.js +60648 -59800
- package/dist/host-control/main.js +730 -18
- package/dist/vault/approvals/kernel-server.js +51 -37
- package/dist/vault/broker/server.js +72 -58
- package/package.json +2 -1
- package/profiles/_shared/agent-self-service.md.hbs +7 -2
- package/telegram-plugin/auth-snapshot-format.ts +173 -67
- package/telegram-plugin/auto-fallback-fleet.ts +3 -6
- package/telegram-plugin/card-format.ts +52 -2
- package/telegram-plugin/credits-watch.ts +4 -7
- package/telegram-plugin/dist/gateway/gateway.js +2855 -1034
- package/telegram-plugin/dist/server.js +0 -1
- package/telegram-plugin/format.ts +264 -7
- package/telegram-plugin/gateway/approval-card.ts +7 -14
- package/telegram-plugin/gateway/approvals-commands.ts +6 -9
- package/telegram-plugin/gateway/auth-command.ts +35 -38
- package/telegram-plugin/gateway/boot-card.ts +7 -2
- package/telegram-plugin/gateway/chat-id-fallback.ts +46 -0
- package/telegram-plugin/gateway/config-approval-handler.ts +6 -9
- package/telegram-plugin/gateway/diff-preview-card.ts +3 -6
- package/telegram-plugin/gateway/gateway.ts +1293 -97
- package/telegram-plugin/gateway/ipc-protocol.ts +66 -2
- package/telegram-plugin/gateway/ipc-server.ts +91 -1
- package/telegram-plugin/gateway/linear-activity.ts +2 -5
- package/telegram-plugin/gateway/obligation-turn-end.ts +27 -0
- package/telegram-plugin/gateway/permission-card-store.ts +104 -0
- package/telegram-plugin/gateway/permission-timeout.ts +25 -6
- package/telegram-plugin/gateway/status-pin-store.ts +302 -0
- package/telegram-plugin/gateway/turns-jsonl-rotate.ts +30 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +12 -1
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +35 -0
- package/telegram-plugin/gateway/vault-request-access-card.ts +61 -0
- package/telegram-plugin/history.ts +17 -7
- package/telegram-plugin/idle-footer.ts +2 -2
- package/telegram-plugin/issues-card.ts +12 -7
- package/telegram-plugin/model-unavailable.ts +3 -6
- package/telegram-plugin/operator-events.ts +4 -6
- package/telegram-plugin/quota-check.ts +2 -2
- package/telegram-plugin/quota-watch.ts +7 -10
- package/telegram-plugin/server.ts +3 -1
- package/telegram-plugin/status-pin-driver.ts +102 -0
- package/telegram-plugin/status-pin.ts +76 -0
- package/telegram-plugin/stream-reply-handler.ts +14 -1
- package/telegram-plugin/subagent-watcher.ts +6 -3
- package/telegram-plugin/tests/auth-command-format2.test.ts +6 -2
- package/telegram-plugin/tests/auth-command-vernacular.test.ts +18 -0
- package/telegram-plugin/tests/auth-snapshot-format.test.ts +204 -45
- package/telegram-plugin/tests/card-format.test.ts +51 -0
- package/telegram-plugin/tests/chat-id-fallback.test.ts +74 -0
- package/telegram-plugin/tests/codespan-escaping-golden.test.ts +166 -0
- package/telegram-plugin/tests/credits-watch.test.ts +18 -0
- package/telegram-plugin/tests/history.test.ts +38 -2
- package/telegram-plugin/tests/idle-footer.test.ts +53 -9
- package/telegram-plugin/tests/ipc-server-validate-rollout-status.test.ts +66 -0
- package/telegram-plugin/tests/issues-card.test.ts +24 -0
- package/telegram-plugin/tests/obligation-turn-end.test.ts +76 -0
- package/telegram-plugin/tests/paragraph-normalizer.test.ts +200 -1
- package/telegram-plugin/tests/permission-card-single-edit.test.ts +66 -0
- package/telegram-plugin/tests/permission-card-store.test.ts +83 -0
- package/telegram-plugin/tests/permission-timeout.test.ts +37 -6
- package/telegram-plugin/tests/permission-verdict-resume-guard.test.ts +23 -6
- package/telegram-plugin/tests/quota-check.test.ts +9 -2
- package/telegram-plugin/tests/quota-watch.test.ts +7 -7
- package/telegram-plugin/tests/rollout-status-wiring.test.ts +81 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +294 -0
- package/telegram-plugin/tests/status-pin-service-message-suppression.test.ts +210 -0
- package/telegram-plugin/tests/status-pin-store.test.ts +450 -0
- package/telegram-plugin/tests/status-pin.test.ts +202 -0
- package/telegram-plugin/tests/stream-reply-handler.test.ts +39 -0
- package/telegram-plugin/tests/telegram-format.test.ts +88 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +82 -24
- package/telegram-plugin/tests/turns-jsonl-rotate.test.ts +39 -0
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +12 -0
- package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +54 -0
- package/telegram-plugin/tests/vault-request-access-card.test.ts +94 -0
- package/telegram-plugin/tests/voice-normalize-text.test.ts +256 -0
- package/telegram-plugin/tests/voice-ondemand.test.ts +202 -0
- package/telegram-plugin/tests/voice-out-one-send.test.ts +153 -0
- package/telegram-plugin/tests/voice-synthesize-sidecar.test.ts +352 -0
- package/telegram-plugin/tests/voice-transcribe-sidecar.test.ts +332 -0
- package/telegram-plugin/tests/voice-transcribe.test.ts +188 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +28 -10
- package/telegram-plugin/tool-activity-summary.ts +11 -8
- package/telegram-plugin/uat/driver.ts +8 -1
- package/telegram-plugin/voice-normalize-text.ts +340 -0
- package/telegram-plugin/voice-ondemand.ts +138 -0
- package/telegram-plugin/voice-synthesize-sidecar.ts +259 -0
- package/telegram-plugin/voice-synthesize.ts +128 -0
- package/telegram-plugin/voice-transcribe-sidecar.ts +176 -0
- package/telegram-plugin/worker-activity-feed.ts +13 -4
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
AGENT_LIVE_WINDOW_MS,
|
|
52
52
|
AGENT_LIVE_POLL_INTERVAL_MS,
|
|
53
53
|
} from './boot-probes.js'
|
|
54
|
-
import { escapeMarkdown } from '../card-format.js'
|
|
54
|
+
import { escapeMarkdown, stackCardLines } from '../card-format.js'
|
|
55
55
|
import {
|
|
56
56
|
loadCache as loadBootIssueCache,
|
|
57
57
|
diffProbes as diffBootProbes,
|
|
@@ -468,7 +468,12 @@ export function renderBootCard(opts: RenderBootCardOpts): string {
|
|
|
468
468
|
sections.push('', ...configChangeRows)
|
|
469
469
|
}
|
|
470
470
|
if (sections.length === 1) return ack
|
|
471
|
-
|
|
471
|
+
// Stack rows with GFM hard breaks so the boot card's styled prose rows don't
|
|
472
|
+
// collapse onto one visual line in the rich-message renderer (#2669). A
|
|
473
|
+
// section entry may itself carry an internal newline (a multi-line `ack`, an
|
|
474
|
+
// update-outcome blob), so flatten to single lines first — see stackCardLines.
|
|
475
|
+
const flatLines = sections.flatMap((s) => s.split('\n'))
|
|
476
|
+
return stackCardLines(flatLines)
|
|
472
477
|
}
|
|
473
478
|
|
|
474
479
|
/**
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared chat_id allowlist fallback for the reply / stream_reply final-answer
|
|
3
|
+
* paths.
|
|
4
|
+
*
|
|
5
|
+
* Non-Claude models (Gemini via LiteLLM `sr-*` routing) sometimes pass a
|
|
6
|
+
* chat_id that fails the allowlist:
|
|
7
|
+
*
|
|
8
|
+
* - **Bug A (numeric coercion):** the model passes a JSON number, so the
|
|
9
|
+
* caller must `String(...)`-coerce before the allowlist compare (the
|
|
10
|
+
* allowlist is a string set). Without coercion `42 !== "42"` and every
|
|
11
|
+
* reply looks un-allowlisted.
|
|
12
|
+
* - **Bug B (executeReply fallback):** the raw value is wrong, so fall back
|
|
13
|
+
* to the *active* turn's already-validated `sessionChatId`.
|
|
14
|
+
* - **Bug D (lastActiveTurnChatId survives silence poke):** a ≥5-min silence
|
|
15
|
+
* poke fires `clearTurnStarted`, so `currentTurn` is null by the time the
|
|
16
|
+
* model finally replies. The active-turn tier is gone, but the last-known
|
|
17
|
+
* turn's chat still routes the answer correctly.
|
|
18
|
+
*
|
|
19
|
+
* These hotfixes shipped test-free; this helper isolates the pure decision so
|
|
20
|
+
* each bug gets a focused regression test.
|
|
21
|
+
*
|
|
22
|
+
* Returns the resolved chat_id (rawChatId unchanged when no valid fallback
|
|
23
|
+
* exists, so the caller's `assertAllowedChat` throws the human-readable error)
|
|
24
|
+
* plus the tier used, for logging. Pure — no module state read.
|
|
25
|
+
*/
|
|
26
|
+
export interface ChatIdAllowlist {
|
|
27
|
+
allowFrom: string[]
|
|
28
|
+
groups: Record<string, unknown>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveChatIdFallback(
|
|
32
|
+
rawChatId: string,
|
|
33
|
+
access: ChatIdAllowlist,
|
|
34
|
+
turnSessionChatId: string | undefined,
|
|
35
|
+
lastKnownChatId: string | undefined,
|
|
36
|
+
hasLiveTurn: boolean,
|
|
37
|
+
): { chatId: string; tier: 'raw' | 'active' | 'last-known' } {
|
|
38
|
+
if (access.allowFrom.includes(rawChatId) || rawChatId in access.groups) {
|
|
39
|
+
return { chatId: rawChatId, tier: 'raw' }
|
|
40
|
+
}
|
|
41
|
+
const fallbackChatId = turnSessionChatId ?? lastKnownChatId
|
|
42
|
+
if (fallbackChatId && (access.allowFrom.includes(fallbackChatId) || fallbackChatId in access.groups)) {
|
|
43
|
+
return { chatId: fallbackChatId, tier: hasLiveTurn ? 'active' : 'last-known' }
|
|
44
|
+
}
|
|
45
|
+
return { chatId: rawChatId, tier: 'raw' }
|
|
46
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// card, resolves the verdict back to hostd over IPC, and flips the card to
|
|
3
3
|
// a terminal state on finalize.
|
|
4
4
|
|
|
5
|
+
import { escapeMarkdown } from '../format.js';
|
|
5
6
|
import { randomBytes } from "node:crypto";
|
|
6
7
|
import type { IpcClient } from "./ipc-server.js";
|
|
7
8
|
import type {
|
|
@@ -180,10 +181,6 @@ const REASON_ELLIPSIS = "…";
|
|
|
180
181
|
*/
|
|
181
182
|
export const DIFF_SENTINEL = "\n[… diff continues, see attached file]";
|
|
182
183
|
|
|
183
|
-
function escapeMd(s: string): string {
|
|
184
|
-
return s.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
185
|
-
}
|
|
186
|
-
|
|
187
184
|
function clipReason(reason: string): string {
|
|
188
185
|
if (reason.length <= REASON_MAX_CHARS) return reason;
|
|
189
186
|
return reason.slice(0, REASON_MAX_CHARS - REASON_ELLIPSIS.length) +
|
|
@@ -214,7 +211,7 @@ export function buildConfigApprovalCardBody(args: {
|
|
|
214
211
|
const render = (diff: string): string =>
|
|
215
212
|
`🛠 **Config edit proposed**\n` +
|
|
216
213
|
`Agent: \`${args.agentName}\`\n` +
|
|
217
|
-
`Reason: ${
|
|
214
|
+
`Reason: ${escapeMarkdown(safeReason)}\n\n` +
|
|
218
215
|
"```\n" + diff + "\n```";
|
|
219
216
|
|
|
220
217
|
return truncateRawToFit({
|
|
@@ -447,8 +444,8 @@ export function buildLiveNote(affectedAgents?: string[], fleetWide?: boolean): s
|
|
|
447
444
|
}
|
|
448
445
|
const agents = (affectedAgents ?? []).filter((a) => typeof a === "string" && a.length > 0);
|
|
449
446
|
if (agents.length === 0) return "";
|
|
450
|
-
const list = agents.map(
|
|
451
|
-
const cmds = agents.map((a) => `/restart ${
|
|
447
|
+
const list = agents.map(escapeMarkdown).join(", ");
|
|
448
|
+
const cmds = agents.map((a) => `/restart ${escapeMarkdown(a)}`).join(" · ");
|
|
452
449
|
return `\n\n🔄 Not live until restart — affects: **${list}**\n${cmds}`;
|
|
453
450
|
}
|
|
454
451
|
|
|
@@ -476,8 +473,8 @@ export async function handleRequestConfigFinalize(
|
|
|
476
473
|
msg.outcome === "applied" ? buildLiveNote(msg.affectedAgents, msg.fleetWide) : "";
|
|
477
474
|
const body =
|
|
478
475
|
msg.outcome === "applied"
|
|
479
|
-
? `✅ **Applied**${msg.detail ? `\n${
|
|
480
|
-
: `⚠️ **Reconcile failed; rolled back**${msg.detail ? `\n${
|
|
476
|
+
? `✅ **Applied**${msg.detail ? `\n${escapeMarkdown(msg.detail)}` : ""}${liveNote}`
|
|
477
|
+
: `⚠️ **Reconcile failed; rolled back**${msg.detail ? `\n${escapeMarkdown(msg.detail)}` : ""}`;
|
|
481
478
|
try {
|
|
482
479
|
// Finalize is terminal — strip the keyboard so the buttons are gone.
|
|
483
480
|
await deps.editCard({
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* single-shot "do this edit now" intent.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import { escapeMarkdown } from '../format.js';
|
|
24
25
|
import { InlineKeyboard } from "grammy";
|
|
25
26
|
import type { DiffPreview } from "../../src/drive/diff-preview.js";
|
|
26
27
|
|
|
@@ -88,9 +89,9 @@ export function buildDiffPreviewCard(
|
|
|
88
89
|
// The 📍 + line-count rows are surfaced verbatim — they're
|
|
89
90
|
// wrapper-attested and the agent has no input into their content.
|
|
90
91
|
const bodyLines: string[] = [];
|
|
91
|
-
bodyLines.push(`**${
|
|
92
|
+
bodyLines.push(`**${escapeMarkdown(preview.title)}**`);
|
|
92
93
|
for (const line of preview.lines) {
|
|
93
|
-
bodyLines.push(
|
|
94
|
+
bodyLines.push(escapeMarkdown(line.text));
|
|
94
95
|
}
|
|
95
96
|
const text = bodyLines.join("\n");
|
|
96
97
|
|
|
@@ -161,7 +162,3 @@ export function buildDiffPreviewCard(
|
|
|
161
162
|
|
|
162
163
|
return { text, reply_markup: kb };
|
|
163
164
|
}
|
|
164
|
-
|
|
165
|
-
function escapeHtml(s: string): string {
|
|
166
|
-
return s.replace(/([\\`*_~=\[\]|])/g, "\\$1");
|
|
167
|
-
}
|