switchroom 0.15.44 → 0.16.4

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 (150) hide show
  1. package/dist/agent-scheduler/index.js +122 -88
  2. package/dist/auth-broker/index.js +463 -177
  3. package/dist/cli/autoaccept-poll.js +4842 -35
  4. package/dist/cli/drive-write-pretool.mjs +17 -14
  5. package/dist/cli/notion-write-pretool.mjs +117 -86
  6. package/dist/cli/self-improve-apply-guard-pretool.mjs +626 -0
  7. package/dist/cli/self-improve-stop.mjs +428 -0
  8. package/dist/cli/skill-validate-pretool.mjs +72 -72
  9. package/dist/cli/switchroom.js +3249 -1241
  10. package/dist/cli/ui/index.html +1 -1
  11. package/dist/host-control/main.js +2833 -355
  12. package/dist/vault/approvals/kernel-server.js +7482 -7439
  13. package/dist/vault/broker/server.js +11315 -11272
  14. package/examples/minimal.yaml +1 -0
  15. package/examples/switchroom.yaml +1 -0
  16. package/package.json +3 -3
  17. package/profiles/_base/start.sh.hbs +88 -1
  18. package/profiles/_shared/execution-discipline.md.hbs +18 -0
  19. package/profiles/default/CLAUDE.md.hbs +3 -22
  20. package/telegram-plugin/.claude-plugin/plugin.json +2 -2
  21. package/telegram-plugin/answer-stream-flag.ts +12 -49
  22. package/telegram-plugin/answer-stream.ts +5 -150
  23. package/telegram-plugin/auth-snapshot-format.ts +280 -48
  24. package/telegram-plugin/auto-fallback-fleet.ts +44 -1
  25. package/telegram-plugin/context-exhaustion.ts +12 -0
  26. package/telegram-plugin/demo-mask.ts +154 -0
  27. package/telegram-plugin/dist/bridge/bridge.js +167 -124
  28. package/telegram-plugin/dist/gateway/gateway.js +3039 -1159
  29. package/telegram-plugin/dist/server.js +215 -172
  30. package/telegram-plugin/docs/waiting-ux-spec.md +2 -2
  31. package/telegram-plugin/draft-stream.ts +47 -410
  32. package/telegram-plugin/final-answer-detect.ts +17 -12
  33. package/telegram-plugin/fleet-fallback-resume.ts +131 -0
  34. package/telegram-plugin/format.ts +56 -19
  35. package/telegram-plugin/gateway/auth-add-flow.ts +332 -127
  36. package/telegram-plugin/gateway/auth-broker-client.ts +2 -2
  37. package/telegram-plugin/gateway/auth-command.ts +70 -14
  38. package/telegram-plugin/gateway/clean-shutdown-marker.ts +44 -0
  39. package/telegram-plugin/gateway/config-approval-handler.test.ts +91 -4
  40. package/telegram-plugin/gateway/config-approval-handler.ts +94 -13
  41. package/telegram-plugin/gateway/current-turn-map.ts +188 -0
  42. package/telegram-plugin/gateway/disconnect-flush.ts +3 -1
  43. package/telegram-plugin/gateway/effort-command.ts +8 -3
  44. package/telegram-plugin/gateway/emission-authority.ts +369 -0
  45. package/telegram-plugin/gateway/feed-open-gate.ts +292 -0
  46. package/telegram-plugin/gateway/gateway.ts +1837 -291
  47. package/telegram-plugin/gateway/inject-handler.test.ts +2 -1
  48. package/telegram-plugin/gateway/ms365-write-approval.test.ts +4 -4
  49. package/telegram-plugin/gateway/represent-guard.ts +72 -0
  50. package/telegram-plugin/gateway/status-surface-log.test.ts +5 -4
  51. package/telegram-plugin/gateway/status-surface-log.ts +14 -3
  52. package/telegram-plugin/history.ts +33 -11
  53. package/telegram-plugin/hooks/repo-context-pretool.mjs +26 -0
  54. package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +5 -0
  55. package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +8 -0
  56. package/telegram-plugin/hooks/tool-label-pretool.mjs +39 -15
  57. package/telegram-plugin/issues-card.ts +4 -0
  58. package/telegram-plugin/model-unavailable.ts +124 -0
  59. package/telegram-plugin/narrative-dedup.ts +69 -0
  60. package/telegram-plugin/over-ping-safety-net.ts +70 -4
  61. package/telegram-plugin/package.json +3 -3
  62. package/telegram-plugin/pending-work-progress.ts +12 -0
  63. package/telegram-plugin/permission-rule.ts +32 -5
  64. package/telegram-plugin/permission-title.ts +152 -9
  65. package/telegram-plugin/quota-check.ts +13 -0
  66. package/telegram-plugin/quota-watch.ts +135 -7
  67. package/telegram-plugin/registry/turns-schema.test.ts +24 -0
  68. package/telegram-plugin/registry/turns-schema.ts +9 -0
  69. package/telegram-plugin/runtime-metrics.ts +13 -0
  70. package/telegram-plugin/session-tail.ts +96 -11
  71. package/telegram-plugin/silence-poke.ts +170 -24
  72. package/telegram-plugin/slot-banner-driver.ts +3 -0
  73. package/telegram-plugin/status-no-truncate.ts +44 -0
  74. package/telegram-plugin/status-reactions.ts +20 -3
  75. package/telegram-plugin/stream-controller.ts +4 -23
  76. package/telegram-plugin/stream-reply-handler.ts +6 -24
  77. package/telegram-plugin/streaming-metrics.ts +91 -0
  78. package/telegram-plugin/subagent-watcher.ts +212 -66
  79. package/telegram-plugin/tests/activity-ever-opened-sticky.test.ts +47 -0
  80. package/telegram-plugin/tests/answer-stream-dedup.test.ts +9 -26
  81. package/telegram-plugin/tests/answer-stream-flag.test.ts +25 -58
  82. package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +41 -51
  83. package/telegram-plugin/tests/answer-stream.test.ts +2 -411
  84. package/telegram-plugin/tests/auth-add-flow.test.ts +488 -253
  85. package/telegram-plugin/tests/auth-command-format2.test.ts +71 -1
  86. package/telegram-plugin/tests/auth-snapshot-format.test.ts +376 -6
  87. package/telegram-plugin/tests/auto-fallback-fleet.test.ts +120 -0
  88. package/telegram-plugin/tests/cross-turn-card-gate.test.ts +424 -0
  89. package/telegram-plugin/tests/demo-mask.test.ts +127 -0
  90. package/telegram-plugin/tests/draft-stream.test.ts +0 -827
  91. package/telegram-plugin/tests/emission-authority-card-drain-gate.test.ts +236 -0
  92. package/telegram-plugin/tests/emission-authority-facade.test.ts +488 -0
  93. package/telegram-plugin/tests/emission-authority-open-gate.test.ts +179 -0
  94. package/telegram-plugin/tests/emission-authority-ping-gate.test.ts +395 -0
  95. package/telegram-plugin/tests/emission-determinism-wiring.test.ts +177 -0
  96. package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +146 -0
  97. package/telegram-plugin/tests/feed-open-gate.test.ts +259 -0
  98. package/telegram-plugin/tests/feed-survival.test.ts +526 -0
  99. package/telegram-plugin/tests/fleet-fallback-resume.test.ts +197 -0
  100. package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +117 -0
  101. package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +4 -11
  102. package/telegram-plugin/tests/history.test.ts +60 -0
  103. package/telegram-plugin/tests/model-unavailable.test.ts +118 -0
  104. package/telegram-plugin/tests/narrative-dedup.test.ts +118 -0
  105. package/telegram-plugin/tests/orphaned-reply-rearm.test.ts +285 -0
  106. package/telegram-plugin/tests/over-ping-final-answer-decoupling.test.ts +194 -0
  107. package/telegram-plugin/tests/over-ping-safety-net.test.ts +2 -2
  108. package/telegram-plugin/tests/per-topic-current-turn.test.ts +373 -0
  109. package/telegram-plugin/tests/permission-card-origin-kill-switch.test.ts +42 -0
  110. package/telegram-plugin/tests/permission-rule.test.ts +17 -0
  111. package/telegram-plugin/tests/permission-title.test.ts +206 -17
  112. package/telegram-plugin/tests/quota-watch.test.ts +252 -9
  113. package/telegram-plugin/tests/reply-terminal-reaction.test.ts +6 -1
  114. package/telegram-plugin/tests/repo-context-pretool.test.ts +62 -0
  115. package/telegram-plugin/tests/represent-guard.test.ts +162 -0
  116. package/telegram-plugin/tests/session-tail.test.ts +147 -3
  117. package/telegram-plugin/tests/silence-liveness-wiring.test.ts +18 -0
  118. package/telegram-plugin/tests/status-card-budget-parity.test.ts +72 -0
  119. package/telegram-plugin/tests/status-surface-log.test.ts +146 -0
  120. package/telegram-plugin/tests/subagent-watcher-clip-narrative.test.ts +58 -0
  121. package/telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts +102 -0
  122. package/telegram-plugin/tests/subagent-watcher-workflow-visibility.test.ts +225 -0
  123. package/telegram-plugin/tests/subagent-watcher.test.ts +147 -0
  124. package/telegram-plugin/tests/telegram-activity-visibility-integration.test.ts +597 -0
  125. package/telegram-plugin/tests/telegram-format.test.ts +101 -6
  126. package/telegram-plugin/tests/tool-activity-summary.test.ts +550 -15
  127. package/telegram-plugin/tests/tool-label-pretool.test.ts +73 -0
  128. package/telegram-plugin/tests/tool-label-sidecar.test.ts +44 -0
  129. package/telegram-plugin/tests/tool-labels.test.ts +67 -0
  130. package/telegram-plugin/tests/turn-liveness-floor.test.ts +196 -0
  131. package/telegram-plugin/tests/turn-liveness-invariant.test.ts +340 -0
  132. package/telegram-plugin/tests/welcome-text.test.ts +32 -3
  133. package/telegram-plugin/tests/worker-activity-feed.test.ts +470 -22
  134. package/telegram-plugin/tool-activity-summary.ts +375 -58
  135. package/telegram-plugin/turn-liveness-floor.ts +240 -0
  136. package/telegram-plugin/uat/assertions.ts +115 -0
  137. package/telegram-plugin/uat/driver.ts +68 -0
  138. package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +119 -133
  139. package/telegram-plugin/uat/scenarios/jtbd-answer-pings.test.ts +94 -0
  140. package/telegram-plugin/uat/scenarios/jtbd-cross-turn-card-dm.test.ts +109 -0
  141. package/telegram-plugin/uat/scenarios/jtbd-foreground-feed-thinkgap-dm.test.ts +478 -0
  142. package/telegram-plugin/uat/scenarios/jtbd-foreground-feed-visibility-dm.test.ts +396 -0
  143. package/telegram-plugin/uat/scenarios/jtbd-liveness-feed-open-dm.test.ts +202 -0
  144. package/telegram-plugin/uat/scenarios/jtbd-reply-is-last-dm.test.ts +202 -0
  145. package/telegram-plugin/uat/scenarios/reactions-dm.test.ts +93 -87
  146. package/telegram-plugin/welcome-text.ts +13 -1
  147. package/telegram-plugin/worker-activity-feed.ts +157 -82
  148. package/telegram-plugin/draft-transport.ts +0 -122
  149. package/telegram-plugin/tests/draft-retirement-wiring.test.ts +0 -82
  150. package/telegram-plugin/tests/draft-transport.test.ts +0 -211
