switchroom 0.18.17 → 0.18.19
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 -0
- package/dist/auth-broker/index.js +13 -0
- package/dist/cli/notion-write-pretool.mjs +13 -0
- package/dist/cli/switchroom.js +605 -479
- package/dist/host-control/main.js +17 -1
- package/dist/vault/approvals/kernel-server.js +13 -0
- package/dist/vault/broker/server.js +13 -0
- package/package.json +1 -1
- package/telegram-plugin/bridge/bridge.ts +7 -1
- package/telegram-plugin/dist/bridge/bridge.js +26 -1
- package/telegram-plugin/dist/gateway/gateway.js +1544 -619
- package/telegram-plugin/dist/server.js +32 -1
- package/telegram-plugin/fleet-fallback-resume.ts +26 -3
- package/telegram-plugin/format.ts +137 -213
- package/telegram-plugin/gateway/approval-hold.ts +49 -0
- package/telegram-plugin/gateway/bridge-dead-watchdog.ts +61 -18
- package/telegram-plugin/gateway/gateway.ts +399 -85
- package/telegram-plugin/gateway/linear-activity.ts +20 -4
- package/telegram-plugin/gateway/outbound-send-path.ts +9 -7
- package/telegram-plugin/gateway/premium-recovery-wiring.ts +122 -0
- package/telegram-plugin/gateway/session-model-file.ts +103 -0
- package/telegram-plugin/gateway/tier-downgrade-wiring.ts +121 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +14 -1
- package/telegram-plugin/llm-error-present.ts +474 -0
- package/telegram-plugin/operator-events.ts +7 -1
- package/telegram-plugin/permission-title.ts +172 -10
- package/telegram-plugin/premium-recovery.ts +101 -0
- package/telegram-plugin/raw-error-scrub.ts +73 -0
- package/telegram-plugin/retry-api-call.ts +8 -2
- package/telegram-plugin/send-gate-degraded.test.ts +152 -1
- package/telegram-plugin/send-gate-observability.test.ts +140 -0
- package/telegram-plugin/send-gate-observability.ts +65 -20
- package/telegram-plugin/send-gate.test.ts +143 -1
- package/telegram-plugin/send-gate.ts +212 -19
- package/telegram-plugin/session-tail.ts +16 -0
- package/telegram-plugin/shared/local-time.ts +69 -0
- package/telegram-plugin/stream-reply-handler.ts +5 -14
- package/telegram-plugin/tests/approval-hold-harness.ts +6 -6
- package/telegram-plugin/tests/approval-hold-outcome.test.ts +10 -2
- package/telegram-plugin/tests/bridge-dead-watchdog.test.ts +61 -0
- package/telegram-plugin/tests/fleet-fallback-resume.test.ts +39 -0
- package/telegram-plugin/tests/flood-windows-persistence.test.ts +3 -2
- package/telegram-plugin/tests/format-consistency.test.ts +68 -53
- package/telegram-plugin/tests/formatting-parse-regression.test.ts +5 -6
- package/telegram-plugin/tests/formatting-torture-set.ts +1 -1
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +26 -0
- package/telegram-plugin/tests/linear-create-issue.test.ts +30 -2
- package/telegram-plugin/tests/llm-error-present.test.ts +481 -0
- package/telegram-plugin/tests/outbound-send-path.test.ts +4 -3
- package/telegram-plugin/tests/paragraph-normalizer.test.ts +42 -100
- package/telegram-plugin/tests/permission-title.test.ts +167 -4
- package/telegram-plugin/tests/premium-recovery-wiring.test.ts +150 -0
- package/telegram-plugin/tests/premium-recovery.test.ts +165 -0
- package/telegram-plugin/tests/reaction-gate-routing.test.ts +6 -1
- package/telegram-plugin/tests/retry-api-call.test.ts +21 -0
- package/telegram-plugin/tests/stream-reply-handler.test.ts +9 -12
- package/telegram-plugin/tests/telegram-format.test.ts +86 -31
- package/telegram-plugin/tests/tier-downgrade-wiring.test.ts +165 -0
- package/telegram-plugin/tests/tier-downgrade.test.ts +141 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +17 -21
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +27 -1
- package/telegram-plugin/tests/worker-activity-feed.test.ts +5 -2
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +492 -0
- package/telegram-plugin/tier-downgrade.ts +198 -0
- package/telegram-plugin/tool-activity-summary.ts +99 -0
- package/telegram-plugin/turn-flush-safety.ts +4 -3
- package/telegram-plugin/worker-activity-feed.ts +509 -409
|
@@ -557,6 +557,105 @@ export function renderActivityFeedWithNested(
|
|
|
557
557
|
})
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
// ─── Combined multi-worker feed (coalesced one-message-per-chat) ────────────
|
|
561
|
+
//
|
|
562
|
+
// When 2+ background workers are live in the SAME chat/thread, they render into
|
|
563
|
+
// ONE shared message rather than one message each — so their edits form a
|
|
564
|
+
// single per-message edit stream under the send gate's 1/sec per-chat ceiling
|
|
565
|
+
// instead of N streams that contend and shed (#3084 per-chat bucket). Each
|
|
566
|
+
// worker is a compact two-line row (header + live → step); the body is capped
|
|
567
|
+
// at `maxRows` with a `+M more working…` spill so it stays compact/legible and
|
|
568
|
+
// under the rich-message wire ceiling (STATUS_CARD_CHAR_BUDGET). A single-worker
|
|
569
|
+
// chat still renders through
|
|
570
|
+
// `renderStatusCard` (the 🛠 Worker card) — this primitive owns the N≥2 case.
|
|
571
|
+
|
|
572
|
+
/** Dispatch-time task description cap for a combined-feed worker row. */
|
|
573
|
+
const COMBINED_ROW_DESC_MAX = 72
|
|
574
|
+
|
|
575
|
+
/** One live worker's render-relevant snapshot for the combined feed. */
|
|
576
|
+
export interface CombinedWorkerRow {
|
|
577
|
+
/** Dispatch-time task description (raw, unescaped). */
|
|
578
|
+
description: string
|
|
579
|
+
/** Wall-clock since dispatch, ms — climbs on every heartbeat re-render. */
|
|
580
|
+
elapsedMs: number
|
|
581
|
+
/** Tool calls observed so far. */
|
|
582
|
+
toolCount: number
|
|
583
|
+
/** The worker's latest step line (raw prose or friendly tool label). */
|
|
584
|
+
currentStep: string
|
|
585
|
+
/** Live model id (raw, e.g. `claude-opus-4-8`); omitted when unknown. */
|
|
586
|
+
model?: string
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export interface CombinedWorkerFeedOpts {
|
|
590
|
+
/** Max worker rows rendered before the `+M more working…` spill line.
|
|
591
|
+
* The overflow is ordered oldest-hidden-first (the newest/most-recently
|
|
592
|
+
* active workers stay visible). */
|
|
593
|
+
maxRows: number
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Render N≥1 live workers into ONE combined feed body (ready Telegram
|
|
598
|
+
* markdown; callers send verbatim — do NOT re-escape). Layout:
|
|
599
|
+
*
|
|
600
|
+
* 🛠 **Workers** · _N running_
|
|
601
|
+
* **{desc1}** _· {elapsed} · {n} tools_
|
|
602
|
+
* → _{step1}_
|
|
603
|
+
* **{desc2}** _· {elapsed} · {n} tools_
|
|
604
|
+
* → _{step2}_
|
|
605
|
+
* _+M more working…_
|
|
606
|
+
*
|
|
607
|
+
* Pure. Rows are rendered in the order supplied (the manager passes them
|
|
608
|
+
* dispatch-order, oldest first). `maxRows` caps the visible rows; the hidden
|
|
609
|
+
* remainder collapses to a single `+M more working…` line. A total-budget
|
|
610
|
+
* backstop drops the OLDEST visible rows one at a time (growing the spill)
|
|
611
|
+
* until the body fits STATUS_CARD_CHAR_BUDGET, so a burst of long descriptions
|
|
612
|
+
* can never overflow the wire limit. Returns null only when `rows` is empty.
|
|
613
|
+
*/
|
|
614
|
+
export function renderCombinedWorkerFeed(
|
|
615
|
+
rows: CombinedWorkerRow[],
|
|
616
|
+
opts: CombinedWorkerFeedOpts,
|
|
617
|
+
): string | null {
|
|
618
|
+
if (rows.length === 0) return null
|
|
619
|
+
const maxRows = Math.max(1, Math.floor(opts.maxRows))
|
|
620
|
+
|
|
621
|
+
const rowLines = (r: CombinedWorkerRow): [string, string] => {
|
|
622
|
+
const desc = escapeMarkdown(
|
|
623
|
+
truncate(stripMarkdown(r.description).replace(/\s+/g, ' ').trim() || 'background task', COMBINED_ROW_DESC_MAX),
|
|
624
|
+
)
|
|
625
|
+
const toolWord = r.toolCount === 1 ? 'tool' : 'tools'
|
|
626
|
+
const modelLabel = formatModelLabel(r.model)
|
|
627
|
+
const modelPart = modelLabel != null ? ` · ${escapeMarkdown(modelLabel)}` : ''
|
|
628
|
+
const header = `**${desc}** _· ${formatFeedElapsed(r.elapsedMs)} · ${r.toolCount} ${toolWord}${modelPart}_`
|
|
629
|
+
const stepClean = stripMarkdown(r.currentStep).replace(/\s+/g, ' ').trim()
|
|
630
|
+
const step =
|
|
631
|
+
stepClean.length > 0
|
|
632
|
+
? `→ _${escapeMarkdown(truncate(stepClean, STATUS_LINE_MAX))}_`
|
|
633
|
+
: `→ _starting…_`
|
|
634
|
+
return [header, step]
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const compose = (visibleCount: number): string => {
|
|
638
|
+
const shown = rows.slice(0, visibleCount)
|
|
639
|
+
const hidden = rows.length - shown.length
|
|
640
|
+
const out: string[] = [`🛠 **Workers** · _${rows.length} running_`]
|
|
641
|
+
for (const r of shown) {
|
|
642
|
+
const [h, s] = rowLines(r)
|
|
643
|
+
out.push(h, s)
|
|
644
|
+
}
|
|
645
|
+
if (hidden > 0) out.push(`_+${hidden} more working…_`)
|
|
646
|
+
return stackCardLines(out)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Cap to maxRows first, then shrink further only if the char budget demands.
|
|
650
|
+
let visible = Math.min(rows.length, maxRows)
|
|
651
|
+
let body = compose(visible)
|
|
652
|
+
while (body.length > STATUS_CARD_CHAR_BUDGET && visible > 1) {
|
|
653
|
+
visible -= 1
|
|
654
|
+
body = compose(visible)
|
|
655
|
+
}
|
|
656
|
+
return body
|
|
657
|
+
}
|
|
658
|
+
|
|
560
659
|
/**
|
|
561
660
|
* Like appendActivityLine, but for a pre-computed label (from the
|
|
562
661
|
* real-time PreToolUse sidecar / `tool_label` event) — the hook already
|
|
@@ -197,9 +197,10 @@ export function decideTurnFlush(input: FlushDecisionInput): FlushDecision {
|
|
|
197
197
|
// lone `\n` collapses adjacent blocks into one run — on the Bot API 10.1
|
|
198
198
|
// rich-markdown path (#2669) a single newline is a soft break, so the blocks
|
|
199
199
|
// render as an undifferentiated wall-of-text. `\n\n` is the GFM paragraph
|
|
200
|
-
// separator; the
|
|
201
|
-
//
|
|
202
|
-
//
|
|
200
|
+
// separator; the Bot API 10.1 rich renderer shows it as one visible blank
|
|
201
|
+
// line, so the paragraphs render with real separation on their own (the
|
|
202
|
+
// former NBSP spacer pass was removed in the #2669 follow-up — it added a
|
|
203
|
+
// spurious second blank line).
|
|
203
204
|
//
|
|
204
205
|
// The silent-marker guards below are unaffected by this change:
|
|
205
206
|
// isSilentFlushMarker length-guards the whole joined string; the composite /
|