switchroom 0.7.15 → 0.10.0

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 (301) hide show
  1. package/README.md +51 -59
  2. package/bin/run-hook.sh +27 -11
  3. package/bin/timezone-hook.sh +9 -7
  4. package/dist/agent-scheduler/index.js +410 -133
  5. package/dist/auth-broker/index.js +13932 -0
  6. package/dist/cli/switchroom.js +26937 -5601
  7. package/dist/host-control/main.js +12702 -0
  8. package/dist/vault/approvals/kernel-server.js +467 -184
  9. package/dist/vault/broker/server.js +1430 -724
  10. package/examples/minimal.yaml +63 -0
  11. package/examples/personal-google-workspace-mcp/.env.example +34 -0
  12. package/examples/personal-google-workspace-mcp/README.md +194 -0
  13. package/examples/personal-google-workspace-mcp/compose.yaml +66 -0
  14. package/examples/switchroom.yaml +220 -0
  15. package/package.json +7 -4
  16. package/profiles/_base/settings.json.hbs +20 -5
  17. package/profiles/_base/start.sh.hbs +16 -3
  18. package/profiles/_shared/agent-self-service.md.hbs +126 -0
  19. package/profiles/_shared/telegram-style.md.hbs +20 -90
  20. package/profiles/_shared/vault-protocol.md.hbs +68 -0
  21. package/profiles/default/CLAUDE.md +50 -96
  22. package/profiles/default/CLAUDE.md.hbs +36 -6
  23. package/profiles/default/workspace/SOUL.md.hbs +12 -5
  24. package/skills/buildkite-agent-infrastructure/SKILL.md +30 -11
  25. package/skills/buildkite-agent-runtime/SKILL.md +44 -11
  26. package/skills/buildkite-api/SKILL.md +31 -8
  27. package/skills/buildkite-cli/SKILL.md +27 -9
  28. package/skills/buildkite-migration/SKILL.md +22 -9
  29. package/skills/buildkite-pipelines/SKILL.md +26 -9
  30. package/skills/buildkite-secure-delivery/SKILL.md +23 -9
  31. package/skills/buildkite-test-engine/SKILL.md +25 -8
  32. package/skills/docx/SKILL.md +1 -1
  33. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
  34. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
  35. package/skills/file-bug/SKILL.md +34 -6
  36. package/skills/humanizer/SKILL.md +15 -0
  37. package/skills/humanizer-calibrate/SKILL.md +7 -1
  38. package/skills/mcp-builder/SKILL.md +1 -1
  39. package/skills/pdf/SKILL.md +1 -1
  40. package/skills/pptx/SKILL.md +1 -1
  41. package/skills/skill-creator/SKILL.md +21 -1
  42. package/skills/skill-creator/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  43. package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-313.pyc +0 -0
  44. package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-313.pyc +0 -0
  45. package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-313.pyc +0 -0
  46. package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-313.pyc +0 -0
  47. package/skills/skill-creator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  48. package/skills/switchroom-cli/SKILL.md +63 -64
  49. package/skills/switchroom-health/SKILL.md +23 -10
  50. package/skills/switchroom-install/SKILL.md +3 -3
  51. package/skills/switchroom-manage/SKILL.md +26 -19
  52. package/skills/switchroom-runtime/SKILL.md +191 -0
  53. package/skills/switchroom-status/SKILL.md +27 -2
  54. package/skills/telegram-test-harness/SKILL.md +3 -0
  55. package/skills/token-helpers/SKILL.md +24 -1
  56. package/skills/webapp-testing/SKILL.md +31 -1
  57. package/skills/xlsx/SKILL.md +1 -1
  58. package/telegram-plugin/admin-commands/index.ts +7 -5
  59. package/telegram-plugin/analytics-posthog.ts +191 -0
  60. package/telegram-plugin/bridge/bridge.ts +69 -0
  61. package/telegram-plugin/bridge/ipc-client.ts +4 -1
  62. package/telegram-plugin/dist/bridge/bridge.js +194 -119
  63. package/telegram-plugin/dist/gateway/gateway.js +23611 -19671
  64. package/telegram-plugin/dist/server.js +245 -189
  65. package/telegram-plugin/first-paint.ts +3 -24
  66. package/telegram-plugin/gateway/auth-add-flow.ts +326 -0
  67. package/telegram-plugin/gateway/auth-broker-client.ts +75 -0
  68. package/telegram-plugin/gateway/auth-command.ts +794 -0
  69. package/telegram-plugin/gateway/auth-line.ts +123 -0
  70. package/telegram-plugin/gateway/boot-card.ts +169 -40
  71. package/telegram-plugin/gateway/boot-issue-cache.ts +308 -0
  72. package/telegram-plugin/gateway/boot-probes.ts +166 -123
  73. package/telegram-plugin/gateway/boot-reason.ts +41 -7
  74. package/telegram-plugin/gateway/boot-version.ts +66 -0
  75. package/telegram-plugin/gateway/gateway.ts +3499 -1885
  76. package/telegram-plugin/gateway/hostd-dispatch.ts +117 -0
  77. package/telegram-plugin/gateway/ipc-protocol.ts +18 -0
  78. package/telegram-plugin/gateway/pending-inbound-buffer.ts +106 -0
  79. package/telegram-plugin/gateway/quarantine.ts +69 -0
  80. package/telegram-plugin/gateway/quota-cache.ts +9 -4
  81. package/telegram-plugin/gateway/reaction-trigger.ts +401 -0
  82. package/telegram-plugin/gateway/recent-denials.test.ts +103 -0
  83. package/telegram-plugin/gateway/recent-denials.ts +77 -0
  84. package/telegram-plugin/gateway/startup-network-retry.ts +109 -31
  85. package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +125 -0
  86. package/telegram-plugin/history.ts +91 -0
  87. package/telegram-plugin/hooks/hooks.json +10 -0
  88. package/telegram-plugin/hooks/sandbox-hint-posttool.mjs +130 -0
  89. package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +19 -2
  90. package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +22 -2
  91. package/telegram-plugin/hooks/tool-label-pretool.mjs +11 -0
  92. package/telegram-plugin/hooks/wedge-detect-posttool.mjs +303 -0
  93. package/telegram-plugin/inbound-classifier.ts +50 -0
  94. package/telegram-plugin/inline-keyboard-callbacks.ts +136 -0
  95. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  96. package/telegram-plugin/package.json +4 -2
  97. package/telegram-plugin/permission-rule.ts +51 -0
  98. package/telegram-plugin/permission-title.ts +56 -0
  99. package/telegram-plugin/quota-check.ts +19 -41
  100. package/telegram-plugin/registry/reaper.ts +223 -0
  101. package/telegram-plugin/retry-api-call.ts +80 -0
  102. package/telegram-plugin/runtime-metrics.ts +177 -0
  103. package/telegram-plugin/scripts/build.mjs +0 -1
  104. package/telegram-plugin/secret-detect/index.ts +24 -0
  105. package/telegram-plugin/secret-detect/vault-error.test.ts +64 -12
  106. package/telegram-plugin/secret-detect/vault-error.ts +78 -11
  107. package/telegram-plugin/secret-detect/vault-write.ts +14 -2
  108. package/telegram-plugin/server.js +41795 -0
  109. package/telegram-plugin/session-tail.ts +6 -1
  110. package/telegram-plugin/shared/bot-runtime.ts +5 -4
  111. package/telegram-plugin/silence-poke.ts +420 -0
  112. package/telegram-plugin/silent-end.ts +174 -0
  113. package/telegram-plugin/stream-controller.ts +13 -0
  114. package/telegram-plugin/stream-reply-handler.ts +7 -0
  115. package/telegram-plugin/subagent-watcher.ts +213 -4
  116. package/telegram-plugin/tests/auth-add-flow.test.ts +559 -0
  117. package/telegram-plugin/tests/auth-code-redact.test.ts +8 -4
  118. package/telegram-plugin/tests/auth-command-vernacular.test.ts +531 -0
  119. package/telegram-plugin/tests/boot-card-issue-dedup.test.ts +247 -0
  120. package/telegram-plugin/tests/boot-card-reason-to-render.test.ts +182 -0
  121. package/telegram-plugin/tests/boot-card-reason.test.ts +65 -2
  122. package/telegram-plugin/tests/boot-card-render.test.ts +146 -0
  123. package/telegram-plugin/tests/boot-card-silent-on-operator.test.ts +103 -0
  124. package/telegram-plugin/tests/boot-probes.test.ts +216 -10
  125. package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
  126. package/telegram-plugin/tests/finalize-callback.test.ts +190 -0
  127. package/telegram-plugin/tests/gateway-message-validator.test.ts +26 -0
  128. package/telegram-plugin/tests/gateway-secret-detect.test.ts +12 -3
  129. package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +104 -0
  130. package/telegram-plugin/tests/history-reaper.test.ts +378 -0
  131. package/telegram-plugin/tests/hostd-dispatch.test.ts +129 -0
  132. package/telegram-plugin/tests/inbound-classifier.test.ts +76 -0
  133. package/telegram-plugin/tests/inbound-message-types.test.ts +267 -0
  134. package/telegram-plugin/tests/issues-card.test.ts +49 -0
  135. package/telegram-plugin/tests/pending-inbound-buffer.test.ts +132 -0
  136. package/telegram-plugin/tests/permission-rule.test.ts +80 -1
  137. package/telegram-plugin/tests/permission-title.test.ts +31 -0
  138. package/telegram-plugin/tests/quota-check.test.ts +5 -35
  139. package/telegram-plugin/tests/races.test.ts +179 -0
  140. package/telegram-plugin/tests/reaction-trigger-flow.test.ts +353 -0
  141. package/telegram-plugin/tests/reaction-trigger.test.ts +397 -0
  142. package/telegram-plugin/tests/retry-api-call.test.ts +152 -1
  143. package/telegram-plugin/tests/runtime-metrics.test.ts +145 -0
  144. package/telegram-plugin/tests/sandbox-hint-posttool.test.ts +155 -0
  145. package/telegram-plugin/tests/secret-detect-delete-must-surface-failures.test.ts +133 -0
  146. package/telegram-plugin/tests/secret-detect-false-positives.test.ts +137 -0
  147. package/telegram-plugin/tests/silence-poke.test.ts +493 -0
  148. package/telegram-plugin/tests/silent-end.test.ts +206 -0
  149. package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +107 -0
  150. package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +224 -0
  151. package/telegram-plugin/tests/subagent-watcher-stall-terminal.test.ts +316 -0
  152. package/telegram-plugin/tests/subagent-watcher.test.ts +263 -0
  153. package/telegram-plugin/tests/turn-signal-tracker.test.ts +81 -0
  154. package/telegram-plugin/tests/vault-approval-posture.test.ts +256 -0
  155. package/telegram-plugin/tests/vault-grant-auto-resume.test.ts +73 -0
  156. package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +226 -0
  157. package/telegram-plugin/tests/vault-grant-union.test.ts +130 -0
  158. package/telegram-plugin/tests/vault-key-regex-allows-slash.test.ts +140 -0
  159. package/telegram-plugin/tests/vault-posture-quarantine.test.ts +104 -0
  160. package/telegram-plugin/tests/vault-request-access-tool.test.ts +114 -0
  161. package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +106 -0
  162. package/telegram-plugin/turn-signal-tracker.ts +100 -24
  163. package/telegram-plugin/uat/SETUP.md +210 -35
  164. package/telegram-plugin/uat/assertions.ts +264 -37
  165. package/telegram-plugin/uat/driver-info.ts +57 -0
  166. package/telegram-plugin/uat/driver.ts +590 -51
  167. package/telegram-plugin/uat/harness.ts +140 -94
  168. package/telegram-plugin/uat/load-env.test.ts +72 -0
  169. package/telegram-plugin/uat/load-env.ts +48 -0
  170. package/telegram-plugin/uat/login.ts +96 -53
  171. package/telegram-plugin/uat/runners/agent-self-sufficiency.ts +457 -0
  172. package/telegram-plugin/uat/runners/paraphrases.ts +231 -0
  173. package/telegram-plugin/uat/runners/report.ts +150 -0
  174. package/telegram-plugin/uat/runners/run-agent-self-sufficiency.sh +50 -0
  175. package/telegram-plugin/uat/runners/scorer.test.ts +196 -0
  176. package/telegram-plugin/uat/runners/scorer.ts +106 -0
  177. package/telegram-plugin/uat/runners/skill-coverage.test.ts +100 -0
  178. package/telegram-plugin/uat/runners/skill-coverage.ts +620 -0
  179. package/telegram-plugin/uat/scenarios/ask-user-button-tap-dm.test.ts +141 -0
  180. package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +191 -0
  181. package/telegram-plugin/uat/scenarios/fuzz-extended-dm.test.ts +255 -0
  182. package/telegram-plugin/uat/scenarios/fuzz-human-style-dm.test.ts +275 -0
  183. package/telegram-plugin/uat/scenarios/fuzz-random-prompts-dm.test.ts +146 -0
  184. package/telegram-plugin/uat/scenarios/fuzz-status-ask-dm.test.ts +486 -0
  185. package/telegram-plugin/uat/scenarios/jtbd-interrupt-marker-dm.test.ts +67 -0
  186. package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +100 -0
  187. package/telegram-plugin/uat/scenarios/jtbd-soft-commit-dm.test.ts +67 -0
  188. package/telegram-plugin/uat/scenarios/jtbd-status-query-dm.test.ts +49 -0
  189. package/telegram-plugin/uat/scenarios/location-inbound-dm.test.ts +65 -0
  190. package/telegram-plugin/uat/scenarios/midturn-silent-dm.test.ts +175 -0
  191. package/telegram-plugin/uat/scenarios/reactions-dm.test.ts +142 -0
  192. package/telegram-plugin/uat/scenarios/reactions-trigger-turn-dm.test.ts +96 -0
  193. package/telegram-plugin/uat/scenarios/secret-redaction-deletes-original-dm.test.ts +123 -0
  194. package/telegram-plugin/uat/scenarios/secret-redaction-no-false-positive-dm.test.ts +87 -0
  195. package/telegram-plugin/uat/scenarios/silence-poke-soft-dm.test.ts +155 -0
  196. package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +95 -0
  197. package/telegram-plugin/uat/scenarios/smoke-dm-reply.test.ts +57 -0
  198. package/telegram-plugin/uat/scenarios/subagent-watcher-no-rerun-dm.test.ts +135 -0
  199. package/telegram-plugin/uat/scenarios/vault-approval-posture-telegram-id-dm.test.ts +191 -0
  200. package/telegram-plugin/uat/scenarios/vault-audit-allow-dm.test.ts +108 -0
  201. package/telegram-plugin/uat/scenarios/vault-grant-auto-resume-dm.test.ts +121 -0
  202. package/telegram-plugin/uat/scenarios/vault-request-access-concurrent-dm.test.ts +161 -0
  203. package/telegram-plugin/uat/scenarios/vault-request-access-end-to-end-dm.test.ts +158 -0
  204. package/telegram-plugin/uat/scenarios/voice-inbound-dm.test.ts +65 -0
  205. package/telegram-plugin/vault-approval-posture.ts +42 -0
  206. package/telegram-plugin/welcome-text.ts +1 -0
  207. package/telegram-plugin/active-pins-sweep.ts +0 -204
  208. package/telegram-plugin/active-pins.ts +0 -146
  209. package/telegram-plugin/auth-dashboard.ts +0 -1104
  210. package/telegram-plugin/auth-slot-parser.ts +0 -497
  211. package/telegram-plugin/card-event-log.ts +0 -138
  212. package/telegram-plugin/dist/foreman/foreman.js +0 -31106
  213. package/telegram-plugin/docs/multi-agent-card-design.md +0 -847
  214. package/telegram-plugin/docs/pinned-progress-card-reliability.md +0 -144
  215. package/telegram-plugin/foreman/foreman-create-flow.ts +0 -202
  216. package/telegram-plugin/foreman/foreman-handlers.ts +0 -493
  217. package/telegram-plugin/foreman/foreman.ts +0 -1165
  218. package/telegram-plugin/foreman/setup-flow.ts +0 -345
  219. package/telegram-plugin/foreman/setup-state.ts +0 -239
  220. package/telegram-plugin/foreman/state.ts +0 -203
  221. package/telegram-plugin/pin-event-log.ts +0 -76
  222. package/telegram-plugin/progress-card-driver.ts +0 -2886
  223. package/telegram-plugin/progress-card-pin-manager.ts +0 -589
  224. package/telegram-plugin/progress-card-pin-watchdog.ts +0 -98
  225. package/telegram-plugin/progress-card.ts +0 -1409
  226. package/telegram-plugin/tests/HARNESS.md +0 -340
  227. package/telegram-plugin/tests/_progress-card-harness.ts +0 -109
  228. package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +0 -211
  229. package/telegram-plugin/tests/active-pins-sweep.test.ts +0 -309
  230. package/telegram-plugin/tests/active-pins.test.ts +0 -187
  231. package/telegram-plugin/tests/auth-account-identity-surface.test.ts +0 -118
  232. package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +0 -260
  233. package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +0 -140
  234. package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +0 -559
  235. package/telegram-plugin/tests/auth-dashboard.test.ts +0 -1045
  236. package/telegram-plugin/tests/auth-slot-commands.test.ts +0 -640
  237. package/telegram-plugin/tests/bg-agent-progress-card-757.test.ts +0 -201
  238. package/telegram-plugin/tests/boot-card-account-quota.test.ts +0 -137
  239. package/telegram-plugin/tests/card-event-log.test.ts +0 -145
  240. package/telegram-plugin/tests/first-paint.test.ts +0 -257
  241. package/telegram-plugin/tests/foreman-create-flow.test.ts +0 -359
  242. package/telegram-plugin/tests/foreman-handlers.test.ts +0 -347
  243. package/telegram-plugin/tests/foreman-state.test.ts +0 -164
  244. package/telegram-plugin/tests/foreman-write-ops.test.ts +0 -214
  245. package/telegram-plugin/tests/harness-ordering-invariants.test.ts +0 -243
  246. package/telegram-plugin/tests/pin-event-log.test.ts +0 -124
  247. package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +0 -73
  248. package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +0 -272
  249. package/telegram-plugin/tests/progress-card-cross-turn.test.ts +0 -258
  250. package/telegram-plugin/tests/progress-card-delay-842.test.ts +0 -160
  251. package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +0 -81
  252. package/telegram-plugin/tests/progress-card-draft-flag.test.ts +0 -80
  253. package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +0 -215
  254. package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +0 -123
  255. package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +0 -76
  256. package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +0 -62
  257. package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +0 -84
  258. package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +0 -139
  259. package/telegram-plugin/tests/progress-card-pin-manager.test.ts +0 -773
  260. package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +0 -66
  261. package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +0 -64
  262. package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +0 -190
  263. package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +0 -146
  264. package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +0 -123
  265. package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +0 -82
  266. package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +0 -114
  267. package/telegram-plugin/tests/real-gateway-harness.ts +0 -699
  268. package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +0 -313
  269. package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +0 -299
  270. package/telegram-plugin/tests/real-gateway-spec.test.ts +0 -487
  271. package/telegram-plugin/tests/real-gateway.smoke.test.ts +0 -101
  272. package/telegram-plugin/tests/setup-flow.test.ts +0 -510
  273. package/telegram-plugin/tests/setup-state.test.ts +0 -146
  274. package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +0 -116
  275. package/telegram-plugin/tests/turn-end-regressions.test.ts +0 -489
  276. package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +0 -218
  277. package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +0 -78
  278. package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +0 -131
  279. package/telegram-plugin/tests/two-zone-bg-detection.test.ts +0 -120
  280. package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +0 -116
  281. package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +0 -87
  282. package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +0 -211
  283. package/telegram-plugin/tests/two-zone-card-cap.test.ts +0 -62
  284. package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +0 -101
  285. package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +0 -78
  286. package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +0 -110
  287. package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +0 -128
  288. package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +0 -58
  289. package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +0 -133
  290. package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +0 -155
  291. package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +0 -117
  292. package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +0 -187
  293. package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +0 -149
  294. package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +0 -101
  295. package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +0 -114
  296. package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +0 -105
  297. package/telegram-plugin/tests/waiting-ux-harness.ts +0 -381
  298. package/telegram-plugin/tests/waiting-ux.e2e.test.ts +0 -233
  299. package/telegram-plugin/turn-flush-prose-recovery.ts +0 -40
  300. package/telegram-plugin/two-zone-card.ts +0 -269
  301. package/telegram-plugin/uat/scenarios/smoke-clerk-reply.test.ts +0 -61
