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
@@ -17,7 +17,7 @@ var __export = (target, all) => {
17
17
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
19
 
20
- // node_modules/zod/v3/helpers/util.js
20
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
21
21
  var util, objectUtil, ZodParsedType, getParsedType = (data) => {
22
22
  const t = typeof data;
23
23
  switch (t) {
@@ -148,7 +148,7 @@ var init_util = __esm(() => {
148
148
  ]);
149
149
  });
150
150
 
151
- // node_modules/zod/v3/ZodError.js
151
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
152
152
  var ZodIssueCode, quotelessJson = (obj) => {
153
153
  const json = JSON.stringify(obj, null, 2);
154
154
  return json.replace(/"([^"]+)":/g, "$1:");
@@ -269,7 +269,7 @@ var init_ZodError = __esm(() => {
269
269
  };
270
270
  });
271
271
 
272
- // node_modules/zod/v3/locales/en.js
272
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
273
273
  var errorMap = (issue, _ctx) => {
274
274
  let message;
275
275
  switch (issue.code) {
@@ -376,7 +376,7 @@ var init_en = __esm(() => {
376
376
  en_default = errorMap;
377
377
  });
378
378
 
379
- // node_modules/zod/v3/errors.js
379
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
380
380
  function setErrorMap(map) {
381
381
  overrideErrorMap = map;
382
382
  }
@@ -389,7 +389,7 @@ var init_errors = __esm(() => {
389
389
  overrideErrorMap = en_default;
390
390
  });
391
391
 
392
- // node_modules/zod/v3/helpers/parseUtil.js
392
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
393
393
  function addIssueToContext(ctx, issueData) {
394
394
  const overrideMap = getErrorMap();
395
395
  const issue = makeIssue({
@@ -494,10 +494,10 @@ var init_parseUtil = __esm(() => {
494
494
  });
495
495
  });
496
496
 
497
- // node_modules/zod/v3/helpers/typeAliases.js
497
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/typeAliases.js
498
498
  var init_typeAliases = () => {};
499
499
 
500
- // node_modules/zod/v3/helpers/errorUtil.js
500
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
501
501
  var errorUtil;
502
502
  var init_errorUtil = __esm(() => {
503
503
  (function(errorUtil2) {
@@ -506,7 +506,7 @@ var init_errorUtil = __esm(() => {
506
506
  })(errorUtil || (errorUtil = {}));
507
507
  });
508
508
 
509
- // node_modules/zod/v3/types.js
509
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
510
510
  class ParseInputLazyPath {
511
511
  constructor(parent, value, path, key) {
512
512
  this._cachedPath = [];
@@ -3857,7 +3857,7 @@ var init_types = __esm(() => {
3857
3857
  NEVER = INVALID;
3858
3858
  });
3859
3859
 
3860
- // node_modules/zod/v3/external.js
3860
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
3861
3861
  var exports_external = {};
3862
3862
  __export(exports_external, {
3863
3863
  void: () => voidType,
@@ -3977,13 +3977,13 @@ var init_external = __esm(() => {
3977
3977
  init_ZodError();
3978
3978
  });
3979
3979
 
3980
- // node_modules/zod/index.js
3980
+ // node_modules/.bun/zod@3.25.76/node_modules/zod/index.js
3981
3981
  var init_zod = __esm(() => {
3982
3982
  init_external();
3983
3983
  init_external();
3984
3984
  });
3985
3985
 
3986
- // node_modules/yaml/dist/nodes/identity.js
3986
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/identity.js
3987
3987
  var require_identity = __commonJS((exports) => {
3988
3988
  var ALIAS = Symbol.for("yaml.alias");
3989
3989
  var DOC = Symbol.for("yaml.document");
@@ -4037,7 +4037,7 @@ var require_identity = __commonJS((exports) => {
4037
4037
  exports.isSeq = isSeq;
4038
4038
  });
4039
4039
 
4040
- // node_modules/yaml/dist/visit.js
4040
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/visit.js
4041
4041
  var require_visit = __commonJS((exports) => {
4042
4042
  var identity = require_identity();
4043
4043
  var BREAK = Symbol("break visit");
@@ -4192,7 +4192,7 @@ var require_visit = __commonJS((exports) => {
4192
4192
  exports.visitAsync = visitAsync;
4193
4193
  });
4194
4194
 
4195
- // node_modules/yaml/dist/doc/directives.js
4195
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/directives.js
4196
4196
  var require_directives = __commonJS((exports) => {
4197
4197
  var identity = require_identity();
4198
4198
  var visit = require_visit();
@@ -4344,7 +4344,7 @@ var require_directives = __commonJS((exports) => {
4344
4344
  exports.Directives = Directives;
4345
4345
  });
4346
4346
 
4347
- // node_modules/yaml/dist/doc/anchors.js
4347
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/anchors.js
4348
4348
  var require_anchors = __commonJS((exports) => {
4349
4349
  var identity = require_identity();
4350
4350
  var visit = require_visit();
@@ -4406,7 +4406,7 @@ var require_anchors = __commonJS((exports) => {
4406
4406
  exports.findNewAnchor = findNewAnchor;
4407
4407
  });
4408
4408
 
4409
- // node_modules/yaml/dist/doc/applyReviver.js
4409
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/applyReviver.js
4410
4410
  var require_applyReviver = __commonJS((exports) => {
4411
4411
  function applyReviver(reviver, obj, key, val) {
4412
4412
  if (val && typeof val === "object") {
@@ -4453,7 +4453,7 @@ var require_applyReviver = __commonJS((exports) => {
4453
4453
  exports.applyReviver = applyReviver;
4454
4454
  });
4455
4455
 
4456
- // node_modules/yaml/dist/nodes/toJS.js
4456
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/toJS.js
4457
4457
  var require_toJS = __commonJS((exports) => {
4458
4458
  var identity = require_identity();
4459
4459
  function toJS(value, arg, ctx) {
@@ -4480,7 +4480,7 @@ var require_toJS = __commonJS((exports) => {
4480
4480
  exports.toJS = toJS;
4481
4481
  });
4482
4482
 
4483
- // node_modules/yaml/dist/nodes/Node.js
4483
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Node.js
4484
4484
  var require_Node = __commonJS((exports) => {
4485
4485
  var applyReviver = require_applyReviver();
4486
4486
  var identity = require_identity();
@@ -4517,7 +4517,7 @@ var require_Node = __commonJS((exports) => {
4517
4517
  exports.NodeBase = NodeBase;
4518
4518
  });
4519
4519
 
4520
- // node_modules/yaml/dist/nodes/Alias.js
4520
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Alias.js
4521
4521
  var require_Alias = __commonJS((exports) => {
4522
4522
  var anchors = require_anchors();
4523
4523
  var visit = require_visit();
@@ -4625,7 +4625,7 @@ var require_Alias = __commonJS((exports) => {
4625
4625
  exports.Alias = Alias;
4626
4626
  });
4627
4627
 
4628
- // node_modules/yaml/dist/nodes/Scalar.js
4628
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Scalar.js
4629
4629
  var require_Scalar = __commonJS((exports) => {
4630
4630
  var identity = require_identity();
4631
4631
  var Node = require_Node();
@@ -4653,7 +4653,7 @@ var require_Scalar = __commonJS((exports) => {
4653
4653
  exports.isScalarValue = isScalarValue;
4654
4654
  });
4655
4655
 
4656
- // node_modules/yaml/dist/doc/createNode.js
4656
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/createNode.js
4657
4657
  var require_createNode = __commonJS((exports) => {
4658
4658
  var Alias = require_Alias();
4659
4659
  var identity = require_identity();
@@ -4725,7 +4725,7 @@ var require_createNode = __commonJS((exports) => {
4725
4725
  exports.createNode = createNode;
4726
4726
  });
4727
4727
 
4728
- // node_modules/yaml/dist/nodes/Collection.js
4728
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Collection.js
4729
4729
  var require_Collection = __commonJS((exports) => {
4730
4730
  var createNode = require_createNode();
4731
4731
  var identity = require_identity();
@@ -4840,7 +4840,7 @@ var require_Collection = __commonJS((exports) => {
4840
4840
  exports.isEmptyPath = isEmptyPath;
4841
4841
  });
4842
4842
 
4843
- // node_modules/yaml/dist/stringify/stringifyComment.js
4843
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyComment.js
4844
4844
  var require_stringifyComment = __commonJS((exports) => {
4845
4845
  var stringifyComment = (str) => str.replace(/^(?!$)(?: $)?/gm, "#");
4846
4846
  function indentComment(comment, indent) {
@@ -4857,7 +4857,7 @@ var require_stringifyComment = __commonJS((exports) => {
4857
4857
  exports.stringifyComment = stringifyComment;
4858
4858
  });
4859
4859
 
4860
- // node_modules/yaml/dist/stringify/foldFlowLines.js
4860
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/foldFlowLines.js
4861
4861
  var require_foldFlowLines = __commonJS((exports) => {
4862
4862
  var FOLD_FLOW = "flow";
4863
4863
  var FOLD_BLOCK = "block";
@@ -4994,7 +4994,7 @@ ${indent}${text.slice(fold + 1, end2)}`;
4994
4994
  exports.foldFlowLines = foldFlowLines;
4995
4995
  });
4996
4996
 
4997
- // node_modules/yaml/dist/stringify/stringifyString.js
4997
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyString.js
4998
4998
  var require_stringifyString = __commonJS((exports) => {
4999
4999
  var Scalar = require_Scalar();
5000
5000
  var foldFlowLines = require_foldFlowLines();
@@ -5292,7 +5292,7 @@ ${indent}`);
5292
5292
  exports.stringifyString = stringifyString;
5293
5293
  });
5294
5294
 
5295
- // node_modules/yaml/dist/stringify/stringify.js
5295
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringify.js
5296
5296
  var require_stringify = __commonJS((exports) => {
5297
5297
  var anchors = require_anchors();
5298
5298
  var identity = require_identity();
@@ -5413,7 +5413,7 @@ ${ctx.indent}${str}`;
5413
5413
  exports.stringify = stringify;
5414
5414
  });
5415
5415
 
5416
- // node_modules/yaml/dist/stringify/stringifyPair.js
5416
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyPair.js
5417
5417
  var require_stringifyPair = __commonJS((exports) => {
5418
5418
  var identity = require_identity();
5419
5419
  var Scalar = require_Scalar();
@@ -5549,7 +5549,7 @@ ${ctx.indent}`;
5549
5549
  exports.stringifyPair = stringifyPair;
5550
5550
  });
5551
5551
 
5552
- // node_modules/yaml/dist/log.js
5552
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/log.js
5553
5553
  var require_log = __commonJS((exports) => {
5554
5554
  var node_process = __require("process");
5555
5555
  function debug(logLevel, ...messages) {
@@ -5568,7 +5568,7 @@ var require_log = __commonJS((exports) => {
5568
5568
  exports.warn = warn;
5569
5569
  });
5570
5570
 
5571
- // node_modules/yaml/dist/schema/yaml-1.1/merge.js
5571
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/merge.js
5572
5572
  var require_merge = __commonJS((exports) => {
5573
5573
  var identity = require_identity();
5574
5574
  var Scalar = require_Scalar();
@@ -5622,7 +5622,7 @@ var require_merge = __commonJS((exports) => {
5622
5622
  exports.merge = merge;
5623
5623
  });
5624
5624
 
5625
- // node_modules/yaml/dist/nodes/addPairToJSMap.js
5625
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/addPairToJSMap.js
5626
5626
  var require_addPairToJSMap = __commonJS((exports) => {
5627
5627
  var log = require_log();
5628
5628
  var merge = require_merge();
@@ -5683,7 +5683,7 @@ var require_addPairToJSMap = __commonJS((exports) => {
5683
5683
  exports.addPairToJSMap = addPairToJSMap;
5684
5684
  });
5685
5685
 
5686
- // node_modules/yaml/dist/nodes/Pair.js
5686
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/Pair.js
5687
5687
  var require_Pair = __commonJS((exports) => {
5688
5688
  var createNode = require_createNode();
5689
5689
  var stringifyPair = require_stringifyPair();
@@ -5721,7 +5721,7 @@ var require_Pair = __commonJS((exports) => {
5721
5721
  exports.createPair = createPair;
5722
5722
  });
5723
5723
 
5724
- // node_modules/yaml/dist/stringify/stringifyCollection.js
5724
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyCollection.js
5725
5725
  var require_stringifyCollection = __commonJS((exports) => {
5726
5726
  var identity = require_identity();
5727
5727
  var stringify = require_stringify();
@@ -5873,7 +5873,7 @@ ${indent}${end}`;
5873
5873
  exports.stringifyCollection = stringifyCollection;
5874
5874
  });
5875
5875
 
5876
- // node_modules/yaml/dist/nodes/YAMLMap.js
5876
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/YAMLMap.js
5877
5877
  var require_YAMLMap = __commonJS((exports) => {
5878
5878
  var stringifyCollection = require_stringifyCollection();
5879
5879
  var addPairToJSMap = require_addPairToJSMap();
@@ -6000,7 +6000,7 @@ var require_YAMLMap = __commonJS((exports) => {
6000
6000
  exports.findPair = findPair;
6001
6001
  });
6002
6002
 
6003
- // node_modules/yaml/dist/schema/common/map.js
6003
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/map.js
6004
6004
  var require_map = __commonJS((exports) => {
6005
6005
  var identity = require_identity();
6006
6006
  var YAMLMap = require_YAMLMap();
@@ -6019,7 +6019,7 @@ var require_map = __commonJS((exports) => {
6019
6019
  exports.map = map;
6020
6020
  });
6021
6021
 
6022
- // node_modules/yaml/dist/nodes/YAMLSeq.js
6022
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/nodes/YAMLSeq.js
6023
6023
  var require_YAMLSeq = __commonJS((exports) => {
6024
6024
  var createNode = require_createNode();
6025
6025
  var stringifyCollection = require_stringifyCollection();
@@ -6112,7 +6112,7 @@ var require_YAMLSeq = __commonJS((exports) => {
6112
6112
  exports.YAMLSeq = YAMLSeq;
6113
6113
  });
6114
6114
 
6115
- // node_modules/yaml/dist/schema/common/seq.js
6115
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/seq.js
6116
6116
  var require_seq = __commonJS((exports) => {
6117
6117
  var identity = require_identity();
6118
6118
  var YAMLSeq = require_YAMLSeq();
@@ -6131,7 +6131,7 @@ var require_seq = __commonJS((exports) => {
6131
6131
  exports.seq = seq;
6132
6132
  });
6133
6133
 
6134
- // node_modules/yaml/dist/schema/common/string.js
6134
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/string.js
6135
6135
  var require_string = __commonJS((exports) => {
6136
6136
  var stringifyString = require_stringifyString();
6137
6137
  var string = {
@@ -6147,7 +6147,7 @@ var require_string = __commonJS((exports) => {
6147
6147
  exports.string = string;
6148
6148
  });
6149
6149
 
6150
- // node_modules/yaml/dist/schema/common/null.js
6150
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/common/null.js
6151
6151
  var require_null = __commonJS((exports) => {
6152
6152
  var Scalar = require_Scalar();
6153
6153
  var nullTag = {
@@ -6162,7 +6162,7 @@ var require_null = __commonJS((exports) => {
6162
6162
  exports.nullTag = nullTag;
6163
6163
  });
6164
6164
 
6165
- // node_modules/yaml/dist/schema/core/bool.js
6165
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/bool.js
6166
6166
  var require_bool = __commonJS((exports) => {
6167
6167
  var Scalar = require_Scalar();
6168
6168
  var boolTag = {
@@ -6183,7 +6183,7 @@ var require_bool = __commonJS((exports) => {
6183
6183
  exports.boolTag = boolTag;
6184
6184
  });
6185
6185
 
6186
- // node_modules/yaml/dist/stringify/stringifyNumber.js
6186
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyNumber.js
6187
6187
  var require_stringifyNumber = __commonJS((exports) => {
6188
6188
  function stringifyNumber({ format, minFractionDigits, tag, value }) {
6189
6189
  if (typeof value === "bigint")
@@ -6207,7 +6207,7 @@ var require_stringifyNumber = __commonJS((exports) => {
6207
6207
  exports.stringifyNumber = stringifyNumber;
6208
6208
  });
6209
6209
 
6210
- // node_modules/yaml/dist/schema/core/float.js
6210
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/float.js
6211
6211
  var require_float = __commonJS((exports) => {
6212
6212
  var Scalar = require_Scalar();
6213
6213
  var stringifyNumber = require_stringifyNumber();
@@ -6250,7 +6250,7 @@ var require_float = __commonJS((exports) => {
6250
6250
  exports.floatNaN = floatNaN;
6251
6251
  });
6252
6252
 
6253
- // node_modules/yaml/dist/schema/core/int.js
6253
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/int.js
6254
6254
  var require_int = __commonJS((exports) => {
6255
6255
  var stringifyNumber = require_stringifyNumber();
6256
6256
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
@@ -6292,7 +6292,7 @@ var require_int = __commonJS((exports) => {
6292
6292
  exports.intOct = intOct;
6293
6293
  });
6294
6294
 
6295
- // node_modules/yaml/dist/schema/core/schema.js
6295
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/core/schema.js
6296
6296
  var require_schema = __commonJS((exports) => {
6297
6297
  var map = require_map();
6298
6298
  var _null = require_null();
@@ -6317,7 +6317,7 @@ var require_schema = __commonJS((exports) => {
6317
6317
  exports.schema = schema;
6318
6318
  });
6319
6319
 
6320
- // node_modules/yaml/dist/schema/json/schema.js
6320
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/json/schema.js
6321
6321
  var require_schema2 = __commonJS((exports) => {
6322
6322
  var Scalar = require_Scalar();
6323
6323
  var map = require_map();
@@ -6381,7 +6381,7 @@ var require_schema2 = __commonJS((exports) => {
6381
6381
  exports.schema = schema;
6382
6382
  });
6383
6383
 
6384
- // node_modules/yaml/dist/schema/yaml-1.1/binary.js
6384
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/binary.js
6385
6385
  var require_binary = __commonJS((exports) => {
6386
6386
  var node_buffer = __require("buffer");
6387
6387
  var Scalar = require_Scalar();
@@ -6436,7 +6436,7 @@ var require_binary = __commonJS((exports) => {
6436
6436
  exports.binary = binary;
6437
6437
  });
6438
6438
 
6439
- // node_modules/yaml/dist/schema/yaml-1.1/pairs.js
6439
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/pairs.js
6440
6440
  var require_pairs = __commonJS((exports) => {
6441
6441
  var identity = require_identity();
6442
6442
  var Pair = require_Pair();
@@ -6511,7 +6511,7 @@ ${cn.comment}` : item.comment;
6511
6511
  exports.resolvePairs = resolvePairs;
6512
6512
  });
6513
6513
 
6514
- // node_modules/yaml/dist/schema/yaml-1.1/omap.js
6514
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/omap.js
6515
6515
  var require_omap = __commonJS((exports) => {
6516
6516
  var identity = require_identity();
6517
6517
  var toJS = require_toJS();
@@ -6583,7 +6583,7 @@ var require_omap = __commonJS((exports) => {
6583
6583
  exports.omap = omap;
6584
6584
  });
6585
6585
 
6586
- // node_modules/yaml/dist/schema/yaml-1.1/bool.js
6586
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/bool.js
6587
6587
  var require_bool2 = __commonJS((exports) => {
6588
6588
  var Scalar = require_Scalar();
6589
6589
  function boolStringify({ value, source }, ctx) {
@@ -6612,7 +6612,7 @@ var require_bool2 = __commonJS((exports) => {
6612
6612
  exports.trueTag = trueTag;
6613
6613
  });
6614
6614
 
6615
- // node_modules/yaml/dist/schema/yaml-1.1/float.js
6615
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/float.js
6616
6616
  var require_float2 = __commonJS((exports) => {
6617
6617
  var Scalar = require_Scalar();
6618
6618
  var stringifyNumber = require_stringifyNumber();
@@ -6658,7 +6658,7 @@ var require_float2 = __commonJS((exports) => {
6658
6658
  exports.floatNaN = floatNaN;
6659
6659
  });
6660
6660
 
6661
- // node_modules/yaml/dist/schema/yaml-1.1/int.js
6661
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/int.js
6662
6662
  var require_int2 = __commonJS((exports) => {
6663
6663
  var stringifyNumber = require_stringifyNumber();
6664
6664
  var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
@@ -6734,7 +6734,7 @@ var require_int2 = __commonJS((exports) => {
6734
6734
  exports.intOct = intOct;
6735
6735
  });
6736
6736
 
6737
- // node_modules/yaml/dist/schema/yaml-1.1/set.js
6737
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/set.js
6738
6738
  var require_set = __commonJS((exports) => {
6739
6739
  var identity = require_identity();
6740
6740
  var Pair = require_Pair();
@@ -6817,7 +6817,7 @@ var require_set = __commonJS((exports) => {
6817
6817
  exports.set = set;
6818
6818
  });
6819
6819
 
6820
- // node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
6820
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js
6821
6821
  var require_timestamp = __commonJS((exports) => {
6822
6822
  var stringifyNumber = require_stringifyNumber();
6823
6823
  function parseSexagesimal(str, asBigInt) {
@@ -6899,7 +6899,7 @@ var require_timestamp = __commonJS((exports) => {
6899
6899
  exports.timestamp = timestamp;
6900
6900
  });
6901
6901
 
6902
- // node_modules/yaml/dist/schema/yaml-1.1/schema.js
6902
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/yaml-1.1/schema.js
6903
6903
  var require_schema3 = __commonJS((exports) => {
6904
6904
  var map = require_map();
6905
6905
  var _null = require_null();
@@ -6940,7 +6940,7 @@ var require_schema3 = __commonJS((exports) => {
6940
6940
  exports.schema = schema;
6941
6941
  });
6942
6942
 
6943
- // node_modules/yaml/dist/schema/tags.js
6943
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/tags.js
6944
6944
  var require_tags = __commonJS((exports) => {
6945
6945
  var map = require_map();
6946
6946
  var _null = require_null();
@@ -7031,7 +7031,7 @@ var require_tags = __commonJS((exports) => {
7031
7031
  exports.getTags = getTags;
7032
7032
  });
7033
7033
 
7034
- // node_modules/yaml/dist/schema/Schema.js
7034
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/schema/Schema.js
7035
7035
  var require_Schema = __commonJS((exports) => {
7036
7036
  var identity = require_identity();
7037
7037
  var map = require_map();
@@ -7061,7 +7061,7 @@ var require_Schema = __commonJS((exports) => {
7061
7061
  exports.Schema = Schema;
7062
7062
  });
7063
7063
 
7064
- // node_modules/yaml/dist/stringify/stringifyDocument.js
7064
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/stringify/stringifyDocument.js
7065
7065
  var require_stringifyDocument = __commonJS((exports) => {
7066
7066
  var identity = require_identity();
7067
7067
  var stringify = require_stringify();
@@ -7141,7 +7141,7 @@ var require_stringifyDocument = __commonJS((exports) => {
7141
7141
  exports.stringifyDocument = stringifyDocument;
7142
7142
  });
7143
7143
 
7144
- // node_modules/yaml/dist/doc/Document.js
7144
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/doc/Document.js
7145
7145
  var require_Document = __commonJS((exports) => {
7146
7146
  var Alias = require_Alias();
7147
7147
  var Collection = require_Collection();
@@ -7376,7 +7376,7 @@ var require_Document = __commonJS((exports) => {
7376
7376
  exports.Document = Document;
7377
7377
  });
7378
7378
 
7379
- // node_modules/yaml/dist/errors.js
7379
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/errors.js
7380
7380
  var require_errors = __commonJS((exports) => {
7381
7381
  class YAMLError extends Error {
7382
7382
  constructor(name, pos, code, message) {
@@ -7441,7 +7441,7 @@ ${pointer}
7441
7441
  exports.prettifyError = prettifyError;
7442
7442
  });
7443
7443
 
7444
- // node_modules/yaml/dist/compose/resolve-props.js
7444
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-props.js
7445
7445
  var require_resolve_props = __commonJS((exports) => {
7446
7446
  function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
7447
7447
  let spaceBefore = false;
@@ -7571,7 +7571,7 @@ var require_resolve_props = __commonJS((exports) => {
7571
7571
  exports.resolveProps = resolveProps;
7572
7572
  });
7573
7573
 
7574
- // node_modules/yaml/dist/compose/util-contains-newline.js
7574
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-contains-newline.js
7575
7575
  var require_util_contains_newline = __commonJS((exports) => {
7576
7576
  function containsNewline(key) {
7577
7577
  if (!key)
@@ -7611,7 +7611,7 @@ var require_util_contains_newline = __commonJS((exports) => {
7611
7611
  exports.containsNewline = containsNewline;
7612
7612
  });
7613
7613
 
7614
- // node_modules/yaml/dist/compose/util-flow-indent-check.js
7614
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-flow-indent-check.js
7615
7615
  var require_util_flow_indent_check = __commonJS((exports) => {
7616
7616
  var utilContainsNewline = require_util_contains_newline();
7617
7617
  function flowIndentCheck(indent, fc, onError) {
@@ -7626,7 +7626,7 @@ var require_util_flow_indent_check = __commonJS((exports) => {
7626
7626
  exports.flowIndentCheck = flowIndentCheck;
7627
7627
  });
7628
7628
 
7629
- // node_modules/yaml/dist/compose/util-map-includes.js
7629
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-map-includes.js
7630
7630
  var require_util_map_includes = __commonJS((exports) => {
7631
7631
  var identity = require_identity();
7632
7632
  function mapIncludes(ctx, items, search) {
@@ -7639,7 +7639,7 @@ var require_util_map_includes = __commonJS((exports) => {
7639
7639
  exports.mapIncludes = mapIncludes;
7640
7640
  });
7641
7641
 
7642
- // node_modules/yaml/dist/compose/resolve-block-map.js
7642
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-block-map.js
7643
7643
  var require_resolve_block_map = __commonJS((exports) => {
7644
7644
  var Pair = require_Pair();
7645
7645
  var YAMLMap = require_YAMLMap();
@@ -7746,7 +7746,7 @@ var require_resolve_block_map = __commonJS((exports) => {
7746
7746
  exports.resolveBlockMap = resolveBlockMap;
7747
7747
  });
7748
7748
 
7749
- // node_modules/yaml/dist/compose/resolve-block-seq.js
7749
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-block-seq.js
7750
7750
  var require_resolve_block_seq = __commonJS((exports) => {
7751
7751
  var YAMLSeq = require_YAMLSeq();
7752
7752
  var resolveProps = require_resolve_props();
@@ -7794,7 +7794,7 @@ var require_resolve_block_seq = __commonJS((exports) => {
7794
7794
  exports.resolveBlockSeq = resolveBlockSeq;
7795
7795
  });
7796
7796
 
7797
- // node_modules/yaml/dist/compose/resolve-end.js
7797
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-end.js
7798
7798
  var require_resolve_end = __commonJS((exports) => {
7799
7799
  function resolveEnd(end, offset, reqSpace, onError) {
7800
7800
  let comment = "";
@@ -7834,7 +7834,7 @@ var require_resolve_end = __commonJS((exports) => {
7834
7834
  exports.resolveEnd = resolveEnd;
7835
7835
  });
7836
7836
 
7837
- // node_modules/yaml/dist/compose/resolve-flow-collection.js
7837
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-flow-collection.js
7838
7838
  var require_resolve_flow_collection = __commonJS((exports) => {
7839
7839
  var identity = require_identity();
7840
7840
  var Pair = require_Pair();
@@ -8025,7 +8025,7 @@ var require_resolve_flow_collection = __commonJS((exports) => {
8025
8025
  exports.resolveFlowCollection = resolveFlowCollection;
8026
8026
  });
8027
8027
 
8028
- // node_modules/yaml/dist/compose/compose-collection.js
8028
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-collection.js
8029
8029
  var require_compose_collection = __commonJS((exports) => {
8030
8030
  var identity = require_identity();
8031
8031
  var Scalar = require_Scalar();
@@ -8087,7 +8087,7 @@ var require_compose_collection = __commonJS((exports) => {
8087
8087
  exports.composeCollection = composeCollection;
8088
8088
  });
8089
8089
 
8090
- // node_modules/yaml/dist/compose/resolve-block-scalar.js
8090
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-block-scalar.js
8091
8091
  var require_resolve_block_scalar = __commonJS((exports) => {
8092
8092
  var Scalar = require_Scalar();
8093
8093
  function resolveBlockScalar(ctx, scalar, onError) {
@@ -8280,7 +8280,7 @@ var require_resolve_block_scalar = __commonJS((exports) => {
8280
8280
  exports.resolveBlockScalar = resolveBlockScalar;
8281
8281
  });
8282
8282
 
8283
- // node_modules/yaml/dist/compose/resolve-flow-scalar.js
8283
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/resolve-flow-scalar.js
8284
8284
  var require_resolve_flow_scalar = __commonJS((exports) => {
8285
8285
  var Scalar = require_Scalar();
8286
8286
  var resolveEnd = require_resolve_end();
@@ -8496,7 +8496,7 @@ var require_resolve_flow_scalar = __commonJS((exports) => {
8496
8496
  exports.resolveFlowScalar = resolveFlowScalar;
8497
8497
  });
8498
8498
 
8499
- // node_modules/yaml/dist/compose/compose-scalar.js
8499
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-scalar.js
8500
8500
  var require_compose_scalar = __commonJS((exports) => {
8501
8501
  var identity = require_identity();
8502
8502
  var Scalar = require_Scalar();
@@ -8574,7 +8574,7 @@ var require_compose_scalar = __commonJS((exports) => {
8574
8574
  exports.composeScalar = composeScalar;
8575
8575
  });
8576
8576
 
8577
- // node_modules/yaml/dist/compose/util-empty-scalar-position.js
8577
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/util-empty-scalar-position.js
8578
8578
  var require_util_empty_scalar_position = __commonJS((exports) => {
8579
8579
  function emptyScalarPosition(offset, before, pos) {
8580
8580
  if (before) {
@@ -8601,7 +8601,7 @@ var require_util_empty_scalar_position = __commonJS((exports) => {
8601
8601
  exports.emptyScalarPosition = emptyScalarPosition;
8602
8602
  });
8603
8603
 
8604
- // node_modules/yaml/dist/compose/compose-node.js
8604
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-node.js
8605
8605
  var require_compose_node = __commonJS((exports) => {
8606
8606
  var Alias = require_Alias();
8607
8607
  var identity = require_identity();
@@ -8704,7 +8704,7 @@ var require_compose_node = __commonJS((exports) => {
8704
8704
  exports.composeNode = composeNode;
8705
8705
  });
8706
8706
 
8707
- // node_modules/yaml/dist/compose/compose-doc.js
8707
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/compose-doc.js
8708
8708
  var require_compose_doc = __commonJS((exports) => {
8709
8709
  var Document = require_Document();
8710
8710
  var composeNode = require_compose_node();
@@ -8744,7 +8744,7 @@ var require_compose_doc = __commonJS((exports) => {
8744
8744
  exports.composeDoc = composeDoc;
8745
8745
  });
8746
8746
 
8747
- // node_modules/yaml/dist/compose/composer.js
8747
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/compose/composer.js
8748
8748
  var require_composer = __commonJS((exports) => {
8749
8749
  var node_process = __require("process");
8750
8750
  var directives = require_directives();
@@ -8933,7 +8933,7 @@ ${end.comment}` : end.comment;
8933
8933
  exports.Composer = Composer;
8934
8934
  });
8935
8935
 
8936
- // node_modules/yaml/dist/parse/cst-scalar.js
8936
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst-scalar.js
8937
8937
  var require_cst_scalar = __commonJS((exports) => {
8938
8938
  var resolveBlockScalar = require_resolve_block_scalar();
8939
8939
  var resolveFlowScalar = require_resolve_flow_scalar();
@@ -9123,7 +9123,7 @@ var require_cst_scalar = __commonJS((exports) => {
9123
9123
  exports.setScalarValue = setScalarValue;
9124
9124
  });
9125
9125
 
9126
- // node_modules/yaml/dist/parse/cst-stringify.js
9126
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst-stringify.js
9127
9127
  var require_cst_stringify = __commonJS((exports) => {
9128
9128
  var stringify = (cst) => ("type" in cst) ? stringifyToken(cst) : stringifyItem(cst);
9129
9129
  function stringifyToken(token) {
@@ -9181,7 +9181,7 @@ var require_cst_stringify = __commonJS((exports) => {
9181
9181
  exports.stringify = stringify;
9182
9182
  });
9183
9183
 
9184
- // node_modules/yaml/dist/parse/cst-visit.js
9184
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst-visit.js
9185
9185
  var require_cst_visit = __commonJS((exports) => {
9186
9186
  var BREAK = Symbol("break visit");
9187
9187
  var SKIP = Symbol("skip children");
@@ -9240,7 +9240,7 @@ var require_cst_visit = __commonJS((exports) => {
9240
9240
  exports.visit = visit;
9241
9241
  });
9242
9242
 
9243
- // node_modules/yaml/dist/parse/cst.js
9243
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/cst.js
9244
9244
  var require_cst = __commonJS((exports) => {
9245
9245
  var cstScalar = require_cst_scalar();
9246
9246
  var cstStringify = require_cst_stringify();
@@ -9341,7 +9341,7 @@ var require_cst = __commonJS((exports) => {
9341
9341
  exports.tokenType = tokenType;
9342
9342
  });
9343
9343
 
9344
- // node_modules/yaml/dist/parse/lexer.js
9344
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/lexer.js
9345
9345
  var require_lexer = __commonJS((exports) => {
9346
9346
  var cst = require_cst();
9347
9347
  function isEmpty(ch) {
@@ -9927,7 +9927,7 @@ var require_lexer = __commonJS((exports) => {
9927
9927
  exports.Lexer = Lexer;
9928
9928
  });
9929
9929
 
9930
- // node_modules/yaml/dist/parse/line-counter.js
9930
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/line-counter.js
9931
9931
  var require_line_counter = __commonJS((exports) => {
9932
9932
  class LineCounter {
9933
9933
  constructor() {
@@ -9955,7 +9955,7 @@ var require_line_counter = __commonJS((exports) => {
9955
9955
  exports.LineCounter = LineCounter;
9956
9956
  });
9957
9957
 
9958
- // node_modules/yaml/dist/parse/parser.js
9958
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/parse/parser.js
9959
9959
  var require_parser = __commonJS((exports) => {
9960
9960
  var node_process = __require("process");
9961
9961
  var cst = require_cst();
@@ -10804,7 +10804,7 @@ var require_parser = __commonJS((exports) => {
10804
10804
  exports.Parser = Parser;
10805
10805
  });
10806
10806
 
10807
- // node_modules/yaml/dist/public-api.js
10807
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/public-api.js
10808
10808
  var require_public_api = __commonJS((exports) => {
10809
10809
  var composer = require_composer();
10810
10810
  var Document = require_Document();
@@ -10898,7 +10898,7 @@ var require_public_api = __commonJS((exports) => {
10898
10898
  exports.stringify = stringify;
10899
10899
  });
10900
10900
 
10901
- // node_modules/yaml/dist/index.js
10901
+ // node_modules/.bun/yaml@2.8.3/node_modules/yaml/dist/index.js
10902
10902
  var composer, Document, Schema, errors2, Alias, identity, Pair, Scalar, YAMLMap, YAMLSeq, cst, lexer, lineCounter, parser, publicApi, visit, $Composer, $Document, $Schema, $YAMLError, $YAMLParseError, $YAMLWarning, $Alias, $isAlias, $isCollection, $isDocument, $isMap, $isNode, $isPair, $isScalar, $isSeq, $Pair, $Scalar, $YAMLMap, $YAMLSeq, $Lexer, $LineCounter, $Parser, $parse, $parseAllDocuments, $parseDocument, $stringify, $visit, $visitAsync;
10903
10903
  var init_dist = __esm(() => {
10904
10904
  composer = require_composer();
@@ -10948,7 +10948,7 @@ var init_dist = __esm(() => {
10948
10948
  });
10949
10949
 
10950
10950
  // src/config/schema.ts
10951
- var CodeRepoEntrySchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, DriveConfigSchema, AgentDriveConfigSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, SwitchroomConfigSchema;
10951
+ var CodeRepoEntrySchema, AgentBindMountSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, ReactionsSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, HostControlConfigSchema, SwitchroomConfigSchema;
10952
10952
  var init_schema = __esm(() => {
10953
10953
  init_zod();
10954
10954
  CodeRepoEntrySchema = exports_external.object({
@@ -10956,10 +10956,15 @@ var init_schema = __esm(() => {
10956
10956
  source: exports_external.string().describe("Absolute or home-relative path to the repo (e.g. ~/code/switchroom)"),
10957
10957
  concurrency: exports_external.number().int().positive().optional().describe("Max simultaneous worktrees for this repo (default 5)")
10958
10958
  });
10959
+ AgentBindMountSchema = exports_external.object({
10960
+ source: exports_external.string().describe("Absolute host path to bind-mount into the container. Tilde-expansion " + "is not performed — use the literal absolute path (e.g. " + "'/home/me/code/switchroom'). The compose generator refuses sources " + "under system paths (/, /etc, /proc, /sys, /dev, /run, /var/run, " + "/boot, /var/lib/docker) and the docker socket."),
10961
+ target: exports_external.string().optional().describe("Container path the source mounts to. Must be absolute. Defaults to " + "the same path as `source` (matches switchroom's existing dual-mount " + "convention so absolute paths in scaffolded scripts Just Work)."),
10962
+ mode: exports_external.enum(["ro", "rw"]).optional().describe("Read-only (default) or read-write. Use `rw` only when the agent " + "must mutate the host path (e.g. editing switchroom source). " + "Default: 'ro'.")
10963
+ });
10959
10964
  ScheduleEntrySchema = exports_external.object({
10960
10965
  cron: exports_external.string().describe("Cron expression (e.g., '0 8 * * *')"),
10961
10966
  prompt: exports_external.string().describe("Prompt to send at the scheduled time"),
10962
- model: exports_external.string().optional().describe("Model for this task. Defaults to claude-sonnet-4-6 (cheap, fast). " + "Use claude-opus-4-6 for tasks needing complex reasoning."),
10967
+ model: exports_external.string().optional().describe("Model for this task. Defaults to claude-sonnet-4-6 (cheap, fast). " + "Use claude-opus-4-7 for tasks needing complex reasoning."),
10963
10968
  secrets: exports_external.array(exports_external.string().regex(/^[a-zA-Z0-9_\-/]+$/, "Secret key names must contain only alphanumeric characters, underscores, hyphens, and forward slashes")).default([]).describe("Vault key names this cron task may read via the vault-broker daemon. " + "Empty by default — broker requests for unlisted keys are denied. " + "Note: this is misconfiguration protection (a typo in cron-A doesn't " + "accidentally read cron-B's keys) rather than a security boundary — " + "anyone who can edit cron scripts can also edit switchroom.yaml, and " + "anyone with the vault passphrase can read the vault file directly. " + "See docs/configuration.md for the full framing.")
10964
10969
  });
10965
10970
  AgentSoulSchema = exports_external.object({
@@ -10999,7 +11004,7 @@ var init_schema = __esm(() => {
10999
11004
  SessionEnd: exports_external.array(HookEntrySchema).optional()
11000
11005
  }).catchall(exports_external.array(HookEntrySchema)).optional();
11001
11006
  SubagentSchema = exports_external.object({
11002
- description: exports_external.string().describe("When the main agent should delegate to this sub-agent"),
11007
+ description: exports_external.string().optional().describe("When the main agent should delegate to this sub-agent"),
11003
11008
  model: exports_external.string().optional().describe("Model: 'sonnet', 'opus', 'haiku', full ID, or 'inherit' (default)"),
11004
11009
  background: exports_external.boolean().optional().describe("Run in background by default (non-blocking). Default false"),
11005
11010
  isolation: exports_external.enum(["worktree"]).optional().describe("'worktree' gives the sub-agent its own git branch"),
@@ -11036,10 +11041,6 @@ var init_schema = __esm(() => {
11036
11041
  deferred_completion_timeout_ms: exports_external.number().int().nonnegative().optional().describe("Force-close timeout (ms) for deferred sub-agent completion. After " + "the parent turn_end arrives while sub-agents are still running, the " + "card is force-closed after this many ms even if sub-agents never " + "finish. Watcher-disconnect safety net. Default 180000 (3 min)."),
11037
11042
  sub_agent_tick_interval_ms: exports_external.number().int().nonnegative().optional().describe("Heartbeat tick interval (ms) for sub-agent rendering. Forces a " + "re-render of the elapsed-time counter while sub-agents are running, " + "even during silent stretches between tool calls. Default 10000 (10 s). " + "Set to 0 to disable the elapsed-ticker path."),
11038
11043
  edit_budget_threshold: exports_external.number().int().nonnegative().optional().describe("Telegram API edit budget per minute before the progress-card driver " + "falls back to a slower coalesce window. When a chat accumulates more " + "than this many card edits in the trailing 60 s, the driver switches " + "to a wider coalesce interval until the rate drops back. Default 18. " + "Increase if your gateway frequently bumps the Telegram edit-rate ceiling " + "with many parallel sub-agents; decrease for a more conservative buffer."),
11039
- progress_card: exports_external.object({
11040
- delay_ms: exports_external.number().int().nonnegative().optional().describe("First-render delay (ms) for the pinned progress card (#842). The " + "driver buffers SessionEvents for this long after the turn starts; " + "if the turn ends before the threshold trips, no card is ever " + "posted. When the threshold trips, the card renders the full " + "buffered event stream and the live-update loop takes over. " + "Default 45000 (45 s). Set to 0 for the legacy immediate-render " + "behaviour."),
11041
- delay_ms_background: exports_external.number().int().nonnegative().optional().describe("First-render delay (ms) override for explicit background " + "sub-agent dispatches (#842). When the agent calls " + "`Agent({ run_in_background: true })`, the card is promoted out " + "of the suppression window using this delay instead of " + "`delay_ms`. Default 0 (immediate render — backgrounded work " + "should be visible right away).")
11042
- }).optional().describe("Progress-card first-render gating (#842). Defers the card until the " + "turn looks meaningful — short turns never flash a card at all."),
11043
11044
  stickers: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Sticker aliases for the `send_sticker` MCP tool (#576). Maps a " + "short alias name (e.g. 'happy', 'thinking') to a Telegram file_id. " + "Operator-curated — capture file_ids from inbound stickers the user " + "sends and add them here. The agent calls send_sticker(chat_id, " + "alias='happy') and the gateway resolves to the file_id at send " + "time. Aliases enable persona-flavored expressiveness without " + "exposing raw file_ids in the agent prompt. Personal-assistant / " + "health-coach personas benefit; coding agents typically don't " + "configure any."),
11044
11045
  voice_in: exports_external.object({
11045
11046
  enabled: exports_external.boolean().optional().describe("Master switch for voice-message transcription."),
@@ -11082,13 +11083,27 @@ var init_schema = __esm(() => {
11082
11083
  }).optional();
11083
11084
  TIMEZONE_REGEX = /^UTC$|^[A-Z][A-Za-z0-9_+-]+(\/[A-Z][A-Za-z0-9_+-]+){1,2}$/;
11084
11085
  ApproverIdSchema = exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+$/)]);
11085
- DriveConfigSchema = exports_external.object({
11086
+ GoogleWorkspaceTierSchema = exports_external.enum([
11087
+ "core",
11088
+ "extended",
11089
+ "complete"
11090
+ ]);
11091
+ GoogleWorkspaceConfigSchema = exports_external.object({
11086
11092
  google_client_id: exports_external.string().min(1).describe("Google OAuth client ID (literal string or vault reference e.g. 'vault:google-oauth-client-id')"),
11087
11093
  google_client_secret: exports_external.string().min(1).describe("Google OAuth client secret (literal string or vault reference e.g. 'vault:google-oauth-client-secret')"),
11088
- approvers: exports_external.array(ApproverIdSchema).min(1).describe("Array of numeric Telegram user IDs authorized to approve drive onboarding. " + "At least one must be specified.")
11094
+ approvers: exports_external.array(ApproverIdSchema).min(1).describe("Array of numeric Telegram user IDs authorized to approve drive onboarding. " + "At least one must be specified."),
11095
+ tier: GoogleWorkspaceTierSchema.optional().describe("RFC G Phase 1: which upstream MCP tier to expose. " + "core (default) = ~16 tools (Drive+Docs+Sheets+Calendar). " + "extended = ~40 tools (+Slides, Forms, Tasks, Chat). " + "complete = ~60+ tools (+Gmail; not recommended yet — see RFC G §5).")
11096
+ }).optional();
11097
+ AgentGoogleWorkspaceConfigSchema = exports_external.object({
11098
+ approvers: exports_external.array(ApproverIdSchema).min(1).optional().describe("Per-agent approver override. When set, replaces (does not extend) " + "the top-level drive.approvers list for this agent's onboarding card."),
11099
+ tier: GoogleWorkspaceTierSchema.optional().describe("Per-agent tier override (RFC G Phase 1). When set, replaces the " + "top-level google_workspace.tier for this agent. Common case: most " + "agents on `core`, one specialist on `extended` for Slides access.")
11089
11100
  }).optional();
11090
- AgentDriveConfigSchema = exports_external.object({
11091
- approvers: exports_external.array(ApproverIdSchema).min(1).optional().describe("Per-agent approver override. When set, replaces (does not extend) " + "the top-level drive.approvers list for this agent's onboarding card.")
11101
+ ReactionsSchema = exports_external.object({
11102
+ enabled: exports_external.boolean().optional().describe("Master switch for the reaction-trigger path. When false, " + "reactions are still persisted via recordReaction but never " + "dispatched to the agent as synthetic inbound turns. Default true."),
11103
+ trigger_emojis: exports_external.array(exports_external.string()).optional().describe("Emoji allowlist that triggers a synthetic inbound when reacted " + "to a bot message. Default ['\uD83D\uDC4E', '❌', '\uD83D\uDC4D', '✅']. Cascade " + "mode: REPLACE (not union) — setting this at a layer replaces " + "lower layers entirely, so an operator can narrow to [] to " + "disable triggering without flipping `enabled`."),
11104
+ debounce_ms: exports_external.number().int().nonnegative().optional().describe("Per-chat debounce window in ms. A qualifying reaction holds for " + "this long; a second qualifying reaction within the window " + "collapses both into a single batched synthetic turn. Default 30000."),
11105
+ per_hour_cap: exports_external.number().int().nonnegative().optional().describe("Max reaction-triggered synthetic turns per chat per rolling hour. " + "Refusals are stderr-logged but not surfaced to the agent. " + "Default 10. Set to 0 to disable triggering via the cap path."),
11106
+ group_admin_only: exports_external.boolean().optional().describe("In groups/supergroups (negative chat_id), only trigger a synthetic " + "turn when the reacter is a chat admin (creator or administrator). " + "Failing the lookup is treated as non-admin (fail-closed). " + "DMs are never affected by this flag — the reacter IS the user. " + "Default true.")
11092
11107
  }).optional();
11093
11108
  profileFields = {
11094
11109
  extends: exports_external.string().optional(),
@@ -11114,6 +11129,7 @@ var init_schema = __esm(() => {
11114
11129
  }).optional()
11115
11130
  }).optional(),
11116
11131
  schedule: exports_external.array(ScheduleEntrySchema).optional(),
11132
+ reactions: ReactionsSchema,
11117
11133
  model: exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._\-/\[\]:]*$/, "Model name must be alphanumeric with ._-/[]: only").optional(),
11118
11134
  thinking_effort: exports_external.enum(["low", "medium", "high", "xhigh", "max"]).optional().describe("Adaptive-thinking effort level passed as --effort to the claude CLI. " + "lower = faster/cheaper, higher = more reasoning. Omit to use Claude's default."),
11119
11135
  permission_mode: exports_external.enum(["acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"]).optional().describe("Permission mode passed as --permission-mode to the claude CLI. " + "Omit to use Claude's default (acceptEdits for switchroom agents). " + "Warning: bypassPermissions and dontAsk skip all safety checks — use only in trusted sandboxes."),
@@ -11134,6 +11150,12 @@ var init_schema = __esm(() => {
11134
11150
  claude_md_raw: exports_external.string().optional(),
11135
11151
  cli_args: exports_external.array(exports_external.string()).optional(),
11136
11152
  extra_stable_files: exports_external.array(exports_external.string()).optional().describe("Extra filenames (relative to the agent's workspace directory) to append " + "to the stable bootstrap render. Loaded once at session start via " + "`--append-system-prompt`. Missing files are silently skipped. " + "Example: ['BRIEF.md', 'CONTEXT.md']."),
11153
+ resources: exports_external.object({
11154
+ memory: exports_external.string().regex(/^\d+(\.\d+)?[kmgKMG]?$/, "memory must be a Docker size string like '6g', '512m', '1.5g'").optional().describe("Hard memory cap (Docker `mem_limit` → cgroup memory.max). When the " + "container exceeds this, the kernel OOM-kills processes in the cgroup. " + "Format: '6g', '1.5g', '512m'. When unset at every cascade layer the " + "compose generator falls back to the hard-coded per-profile defaults " + "in src/agents/compose.ts (klanker 6g, coding 2g, conversational 1.5g, " + "lightweight 1g, default 1.5g)."),
11155
+ memory_reservation: exports_external.string().regex(/^\d+(\.\d+)?[kmgKMG]?$/, "memory_reservation must be a Docker size string like '4g', '256m'").optional().describe("Soft memory floor (Docker `mem_reservation` → cgroup memory.low). " + "Under host-wide memory pressure, the kernel protects at least this " + "much from being reclaimed from the cgroup. Must be ≤ memory. Use to " + "keep an agent RAM-resident when the host has other tenants that " + "might push the box (Coolify apps, build jobs). Default: unset."),
11156
+ pids_limit: exports_external.number().int().positive().optional().describe("Max processes the cgroup can spawn (cgroup pids.max). Prevents " + "fork bombs and runaway test runners. Counts every process in the " + "cgroup including bash subprocesses, claude itself, sidecars, and " + "any test/build worker. A typical agent at idle uses ~30 PIDs; " + "`npm test`-style workloads can spike to 200+. Set generously " + "(2000 is a comfortable cap for test-running agents). Default: " + "unset (no cgroup pid cap)."),
11157
+ cpus: exports_external.number().positive().optional().describe("CPU quota (Docker `cpus`). Fractional values OK (e.g. 0.5, 2.0). " + "When unset at every cascade layer the compose generator falls " + "back to the per-profile default (klanker/coding 2.0, default 1.0, " + "lightweight 0.5).")
11158
+ }).optional().describe("Per-agent resource limits. Cascades through defaults → profile → " + "per-agent with per-field merge (agent wins on each field independently). " + "Any field left unset at every layer falls back to the hard-coded " + "per-profile defaults in src/agents/compose.ts."),
11137
11159
  experimental: exports_external.object({
11138
11160
  legacy_pty: exports_external.boolean().optional().describe("Opt out of the default tmux supervisor (#725) and run the agent under " + "the legacy PTY supervisor instead. Default: false (tmux is the default)."),
11139
11161
  legacy_autoaccept_expect: exports_external.boolean().optional().describe("Opt the autoaccept gateway back into the legacy expect-script behaviour " + "instead of the tmux send-keys path. Default: false.")
@@ -11147,13 +11169,13 @@ var init_schema = __esm(() => {
11147
11169
  bot_token: exports_external.string().optional().describe("Per-agent Telegram bot token or vault reference (overrides global telegram.bot_token)"),
11148
11170
  bot_username: exports_external.string().optional().describe("Per-agent Telegram bot username (without leading @) when it doesn't " + "contain the agent slug. Replaces the default 'username includes slug' " + "preflight check with an exact (case-insensitive) match. Use when an " + "agent and its bot have intentionally divergent names (e.g. agent " + "'lawgpt' paired with bot '@meken_law_bot')."),
11149
11171
  timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC' " + "(three-letter aliases like EST/PST and bare offsets like UTC+10 are not accepted)").optional().describe("Per-agent IANA timezone override. Wins over any profile/defaults " + "value and over the top-level switchroom.timezone global. Controls " + "the UserPromptSubmit timezone hook's emitted local time and the " + "systemd unit's TZ= env."),
11150
- auth_label: exports_external.string().optional().describe("Human-readable identity for the session-start greeting (e.g. 'user@example.com'). " + "Anthropic does not expose a public user-profile endpoint for OAuth tokens, so the " + "email/account cannot be read locally; the user declares it here. Appears in the Auth " + "row as '✓ max · <label> · expires ...'."),
11151
11172
  auth: exports_external.object({
11152
- accounts: exports_external.array(exports_external.string()).optional().describe("Ordered list of Anthropic account labels (from `~/.switchroom/accounts/`) " + "this agent can use. The first non-quota-exhausted account is the active one; " + "subsequent entries are auto-fallback targets. switchroom-auth-broker keeps " + "`<agentDir>/.claude/credentials.json` in sync with the active account on " + "every refresh and on every quota event. When unset, the agent falls back to " + "a single 'default' account; if no `default` account exists, the boot self-test " + "surfaces a one-line nudge to run `switchroom auth account add`.")
11153
- }).optional().describe("Account routing for switchroom-auth-broker. See " + "reference/share-auth-across-the-fleet.md for the unit-of-authentication model."),
11173
+ override: exports_external.string().min(1).optional().describe("Per-agent override of the fleet-wide `auth.active`. Edge-case use only " + "this agent talks to the named account regardless of fleet active. See RFC H §4.5.")
11174
+ }).optional().describe("Account routing for switchroom-auth-broker. RFC H schema uses " + "fleet-wide `auth.active` plus per-agent `override:` for edge cases. " + "Pre-RFC-H `auth.accounts: [..]` and `auth_label:` are migrated in-place " + "on first apply (see src/auth/migrate-schema.ts)."),
11154
11175
  dm_only: exports_external.boolean().optional().describe("Mark this agent as a DM-only bot — has its own bot_token and lives " + "exclusively in a private chat with the operator. Suppresses " + "scaffolding's default behavior of inheriting the global " + "telegram.forum_chat_id into the agent's access.json `groups` entry " + "(the forum chat the bot isn't a member of, which would otherwise " + "trigger a 'boot-probe-failed: 400 chat not found' warning every " + "restart). topic_name is still schema-required but unused — set it " + "to a display label like 'DM' for /switchroom status output."),
11155
11176
  topic_name: exports_external.string().describe("Telegram forum topic display name"),
11156
11177
  topic_emoji: exports_external.string().optional().describe("Emoji for the topic (e.g., '\uD83C\uDFCB️')"),
11178
+ purpose: exports_external.string().max(140).optional().describe("One-line description of what this agent does (≤140 chars). Shown to " + "peer agents when they call the agent-config MCP `peers_list` tool, so " + "every agent on the instance can answer 'is there an agent that does X' " + "without baking the fleet into prompts. Sourced live from " + "switchroom.yaml — never memorized into Hindsight. Falls back to " + "`topic_name` when absent."),
11157
11179
  role: exports_external.enum(["assistant", "foreman"]).optional().describe("Agent role. Default (omitted) is `assistant` — a fleet agent doing " + "user-facing tasks. `foreman` opts the agent in to switchroom's bundled " + "operator skills (switchroom-architecture / cli / health / install / manage " + "/ status), auto-symlinked into the agent's .claude/skills/ on scaffold and " + "reconcile. Fleet agents (assistant role) get no operator skills; reconcile " + "actively retracts them if the role flips back. See docs/skills.md for the model."),
11158
11180
  topic_id: exports_external.number().optional().describe("Telegram topic thread ID (auto-populated by switchroom topics sync)"),
11159
11181
  webhook_sources: exports_external.array(exports_external.enum(["github", "generic"])).optional().describe("[DEPRECATED — moved to channels.telegram.webhook_sources in #596] " + "Old per-agent location. Still read but logs a deprecation warning. " + "See channels.telegram.webhook_sources for the canonical spot."),
@@ -11172,6 +11194,7 @@ var init_schema = __esm(() => {
11172
11194
  tools: AgentToolsSchema,
11173
11195
  memory: AgentMemorySchema,
11174
11196
  schedule: exports_external.array(ScheduleEntrySchema).default([]),
11197
+ reactions: ReactionsSchema,
11175
11198
  model: exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._\-/\[\]:]*$/, "Model name must be alphanumeric with ._-/[]: only (no spaces or shell specials)").optional().describe("Claude model override (e.g., 'claude-sonnet-4-6')"),
11176
11199
  thinking_effort: exports_external.enum(["low", "medium", "high", "xhigh", "max"]).optional().describe("Adaptive-thinking effort level passed as --effort to the claude CLI. " + "Per-agent override wins over defaults.thinking_effort. " + "lower = faster/cheaper, higher = more reasoning. Omit to use Claude's default."),
11177
11200
  permission_mode: exports_external.enum(["acceptEdits", "auto", "bypassPermissions", "default", "dontAsk", "plan"]).optional().describe("Permission mode passed as --permission-mode to the claude CLI. " + "Per-agent override wins over defaults.permission_mode. " + "Warning: bypassPermissions and dontAsk skip all safety checks — use only in trusted sandboxes."),
@@ -11188,17 +11211,19 @@ var init_schema = __esm(() => {
11188
11211
  session_continuity: SessionContinuitySchema.describe("Handoff-briefing settings. When enabled (default), a Stop hook " + "summarizes each session at shutdown and start.sh injects that " + "briefing into the next session via --append-system-prompt."),
11189
11212
  channels: ChannelsSchema.describe("Per-channel configuration. Today only `telegram` is defined; the " + "shape is designed to expand to other channels (Slack, Discord, " + "Matrix, Email) as they're added."),
11190
11213
  dangerous_mode: exports_external.boolean().optional().describe("If true, include --dangerously-skip-permissions in start.sh"),
11191
- skip_permission_prompt: exports_external.boolean().optional().describe("If true, add skipDangerousModePermissionPrompt to settings.json"),
11214
+ skip_permission_prompt: exports_external.boolean().optional().describe("DEPRECATED no-op (accepted for backwards compatibility). Claude Code " + "ignores skipDangerousModePermissionPrompt at project scope; autoaccept " + "(src/agents/autoaccept.ts) handles the bypass-mode prompt instead. " + "Safe to remove from switchroom.yaml."),
11192
11215
  admin: exports_external.boolean().optional().describe("If true, the agent's Telegram gateway intercepts admin slash commands " + "(/agents, /logs, /restart, /delete, /update, /auth, /reconcile, etc.) " + "locally before forwarding to Claude. Commands are handled silently — " + "Claude never sees them. Requires the agent to use the switchroom-telegram " + "plugin. When false or absent, all messages pass through to Claude unchanged."),
11193
11216
  settings_raw: exports_external.record(exports_external.string(), exports_external.unknown()).optional().describe("Escape hatch: raw object deep-merged into the generated " + "settings.json as the final step. Use for Claude Code settings " + "keys switchroom doesn't wrap directly (e.g. effort, apiKeyHelper). " + "Power-user-only — prefer the typed fields when they exist."),
11194
11217
  claude_md_raw: exports_external.string().optional().describe("Escape hatch: markdown text appended verbatim to CLAUDE.md on " + "initial scaffold. Not re-applied on reconcile (CLAUDE.md is " + "user-protected). Use for one-off persona tuning that isn't " + "worth a template."),
11195
11218
  cli_args: exports_external.array(exports_external.string()).optional().describe("Escape hatch: extra arguments appended to the `exec claude` " + "invocation in start.sh. Use for Claude Code CLI flags switchroom " + "doesn't expose directly (e.g. --effort high, " + "--exclude-dynamic-system-prompt-sections)."),
11196
- add_dirs: exports_external.array(exports_external.string()).optional().describe("Additional filesystem paths the agent's tools can access. Passed " + "as repeated --add-dir <path> on the claude invocation. Use to grant " + "an agent reach into shared dirs (e.g. '/share/collab') without " + "scaffold hacks. Per-agent only — paths are persona-specific. See #199."),
11219
+ add_dirs: exports_external.array(exports_external.string()).optional().describe("Additional filesystem paths the agent's tools can access. Passed " + "as repeated --add-dir <path> on the claude invocation. Use to grant " + "an agent reach into shared dirs (e.g. '/share/collab') without " + "scaffold hacks. Per-agent only — paths are persona-specific. See #199. " + "Note: this only adjusts the claude CLI's --add-dir tool-reach allowlist. " + "If the path is not already inside the agent's container, also declare " + "it in `bind_mounts:` (admin agents only) — otherwise the path doesn't " + "exist inside the sandbox and --add-dir is a no-op."),
11220
+ bind_mounts: exports_external.array(AgentBindMountSchema).optional().describe("Extra host paths bind-mounted into this agent's container, on top of " + "the standard dual-mount baseline. ADMIN-ONLY: the compose generator " + "refuses to emit bind_mounts unless `admin: true` is also set on the " + "same agent. Use to dogfood / self-modify switchroom or another repo " + "(see issue #1164). Pair with `add_dirs:` so claude's tool-reach " + "allowlist also covers the mounted path. System paths (/, /etc, " + "/proc, /sys, /dev, /run, /var/run, /boot, /var/lib/docker, " + "/var/run/docker.sock) are denylisted regardless of mode."),
11197
11221
  allowed_tools: exports_external.array(exports_external.string()).optional().describe("Granular tool allowlist passed verbatim to Claude Code's --allowedTools " + "flag. Supports patterns like 'Bash(git *)' or 'Edit(*.md)' that the " + "coarse `tools.allow` field can't express. When set, Claude Code OR-merges " + "with `tools.allow` (granular only when present, otherwise coarse — chosen " + "via #199 to keep blast radius minimal for existing operators on tools.allow). " + "See #199."),
11198
11222
  disallowed_tools: exports_external.array(exports_external.string()).optional().describe("Granular tool denylist passed verbatim to Claude Code's --disallowedTools " + "flag. Same pattern syntax as allowed_tools (e.g. 'Bash(rm *)'). See #199."),
11199
11223
  extra_stable_files: exports_external.array(exports_external.string()).optional().describe("Extra filenames (relative to the agent's workspace directory) to append " + "to the stable bootstrap render. Loaded once at session start via " + "`--append-system-prompt`. Missing files are silently skipped. " + "Example: ['BRIEF.md', 'CONTEXT.md']."),
11200
11224
  code_repos: exports_external.array(CodeRepoEntrySchema).optional().describe("Git repositories this agent is allowed to claim worktrees from. " + "Each entry provides a short name alias, a source path, and an " + "optional concurrency cap (default 5). When code_repos is set, " + "claim_worktree accepts the alias as the repo argument. " + "Absolute paths may always be passed regardless of this list."),
11201
- drive: AgentDriveConfigSchema.describe("Per-agent drive onboarding overrides (currently just approvers). " + "When set, replaces the top-level drive.approvers list for this agent. " + "google_client_id/secret are not per-agent — they live at the top level."),
11225
+ drive: AgentGoogleWorkspaceConfigSchema.describe("RFC D legacy key — use `google_workspace:` instead. Per-agent " + "google_workspace overrides (currently approvers + tier). When set, " + "replaces the top-level approvers list for this agent. " + "google_client_id/secret are not per-agent — they live at the top level."),
11226
+ google_workspace: AgentGoogleWorkspaceConfigSchema.describe("RFC G canonical key. Per-agent Google Workspace overrides — currently " + "approvers (replaces, does not extend the top-level list) and tier " + "(`core` | `extended` | `complete`, replaces top-level default). " + "google_client_id/secret are not per-agent — they live at the top level. " + "Mutually exclusive with `drive:` on the same agent (loader fails fast " + "if both are set)."),
11202
11227
  repos: exports_external.record(exports_external.string().regex(/^[a-z0-9][a-z0-9-]*$/, "Repo slug must be kebab-case ASCII: start with a lowercase letter or digit, contain only lowercase letters, digits, and hyphens"), exports_external.object({
11203
11228
  url: exports_external.string().min(1).describe("Git remote URL for the repo (e.g. 'git@github.com:org/repo.git' or " + "'https://github.com/org/repo.git'). Used verbatim for git clone."),
11204
11229
  branch_default: exports_external.string().optional().describe("Default branch to track (defaults to the remote's HEAD, typically 'main'). " + "The per-agent branch 'agent/<agentName>/main' fast-forwards to this branch " + "when the worktree is clean on session start.")
@@ -11206,7 +11231,13 @@ var init_schema = __esm(() => {
11206
11231
  experimental: exports_external.object({
11207
11232
  legacy_pty: exports_external.boolean().optional().describe("Opt out of the default tmux supervisor (#725) and run the agent " + "under the legacy PTY supervisor instead. Default: false."),
11208
11233
  legacy_autoaccept_expect: exports_external.boolean().optional().describe("Opt the autoaccept gateway back into the legacy expect-script " + "behaviour instead of the tmux send-keys path. Default: false.")
11209
- }).optional().describe("Opt-in flags for experimental / legacy behaviours. Cascades through " + "defaults → profile → per-agent.")
11234
+ }).optional().describe("Opt-in flags for experimental / legacy behaviours. Cascades through " + "defaults → profile → per-agent."),
11235
+ resources: exports_external.object({
11236
+ memory: exports_external.string().regex(/^\d+(\.\d+)?[kmgKMG]?$/).optional(),
11237
+ memory_reservation: exports_external.string().regex(/^\d+(\.\d+)?[kmgKMG]?$/).optional(),
11238
+ pids_limit: exports_external.number().int().positive().optional(),
11239
+ cpus: exports_external.number().positive().optional()
11240
+ }).optional()
11210
11241
  });
11211
11242
  TelegramConfigSchema = exports_external.object({
11212
11243
  bot_token: exports_external.string().describe("Telegram bot token or vault reference (e.g., 'vault:telegram-bot-token')"),
@@ -11229,13 +11260,26 @@ var init_schema = __esm(() => {
11229
11260
  socket: exports_external.string().default("~/.switchroom/vault-broker.sock").describe("Unix domain socket path for the vault-broker daemon"),
11230
11261
  enabled: exports_external.boolean().default(true).describe("Whether to start the vault-broker daemon on agent launch"),
11231
11262
  autoUnlock: exports_external.boolean().default(false).describe("Auto-unlock the vault at broker start using a machine-bound " + "encrypted blob. Off by default. When enabled, the broker reads " + "the configured blob path, derives the AES key from /etc/machine-id, " + "decrypts the passphrase, and unlocks the vault — no sudo, no " + "systemd-creds, no TPM. Run `switchroom vault broker " + "enable-auto-unlock` once to write the blob."),
11232
- autoUnlockCredentialPath: exports_external.string().default("~/.switchroom/vault-auto-unlock").describe("Path to the machine-bound auto-unlock blob (see " + "src/vault/auto-unlock.ts for the format). Default lives under " + "~/.switchroom so it can be bind-mounted into the vault-broker " + "container by docker compose. Tilde-expansion happens " + "at read time.")
11233
- }).default({}).describe("Vault-broker daemon configuration. The broker holds the decrypted vault " + "in memory and serves secrets to cron scripts via a Unix socket, so the " + "vault passphrase is entered once at startup rather than per-cron invocation.")
11263
+ autoUnlockCredentialPath: exports_external.string().default("~/.switchroom/vault-auto-unlock").describe("Path to the machine-bound auto-unlock blob (see " + "src/vault/auto-unlock.ts for the format). Default lives under " + "~/.switchroom so it can be bind-mounted into the vault-broker " + "container by docker compose. Tilde-expansion happens " + "at read time."),
11264
+ approvalAuth: exports_external.enum(["passphrase", "telegram-id"]).default("passphrase").describe("Posture for tap-to-Approve on vault grant cards. `passphrase` " + "(default) prompts the operator to type the vault passphrase on " + "every Approve two-factor (Telegram ID + passphrase). " + "`telegram-id` mints immediately on Approve with no passphrase " + "prompt — single-factor (Telegram ID only); REQUIRES " + "`autoUnlock: true` so the broker already holds the passphrase. " + "Trades a factor of security for smoother UX; opt-in only."),
11265
+ postureMintAgents: exports_external.array(exports_external.string().min(1)).default([]).describe("Per-agent opt-in for posture-attested broker calls (`mint_grant` / " + "`list_grants` / `put` with `attest_via_posture: true`). Only agents " + "whose names are in this list can use the silent-mint path under " + "`approvalAuth: telegram-id`. Default `[]` — no agent can self-mint " + "until the operator explicitly opts it in. The request's `agent` " + "field must also equal the calling peer's resolved agent name " + "(broker rejects cross-agent posture mints). When `approvalAuth` is " + "`passphrase` this list is ignored — passphrase attestation still " + "works as before. Each entry is an agent slug exactly as it appears " + "under `agents:` in this config.")
11266
+ }).default({}).superRefine((broker, ctx) => {
11267
+ if (broker.approvalAuth === "telegram-id" && broker.autoUnlock !== true) {
11268
+ ctx.addIssue({
11269
+ code: exports_external.ZodIssueCode.custom,
11270
+ message: "`vault.broker.approvalAuth: telegram-id` requires `autoUnlock: true` — single-factor approval needs the broker already unlocked at startup.",
11271
+ path: ["approvalAuth"]
11272
+ });
11273
+ }
11274
+ }).describe("Vault-broker daemon configuration. The broker holds the decrypted vault " + "in memory and serves secrets to cron scripts via a Unix socket, so the " + "vault passphrase is entered once at startup rather than per-cron invocation.")
11234
11275
  });
11235
11276
  QuotaConfigSchema = exports_external.object({
11236
11277
  weekly_budget_usd: exports_external.number().positive().optional().describe("Weekly USD spend budget. If unset, the greeting shows raw usage only."),
11237
11278
  monthly_budget_usd: exports_external.number().positive().optional().describe("Monthly USD spend budget. If unset, the greeting shows raw usage only.")
11238
11279
  });
11280
+ HostControlConfigSchema = exports_external.object({
11281
+ enabled: exports_external.boolean().optional().describe("Opt-in to the host-control daemon. Default: false. " + "When true, the compose generator emits per-agent bind mounts " + "at `~/.switchroom/hostd/<name>/sock` for every admin-flagged " + "agent. Install the daemon with `switchroom hostd install` — " + "it runs as a docker container in its own compose project " + "(`switchroom-hostd`), separate from the agent fleet's compose " + "project so `up -d --remove-orphans` cycles of the fleet " + "can't recreate the daemon mid-RPC. See RFC C §5.1. " + "Since Phase 2 (#1175 PR γ) the gateway's /restart, /new, /reset, " + "and /update apply slash-commands automatically dispatch through " + "hostd when enabled — replacing the in-container " + "`spawnSwitchroomDetached` shellout that requires docker access. " + "Set enabled: true on docker-mode installs to make those verbs work " + "(they otherwise fail because the agent container has no docker " + "binary/socket).")
11282
+ });
11239
11283
  SwitchroomConfigSchema = exports_external.object({
11240
11284
  switchroom: exports_external.object({
11241
11285
  version: exports_external.literal(1).describe("Config schema version"),
@@ -11246,8 +11290,28 @@ var init_schema = __esm(() => {
11246
11290
  telegram: TelegramConfigSchema,
11247
11291
  memory: MemoryBackendConfigSchema.optional(),
11248
11292
  vault: VaultConfigSchema.optional(),
11249
- drive: DriveConfigSchema.describe("Optional drive onboarding configuration. When set, supplies Google " + "OAuth client credentials and the approver allowlist for `switchroom " + "drive connect`. Env vars (SWITCHROOM_GOOGLE_CLIENT_ID, " + "SWITCHROOM_GOOGLE_CLIENT_SECRET, SWITCHROOM_APPROVER_USER_ID) take " + "precedence over this block when set, preserving back-compat with " + "the env-only flow shipped in #766."),
11293
+ auth: exports_external.object({
11294
+ active: exports_external.string().min(1).optional().describe("Fleet-wide active Anthropic account label. Every agent without " + "an explicit `agent.auth.override` uses this account. See " + "docs/auth.md for the full model. Set by `switchroom auth use <label>`."),
11295
+ fallback_order: exports_external.array(exports_external.string().min(1)).optional().describe("Ordered list of account labels for `switchroom auth rotate` to cycle " + "through when the active account hits a quota event. First entry is " + "normally the same as `auth.active`. When unset, `rotate` is a no-op."),
11296
+ consumers: exports_external.array(exports_external.object({
11297
+ name: exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, {
11298
+ message: "Consumer name must be a path-safe slug (letters, digits, underscore, hyphen)"
11299
+ }).describe("Socket-path identity; binds at /run/switchroom/auth-broker/<name>/sock"),
11300
+ account: exports_external.string().min(1).describe("Pinned account label for this consumer. `get-credentials` returns " + "this account's credentials; `mark-exhausted` from this consumer " + "only affects this account."),
11301
+ uid: exports_external.number().int().nonnegative().optional().describe("Optional UID to chown the consumer socket to (defaults to 0 = root, " + "suitable for sibling containers running as root).")
11302
+ })).optional().describe("Non-agent peers that hold a broker socket (RFC H §4.8). Each gets " + "its own `/run/switchroom/auth-broker/<name>/sock` chowned to its UID. " + "Consumers cannot be admins; a consumer name that collides with an " + "agent (whether that agent has `admin: true` or not) is a config " + "error caught at schema validation.")
11303
+ }).optional().describe("Switchroom-auth-broker configuration (RFC H). Fleet-wide active account, " + "fallback order, admin-agent ACL, and ephemeral-consumer surface. " + "Required from the v0.8+ schema onwards; pre-v0.8 fleets are migrated " + "in-place by `switchroom apply` (see src/auth/migrate-schema.ts)."),
11304
+ drive: GoogleWorkspaceConfigSchema.describe("RFC D legacy key — use `google_workspace:` instead. Optional Google " + "Workspace onboarding configuration. When set, supplies Google OAuth " + "client credentials, the approver allowlist for `switchroom drive " + "connect`, and the optional tier knob. Env vars " + "(SWITCHROOM_GOOGLE_CLIENT_ID, SWITCHROOM_GOOGLE_CLIENT_SECRET, " + "SWITCHROOM_APPROVER_USER_ID) take precedence over this block when " + "set, preserving back-compat with the env-only flow shipped in #766."),
11305
+ google_workspace: GoogleWorkspaceConfigSchema.describe("RFC G canonical key. Top-level Google Workspace configuration — " + "OAuth client credentials, approver allowlist, and tier knob (`core` " + "| `extended` | `complete`, default `core`). Mutually exclusive with " + "`drive:` at the top level (loader fails fast if both are set)."),
11250
11306
  quota: QuotaConfigSchema.optional().describe("Optional weekly/monthly USD spend budgets rendered in the session " + "greeting. Usage is read from ccusage at runtime; no network calls."),
11307
+ host_control: HostControlConfigSchema.optional().describe("Optional host-control daemon configuration. See RFC C " + "(docs/rfcs/host-control-daemon.md) and the field-level help on " + "`enabled` for the Phase 1 scope."),
11308
+ google_accounts: exports_external.record(exports_external.string().regex(/^[^@\s:]+@[^@\s:]+\.[^@\s:]+$/, {
11309
+ message: "Account key must be a Google account email like 'alice@example.com' (colons not allowed)"
11310
+ }).transform((v) => v.trim().toLowerCase()), exports_external.object({
11311
+ enabled_for: exports_external.array(exports_external.string().regex(/^[a-z0-9][a-z0-9_-]{0,50}$/, {
11312
+ message: "Agent name must match the standard agent-name pattern"
11313
+ })).describe("Agent slugs that may read this account's vault slots " + "(`google:<account>:refresh_token` etc). Per-agent ACL is " + "enforced at the broker, not at the agent identity layer — " + "the agent still authenticates via socket-path-as-identity " + "per RFC D §4.1, broker just gates the cross-agent token share.")
11314
+ })).optional().describe("RFC G Phase 2: per-Google-account ACL for vault slots holding " + "OAuth refresh tokens. Maps account email → list of agents " + "permitted to read that account's slots. Written by `switchroom " + "auth google enable|disable` (Phase 3); read by the broker on " + "every Google slot access. Replaces RFC D's per-agent vault slot " + "scope (which can't express 'two agents share one Google account')."),
11251
11315
  defaults: AgentDefaultsSchema.describe("Implicit bottom-of-cascade profile applied to every agent before " + "per-agent config and `extends:` resolution. Tools, mcp_servers, and " + "schedule are unioned/concatenated; scalars and nested objects are " + "shallow-merged with per-agent values winning."),
11252
11316
  profiles: exports_external.record(exports_external.string(), ProfileSchema).optional().describe("Named profile definitions. Agents reference via `extends: <name>`. " + "Inline profiles declared here take priority over filesystem " + "profiles/<name>/ directories when both exist."),
11253
11317
  agents: exports_external.record(exports_external.string().regex(/^[a-z0-9][a-z0-9_-]{0,50}$/, {
@@ -11282,6 +11346,142 @@ function resolveDualPath(pathStr) {
11282
11346
  var DEFAULT_STATE_DIR = ".switchroom", LEGACY_STATE_DIR = ".clerk";
11283
11347
  var init_paths = () => {};
11284
11348
 
11349
+ // src/config/overlay-schema.ts
11350
+ var OverlayDocSchema;
11351
+ var init_overlay_schema = __esm(() => {
11352
+ init_zod();
11353
+ init_schema();
11354
+ OverlayDocSchema = exports_external.object({
11355
+ schedule: exports_external.array(ScheduleEntrySchema).optional(),
11356
+ skills: exports_external.array(exports_external.string()).optional()
11357
+ }).strict();
11358
+ });
11359
+
11360
+ // src/config/overlay-loader.ts
11361
+ import { existsSync as existsSync2, readFileSync, readdirSync, statSync } from "node:fs";
11362
+ import { resolve as resolve2 } from "node:path";
11363
+ function overlayDirFor(agentName, subdir) {
11364
+ const base = resolveDualPath(`~/.switchroom/agents/${agentName}/${subdir}`);
11365
+ return resolve2(base);
11366
+ }
11367
+ function listYamlFiles(dir) {
11368
+ if (!existsSync2(dir))
11369
+ return [];
11370
+ let entries;
11371
+ try {
11372
+ entries = readdirSync(dir);
11373
+ } catch {
11374
+ return [];
11375
+ }
11376
+ const out = [];
11377
+ for (const name of entries) {
11378
+ if (!/\.ya?ml$/i.test(name))
11379
+ continue;
11380
+ const full = resolve2(dir, name);
11381
+ try {
11382
+ if (statSync(full).isFile())
11383
+ out.push(full);
11384
+ } catch {}
11385
+ }
11386
+ return out.sort();
11387
+ }
11388
+ function stampOverlay(entry) {
11389
+ Object.defineProperty(entry, OVERLAY_SOURCE, {
11390
+ value: true,
11391
+ enumerable: false,
11392
+ configurable: false,
11393
+ writable: false
11394
+ });
11395
+ return entry;
11396
+ }
11397
+ function applyAgentOverlays(config) {
11398
+ const warnings = [];
11399
+ const agents = config.agents ?? {};
11400
+ for (const [agentName, agentCfg] of Object.entries(agents)) {
11401
+ try {
11402
+ const scheduleDir = overlayDirFor(agentName, "schedule.d");
11403
+ const files = listYamlFiles(scheduleDir);
11404
+ if (files.length > 0) {
11405
+ const merged = [...agentCfg.schedule ?? []];
11406
+ for (const file of files) {
11407
+ try {
11408
+ const raw = readFileSync(file, "utf-8");
11409
+ const parsed = $parse(raw);
11410
+ const doc = OverlayDocSchema.parse(parsed);
11411
+ for (const entry of doc.schedule ?? []) {
11412
+ if (entry.secrets && entry.secrets.length > 0) {
11413
+ const w = {
11414
+ agent: agentName,
11415
+ file,
11416
+ reason: "Overlay schedule entry declares secrets — dropped pending Phase E operator approval"
11417
+ };
11418
+ warnings.push(w);
11419
+ console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${w.reason}`);
11420
+ continue;
11421
+ }
11422
+ merged.push(stampOverlay(entry));
11423
+ }
11424
+ } catch (err) {
11425
+ const reason = err instanceof ZodError ? `schema rejection: ${err.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ")}` : `parse error: ${err.message}`;
11426
+ warnings.push({ agent: agentName, file, reason });
11427
+ console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${reason}`);
11428
+ }
11429
+ }
11430
+ agentCfg.schedule = merged;
11431
+ }
11432
+ } catch (err) {
11433
+ warnings.push({
11434
+ agent: agentName,
11435
+ file: "(agent schedule overlay scan)",
11436
+ reason: `unexpected error: ${err.message}`
11437
+ });
11438
+ console.warn(`[switchroom] overlay-loader: agent='${agentName}' schedule.d: unexpected error: ${err.message}`);
11439
+ }
11440
+ try {
11441
+ const skillsDir = overlayDirFor(agentName, "skills.d");
11442
+ const skillFiles = listYamlFiles(skillsDir);
11443
+ if (skillFiles.length === 0) {} else {
11444
+ const merged = [...agentCfg.skills ?? []];
11445
+ const seen = new Set(merged);
11446
+ for (const file of skillFiles) {
11447
+ try {
11448
+ const raw = readFileSync(file, "utf-8");
11449
+ const parsed = $parse(raw);
11450
+ const doc = OverlayDocSchema.parse(parsed);
11451
+ for (const skillName of doc.skills ?? []) {
11452
+ if (seen.has(skillName))
11453
+ continue;
11454
+ seen.add(skillName);
11455
+ merged.push(skillName);
11456
+ }
11457
+ } catch (err) {
11458
+ const reason = err instanceof ZodError ? `schema rejection: ${err.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ")}` : `parse error: ${err.message}`;
11459
+ warnings.push({ agent: agentName, file, reason });
11460
+ console.warn(`[switchroom] overlay-loader: agent='${agentName}' file='${file}': ${reason}`);
11461
+ }
11462
+ }
11463
+ agentCfg.skills = merged;
11464
+ }
11465
+ } catch (err) {
11466
+ warnings.push({
11467
+ agent: agentName,
11468
+ file: "(agent skills overlay scan)",
11469
+ reason: `unexpected error: ${err.message}`
11470
+ });
11471
+ console.warn(`[switchroom] overlay-loader: agent='${agentName}' skills.d: unexpected error: ${err.message}`);
11472
+ }
11473
+ }
11474
+ return { config, warnings };
11475
+ }
11476
+ var OVERLAY_SOURCE;
11477
+ var init_overlay_loader = __esm(() => {
11478
+ init_dist();
11479
+ init_zod();
11480
+ init_overlay_schema();
11481
+ init_paths();
11482
+ OVERLAY_SOURCE = Symbol.for("switchroom.config.overlay-source");
11483
+ });
11484
+
11285
11485
  // src/config/loader.ts
11286
11486
  var exports_loader = {};
11287
11487
  __export(exports_loader, {
@@ -11291,36 +11491,74 @@ __export(exports_loader, {
11291
11491
  findConfigFile: () => findConfigFile,
11292
11492
  ConfigError: () => ConfigError
11293
11493
  });
11294
- import { readFileSync, existsSync as existsSync2 } from "node:fs";
11494
+ import { readFileSync as readFileSync2, existsSync as existsSync3 } from "node:fs";
11295
11495
  import { homedir } from "node:os";
11296
- import { resolve as resolve2 } from "node:path";
11496
+ import { resolve as resolve3 } from "node:path";
11297
11497
  function formatZodErrors(error) {
11298
11498
  return error.errors.map((e) => {
11299
11499
  const path = e.path.join(".");
11300
11500
  return ` ${path}: ${e.message}`;
11301
11501
  });
11302
11502
  }
11503
+ function coerceLegacyGoogleWorkspaceKeys(parsed, filePath) {
11504
+ const stableStringify = (v) => {
11505
+ if (v === null || typeof v !== "object")
11506
+ return JSON.stringify(v);
11507
+ if (Array.isArray(v))
11508
+ return `[${v.map(stableStringify).join(",")}]`;
11509
+ const obj = v;
11510
+ const keys = Object.keys(obj).sort();
11511
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
11512
+ };
11513
+ const aliasInPlace = (obj, where) => {
11514
+ const a = obj.drive;
11515
+ const b = obj.google_workspace;
11516
+ if (a !== undefined && b !== undefined) {
11517
+ if (stableStringify(a) !== stableStringify(b)) {
11518
+ throw new ConfigError(`Both \`drive:\` and \`google_workspace:\` are set on ${where} in ${filePath} with different values.`, [
11519
+ " These are aliases — pick one and remove the other.",
11520
+ " `google_workspace:` is the RFC G canonical key; `drive:` is the legacy alias.",
11521
+ " Allowed during transition: setting both with identical values."
11522
+ ]);
11523
+ }
11524
+ return;
11525
+ }
11526
+ if (a !== undefined && b === undefined)
11527
+ obj.google_workspace = a;
11528
+ if (b !== undefined && a === undefined)
11529
+ obj.drive = b;
11530
+ };
11531
+ aliasInPlace(parsed, "the top level");
11532
+ const agents = parsed.agents;
11533
+ if (agents && typeof agents === "object" && !Array.isArray(agents)) {
11534
+ for (const [name, agent] of Object.entries(agents)) {
11535
+ if (agent && typeof agent === "object" && !Array.isArray(agent)) {
11536
+ aliasInPlace(agent, `agent \`${name}\``);
11537
+ }
11538
+ }
11539
+ }
11540
+ }
11303
11541
  function findConfigFile(startDir) {
11304
11542
  const envPath = process.env.SWITCHROOM_CONFIG;
11305
11543
  const home2 = homedir();
11306
- const userDir = resolve2(home2, ".switchroom");
11544
+ const userDir = resolve3(home2, ".switchroom");
11307
11545
  const searchPaths = [
11308
- envPath ? resolve2(envPath) : null,
11309
- startDir ? resolve2(startDir, "switchroom.yaml") : null,
11310
- startDir ? resolve2(startDir, "switchroom.yml") : null,
11311
- startDir ? resolve2(startDir, "clerk.yaml") : null,
11312
- startDir ? resolve2(startDir, "clerk.yml") : null,
11313
- resolve2(process.cwd(), "switchroom.yaml"),
11314
- resolve2(process.cwd(), "switchroom.yml"),
11315
- resolve2(process.cwd(), "clerk.yaml"),
11316
- resolve2(process.cwd(), "clerk.yml"),
11317
- resolve2(userDir, "switchroom.yaml"),
11318
- resolve2(userDir, "switchroom.yml"),
11319
- resolve2(userDir, "clerk.yaml"),
11320
- resolve2(userDir, "clerk.yml")
11546
+ envPath ? resolve3(envPath) : null,
11547
+ startDir ? resolve3(startDir, "switchroom.yaml") : null,
11548
+ startDir ? resolve3(startDir, "switchroom.yml") : null,
11549
+ startDir ? resolve3(startDir, "clerk.yaml") : null,
11550
+ startDir ? resolve3(startDir, "clerk.yml") : null,
11551
+ resolve3(process.cwd(), "switchroom.yaml"),
11552
+ resolve3(process.cwd(), "switchroom.yml"),
11553
+ resolve3(process.cwd(), "clerk.yaml"),
11554
+ resolve3(process.cwd(), "clerk.yml"),
11555
+ resolve3(userDir, "switchroom.yaml"),
11556
+ resolve3(userDir, "switchroom.yml"),
11557
+ resolve3(userDir, "clerk.yaml"),
11558
+ resolve3(userDir, "clerk.yml")
11321
11559
  ].filter(Boolean);
11322
11560
  for (const path of searchPaths) {
11323
- if (existsSync2(path)) {
11561
+ if (existsSync3(path)) {
11324
11562
  return path;
11325
11563
  }
11326
11564
  }
@@ -11328,12 +11566,12 @@ function findConfigFile(startDir) {
11328
11566
  }
11329
11567
  function loadConfig(configPath) {
11330
11568
  const filePath = configPath ?? findConfigFile();
11331
- if (!existsSync2(filePath)) {
11569
+ if (!existsSync3(filePath)) {
11332
11570
  throw new ConfigError(`Config file not found: ${filePath}`);
11333
11571
  }
11334
11572
  let raw;
11335
11573
  try {
11336
- raw = readFileSync(filePath, "utf-8");
11574
+ raw = readFileSync2(filePath, "utf-8");
11337
11575
  } catch (err) {
11338
11576
  throw new ConfigError(`Failed to read config file: ${filePath}`, [
11339
11577
  ` ${err.message}`
@@ -11352,16 +11590,26 @@ function loadConfig(configPath) {
11352
11590
  obj.switchroom = obj.clerk;
11353
11591
  delete obj.clerk;
11354
11592
  }
11593
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
11594
+ coerceLegacyGoogleWorkspaceKeys(parsed, filePath);
11595
+ }
11596
+ let config;
11355
11597
  try {
11356
- return SwitchroomConfigSchema.parse(parsed);
11598
+ config = SwitchroomConfigSchema.parse(parsed);
11357
11599
  } catch (err) {
11358
11600
  if (err instanceof ZodError) {
11359
11601
  throw new ConfigError("Invalid switchroom.yaml configuration", formatZodErrors(err));
11360
11602
  }
11361
11603
  throw err;
11362
11604
  }
11605
+ applyAgentOverlays(config);
11606
+ return config;
11363
11607
  }
11364
11608
  function resolveAgentsDir(config) {
11609
+ const override = process.env.SWITCHROOM_AGENTS_DIR;
11610
+ if (override && override.length > 0 && override.startsWith("/")) {
11611
+ return override;
11612
+ }
11365
11613
  return resolveDualPath(config.switchroom.agents_dir);
11366
11614
  }
11367
11615
  function resolvePath(pathStr) {
@@ -11373,6 +11621,7 @@ var init_loader = __esm(() => {
11373
11621
  init_zod();
11374
11622
  init_schema();
11375
11623
  init_paths();
11624
+ init_overlay_loader();
11376
11625
  ConfigError = class ConfigError extends Error {
11377
11626
  details;
11378
11627
  constructor(message, details) {
@@ -11385,8 +11634,8 @@ var init_loader = __esm(() => {
11385
11634
 
11386
11635
  // src/vault/approvals/kernel-server.ts
11387
11636
  import * as net from "node:net";
11388
- import { mkdirSync, chmodSync, chownSync, existsSync as existsSync3, unlinkSync, readdirSync, statSync } from "node:fs";
11389
- import { dirname, resolve as resolve3, basename } from "node:path";
11637
+ import { mkdirSync, chmodSync, chownSync, existsSync as existsSync4, unlinkSync, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
11638
+ import { dirname, resolve as resolve4, basename } from "node:path";
11390
11639
  import { Database } from "bun:sqlite";
11391
11640
 
11392
11641
  // src/vault/broker/protocol.ts
@@ -11408,7 +11657,8 @@ var PutRequestSchema = exports_external.object({
11408
11657
  exports_external.object({ kind: exports_external.literal("binary"), value: exports_external.string() })
11409
11658
  ]),
11410
11659
  token: exports_external.string().optional(),
11411
- passphrase: exports_external.string().optional()
11660
+ passphrase: exports_external.string().optional(),
11661
+ attest_via_posture: exports_external.boolean().optional()
11412
11662
  });
11413
11663
  var ListRequestSchema = exports_external.object({
11414
11664
  v: exports_external.literal(1),
@@ -11422,12 +11672,16 @@ var MintGrantRequestSchema = exports_external.object({
11422
11672
  keys: exports_external.array(exports_external.string().min(1)),
11423
11673
  ttl_seconds: exports_external.number().int().positive().nullable(),
11424
11674
  description: exports_external.string().optional(),
11425
- write_keys: exports_external.array(exports_external.string().min(1)).optional()
11675
+ write_keys: exports_external.array(exports_external.string().min(1)).optional(),
11676
+ passphrase: exports_external.string().optional(),
11677
+ attest_via_posture: exports_external.boolean().optional()
11426
11678
  });
11427
11679
  var ListGrantsRequestSchema = exports_external.object({
11428
11680
  v: exports_external.literal(1),
11429
11681
  op: exports_external.literal("list_grants"),
11430
- agent: exports_external.string().optional()
11682
+ agent: exports_external.string().optional(),
11683
+ passphrase: exports_external.string().optional(),
11684
+ attest_via_posture: exports_external.boolean().optional()
11431
11685
  });
11432
11686
  var RevokeGrantRequestSchema = exports_external.object({
11433
11687
  v: exports_external.literal(1),
@@ -11682,11 +11936,8 @@ function canonicalizeApproverSet(approvers) {
11682
11936
 
11683
11937
  // src/vault/approvals/schema.ts
11684
11938
  function migrateApprovalSchema(db) {
11685
- db.run(`DROP TABLE IF EXISTS approval_audit`);
11686
- db.run(`DROP TABLE IF EXISTS approval_nonces`);
11687
- db.run(`DROP TABLE IF EXISTS approval_decisions`);
11688
11939
  db.run(`
11689
- CREATE TABLE approval_decisions (
11940
+ CREATE TABLE IF NOT EXISTS approval_decisions (
11690
11941
  id TEXT PRIMARY KEY,
11691
11942
  agent_unit TEXT NOT NULL,
11692
11943
  scope TEXT NOT NULL,
@@ -11702,12 +11953,12 @@ function migrateApprovalSchema(db) {
11702
11953
  )
11703
11954
  `);
11704
11955
  db.run(`
11705
- CREATE INDEX approval_decisions_lookup
11956
+ CREATE INDEX IF NOT EXISTS approval_decisions_lookup
11706
11957
  ON approval_decisions(agent_unit, scope, action)
11707
11958
  WHERE revoked_at IS NULL
11708
11959
  `);
11709
11960
  db.run(`
11710
- CREATE TABLE approval_nonces (
11961
+ CREATE TABLE IF NOT EXISTS approval_nonces (
11711
11962
  request_id TEXT PRIMARY KEY,
11712
11963
  decision_id TEXT,
11713
11964
  agent_unit TEXT NOT NULL,
@@ -11722,12 +11973,12 @@ function migrateApprovalSchema(db) {
11722
11973
  )
11723
11974
  `);
11724
11975
  db.run(`
11725
- CREATE INDEX approval_nonces_pending
11976
+ CREATE INDEX IF NOT EXISTS approval_nonces_pending
11726
11977
  ON approval_nonces(agent_unit, expires_at)
11727
11978
  WHERE consumed_at IS NULL
11728
11979
  `);
11729
11980
  db.run(`
11730
- CREATE TABLE approval_audit (
11981
+ CREATE TABLE IF NOT EXISTS approval_audit (
11731
11982
  seq INTEGER PRIMARY KEY AUTOINCREMENT,
11732
11983
  ts INTEGER NOT NULL,
11733
11984
  agent_unit TEXT NOT NULL,
@@ -11740,7 +11991,7 @@ function migrateApprovalSchema(db) {
11740
11991
  )
11741
11992
  `);
11742
11993
  db.run(`
11743
- CREATE INDEX approval_audit_by_scope
11994
+ CREATE INDEX IF NOT EXISTS approval_audit_by_scope
11744
11995
  ON approval_audit(scope, ts)
11745
11996
  `);
11746
11997
  }
@@ -12277,6 +12528,23 @@ function mergeAgentConfig(defaultsIn, agentIn) {
12277
12528
  const a = merged.extra_stable_files ?? [];
12278
12529
  merged.extra_stable_files = dedupe([...d, ...a]);
12279
12530
  }
12531
+ const dReactions = defaults.reactions;
12532
+ const mReactions = merged.reactions;
12533
+ if (dReactions || mReactions) {
12534
+ const base = dReactions ?? {};
12535
+ const override = mReactions ?? {};
12536
+ const combined = { ...base };
12537
+ for (const [k, v] of Object.entries(override)) {
12538
+ if (v !== undefined)
12539
+ combined[k] = v;
12540
+ }
12541
+ merged.reactions = combined;
12542
+ }
12543
+ if (defaults.resources || merged.resources) {
12544
+ const d = defaults.resources ?? {};
12545
+ const a = merged.resources ?? {};
12546
+ merged.resources = { ...d, ...a };
12547
+ }
12280
12548
  if (defaults.experimental || merged.experimental) {
12281
12549
  const d = defaults.experimental ?? {};
12282
12550
  const a = merged.experimental ?? {};
@@ -12289,39 +12557,6 @@ function mergeAgentConfig(defaultsIn, agentIn) {
12289
12557
  mergeAgentConfig.notifiedWorkerIsolationMove = false;
12290
12558
  })(mergeAgentConfig ||= {});
12291
12559
 
12292
- // src/agents/compose.ts
12293
- var AGENT_UID_MIN = 10001;
12294
- var AGENT_UID_MAX = 10999;
12295
- function allocateAgentUid(name) {
12296
- const hash = createHash("sha256").update(name).digest();
12297
- const u32 = hash.readUInt32BE(0);
12298
- const range = AGENT_UID_MAX - AGENT_UID_MIN + 1;
12299
- return AGENT_UID_MIN + u32 % range;
12300
- }
12301
-
12302
- // src/vault/approvals/acl.ts
12303
- function checkApprovalAclByAgent(listenerAgent, claimedAgentUnit) {
12304
- if (!listenerAgent) {
12305
- return {
12306
- allow: false,
12307
- reason: "listener has no bound agent identity (kernel-server bind misconfiguration)"
12308
- };
12309
- }
12310
- if (!claimedAgentUnit) {
12311
- return {
12312
- allow: false,
12313
- reason: "request has no agent_unit; refusing to attribute to listener identity by default"
12314
- };
12315
- }
12316
- if (claimedAgentUnit !== listenerAgent) {
12317
- return {
12318
- allow: false,
12319
- reason: `agent_unit mismatch: socket=${listenerAgent}, claim=${claimedAgentUnit}`
12320
- };
12321
- }
12322
- return { allow: true };
12323
- }
12324
-
12325
12560
  // src/vault/broker/peercred-ffi.ts
12326
12561
  function getPeerCred(fd) {
12327
12562
  if (process.platform !== "linux")
@@ -12374,6 +12609,55 @@ function getPeerCred(fd) {
12374
12609
  }
12375
12610
  }
12376
12611
 
12612
+ // src/vault/broker/peercred.ts
12613
+ var RESERVED_AGENT_NAMES = new Set(["operator", "hostd"]);
12614
+ function isReservedAgentName(name) {
12615
+ return RESERVED_AGENT_NAMES.has(name);
12616
+ }
12617
+
12618
+ // src/memory/hindsight.ts
12619
+ var DEFAULT_RETAIN_MISSION = "Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.";
12620
+
12621
+ // src/agents/reconcile-default-skills.ts
12622
+ var warnedMissingPool = new Set;
12623
+
12624
+ // src/agents/compose.ts
12625
+ var AGENT_UID_MIN = 10001;
12626
+ var AGENT_UID_MAX = 10999;
12627
+ function allocateAgentUid(name) {
12628
+ if (isReservedAgentName(name)) {
12629
+ throw new Error(`agent name '${name}' is reserved by switchroom for another identity kind ` + `(see vault/broker/peercred.ts:RESERVED_AGENT_NAMES). Pick a different name.`);
12630
+ }
12631
+ const hash = createHash("sha256").update(name).digest();
12632
+ const u32 = hash.readUInt32BE(0);
12633
+ const range = AGENT_UID_MAX - AGENT_UID_MIN + 1;
12634
+ return AGENT_UID_MIN + u32 % range;
12635
+ }
12636
+ var BIND_MOUNT_EXACT_SOURCE_DENY = new Set(["/var/run/docker.sock"]);
12637
+
12638
+ // src/vault/approvals/acl.ts
12639
+ function checkApprovalAclByAgent(listenerAgent, claimedAgentUnit) {
12640
+ if (!listenerAgent) {
12641
+ return {
12642
+ allow: false,
12643
+ reason: "listener has no bound agent identity (kernel-server bind misconfiguration)"
12644
+ };
12645
+ }
12646
+ if (!claimedAgentUnit) {
12647
+ return {
12648
+ allow: false,
12649
+ reason: "request has no agent_unit; refusing to attribute to listener identity by default"
12650
+ };
12651
+ }
12652
+ if (claimedAgentUnit !== listenerAgent) {
12653
+ return {
12654
+ allow: false,
12655
+ reason: `agent_unit mismatch: socket=${listenerAgent}, claim=${claimedAgentUnit}`
12656
+ };
12657
+ }
12658
+ return { allow: true };
12659
+ }
12660
+
12377
12661
  // src/vault/approvals/kernel-server.ts
12378
12662
  var DEFAULT_SOCKET_PARENT = "/run/switchroom/kernel";
12379
12663
  var DEFAULT_DB_PATH = "/state/approvals/kernel.db";
@@ -12390,7 +12674,7 @@ function openKernelDb(dbPath) {
12390
12674
  return db;
12391
12675
  }
12392
12676
  async function bindAgentSocket(parentDir, agent, db) {
12393
- const dir = resolve3(parentDir, agent);
12677
+ const dir = resolve4(parentDir, agent);
12394
12678
  mkdirSync(dir, { recursive: true, mode: 448 });
12395
12679
  try {
12396
12680
  chownSync(dir, 0, 0);
@@ -12399,8 +12683,8 @@ async function bindAgentSocket(parentDir, agent, db) {
12399
12683
  chmodSync(dir, 448);
12400
12684
  } catch {}
12401
12685
  const uid = allocateAgentUid(agent);
12402
- const socketPath = resolve3(dir, "sock");
12403
- if (existsSync3(socketPath)) {
12686
+ const socketPath = resolve4(dir, "sock");
12687
+ if (existsSync4(socketPath)) {
12404
12688
  try {
12405
12689
  unlinkSync(socketPath);
12406
12690
  } catch (err) {
@@ -12444,8 +12728,7 @@ function handleConnection(socket, agent, db) {
12444
12728
  socket.on("data", (chunk) => {
12445
12729
  buffer += chunk.toString("utf8");
12446
12730
  if (Buffer.byteLength(buffer, "utf8") > MAX_FRAME_BYTES) {
12447
- socket.write(encodeResponse(errorResponse("BAD_REQUEST", "Frame exceeds 64 KiB limit")));
12448
- socket.destroy();
12731
+ socket.end(encodeResponse(errorResponse("BAD_REQUEST", "Frame exceeds 64 KiB limit")));
12449
12732
  return;
12450
12733
  }
12451
12734
  let nl;
@@ -12614,7 +12897,7 @@ async function bootstrap(opts) {
12614
12897
  l.server.close();
12615
12898
  } catch {}
12616
12899
  try {
12617
- if (existsSync3(l.socketPath))
12900
+ if (existsSync4(l.socketPath))
12618
12901
  unlinkSync(l.socketPath);
12619
12902
  } catch {}
12620
12903
  }
@@ -12626,16 +12909,16 @@ async function bootstrap(opts) {
12626
12909
  }
12627
12910
  async function main() {
12628
12911
  const socketEnv = process.env.SWITCHROOM_KERNEL_SOCKET ?? `${DEFAULT_SOCKET_PARENT}/approval-kernel.sock`;
12629
- const socketParent = dirname(resolve3(socketEnv));
12912
+ const socketParent = dirname(resolve4(socketEnv));
12630
12913
  const dbPath = process.env.SWITCHROOM_KERNEL_DB_PATH ?? DEFAULT_DB_PATH;
12631
12914
  const configPath = process.env.SWITCHROOM_CONFIG;
12632
12915
  let agents = [];
12633
12916
  try {
12634
- if (existsSync3(socketParent)) {
12635
- agents = readdirSync(socketParent).filter((name) => {
12917
+ if (existsSync4(socketParent)) {
12918
+ agents = readdirSync2(socketParent).filter((name) => {
12636
12919
  try {
12637
- const p = resolve3(socketParent, name);
12638
- return statSync(p).isDirectory();
12920
+ const p = resolve4(socketParent, name);
12921
+ return statSync2(p).isDirectory();
12639
12922
  } catch {
12640
12923
  return false;
12641
12924
  }
@@ -12663,8 +12946,8 @@ async function main() {
12663
12946
  chmodSync(socketParent, 493);
12664
12947
  } catch {}
12665
12948
  const db = openKernelDb(dbPath);
12666
- const socketPath = resolve3(socketEnv);
12667
- if (existsSync3(socketPath)) {
12949
+ const socketPath = resolve4(socketEnv);
12950
+ if (existsSync4(socketPath)) {
12668
12951
  try {
12669
12952
  unlinkSync(socketPath);
12670
12953
  } catch {}
@@ -12686,7 +12969,7 @@ async function main() {
12686
12969
  server.close();
12687
12970
  } catch {}
12688
12971
  try {
12689
- if (existsSync3(socketPath))
12972
+ if (existsSync4(socketPath))
12690
12973
  unlinkSync(socketPath);
12691
12974
  } catch {}
12692
12975
  try {