switchroom 0.13.65 → 0.14.1

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.
Files changed (30) hide show
  1. package/dist/agent-scheduler/index.js +80 -80
  2. package/dist/auth-broker/index.js +96 -81
  3. package/dist/cli/drive-write-pretool.mjs +10 -10
  4. package/dist/cli/notion-write-pretool.mjs +82 -82
  5. package/dist/cli/skill-validate-pretool.mjs +72 -72
  6. package/dist/cli/switchroom.js +1883 -1479
  7. package/dist/host-control/main.js +149 -149
  8. package/dist/vault/approvals/kernel-server.js +82 -82
  9. package/dist/vault/broker/server.js +83 -83
  10. package/package.json +1 -1
  11. package/profiles/_shared/telegram-style.md.hbs +1 -1
  12. package/telegram-plugin/auth-snapshot-format.ts +47 -1
  13. package/telegram-plugin/dist/bridge/bridge.js +112 -112
  14. package/telegram-plugin/dist/gateway/gateway.js +1226 -696
  15. package/telegram-plugin/dist/server.js +160 -160
  16. package/telegram-plugin/gateway/boot-card.ts +100 -0
  17. package/telegram-plugin/gateway/config-snapshot.ts +274 -0
  18. package/telegram-plugin/gateway/gateway.ts +256 -36
  19. package/telegram-plugin/operator-events.ts +2 -10
  20. package/telegram-plugin/quota-watch.ts +276 -0
  21. package/telegram-plugin/tests/auth-snapshot-format.test.ts +133 -1
  22. package/telegram-plugin/tests/boot-card-render.test.ts +93 -0
  23. package/telegram-plugin/tests/config-snapshot.test.ts +409 -0
  24. package/telegram-plugin/tests/operator-events.test.ts +12 -6
  25. package/telegram-plugin/tests/quota-watch.test.ts +366 -0
  26. package/telegram-plugin/tests/tool-activity-summary.test.ts +66 -0
  27. package/telegram-plugin/tests/turn-flush-safety.test.ts +48 -0
  28. package/telegram-plugin/tool-activity-summary.ts +137 -0
  29. package/telegram-plugin/turn-flush-safety.ts +47 -0
  30. package/telegram-plugin/uat/assertions.ts +4 -4
@@ -50,6 +50,48 @@ export function isSilentFlushMarker(text: string | undefined): boolean {
50
50
  return SILENT_MARKERS.has(trimmed.toUpperCase())
51
51
  }
52
52
 
53
+ // Trivial end-of-turn confirmations the model emits as terminal text after
54
+ // calling reply (e.g. "Sent." once the reply tool returns). On their own
55
+ // they're harmless; the danger is when they're glued to silent markers
56
+ // across Stop-hook re-prompt cycles into a composite blob like
57
+ // "Sent.\nNO_REPLY\nNO_REPLY" — which `isSilentFlushMarker` can't match
58
+ // (multi-line, over the length guard) so it leaks to chat. See
59
+ // `isCompositeSilentNoise`.
60
+ const TRIVIAL_CONFIRMATIONS = new Set(['SENT', 'DONE', 'OK', 'OKAY', 'ACK'])
61
+
62
+ function isTrivialConfirmationLine(line: string): boolean {
63
+ let t = line.trim()
64
+ if (t.length === 0 || t.length > 8) return false
65
+ if (/\W$/.test(t)) t = t.slice(0, -1) // strip a single trailing punct ("Sent.")
66
+ return TRIVIAL_CONFIRMATIONS.has(t.toUpperCase())
67
+ }
68
+
69
+ /**
70
+ * Recognise a multi-line composite that is *entirely* silent noise — every
71
+ * non-empty line is a silent marker (NO_REPLY / HEARTBEAT_OK) or a trivial
72
+ * confirmation ("Sent."), AND at least one line is a real silent marker.
73
+ *
74
+ * Backstop for the Stop-hook re-prompt leak: the model replies cleanly,
75
+ * emits a terminal "Sent.", gets re-prompted by the silent-end Stop hook,
76
+ * answers "NO_REPLY" one or more times, and the accumulated `capturedText`
77
+ * ("Sent.\nNO_REPLY\nNO_REPLY") flushes as a visible message because
78
+ * `isSilentFlushMarker` only matches a single sentinel. Requiring ≥1 hard
79
+ * marker keeps this conservative — a standalone "Sent." (no NO_REPLY) is NOT
80
+ * suppressed here, so we never silently drop a turn that wasn't already
81
+ * signalling "nothing to add".
82
+ */
83
+ export function isCompositeSilentNoise(text: string | undefined): boolean {
84
+ if (typeof text !== 'string') return false
85
+ const lines = text
86
+ .split('\n')
87
+ .map(l => l.trim())
88
+ .filter(l => l.length > 0)
89
+ if (lines.length === 0) return false
90
+ const hasMarker = lines.some(l => isSilentFlushMarker(l))
91
+ if (!hasMarker) return false
92
+ return lines.every(l => isSilentFlushMarker(l) || isTrivialConfirmationLine(l))
93
+ }
94
+
53
95
  export type FlushDecision =
54
96
  | { kind: 'flush'; text: string }
55
97
  | { kind: 'skip'; reason: FlushSkipReason }
@@ -115,6 +157,11 @@ export function decideTurnFlush(input: FlushDecisionInput): FlushDecision {
115
157
  const joined = input.capturedText.join('\n').trim()
116
158
  if (joined.length === 0) return { kind: 'skip', reason: 'empty-text' }
117
159
  if (isSilentFlushMarker(joined)) return { kind: 'skip', reason: 'silent-marker' }
160
+ // Composite silent noise — e.g. "Sent.\nNO_REPLY\nNO_REPLY" accumulated
161
+ // across Stop-hook re-prompt cycles. The single-sentinel check above
162
+ // misses it (multi-line, over the length guard); without this the blob
163
+ // leaks to chat as a visible message.
164
+ if (isCompositeSilentNoise(joined)) return { kind: 'skip', reason: 'silent-marker' }
118
165
  return { kind: 'flush', text: joined }
119
166
  }
120
167
 
@@ -337,10 +337,10 @@ export async function waitForCardPhase(
337
337
  /**
338
338
  * Detect the progress card's phase from its rendered text.
339
339
  *
340
- * The actual card render (telegram-plugin/progress-card.ts) uses
341
- * emoji markers in the header: `✅` for done, `❌` for errors, `⚙️`
342
- * while working (foreground), `🌀` for Background (parent done but
343
- * fleet still running, see #862 / status-card-design.md §Header),
340
+ * The actual card render uses emoji markers in the header: `✅` for
341
+ * done, `❌` for errors, `⚙️` while working (foreground), `🌀` for
342
+ * Background (parent done but fleet still running, see #862 /
343
+ * reference/conversational-pacing.md),
344
344
  * and `⏳` during the boot-card window. These markers are stable
345
345
  * enough to key on for UAT — finer parsing (checklist items,
346
346
  * sub-agent row content) is out of scope.