@@ -62,17 +62,57 @@ describe("parseVaultCliError", () => {
62
62
  });
63
63
 
64
64
  describe("renderVaultCliError", () => {
65
- it("renders sandbox_context with a host-CLI suggestion", () => {
65
+ // 2026-05-12: renderer copy migrated from host-CLI suggestions to
66
+ // Telegram-native next-step actions (vault_request_save, /vault
67
+ // audit, /vault broker status). Tests below assert the new copy.
68
+ // The pre-fix pins are documented in
69
+ // tests/jtbd-talk-from-anywhere.test.ts as the closed punch-list
70
+ // items.
71
+ it("renders sandbox_context for verb=set with the vault_request_save tool, surfacing the affected key", () => {
66
72
  const out = renderVaultCliError(
67
73
  { kind: "sandbox_context", original: "x" },
68
74
  { verb: "set", key: "my_key" },
69
75
  );
70
76
  expect(out.suppressRaw).toBe(true);
71
- expect(out.html).toContain("must run on the host");
72
- expect(out.html).toContain("switchroom vault set my_key");
77
+ expect(out.html).toMatch(/vault_request_save/);
78
+ expect(out.html).not.toMatch(/Open a host shell/);
79
+ // Reviewer-flagged on #1037: pre-fix test asserted the key
80
+ // appeared in output (so the operator knew which key triggered
81
+ // the card). New copy keeps the key in <code>…</code> form via
82
+ // htmlEscape — assert it.
83
+ expect(out.html).toContain("<code>my_key</code>");
73
84
  });
74
85
 
75
- it("renders needs_approval with the affected key + host hint + P1a teaser", () => {
86
+ it("renders sandbox_context for verb=set WITHOUT a key (defensive fallback)", () => {
87
+ // The gateway sometimes doesn't have the key in hand (e.g. when
88
+ // the agent's stderr was opaque). Renderer should still produce
89
+ // useful output, not crash or render an empty <code></code>.
90
+ const out = renderVaultCliError(
91
+ { kind: "sandbox_context", original: "x" },
92
+ { verb: "set" },
93
+ );
94
+ expect(out.html).toMatch(/vault_request_save/);
95
+ expect(out.html).not.toContain("<code></code>");
96
+ });
97
+
98
+ it("renders sandbox_context for verb=get with /vault get", () => {
99
+ const out = renderVaultCliError(
100
+ { kind: "sandbox_context", original: "x" },
101
+ { verb: "get", key: "my_key" },
102
+ );
103
+ expect(out.html).toMatch(/\/vault get/);
104
+ expect(out.html).toMatch(/my_key/);
105
+ });
106
+
107
+ it("renders sandbox_context for verb=init with the one-time-host-shell note", () => {
108
+ const out = renderVaultCliError(
109
+ { kind: "sandbox_context", original: "x" },
110
+ { verb: "init" },
111
+ );
112
+ expect(out.html).toMatch(/one-time host-shell|switchroom vault init/);
113
+ });
114
+
115
+ it("renders needs_approval naming the live vault_request_save tool (not a 'on the way' stub)", () => {
76
116
  const out = renderVaultCliError(
77
117
  { kind: "needs_approval", original: "x", key: "telegram_bot_token" },
78
118
  { verb: "save" },
@@ -80,35 +120,47 @@ describe("renderVaultCliError", () => {
80
120
  expect(out.suppressRaw).toBe(true);
81
121
  expect(out.html).toContain("operator approval required");
82
122
  expect(out.html).toContain("<code>telegram_bot_token</code>");
83
- expect(out.html).toContain("switchroom vault set telegram_bot_token");
84
- expect(out.html).toContain("P1a"); // forward-pointer to the upcoming flow
123
+ expect(out.html).toMatch(/vault_request_save/);
124
+ expect(out.html).not.toMatch(/on the way/i);
85
125
  });
86
126
 
87
- it("renders broker_unreachable with the status command", () => {
127
+ it("renders broker_unreachable as an honest 'tracked follow-up' instead of a false in-chat promise", () => {
128
+ // Pre-fix (rejected by #1037 reviewer): renderer pointed at
129
+ // `/vault broker status` / `/vault broker restart` — neither
130
+ // command is registered in the gateway dispatcher. Telling the
131
+ // operator to type unregistered commands is worse than the
132
+ // host-CLI punt it was meant to replace.
133
+ //
134
+ // Post-fix: renderer names the in-Telegram follow-up as
135
+ // tracked + unbuilt, and points at the host CLI for now.
136
+ // Honest about the gap until /vault broker {status,restart}
137
+ // actually ships.
88
138
  const out = renderVaultCliError(
89
139
  { kind: "broker_unreachable", original: "x" },
90
140
  { verb: "set" },
91
141
  );
92
142
  expect(out.suppressRaw).toBe(true);
93
143
  expect(out.html).toContain("broker isn't reachable");
94
- expect(out.html).toContain("switchroom vault broker status");
144
+ expect(out.html).toMatch(/tracked as a follow-up/i);
145
+ expect(out.html).toMatch(/switchroom vault broker status/);
95
146
  });
96
147
 
97
- it("renders broker_denied with a grant command + key", () => {
148
+ it("renders broker_denied pointing at /vault audit one-tap allow + vault_request_access", () => {
98
149
  const out = renderVaultCliError(
99
150
  { kind: "broker_denied", original: "x", key: "shared_token" },
100
151
  { verb: "set" },
101
152
  );
102
153
  expect(out.suppressRaw).toBe(true);
103
154
  expect(out.html).toContain("refused the request");
104
- expect(out.html).toContain("switchroom vault grant");
155
+ expect(out.html).toMatch(/\/vault audit/);
156
+ expect(out.html).toMatch(/vault_request_access/);
105
157
  expect(out.html).toContain("shared_token");
106
158
  });
107
159
 
108
- it("prefers verbHint.key over parser-extracted key (verbHint wins for host suggestion)", () => {
160
+ it("prefers verbHint.key over parser-extracted key (verbHint wins for in-Telegram next-step)", () => {
109
161
  // The gateway always knows the key the user asked for; rendering
110
162
  // must use that over any heuristic extraction so a parser glitch
111
- // can't surface the wrong key in the host command suggestion.
163
+ // can't surface the wrong key in the in-chat next-step suggestion.
112
164
  const out = renderVaultCliError(
113
165
  { kind: "broker_denied", original: "x", key: "parser-extracted" },
114
166
  { verb: "set", key: "gateway-supplied" },
@@ -154,15 +154,22 @@ export function renderVaultCliError(
154
154
  const key = verbHint.key ?? err.key;
155
155
  switch (err.kind) {
156
156
  case "sandbox_context":
157
+ // The agent tried direct vault file IO from inside the sandbox.
158
+ // Route the operator at the Telegram-native equivalent for the
159
+ // verb in flight — only `init` needs a one-time host shell.
160
+ // Closes the "leave Telegram for a verb that exists in Telegram"
161
+ // anti-pattern from reference/talk-to-agents-from-anywhere.md.
157
162
  return {
158
163
  suppressRaw: true,
159
164
  html:
160
- `⚠️ <b>This action must run on the host.</b>\n` +
161
- `The vault file isn't mounted inside the agent sandbox; only ` +
162
- `the broker socket is. Open a host shell and run:\n` +
163
- `<pre>switchroom vault ${verbHint.verb}${key ? ` ${htmlEscape(key)}` : ""}</pre>`,
165
+ renderSandboxContextSuggestion(verbHint.verb, key),
164
166
  };
165
167
  case "needs_approval":
168
+ // Agent tried to save a new key. The `vault_request_save` MCP
169
+ // tool shipped in #969 P1a renders an approval card in
170
+ // operator chat — no host shell, no re-paste. Telling the
171
+ // operator to "run switchroom vault set on the host" punts
172
+ // them to the desktop for a feature that exists in Telegram.
166
173
  return {
167
174
  suppressRaw: true,
168
175
  html:
@@ -172,20 +179,34 @@ export function renderVaultCliError(
172
179
  : `The agent tried to save a new key, but `) +
173
180
  `agents can only rotate existing keys via the broker; introducing ` +
174
181
  `a new key needs an operator action.\n\n` +
175
- `For now, run on a host shell:\n` +
176
- `<pre>switchroom vault set${key ? ` ${htmlEscape(key)}` : " &lt;key&gt;"}</pre>\n` +
177
- `<i>A one-tap approval card is on the way (#969 P1a).</i>`,
182
+ `<b>Ask the agent</b> to call its <code>vault_request_save</code> ` +
183
+ `tool it'll render an approval card in this chat with ` +
184
+ `[✅ Save once] [🚫 Discard] [✏️ Rename] buttons. One tap saves the ` +
185
+ `secret to the vault; no re-paste needed.`,
178
186
  };
179
187
  case "broker_unreachable":
188
+ // No Telegram-native broker control plane exists yet —
189
+ // `/vault broker status` and `/vault broker restart` are on
190
+ // the JTBD punch list but not built (the broker socket lives
191
+ // outside the agent container; the gateway would need a
192
+ // host-side daemon to drive it). Be honest about that gap
193
+ // rather than promising commands that don't dispatch.
180
194
  return {
181
195
  suppressRaw: true,
182
196
  html:
183
197
  `⚠️ <b>Vault broker isn't reachable.</b>\n` +
184
198
  `From inside the agent sandbox there's no fallback path. ` +
185
- `Operator can check on the host:\n` +
186
- `<pre>switchroom vault broker status</pre>`,
199
+ `Telegram-native broker recovery is tracked as a follow-up — ` +
200
+ `for now, on the host:\n` +
201
+ `<pre>switchroom vault broker status</pre>\n` +
202
+ `<i>Or, if the broker is wedged: <code>docker compose -p switchroom restart vault-broker</code>.</i>`,
187
203
  };
188
204
  case "broker_denied":
205
+ // Telegram-native grant flow shipped in #969 P2b + #1012:
206
+ // - operator runs `/vault audit <agent>`, taps [🔓 Allow <key>]
207
+ // for a recent denial — one tap, no shell.
208
+ // - OR the agent itself calls `vault_request_access` and the
209
+ // approval card with [✅ Approve] / [🚫 Deny] lands here.
189
210
  return {
190
211
  suppressRaw: true,
191
212
  html:
@@ -193,10 +214,56 @@ export function renderVaultCliError(
193
214
  (key
194
215
  ? `The agent isn't authorized to access <code>${htmlEscape(key)}</code>. `
195
216
  : `The agent isn't authorized to access this key. `) +
196
- `Operator can grant access from a host shell:\n` +
197
- `<pre>switchroom vault grant &lt;agent&gt; --keys ${key ? htmlEscape(key) : "&lt;key&gt;"}</pre>`,
217
+ `Grant access in two taps:\n` +
218
+ `• <code>/vault audit &lt;agent&gt;</code> in this chat tap ` +
219
+ `[🔓 Allow${key ? ` ${htmlEscape(key)}` : ""}] on the recent denial, OR\n` +
220
+ `• ask the agent to call <code>vault_request_access</code> — ` +
221
+ `an approval card lands here with [✅ Approve] / [🚫 Deny].`,
198
222
  };
199
223
  case "other":
200
224
  return { suppressRaw: false, html: "" };
201
225
  }
202
226
  }
227
+
228
+ /**
229
+ * Suggest the Telegram-native command for each sandbox-context verb.
230
+ * Only `init` still needs a one-time host shell (vault bootstrap is
231
+ * a setup step, not a steering-while-mobile workflow).
232
+ */
233
+ function renderSandboxContextSuggestion(
234
+ verb: "set" | "get" | "list" | "init" | "remove" | "save",
235
+ key: string | undefined,
236
+ ): string {
237
+ const head = `⚠️ <b>Direct vault file IO isn't available from inside the agent sandbox.</b>\n`;
238
+ switch (verb) {
239
+ case "set":
240
+ case "save":
241
+ return (
242
+ head +
243
+ (key
244
+ ? `<b>Ask the agent</b> to call its <code>vault_request_save</code> ` +
245
+ `tool for <code>${htmlEscape(key)}</code> — an approval card ` +
246
+ `lands here with [✅ Save once] / [🚫 Discard] / [✏️ Rename] buttons.`
247
+ : `<b>Ask the agent</b> to call its <code>vault_request_save</code> ` +
248
+ `tool — an approval card lands here with [✅ Save once] / [🚫 Discard] / [✏️ Rename] buttons.`)
249
+ );
250
+ case "get":
251
+ return (
252
+ head +
253
+ `From this chat: <code>/vault get${key ? ` ${htmlEscape(key)}` : " &lt;key&gt;"}</code>`
254
+ );
255
+ case "list":
256
+ return head + `From this chat: <code>/vault list</code>`;
257
+ case "remove":
258
+ return (
259
+ head +
260
+ `Use the operator host CLI for now — Telegram-native delete is tracked as a follow-up. ` +
261
+ `<i>(Removing a vault key is a rare, irreversible operation; an in-chat confirmation card is on the punch list.)</i>`
262
+ );
263
+ case "init":
264
+ return (
265
+ head +
266
+ `Vault bootstrap is a one-time host-shell step: <code>switchroom vault init</code>.`
267
+ );
268
+ }
269
+ }
@@ -25,7 +25,13 @@ export type VaultWriteFn = (
25
25
  export type VaultListFn = (passphrase: string) => { ok: boolean; keys: string[] }
26
26
 
27
27
  export const defaultVaultWrite: VaultWriteFn = (slug, value, passphrase) => {
28
- const env = { ...process.env, SWITCHROOM_VAULT_PASSPHRASE: passphrase }
28
+ // Suppress the #969 P3 deprecation warning — the gateway forwarding
29
+ // the operator's passphrase per-spawn is the canonical P1a flow.
30
+ const env = {
31
+ ...process.env,
32
+ SWITCHROOM_VAULT_PASSPHRASE: passphrase,
33
+ SWITCHROOM_NO_VAULT_DEPRECATION_WARNING: '1',
34
+ }
29
35
  try {
30
36
  const result = execFileSync(
31
37
  process.env.SWITCHROOM_CLI_PATH ?? 'switchroom',
@@ -41,7 +47,13 @@ export const defaultVaultWrite: VaultWriteFn = (slug, value, passphrase) => {
41
47
  }
42
48
 
43
49
  export const defaultVaultList: VaultListFn = (passphrase) => {
44
- const env = { ...process.env, SWITCHROOM_VAULT_PASSPHRASE: passphrase }
50
+ // Suppress the #969 P3 deprecation warning — the gateway forwarding
51
+ // the operator's passphrase per-spawn is the canonical P1a flow.
52
+ const env = {
53
+ ...process.env,
54
+ SWITCHROOM_VAULT_PASSPHRASE: passphrase,
55
+ SWITCHROOM_NO_VAULT_DEPRECATION_WARNING: '1',
56
+ }
45
57
  try {
46
58
  const result = execFileSync(
47
59
  process.env.SWITCHROOM_CLI_PATH ?? 'switchroom',