@@ -695,12 +695,57 @@ describe('repairEscapedWhitespace', () => {
695
695
  expect(isLikelyTelegramHtml(repaired)).toBe(true)
696
696
  })
697
697
 
698
- test('leaves text alone when it already contains real newlines', () => {
699
- // If the caller provided real newlines, we trust them completely and
700
- // don't touch literal `\n` that may appear inside their content (e.g.
701
- // a regex or shell snippet).
702
- const input = 'Real newline here\nand a literal \\n in a regex example'
703
- expect(repairEscapedWhitespace(input)).toBe(input)
698
+ test('unescapes literal \\n in prose even when real newlines are present (mixed-message fix, #2456)', () => {
699
+ // The old implementation bailed entirely when any real newline was present.
700
+ // The new implementation unescapes literal \\n outside code spans regardless,
701
+ // so a mixed message (real newlines + stray literal \\n in prose) is repaired.
702
+ const input = 'Real newline here\nand a literal \\n escape in prose'
703
+ const out = repairEscapedWhitespace(input)
704
+ // The literal \n in prose becomes a real newline; the existing real newline is preserved.
705
+ expect(out).toBe('Real newline here\nand a literal \n escape in prose')
706
+ })
707
+
708
+ test('preserves literal \\n inside inline code span when mixed with real newlines (#2456)', () => {
709
+ // The structurally safe version: a \\n inside backticks must stay verbatim,
710
+ // even in a message that also has real newlines and literal \\n in prose.
711
+ const input = 'First line\nUse `grep -P \\n` for newlines\nand prose \\n here'
712
+ const out = repairEscapedWhitespace(input)
713
+ // Inline code span preserved verbatim (\\n inside backticks untouched).
714
+ expect(out).toContain('`grep -P \\n`')
715
+ // The literal \\n in prose is unescaped to a real newline.
716
+ expect(out).toContain('and prose \n here')
717
+ // The original real newlines are preserved.
718
+ expect(out).toContain('First line\n')
719
+ })
720
+
721
+ test('preserves literal \\n inside fenced code block (#2456)', () => {
722
+ // A fenced code block containing a literal \\n (e.g. a regex or shell snippet)
723
+ // must not be unescaped.
724
+ const input = 'Prose \\n here\n```bash\necho "line1\\nline2"\n```\nMore \\n prose'
725
+ const out = repairEscapedWhitespace(input)
726
+ // The \\n inside the fenced block stays verbatim.
727
+ expect(out).toContain('```bash\necho "line1\\nline2"\n```')
728
+ // The \\n in prose is unescaped.
729
+ expect(out).toContain('Prose \n here')
730
+ expect(out).toContain('More \n prose')
731
+ })
732
+
733
+ test('pure no-real-newline case still unescapes (backward compat)', () => {
734
+ // Original pre-#2456 case: message with NO real newlines and literal \\n.
735
+ const input = 'Line one\\nLine two\\nLine three'
736
+ expect(repairEscapedWhitespace(input)).toBe('Line one\nLine two\nLine three')
737
+ })
738
+
739
+ test('genuine escaped backslash (\\\\n) stays literal even in mixed-newline message (#2456)', () => {
740
+ // \\\\n in the source is the two-char sequence \\ then n — the user typed
741
+ // a backslash followed by the letter n, NOT a newline. The protect-\\
742
+ // phase must still work inside mixed messages.
743
+ const input = 'Windows path: C:\\\\temp\\\\file.txt\\nnext line\nreal newline too'
744
+ const out = repairEscapedWhitespace(input)
745
+ // The \\n becomes a real newline.
746
+ expect(out).toContain('C:\\temp\\file.txt\nnext line')
747
+ // The real newline is preserved.
748
+ expect(out).toContain('real newline too')
704
749
  })
705
750
 
706
751
  test('leaves single-line text alone when it has no escape sequences', () => {
@@ -740,6 +785,56 @@ describe('repairEscapedWhitespace', () => {
740
785
  // Literal \n must not survive anywhere.
741
786
  expect(html).not.toContain('\\n')
742
787
  })
788
+
789
+ // ── Sentinel-collision safety (reviewer blocker) ─────────────────────────
790
+
791
+ test('does not produce "undefined" when input contains NUL-byte sequences (#2456)', () => {
792
+ // A per-call random nonce makes sentinel collision statistically impossible.
793
+ // Confirm that a message containing NUL bytes (which could match a hardcoded
794
+ // sentinel) is passed through safely rather than emitting literal "undefined".
795
+ // We craft a string that would have matched the OLD hardcoded sentinel \x00REPMASK0\x00
796
+ // to prove it no longer causes corruption.
797
+ const dangerous = 'hello \x00REPMASK0\x00 world \\n end'
798
+ const out = repairEscapedWhitespace(dangerous)
799
+ // Must not produce the string "undefined" in output.
800
+ expect(out).not.toContain('undefined')
801
+ // The \\n in prose must be unescaped.
802
+ expect(out).toContain(' end')
803
+ expect(out).not.toContain('\\n')
804
+ })
805
+
806
+ // ── Unclosed fenced block (reviewer major) ────────────────────────────────
807
+
808
+ test('handles unclosed fenced block gracefully: \\n in trailing content is still unescaped', () => {
809
+ // An unclosed ``` is not matched by the fenced-block regex (it requires a
810
+ // closing ```). Content after the unmatched opening is treated as prose, so
811
+ // literal \\n there gets unescaped — which is the least-surprising outcome
812
+ // for malformed input (the alternative would be silently swallowing content).
813
+ const input = 'Prose \\n here\n```bash\necho "line1\\nline2"\n'
814
+ const out = repairEscapedWhitespace(input)
815
+ // \\n in prose before the unclosed fence is unescaped.
816
+ expect(out).toContain('Prose \n here')
817
+ // The function must not throw and must return a string.
818
+ expect(typeof out).toBe('string')
819
+ })
820
+
821
+ // ── \\t and CRLF in mixed-newline messages (reviewer gaps) ────────────────
822
+
823
+ test('unescapes \\t in prose even when real newlines are also present (#2456)', () => {
824
+ const input = 'First line\nCol1\\tCol2\\tCol3\nthird'
825
+ const out = repairEscapedWhitespace(input)
826
+ expect(out).toContain('Col1\tCol2\tCol3')
827
+ expect(out).toContain('First line\n')
828
+ expect(out).toContain('\nthird')
829
+ })
830
+
831
+ test('unescapes \\r in prose when real newlines are also present (#2456)', () => {
832
+ // A message that mixes real newlines and a literal \\r escape in prose.
833
+ const input = 'First line\nsome \\r carriage return in prose'
834
+ const out = repairEscapedWhitespace(input)
835
+ expect(out).toContain('some \r carriage return in prose')
836
+ expect(out).toContain('First line\n')
837
+ })
743
838
  })
744
839
 
745
840
  // ---------------------------------------------------------------------------