@poolzin/pool-bot 2026.2.24 → 2026.2.26

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 (646) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/event-mapper.js +87 -22
  4. package/dist/acp/meta.js +12 -6
  5. package/dist/acp/secret-file.js +22 -0
  6. package/dist/agents/agent-paths.js +8 -9
  7. package/dist/agents/agent-scope.js +17 -5
  8. package/dist/agents/auth-profiles/oauth.js +148 -64
  9. package/dist/agents/auth-profiles/session-override.js +13 -7
  10. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  11. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  12. package/dist/agents/bash-tools.exec-host-gateway.js +240 -0
  13. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  14. package/dist/agents/bash-tools.exec-runtime.js +2 -25
  15. package/dist/agents/bash-tools.exec-types.js +1 -0
  16. package/dist/agents/bash-tools.process.js +224 -218
  17. package/dist/agents/bedrock-discovery.js +3 -1
  18. package/dist/agents/byteplus-models.js +97 -0
  19. package/dist/agents/chutes-oauth.js +1 -0
  20. package/dist/agents/cli-runner/helpers.js +4 -0
  21. package/dist/agents/compaction.js +41 -14
  22. package/dist/agents/content-blocks.js +16 -0
  23. package/dist/agents/doubao-models.js +121 -0
  24. package/dist/agents/failover-error.js +2 -0
  25. package/dist/agents/huggingface-models.js +5 -3
  26. package/dist/agents/live-model-filter.js +5 -0
  27. package/dist/agents/minimax-vlm.js +10 -8
  28. package/dist/agents/model-auth.js +6 -0
  29. package/dist/agents/model-catalog.js +3 -1
  30. package/dist/agents/model-fallback.js +96 -101
  31. package/dist/agents/model-selection.js +7 -1
  32. package/dist/agents/models-config.providers.js +364 -165
  33. package/dist/agents/ollama-stream.js +117 -4
  34. package/dist/agents/opencode-zen-models.js +22 -11
  35. package/dist/agents/pi-embedded-helpers/errors.js +55 -33
  36. package/dist/agents/pi-embedded-helpers/messaging-dedupe.js +10 -5
  37. package/dist/agents/pi-embedded-helpers/thinking.js +10 -5
  38. package/dist/agents/pi-embedded-helpers.js +1 -1
  39. package/dist/agents/pi-embedded-payloads.js +1 -0
  40. package/dist/agents/pi-embedded-runner/compact.js +29 -7
  41. package/dist/agents/pi-embedded-runner/extensions.js +28 -26
  42. package/dist/agents/pi-embedded-runner/google.js +20 -8
  43. package/dist/agents/pi-embedded-runner/run/attempt.js +95 -36
  44. package/dist/agents/pi-embedded-runner/run.js +71 -12
  45. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  46. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +11 -2
  47. package/dist/agents/pi-embedded-runner/session-manager-cache.js +11 -7
  48. package/dist/agents/pi-embedded-runner/system-prompt.js +2 -0
  49. package/dist/agents/pi-embedded-runner/thinking.js +42 -0
  50. package/dist/agents/pi-embedded-runner/tool-name-allowlist.js +19 -0
  51. package/dist/agents/pi-embedded-runner/utils.js +7 -10
  52. package/dist/agents/pi-embedded-subscribe.handlers.lifecycle.js +45 -56
  53. package/dist/agents/pi-embedded-subscribe.handlers.tools.js +2 -2
  54. package/dist/agents/pi-embedded-subscribe.js +9 -4
  55. package/dist/agents/pi-embedded-subscribe.tools.js +68 -14
  56. package/dist/agents/pi-embedded-utils.js +3 -0
  57. package/dist/agents/pi-extensions/compaction-safeguard-runtime.js +4 -20
  58. package/dist/agents/pi-extensions/compaction-safeguard.js +75 -33
  59. package/dist/agents/pi-settings.js +40 -0
  60. package/dist/agents/pi-tools.policy.js +2 -1
  61. package/dist/agents/provider/config-loader.js +1 -1
  62. package/dist/agents/sandbox/browser.js +170 -33
  63. package/dist/agents/sandbox/config-hash.js +14 -27
  64. package/dist/agents/sandbox/config.js +21 -2
  65. package/dist/agents/sandbox/constants.js +2 -0
  66. package/dist/agents/sandbox/docker.js +16 -2
  67. package/dist/agents/sandbox/novnc-auth.js +62 -0
  68. package/dist/agents/sandbox/sanitize-env-vars.js +1 -1
  69. package/dist/agents/sandbox/shared.js +10 -6
  70. package/dist/agents/sandbox-paths.js +24 -11
  71. package/dist/agents/schema/clean-for-gemini.js +132 -85
  72. package/dist/agents/session-slug.js +10 -5
  73. package/dist/agents/session-tool-result-guard-wrapper.js +1 -0
  74. package/dist/agents/session-tool-result-guard.js +3 -1
  75. package/dist/agents/session-transcript-repair.js +40 -6
  76. package/dist/agents/skills/bundled-dir.js +19 -5
  77. package/dist/agents/skills/env-overrides.js +124 -43
  78. package/dist/agents/skills/frontmatter.js +6 -6
  79. package/dist/agents/skills/plugin-skills.js +14 -7
  80. package/dist/agents/skills/workspace.js +1 -0
  81. package/dist/agents/skills.test-helpers.js +13 -0
  82. package/dist/agents/stable-stringify.js +12 -0
  83. package/dist/agents/subagent-announce.js +251 -49
  84. package/dist/agents/subagent-lifecycle-events.js +19 -0
  85. package/dist/agents/subagent-registry-cleanup.js +31 -0
  86. package/dist/agents/subagent-registry-completion.js +68 -0
  87. package/dist/agents/subagent-registry-queries.js +117 -0
  88. package/dist/agents/subagent-registry-state.js +46 -0
  89. package/dist/agents/subagent-registry.js +252 -221
  90. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  91. package/dist/agents/subagent-registry.store.js +1 -0
  92. package/dist/agents/subagent-registry.types.js +1 -0
  93. package/dist/agents/subagent-spawn.js +195 -7
  94. package/dist/agents/system-prompt.js +22 -6
  95. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  96. package/dist/agents/test-helpers/fast-coding-tools.js +1 -18
  97. package/dist/agents/test-helpers/fast-core-tools.js +1 -17
  98. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  99. package/dist/agents/timeout.js +18 -6
  100. package/dist/agents/tool-call-id.js +1 -1
  101. package/dist/agents/tool-display-common.js +162 -29
  102. package/dist/agents/tool-images.js +82 -9
  103. package/dist/agents/tool-policy-shared.js +108 -0
  104. package/dist/agents/tool-policy.js +51 -26
  105. package/dist/agents/tools/browser-tool.js +160 -54
  106. package/dist/agents/tools/canvas-tool.js +27 -1
  107. package/dist/agents/tools/common.js +45 -0
  108. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  109. package/dist/agents/tools/discord-actions-guild.js +4 -1
  110. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  111. package/dist/agents/tools/gateway-tool.js +3 -1
  112. package/dist/agents/tools/image-tool.js +214 -99
  113. package/dist/agents/tools/nodes-utils.js +1 -10
  114. package/dist/agents/tools/sessions-history-tool.js +140 -108
  115. package/dist/agents/tools/sessions-send-helpers.js +12 -6
  116. package/dist/agents/tools/sessions-spawn-tool.js +8 -2
  117. package/dist/agents/tools/subagents-tool.js +2 -1
  118. package/dist/agents/tools/whatsapp-actions.js +10 -2
  119. package/dist/agents/tools/whatsapp-target-auth.js +18 -0
  120. package/dist/agents/transcript-policy.js +22 -8
  121. package/dist/agents/venice-models.js +11 -3
  122. package/dist/agents/workspace.js +222 -46
  123. package/dist/auto-reply/commands-registry.data.js +51 -0
  124. package/dist/auto-reply/commands-registry.js +19 -21
  125. package/dist/auto-reply/fallback-state.js +114 -0
  126. package/dist/auto-reply/group-activation.js +10 -5
  127. package/dist/auto-reply/inbound-debounce.js +10 -5
  128. package/dist/auto-reply/model-runtime.js +68 -0
  129. package/dist/auto-reply/reply/abort.js +1 -1
  130. package/dist/auto-reply/reply/agent-runner-execution.js +40 -5
  131. package/dist/auto-reply/reply/agent-runner.js +165 -39
  132. package/dist/auto-reply/reply/bash-command.js +41 -39
  133. package/dist/auto-reply/reply/command-gates.js +25 -0
  134. package/dist/auto-reply/reply/commands-allowlist.js +111 -72
  135. package/dist/auto-reply/reply/commands-bash.js +6 -5
  136. package/dist/auto-reply/reply/commands-config.js +30 -28
  137. package/dist/auto-reply/reply/commands-core.js +2 -1
  138. package/dist/auto-reply/reply/commands-info.js +1 -0
  139. package/dist/auto-reply/reply/commands-models.js +65 -14
  140. package/dist/auto-reply/reply/commands-session.js +237 -82
  141. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  142. package/dist/auto-reply/reply/commands-setunset.js +45 -0
  143. package/dist/auto-reply/reply/commands-subagents/action-agents.js +44 -0
  144. package/dist/auto-reply/reply/commands-subagents/action-focus.js +64 -0
  145. package/dist/auto-reply/reply/commands-subagents/action-help.js +4 -0
  146. package/dist/auto-reply/reply/commands-subagents/action-info.js +45 -0
  147. package/dist/auto-reply/reply/commands-subagents/action-kill.js +60 -0
  148. package/dist/auto-reply/reply/commands-subagents/action-list.js +44 -0
  149. package/dist/auto-reply/reply/commands-subagents/action-log.js +29 -0
  150. package/dist/auto-reply/reply/commands-subagents/action-send.js +119 -0
  151. package/dist/auto-reply/reply/commands-subagents/action-spawn.js +52 -0
  152. package/dist/auto-reply/reply/commands-subagents/action-unfocus.js +30 -0
  153. package/dist/auto-reply/reply/commands-subagents/shared.js +303 -0
  154. package/dist/auto-reply/reply/commands-subagents.js +51 -587
  155. package/dist/auto-reply/reply/commands-tts.js +10 -5
  156. package/dist/auto-reply/reply/config-value.js +10 -5
  157. package/dist/auto-reply/reply/directive-handling.model-picker.js +12 -6
  158. package/dist/auto-reply/reply/directive-handling.persist.js +9 -21
  159. package/dist/auto-reply/reply/directive-handling.shared.js +24 -4
  160. package/dist/auto-reply/reply/followup-runner.js +1 -0
  161. package/dist/auto-reply/reply/get-reply-directives-utils.js +23 -14
  162. package/dist/auto-reply/reply/get-reply-directives.js +17 -28
  163. package/dist/auto-reply/reply/get-reply-inline-actions.js +1 -0
  164. package/dist/auto-reply/reply/get-reply.js +71 -12
  165. package/dist/auto-reply/reply/model-selection.js +80 -39
  166. package/dist/auto-reply/reply/queue/enqueue.js +10 -5
  167. package/dist/auto-reply/reply/queue/state.js +13 -12
  168. package/dist/auto-reply/reply/reply-payloads.js +67 -36
  169. package/dist/auto-reply/reply/reply-reference.js +9 -8
  170. package/dist/auto-reply/reply/route-reply.js +15 -8
  171. package/dist/auto-reply/reply/session-reset-prompt.js +1 -1
  172. package/dist/auto-reply/reply/session.js +22 -6
  173. package/dist/auto-reply/reply/strip-inbound-meta.js +147 -0
  174. package/dist/auto-reply/reply/subagents-utils.js +56 -30
  175. package/dist/auto-reply/reply/typing.js +46 -21
  176. package/dist/auto-reply/send-policy.js +14 -7
  177. package/dist/auto-reply/status.js +140 -16
  178. package/dist/auto-reply/templating.js +10 -5
  179. package/dist/auto-reply/thinking.js +7 -16
  180. package/dist/auto-reply/tokens.js +21 -5
  181. package/dist/browser/bridge-server.js +36 -20
  182. package/dist/browser/cdp.helpers.js +7 -14
  183. package/dist/browser/cdp.js +35 -15
  184. package/dist/browser/chrome.profile-decoration.js +7 -4
  185. package/dist/browser/config.js +30 -0
  186. package/dist/browser/extension-relay-auth.js +55 -0
  187. package/dist/browser/extension-relay.js +74 -29
  188. package/dist/browser/navigation-guard.js +39 -0
  189. package/dist/browser/paths.js +77 -0
  190. package/dist/browser/profiles.js +13 -8
  191. package/dist/browser/pw-ai-module.js +10 -5
  192. package/dist/browser/pw-session.js +76 -39
  193. package/dist/browser/pw-tools-core.interactions.js +14 -7
  194. package/dist/browser/pw-tools-core.state.js +12 -6
  195. package/dist/browser/routes/agent.act.js +431 -424
  196. package/dist/browser/routes/agent.shared.js +47 -3
  197. package/dist/browser/routes/agent.snapshot.js +122 -116
  198. package/dist/browser/routes/agent.storage.js +303 -297
  199. package/dist/browser/routes/tabs.js +154 -100
  200. package/dist/browser/server-context.js +7 -0
  201. package/dist/browser/server-lifecycle.js +37 -0
  202. package/dist/build-info.json +3 -3
  203. package/dist/channels/allow-from.js +26 -0
  204. package/dist/channels/allowlists/resolve-utils.js +43 -19
  205. package/dist/channels/channel-config.js +14 -7
  206. package/dist/channels/draft-stream-loop.js +7 -0
  207. package/dist/channels/model-overrides.js +82 -0
  208. package/dist/channels/plugins/account-action-gate.js +13 -0
  209. package/dist/channels/plugins/message-actions.js +10 -0
  210. package/dist/channels/plugins/normalize/imessage.js +14 -7
  211. package/dist/channels/plugins/normalize/slack.js +10 -5
  212. package/dist/channels/plugins/normalize/telegram.js +14 -7
  213. package/dist/channels/plugins/outbound/discord.js +80 -8
  214. package/dist/channels/plugins/outbound/signal.js +11 -11
  215. package/dist/channels/plugins/setup-helpers.js +10 -5
  216. package/dist/channels/sender-label.js +14 -7
  217. package/dist/channels/session.js +4 -2
  218. package/dist/channels/status-reactions.js +297 -0
  219. package/dist/channels/telegram/api.js +18 -0
  220. package/dist/cli/argv.js +84 -21
  221. package/dist/cli/banner.js +3 -2
  222. package/dist/cli/browser-cli-actions-input/register.files-downloads.js +65 -56
  223. package/dist/cli/cli-name.js +11 -11
  224. package/dist/cli/cli-utils.js +13 -3
  225. package/dist/cli/command-format.js +1 -1
  226. package/dist/cli/config-cli.js +1 -1
  227. package/dist/cli/daemon-cli/lifecycle-core.js +31 -19
  228. package/dist/cli/daemon-cli/lifecycle.js +64 -2
  229. package/dist/cli/daemon-cli/restart-health.js +126 -0
  230. package/dist/cli/daemon-cli/status.gather.js +9 -13
  231. package/dist/cli/daemon-cli/status.print.js +2 -10
  232. package/dist/cli/deps.js +27 -22
  233. package/dist/cli/exec-approvals-cli.js +92 -124
  234. package/dist/cli/gateway-cli/run-loop.js +23 -5
  235. package/dist/cli/memory-cli.js +158 -61
  236. package/dist/cli/node-cli/register.js +14 -5
  237. package/dist/cli/nodes-cli/register.push.js +63 -0
  238. package/dist/cli/nodes-media-utils.js +26 -0
  239. package/dist/cli/outbound-send-deps.js +2 -9
  240. package/dist/cli/outbound-send-mapping.js +11 -0
  241. package/dist/cli/pairing-cli.js +40 -14
  242. package/dist/cli/plugins-cli.js +250 -73
  243. package/dist/cli/ports.js +11 -10
  244. package/dist/cli/program/build-program.js +3 -1
  245. package/dist/cli/program/command-registry.js +214 -136
  246. package/dist/cli/program/command-tree.js +16 -0
  247. package/dist/cli/program/help.js +43 -12
  248. package/dist/cli/program/preaction.js +13 -9
  249. package/dist/cli/program/register.configure.js +3 -18
  250. package/dist/cli/program/register.maintenance.js +2 -2
  251. package/dist/cli/program/register.onboard.js +2 -0
  252. package/dist/cli/program/register.status-health-sessions.js +16 -17
  253. package/dist/cli/program/register.subclis.js +93 -52
  254. package/dist/cli/route.js +12 -8
  255. package/dist/cli/system-cli.js +36 -46
  256. package/dist/cli/test-runtime-capture.js +24 -0
  257. package/dist/cli/update-cli/shared.js +22 -9
  258. package/dist/cli/update-cli/update-command.js +89 -14
  259. package/dist/cli/update-cli/wizard.js +6 -12
  260. package/dist/commands/agent/run-context.js +18 -5
  261. package/dist/commands/agent/session-store.js +17 -4
  262. package/dist/commands/agent.js +185 -89
  263. package/dist/commands/agents.bindings.js +14 -7
  264. package/dist/commands/agents.commands.add.js +13 -9
  265. package/dist/commands/agents.commands.identity.js +12 -6
  266. package/dist/commands/agents.commands.list.js +11 -6
  267. package/dist/commands/agents.config.js +8 -10
  268. package/dist/commands/agents.providers.js +12 -6
  269. package/dist/commands/auth-choice-options.js +103 -75
  270. package/dist/commands/auth-choice.apply.byteplus.js +55 -0
  271. package/dist/commands/auth-choice.apply.js +4 -0
  272. package/dist/commands/auth-choice.apply.minimax.js +61 -13
  273. package/dist/commands/auth-choice.apply.openai.js +3 -1
  274. package/dist/commands/auth-choice.apply.volcengine.js +55 -0
  275. package/dist/commands/auth-choice.preferred-provider.js +2 -0
  276. package/dist/commands/channels/remove.js +13 -6
  277. package/dist/commands/channels/shared.js +4 -14
  278. package/dist/commands/channels.mock-harness.js +23 -0
  279. package/dist/commands/configure.commands.js +14 -0
  280. package/dist/commands/configure.gateway.js +2 -4
  281. package/dist/commands/configure.js +1 -1
  282. package/dist/commands/configure.shared.js +11 -0
  283. package/dist/commands/daemon-install-helpers.js +2 -2
  284. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  285. package/dist/commands/dashboard.js +12 -10
  286. package/dist/commands/docs.js +14 -8
  287. package/dist/commands/doctor-config-flow.js +11 -9
  288. package/dist/commands/doctor-legacy-config.js +281 -0
  289. package/dist/commands/doctor-state-integrity.js +99 -23
  290. package/dist/commands/doctor-update.js +12 -9
  291. package/dist/commands/models/list.list-command.js +7 -5
  292. package/dist/commands/models/set-image.js +2 -21
  293. package/dist/commands/node-daemon-install-helpers.js +10 -8
  294. package/dist/commands/onboard-auth.config-minimax.js +54 -80
  295. package/dist/commands/onboard-auth.config-opencode.js +2 -18
  296. package/dist/commands/onboard-auth.credentials.js +90 -13
  297. package/dist/commands/onboard-auth.js +1 -1
  298. package/dist/commands/onboard-auth.models.js +6 -5
  299. package/dist/commands/onboard-hooks.js +1 -1
  300. package/dist/commands/onboard-non-interactive/api-keys.js +14 -7
  301. package/dist/commands/onboard-non-interactive/local/auth-choice.js +64 -49
  302. package/dist/commands/onboard-provider-auth-flags.js +14 -0
  303. package/dist/commands/onboard-remote.js +14 -7
  304. package/dist/commands/onboard.js +11 -13
  305. package/dist/commands/sandbox-display.js +6 -5
  306. package/dist/commands/sessions.test-helpers.js +61 -0
  307. package/dist/commands/status-all/diagnosis.js +14 -10
  308. package/dist/commands/status-all/format.js +1 -0
  309. package/dist/commands/status.gateway-probe.js +1 -16
  310. package/dist/commands/systemd-linger.js +12 -6
  311. package/dist/config/agent-limits.js +2 -0
  312. package/dist/config/commands.js +32 -15
  313. package/dist/config/config-paths.js +9 -11
  314. package/dist/config/config.js +1 -1
  315. package/dist/config/defaults.js +22 -2
  316. package/dist/config/discord-preview-streaming.js +104 -0
  317. package/dist/config/env-substitution.js +62 -34
  318. package/dist/config/env-vars.js +45 -7
  319. package/dist/config/includes.js +4 -0
  320. package/dist/config/io.js +656 -171
  321. package/dist/config/legacy.migrations.part-1.js +189 -78
  322. package/dist/config/legacy.shared.js +3 -1
  323. package/dist/config/merge-patch.js +54 -4
  324. package/dist/config/prototype-keys.js +4 -0
  325. package/dist/config/redact-snapshot.js +404 -76
  326. package/dist/config/schema.help.js +44 -7
  327. package/dist/config/schema.js +58 -570
  328. package/dist/config/schema.labels.js +38 -6
  329. package/dist/config/sessions/delivery-info.js +10 -3
  330. package/dist/config/sessions/main-session.js +10 -5
  331. package/dist/config/sessions/session-file.js +33 -0
  332. package/dist/config/sessions/session-key.js +10 -5
  333. package/dist/config/sessions/store.js +1 -1
  334. package/dist/config/sessions.js +1 -0
  335. package/dist/config/validation.js +140 -85
  336. package/dist/config/zod-schema.agent-runtime.js +11 -0
  337. package/dist/config/zod-schema.hooks.js +40 -11
  338. package/dist/config/zod-schema.installs.js +20 -0
  339. package/dist/config/zod-schema.js +156 -20
  340. package/dist/config/zod-schema.providers-core.js +78 -4
  341. package/dist/config/zod-schema.providers.js +6 -1
  342. package/dist/config/zod-schema.session.js +41 -2
  343. package/dist/cron/run-log.js +3 -0
  344. package/dist/cron/schedule.js +21 -10
  345. package/dist/cron/service/ops.js +35 -21
  346. package/dist/cron/service/timer.js +116 -16
  347. package/dist/cron/stagger.js +3 -1
  348. package/dist/daemon/cmd-argv.js +21 -0
  349. package/dist/daemon/cmd-set.js +58 -0
  350. package/dist/daemon/service-types.js +1 -0
  351. package/dist/discord/api.js +12 -6
  352. package/dist/discord/draft-chunking.js +22 -0
  353. package/dist/discord/draft-stream.js +124 -0
  354. package/dist/discord/monitor/agent-components.js +1 -1
  355. package/dist/discord/monitor/commands.js +5 -0
  356. package/dist/discord/monitor/exec-approvals.js +357 -162
  357. package/dist/discord/monitor/gateway-plugin.js +2 -1
  358. package/dist/discord/monitor/listeners.js +37 -27
  359. package/dist/discord/monitor/message-handler.js +4 -1
  360. package/dist/discord/monitor/message-handler.preflight.js +65 -8
  361. package/dist/discord/monitor/message-handler.process.js +246 -217
  362. package/dist/discord/monitor/message-utils.js +143 -6
  363. package/dist/discord/monitor/model-picker-preferences.js +143 -0
  364. package/dist/discord/monitor/model-picker.js +651 -0
  365. package/dist/discord/monitor/native-command.js +573 -16
  366. package/dist/discord/monitor/provider.allowlist.js +223 -0
  367. package/dist/discord/monitor/provider.js +275 -347
  368. package/dist/discord/monitor/provider.lifecycle.js +100 -0
  369. package/dist/discord/monitor/reply-delivery.js +123 -16
  370. package/dist/discord/monitor/thread-bindings.discord-api.js +215 -0
  371. package/dist/discord/monitor/thread-bindings.js +4 -0
  372. package/dist/discord/monitor/thread-bindings.lifecycle.js +177 -0
  373. package/dist/discord/monitor/thread-bindings.manager.js +423 -0
  374. package/dist/discord/monitor/thread-bindings.messages.js +55 -0
  375. package/dist/discord/monitor/thread-bindings.state.js +358 -0
  376. package/dist/discord/monitor/thread-bindings.types.js +6 -0
  377. package/dist/discord/resolve-users.js +33 -21
  378. package/dist/discord/send.channels.js +15 -0
  379. package/dist/discord/send.js +3 -2
  380. package/dist/discord/send.outbound.js +82 -26
  381. package/dist/discord/send.permissions.js +83 -30
  382. package/dist/discord/send.reactions.js +8 -4
  383. package/dist/discord/token.js +10 -5
  384. package/dist/discord/voice/command.js +263 -0
  385. package/dist/discord/voice/manager.js +531 -0
  386. package/dist/gateway/auth.js +72 -13
  387. package/dist/gateway/call.js +152 -83
  388. package/dist/gateway/canvas-capability.js +75 -0
  389. package/dist/gateway/client.js +28 -4
  390. package/dist/gateway/config-reload.js +3 -4
  391. package/dist/gateway/control-plane-audit.js +28 -0
  392. package/dist/gateway/control-plane-rate-limit.js +53 -0
  393. package/dist/gateway/control-ui.js +219 -96
  394. package/dist/gateway/events.js +1 -0
  395. package/dist/gateway/hooks-mapping.js +88 -38
  396. package/dist/gateway/hooks.js +109 -54
  397. package/dist/gateway/http-auth-helpers.js +3 -2
  398. package/dist/gateway/http-common.js +22 -0
  399. package/dist/gateway/http-endpoint-helpers.js +1 -0
  400. package/dist/gateway/method-scopes.js +169 -0
  401. package/dist/gateway/net.js +74 -9
  402. package/dist/gateway/node-invoke-system-run-approval.js +14 -35
  403. package/dist/gateway/node-registry.js +10 -5
  404. package/dist/gateway/openai-http.js +1 -0
  405. package/dist/gateway/openresponses-http.js +121 -110
  406. package/dist/gateway/origin-check.js +1 -18
  407. package/dist/gateway/probe-auth.js +2 -0
  408. package/dist/gateway/protocol/index.js +4 -2
  409. package/dist/gateway/protocol/schema/cron.js +1 -0
  410. package/dist/gateway/protocol/schema/devices.js +1 -0
  411. package/dist/gateway/protocol/schema/protocol-schemas.js +4 -1
  412. package/dist/gateway/protocol/schema/push.js +18 -0
  413. package/dist/gateway/protocol/schema/sessions.js +6 -0
  414. package/dist/gateway/protocol/schema.js +1 -0
  415. package/dist/gateway/role-policy.js +17 -0
  416. package/dist/gateway/server/ws-connection/connect-policy.js +37 -0
  417. package/dist/gateway/server/ws-connection/message-handler.js +175 -148
  418. package/dist/gateway/server-chat.js +83 -25
  419. package/dist/gateway/server-constants.js +10 -9
  420. package/dist/gateway/server-cron.js +1 -0
  421. package/dist/gateway/server-http.js +247 -54
  422. package/dist/gateway/server-maintenance.js +20 -5
  423. package/dist/gateway/server-methods/agent.js +162 -24
  424. package/dist/gateway/server-methods/chat.js +465 -130
  425. package/dist/gateway/server-methods/config.js +193 -152
  426. package/dist/gateway/server-methods/devices.js +17 -3
  427. package/dist/gateway/server-methods/models.js +11 -1
  428. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  429. package/dist/gateway/server-methods/nodes.js +251 -69
  430. package/dist/gateway/server-methods/push.js +53 -0
  431. package/dist/gateway/server-methods/sessions.js +64 -8
  432. package/dist/gateway/server-methods/usage.js +162 -75
  433. package/dist/gateway/server-node-events.js +29 -0
  434. package/dist/gateway/server-reload-handlers.js +2 -3
  435. package/dist/gateway/server-runtime-config.js +39 -13
  436. package/dist/gateway/server-runtime-state.js +2 -0
  437. package/dist/gateway/server-startup-memory.js +17 -11
  438. package/dist/gateway/server-ws-runtime.js +1 -0
  439. package/dist/gateway/server.impl.js +296 -139
  440. package/dist/gateway/session-preview.test-helpers.js +11 -0
  441. package/dist/gateway/session-utils.fs.js +32 -34
  442. package/dist/gateway/sessions-resolve.js +17 -5
  443. package/dist/gateway/startup-auth.js +126 -0
  444. package/dist/gateway/test-helpers.agent-results.js +15 -0
  445. package/dist/gateway/test-helpers.mocks.js +37 -14
  446. package/dist/gateway/test-helpers.openai-mock.js +14 -7
  447. package/dist/gateway/test-helpers.server.js +161 -77
  448. package/dist/gateway/tools-invoke-http.js +21 -10
  449. package/dist/hooks/bundled/bootstrap-extra-files/handler.js +3 -1
  450. package/dist/hooks/bundled/command-logger/handler.js +7 -2
  451. package/dist/hooks/bundled/session-memory/handler.js +170 -38
  452. package/dist/hooks/frontmatter.js +6 -6
  453. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  454. package/dist/hooks/gmail-watcher.js +11 -6
  455. package/dist/hooks/internal-hooks.js +11 -1
  456. package/dist/hooks/llm-slug-generator.js +4 -1
  457. package/dist/hooks/workspace.js +47 -17
  458. package/dist/imessage/accounts.js +9 -20
  459. package/dist/imessage/monitor/inbound-processing.js +2 -1
  460. package/dist/infra/archive-path.js +49 -0
  461. package/dist/infra/archive.js +174 -73
  462. package/dist/infra/control-ui-assets.js +14 -6
  463. package/dist/infra/device-pairing.js +204 -144
  464. package/dist/infra/env.js +10 -5
  465. package/dist/infra/exec-approvals-allowlist.js +141 -70
  466. package/dist/infra/exec-approvals-analysis.js +78 -20
  467. package/dist/infra/exec-approvals.js +5 -17
  468. package/dist/infra/exec-safe-bin-policy.js +277 -0
  469. package/dist/infra/fixed-window-rate-limit.js +33 -0
  470. package/dist/infra/fs-safe.js +71 -39
  471. package/dist/infra/gateway-lock.js +6 -2
  472. package/dist/infra/git-root.js +61 -0
  473. package/dist/infra/heartbeat-active-hours.js +2 -2
  474. package/dist/infra/heartbeat-reason.js +40 -0
  475. package/dist/infra/heartbeat-runner.js +72 -32
  476. package/dist/infra/heartbeat-wake.js +6 -12
  477. package/dist/infra/host-env-security-policy.json +19 -0
  478. package/dist/infra/host-env-security.js +66 -0
  479. package/dist/infra/install-source-utils.js +91 -7
  480. package/dist/infra/net/ssrf.js +131 -38
  481. package/dist/infra/node-pairing.js +50 -105
  482. package/dist/infra/npm-integrity.js +45 -0
  483. package/dist/infra/npm-pack-install.js +40 -0
  484. package/dist/infra/outbound/bound-delivery-router.js +88 -0
  485. package/dist/infra/outbound/channel-adapters.js +20 -7
  486. package/dist/infra/outbound/channel-selection.js +12 -6
  487. package/dist/infra/outbound/envelope.js +1 -1
  488. package/dist/infra/outbound/format.js +12 -6
  489. package/dist/infra/outbound/message-action-runner.js +107 -327
  490. package/dist/infra/outbound/message.js +59 -36
  491. package/dist/infra/outbound/outbound-policy.js +52 -25
  492. package/dist/infra/outbound/outbound-send-service.js +58 -71
  493. package/dist/infra/outbound/payloads.js +14 -7
  494. package/dist/infra/outbound/session-binding-service.js +123 -0
  495. package/dist/infra/pairing-files.js +10 -0
  496. package/dist/infra/path-guards.js +25 -0
  497. package/dist/infra/plain-object.js +9 -0
  498. package/dist/infra/provider-usage.fetch.codex.js +7 -15
  499. package/dist/infra/provider-usage.fetch.gemini.js +14 -11
  500. package/dist/infra/provider-usage.fetch.shared.js +30 -1
  501. package/dist/infra/provider-usage.fetch.zai.js +10 -9
  502. package/dist/infra/push-apns.js +365 -0
  503. package/dist/infra/restart-sentinel.js +16 -1
  504. package/dist/infra/restart.js +229 -26
  505. package/dist/infra/retry-policy.js +4 -2
  506. package/dist/infra/retry.js +9 -5
  507. package/dist/infra/scp-host.js +54 -0
  508. package/dist/infra/session-cost-usage.js +107 -59
  509. package/dist/infra/session-maintenance-warning.js +3 -1
  510. package/dist/infra/shell-env.js +98 -34
  511. package/dist/infra/ssh-config.js +12 -6
  512. package/dist/infra/system-run-command.js +49 -4
  513. package/dist/infra/update-channels.js +10 -5
  514. package/dist/infra/update-startup.js +86 -9
  515. package/dist/line/accounts.js +5 -7
  516. package/dist/line/bot-access.js +8 -20
  517. package/dist/line/bot-handlers.js +3 -1
  518. package/dist/link-understanding/detect.js +15 -7
  519. package/dist/media/constants.js +15 -6
  520. package/dist/media/image-ops.js +7 -0
  521. package/dist/media/inbound-path-policy.js +114 -0
  522. package/dist/media/input-files.js +16 -0
  523. package/dist/media/local-roots.js +3 -2
  524. package/dist/media-understanding/apply.js +4 -1
  525. package/dist/media-understanding/concurrency.js +8 -20
  526. package/dist/memory/backend-config.js +45 -6
  527. package/dist/memory/embeddings.js +10 -4
  528. package/dist/memory/fs-utils.js +23 -0
  529. package/dist/memory/manager-search.js +12 -6
  530. package/dist/memory/manager-sync-ops.js +12 -2
  531. package/dist/memory/qmd-manager.js +466 -53
  532. package/dist/memory/query-expansion.js +167 -3
  533. package/dist/memory/status-format.js +10 -5
  534. package/dist/memory/sync-memory-files.js +1 -1
  535. package/dist/memory/test-manager.js +8 -0
  536. package/dist/node-host/invoke-system-run.js +281 -0
  537. package/dist/node-host/invoke.js +55 -337
  538. package/dist/pairing/pairing-store.js +22 -0
  539. package/dist/plugin-sdk/allow-from.js +1 -1
  540. package/dist/plugin-sdk/command-auth.js +3 -1
  541. package/dist/plugin-sdk/index.js +6 -3
  542. package/dist/plugin-sdk/temp-path.js +47 -0
  543. package/dist/plugin-sdk/webhook-targets.js +32 -0
  544. package/dist/plugins/bundled-dir.js +9 -6
  545. package/dist/plugins/discovery.js +217 -23
  546. package/dist/plugins/hook-runner-global.js +16 -0
  547. package/dist/plugins/hooks.js +50 -0
  548. package/dist/plugins/install.js +28 -16
  549. package/dist/plugins/loader.js +192 -26
  550. package/dist/plugins/logger.js +8 -0
  551. package/dist/plugins/manifest-registry.js +3 -0
  552. package/dist/plugins/path-safety.js +34 -0
  553. package/dist/plugins/registry.js +5 -2
  554. package/dist/plugins/runtime/index.js +271 -206
  555. package/dist/plugins/runtime.js +3 -17
  556. package/dist/plugins/update.js +78 -12
  557. package/dist/process/spawn-utils.js +14 -7
  558. package/dist/providers/github-copilot-models.js +4 -1
  559. package/dist/providers/github-copilot-token.js +11 -6
  560. package/dist/providers/qwen-portal-oauth.js +14 -6
  561. package/dist/routing/account-id.js +30 -0
  562. package/dist/routing/resolve-route.js +3 -7
  563. package/dist/routing/session-key.js +2 -16
  564. package/dist/security/audit-channel.js +100 -20
  565. package/dist/security/audit-extra.async.js +505 -179
  566. package/dist/security/audit-extra.js +12 -2
  567. package/dist/security/audit-extra.sync.js +421 -35
  568. package/dist/security/audit-fs.js +31 -13
  569. package/dist/security/audit.js +180 -370
  570. package/dist/security/dm-policy-shared.js +68 -0
  571. package/dist/security/external-content.js +46 -14
  572. package/dist/security/fix.js +49 -85
  573. package/dist/security/scan-paths.js +20 -0
  574. package/dist/security/secret-equal.js +3 -7
  575. package/dist/security/windows-acl.js +30 -15
  576. package/dist/shared/entry-status.js +6 -0
  577. package/dist/shared/frontmatter.js +5 -5
  578. package/dist/shared/node-list-parse.js +13 -0
  579. package/dist/shared/node-match.js +11 -4
  580. package/dist/shared/operator-scope-compat.js +42 -0
  581. package/dist/shared/text-chunking.js +29 -0
  582. package/dist/signal/accounts.js +7 -20
  583. package/dist/signal/monitor/event-handler.js +3 -1
  584. package/dist/slack/accounts.js +6 -19
  585. package/dist/slack/actions.js +11 -3
  586. package/dist/slack/blocks.test-helpers.js +31 -0
  587. package/dist/slack/monitor/auth.js +1 -1
  588. package/dist/slack/monitor/message-handler/dispatch.js +50 -29
  589. package/dist/slack/monitor/mrkdwn.js +8 -0
  590. package/dist/slack/monitor/replies.js +15 -7
  591. package/dist/slack/monitor/slash.js +22 -13
  592. package/dist/slack/resolve-channels.js +10 -5
  593. package/dist/slack/send.js +102 -12
  594. package/dist/slack/stream-mode.js +10 -0
  595. package/dist/slack/streaming.js +4 -2
  596. package/dist/telegram/accounts.js +19 -14
  597. package/dist/telegram/bot/helpers.js +3 -5
  598. package/dist/telegram/bot-access.js +35 -36
  599. package/dist/telegram/bot-handlers.js +120 -148
  600. package/dist/telegram/bot-message-context.js +68 -9
  601. package/dist/telegram/bot-message-dispatch.js +477 -210
  602. package/dist/telegram/bot-native-commands.js +16 -0
  603. package/dist/telegram/draft-stream.js +44 -8
  604. package/dist/telegram/inline-buttons.js +5 -15
  605. package/dist/telegram/monitor.js +11 -7
  606. package/dist/telegram/network-config.js +19 -7
  607. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  608. package/dist/telegram/send.js +3 -2
  609. package/dist/telegram/sent-message-cache.js +5 -6
  610. package/dist/telegram/status-reaction-variants.js +208 -0
  611. package/dist/telegram/sticker-cache.js +11 -9
  612. package/dist/terminal/prompt-select-styled.js +9 -0
  613. package/dist/terminal/theme.js +12 -12
  614. package/dist/test-utils/command-runner.js +6 -0
  615. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  616. package/dist/test-utils/model-auth-mock.js +12 -0
  617. package/dist/test-utils/provider-usage-fetch.js +14 -0
  618. package/dist/test-utils/temp-home.js +33 -0
  619. package/dist/tts/tts.js +80 -567
  620. package/dist/tui/components/chat-log.js +50 -8
  621. package/dist/tui/theme/theme.js +10 -12
  622. package/dist/tui/tui-command-handlers.js +36 -27
  623. package/dist/tui/tui-event-handlers.js +122 -32
  624. package/dist/tui/tui-local-shell.js +16 -6
  625. package/dist/tui/tui.js +236 -48
  626. package/dist/utils/account-id.js +2 -4
  627. package/dist/utils/boolean.js +10 -5
  628. package/dist/utils/directive-tags.js +11 -0
  629. package/dist/utils/mask-api-key.js +10 -0
  630. package/dist/utils/queue-helpers.js +67 -12
  631. package/dist/utils/run-with-concurrency.js +39 -0
  632. package/dist/web/auto-reply/deliver-reply.js +8 -4
  633. package/dist/web/auto-reply/mentions.js +10 -5
  634. package/dist/web/auto-reply/monitor/group-members.js +14 -7
  635. package/dist/web/auto-reply/monitor/process-message.js +45 -24
  636. package/dist/web/inbound/access-control.js +5 -2
  637. package/dist/web/login-qr.js +12 -6
  638. package/dist/web/media.js +126 -15
  639. package/docs/tools/slash-commands.md +5 -1
  640. package/extensions/bluebubbles/src/monitor-processing.ts +580 -139
  641. package/extensions/bluebubbles/src/monitor.ts +208 -1950
  642. package/extensions/feishu/src/external-keys.ts +19 -0
  643. package/extensions/lobster/src/windows-spawn.ts +193 -0
  644. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  645. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  646. package/package.json +1 -1
@@ -1,16 +1,18 @@
1
1
  import fs from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { isSubagentSessionKey } from "../routing/session-key.js";
4
+ import { resolveRequiredHomeDir } from "../infra/home-dir.js";
6
5
  import { runCommandWithTimeout } from "../process/exec.js";
6
+ import { isCronSessionKey, isSubagentSessionKey } from "../routing/session-key.js";
7
7
  import { resolveUserPath } from "../utils.js";
8
+ import { resolveWorkspaceTemplateDir } from "./workspace-templates.js";
8
9
  export function resolveDefaultAgentWorkspaceDir(env = process.env, homedir = os.homedir) {
10
+ const home = resolveRequiredHomeDir(env, homedir);
9
11
  const profile = env.POOLBOT_PROFILE?.trim() || env.CLAWDBOT_PROFILE?.trim();
10
12
  if (profile && profile.toLowerCase() !== "default") {
11
- return path.join(homedir(), `clawd-${profile}`);
13
+ return path.join(home, `clawd-${profile}`);
12
14
  }
13
- return path.join(homedir(), "clawd");
15
+ return path.join(home, "clawd");
14
16
  }
15
17
  export const DEFAULT_AGENT_WORKSPACE_DIR = resolveDefaultAgentWorkspaceDir();
16
18
  export const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
@@ -22,66 +24,203 @@ export const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
22
24
  export const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md";
23
25
  export const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
24
26
  export const DEFAULT_MEMORY_ALT_FILENAME = "memory.md";
25
- const TEMPLATE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../docs/reference/templates");
27
+ const WORKSPACE_STATE_DIRNAME = ".poolbot";
28
+ const WORKSPACE_STATE_FILENAME = "workspace-state.json";
29
+ const WORKSPACE_STATE_VERSION = 1;
30
+ const workspaceTemplateCache = new Map();
31
+ let gitAvailabilityPromise = null;
32
+ // File content cache with mtime invalidation to avoid redundant reads
33
+ const workspaceFileCache = new Map();
34
+ /**
35
+ * Read file with caching based on mtime. Returns cached content if file
36
+ * hasn't changed, otherwise reads from disk and updates cache.
37
+ */
38
+ async function readFileWithCache(filePath) {
39
+ try {
40
+ const stats = await fs.stat(filePath);
41
+ const mtimeMs = stats.mtimeMs;
42
+ const cached = workspaceFileCache.get(filePath);
43
+ // Return cached content if mtime matches
44
+ if (cached && cached.mtimeMs === mtimeMs) {
45
+ return cached.content;
46
+ }
47
+ // Read from disk and update cache
48
+ const content = await fs.readFile(filePath, "utf-8");
49
+ workspaceFileCache.set(filePath, { content, mtimeMs });
50
+ return content;
51
+ }
52
+ catch (error) {
53
+ // Remove from cache if file doesn't exist or is unreadable
54
+ workspaceFileCache.delete(filePath);
55
+ throw error;
56
+ }
57
+ }
26
58
  function stripFrontMatter(content) {
27
- if (!content.startsWith("---"))
59
+ if (!content.startsWith("---")) {
28
60
  return content;
61
+ }
29
62
  const endIndex = content.indexOf("\n---", 3);
30
- if (endIndex === -1)
63
+ if (endIndex === -1) {
31
64
  return content;
65
+ }
32
66
  const start = endIndex + "\n---".length;
33
67
  let trimmed = content.slice(start);
34
68
  trimmed = trimmed.replace(/^\s+/, "");
35
69
  return trimmed;
36
70
  }
37
71
  async function loadTemplate(name) {
38
- const templatePath = path.join(TEMPLATE_DIR, name);
72
+ const cached = workspaceTemplateCache.get(name);
73
+ if (cached) {
74
+ return cached;
75
+ }
76
+ const pending = (async () => {
77
+ const templateDir = await resolveWorkspaceTemplateDir();
78
+ const templatePath = path.join(templateDir, name);
79
+ try {
80
+ const content = await fs.readFile(templatePath, "utf-8");
81
+ return stripFrontMatter(content);
82
+ }
83
+ catch {
84
+ throw new Error(`Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`);
85
+ }
86
+ })();
87
+ workspaceTemplateCache.set(name, pending);
39
88
  try {
40
- const content = await fs.readFile(templatePath, "utf-8");
41
- return stripFrontMatter(content);
89
+ return await pending;
42
90
  }
43
- catch {
44
- throw new Error(`Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`);
91
+ catch (error) {
92
+ workspaceTemplateCache.delete(name);
93
+ throw error;
45
94
  }
46
95
  }
96
+ /** Set of recognized bootstrap filenames for runtime validation */
97
+ const VALID_BOOTSTRAP_NAMES = new Set([
98
+ DEFAULT_AGENTS_FILENAME,
99
+ DEFAULT_SOUL_FILENAME,
100
+ DEFAULT_TOOLS_FILENAME,
101
+ DEFAULT_IDENTITY_FILENAME,
102
+ DEFAULT_USER_FILENAME,
103
+ DEFAULT_HEARTBEAT_FILENAME,
104
+ DEFAULT_BOOTSTRAP_FILENAME,
105
+ DEFAULT_MEMORY_FILENAME,
106
+ DEFAULT_MEMORY_ALT_FILENAME,
107
+ ]);
47
108
  async function writeFileIfMissing(filePath, content) {
48
109
  try {
49
110
  await fs.writeFile(filePath, content, {
50
111
  encoding: "utf-8",
51
112
  flag: "wx",
52
113
  });
114
+ return true;
53
115
  }
54
116
  catch (err) {
55
117
  const anyErr = err;
56
- if (anyErr.code !== "EEXIST")
118
+ if (anyErr.code !== "EEXIST") {
57
119
  throw err;
120
+ }
121
+ return false;
58
122
  }
59
123
  }
60
- async function hasGitRepo(dir) {
124
+ async function fileExists(filePath) {
61
125
  try {
62
- await fs.stat(path.join(dir, ".git"));
126
+ await fs.access(filePath);
63
127
  return true;
64
128
  }
65
129
  catch {
66
130
  return false;
67
131
  }
68
132
  }
69
- async function isGitAvailable() {
133
+ function resolveWorkspaceStatePath(dir) {
134
+ return path.join(dir, WORKSPACE_STATE_DIRNAME, WORKSPACE_STATE_FILENAME);
135
+ }
136
+ function parseWorkspaceOnboardingState(raw) {
137
+ try {
138
+ const parsed = JSON.parse(raw);
139
+ if (!parsed || typeof parsed !== "object") {
140
+ return null;
141
+ }
142
+ return {
143
+ version: WORKSPACE_STATE_VERSION,
144
+ bootstrapSeededAt: typeof parsed.bootstrapSeededAt === "string" ? parsed.bootstrapSeededAt : undefined,
145
+ onboardingCompletedAt: typeof parsed.onboardingCompletedAt === "string" ? parsed.onboardingCompletedAt : undefined,
146
+ };
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ async function readWorkspaceOnboardingState(statePath) {
153
+ try {
154
+ const raw = await fs.readFile(statePath, "utf-8");
155
+ return (parseWorkspaceOnboardingState(raw) ?? {
156
+ version: WORKSPACE_STATE_VERSION,
157
+ });
158
+ }
159
+ catch (err) {
160
+ const anyErr = err;
161
+ if (anyErr.code !== "ENOENT") {
162
+ throw err;
163
+ }
164
+ return {
165
+ version: WORKSPACE_STATE_VERSION,
166
+ };
167
+ }
168
+ }
169
+ async function readWorkspaceOnboardingStateForDir(dir) {
170
+ const statePath = resolveWorkspaceStatePath(resolveUserPath(dir));
171
+ return await readWorkspaceOnboardingState(statePath);
172
+ }
173
+ export async function isWorkspaceOnboardingCompleted(dir) {
174
+ const state = await readWorkspaceOnboardingStateForDir(dir);
175
+ return (typeof state.onboardingCompletedAt === "string" && state.onboardingCompletedAt.trim().length > 0);
176
+ }
177
+ async function writeWorkspaceOnboardingState(statePath, state) {
178
+ await fs.mkdir(path.dirname(statePath), { recursive: true });
179
+ const payload = `${JSON.stringify(state, null, 2)}\n`;
180
+ const tmpPath = `${statePath}.tmp-${process.pid}-${Date.now().toString(36)}`;
70
181
  try {
71
- const result = await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2_000 });
72
- return result.code === 0;
182
+ await fs.writeFile(tmpPath, payload, { encoding: "utf-8" });
183
+ await fs.rename(tmpPath, statePath);
184
+ }
185
+ catch (err) {
186
+ await fs.unlink(tmpPath).catch(() => { });
187
+ throw err;
188
+ }
189
+ }
190
+ async function hasGitRepo(dir) {
191
+ try {
192
+ await fs.stat(path.join(dir, ".git"));
193
+ return true;
73
194
  }
74
195
  catch {
75
196
  return false;
76
197
  }
77
198
  }
199
+ async function isGitAvailable() {
200
+ if (gitAvailabilityPromise) {
201
+ return gitAvailabilityPromise;
202
+ }
203
+ gitAvailabilityPromise = (async () => {
204
+ try {
205
+ const result = await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2_000 });
206
+ return result.code === 0;
207
+ }
208
+ catch {
209
+ return false;
210
+ }
211
+ })();
212
+ return gitAvailabilityPromise;
213
+ }
78
214
  async function ensureGitRepo(dir, isBrandNewWorkspace) {
79
- if (!isBrandNewWorkspace)
215
+ if (!isBrandNewWorkspace) {
80
216
  return;
81
- if (await hasGitRepo(dir))
217
+ }
218
+ if (await hasGitRepo(dir)) {
82
219
  return;
83
- if (!(await isGitAvailable()))
220
+ }
221
+ if (!(await isGitAvailable())) {
84
222
  return;
223
+ }
85
224
  try {
86
225
  await runCommandWithTimeout(["git", "init"], { cwd: dir, timeoutMs: 10_000 });
87
226
  }
@@ -93,8 +232,9 @@ export async function ensureAgentWorkspace(params) {
93
232
  const rawDir = params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR;
94
233
  const dir = resolveUserPath(rawDir);
95
234
  await fs.mkdir(dir, { recursive: true });
96
- if (!params?.ensureBootstrapFiles)
235
+ if (!params?.ensureBootstrapFiles) {
97
236
  return { dir };
237
+ }
98
238
  const agentsPath = path.join(dir, DEFAULT_AGENTS_FILENAME);
99
239
  const soulPath = path.join(dir, DEFAULT_SOUL_FILENAME);
100
240
  const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
@@ -102,6 +242,7 @@ export async function ensureAgentWorkspace(params) {
102
242
  const userPath = path.join(dir, DEFAULT_USER_FILENAME);
103
243
  const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME);
104
244
  const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
245
+ const statePath = resolveWorkspaceStatePath(dir);
105
246
  const isBrandNewWorkspace = await (async () => {
106
247
  const paths = [agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath];
107
248
  const existing = await Promise.all(paths.map(async (p) => {
@@ -121,15 +262,53 @@ export async function ensureAgentWorkspace(params) {
121
262
  const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME);
122
263
  const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME);
123
264
  const heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME);
124
- const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
125
265
  await writeFileIfMissing(agentsPath, agentsTemplate);
126
266
  await writeFileIfMissing(soulPath, soulTemplate);
127
267
  await writeFileIfMissing(toolsPath, toolsTemplate);
128
268
  await writeFileIfMissing(identityPath, identityTemplate);
129
269
  await writeFileIfMissing(userPath, userTemplate);
130
270
  await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
131
- if (isBrandNewWorkspace) {
132
- await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
271
+ let state = await readWorkspaceOnboardingState(statePath);
272
+ let stateDirty = false;
273
+ const markState = (next) => {
274
+ state = { ...state, ...next };
275
+ stateDirty = true;
276
+ };
277
+ const nowIso = () => new Date().toISOString();
278
+ let bootstrapExists = await fileExists(bootstrapPath);
279
+ if (!state.bootstrapSeededAt && bootstrapExists) {
280
+ markState({ bootstrapSeededAt: nowIso() });
281
+ }
282
+ if (!state.onboardingCompletedAt && state.bootstrapSeededAt && !bootstrapExists) {
283
+ markState({ onboardingCompletedAt: nowIso() });
284
+ }
285
+ if (!state.bootstrapSeededAt && !state.onboardingCompletedAt && !bootstrapExists) {
286
+ // Legacy migration path: if USER/IDENTITY diverged from templates, treat onboarding as complete
287
+ // and avoid recreating BOOTSTRAP for already-onboarded workspaces.
288
+ const [identityContent, userContent] = await Promise.all([
289
+ fs.readFile(identityPath, "utf-8"),
290
+ fs.readFile(userPath, "utf-8"),
291
+ ]);
292
+ const legacyOnboardingCompleted = identityContent !== identityTemplate || userContent !== userTemplate;
293
+ if (legacyOnboardingCompleted) {
294
+ markState({ onboardingCompletedAt: nowIso() });
295
+ }
296
+ else {
297
+ const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
298
+ const wroteBootstrap = await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
299
+ if (!wroteBootstrap) {
300
+ bootstrapExists = await fileExists(bootstrapPath);
301
+ }
302
+ else {
303
+ bootstrapExists = true;
304
+ }
305
+ if (bootstrapExists && !state.bootstrapSeededAt) {
306
+ markState({ bootstrapSeededAt: nowIso() });
307
+ }
308
+ }
309
+ }
310
+ if (stateDirty) {
311
+ await writeWorkspaceOnboardingState(statePath, state);
133
312
  }
134
313
  await ensureGitRepo(dir, isBrandNewWorkspace);
135
314
  return {
@@ -159,8 +338,9 @@ async function resolveMemoryBootstrapEntries(resolvedDir) {
159
338
  // optional
160
339
  }
161
340
  }
162
- if (entries.length <= 1)
341
+ if (entries.length <= 1) {
163
342
  return entries;
343
+ }
164
344
  const seen = new Set();
165
345
  const deduped = [];
166
346
  for (const entry of entries) {
@@ -169,8 +349,9 @@ async function resolveMemoryBootstrapEntries(resolvedDir) {
169
349
  key = await fs.realpath(entry.filePath);
170
350
  }
171
351
  catch { }
172
- if (seen.has(key))
352
+ if (seen.has(key)) {
173
353
  continue;
354
+ }
174
355
  seen.add(key);
175
356
  deduped.push(entry);
176
357
  }
@@ -212,7 +393,7 @@ export async function loadWorkspaceBootstrapFiles(dir) {
212
393
  const result = [];
213
394
  for (const entry of entries) {
214
395
  try {
215
- const content = await fs.readFile(entry.filePath, "utf-8");
396
+ const content = await readFileWithCache(entry.filePath);
216
397
  result.push({
217
398
  name: entry.name,
218
399
  path: entry.filePath,
@@ -226,18 +407,13 @@ export async function loadWorkspaceBootstrapFiles(dir) {
226
407
  }
227
408
  return result;
228
409
  }
229
- const SUBAGENT_BOOTSTRAP_ALLOWLIST = new Set([DEFAULT_AGENTS_FILENAME, DEFAULT_TOOLS_FILENAME]);
230
- const VALID_BOOTSTRAP_NAMES = new Set([
231
- DEFAULT_AGENTS_FILENAME,
232
- DEFAULT_SOUL_FILENAME,
233
- DEFAULT_TOOLS_FILENAME,
234
- DEFAULT_IDENTITY_FILENAME,
235
- DEFAULT_USER_FILENAME,
236
- DEFAULT_HEARTBEAT_FILENAME,
237
- DEFAULT_BOOTSTRAP_FILENAME,
238
- DEFAULT_MEMORY_FILENAME,
239
- DEFAULT_MEMORY_ALT_FILENAME,
240
- ]);
410
+ const MINIMAL_BOOTSTRAP_ALLOWLIST = new Set([DEFAULT_AGENTS_FILENAME, DEFAULT_TOOLS_FILENAME]);
411
+ export function filterBootstrapFilesForSession(files, sessionKey) {
412
+ if (!sessionKey || (!isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey))) {
413
+ return files;
414
+ }
415
+ return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name));
416
+ }
241
417
  export async function loadExtraBootstrapFiles(dir, extraPatterns) {
242
418
  if (!extraPatterns.length) {
243
419
  return [];
@@ -250,6 +426,7 @@ export async function loadExtraBootstrapFiles(dir, extraPatterns) {
250
426
  catch {
251
427
  // Keep lexical root if realpath fails.
252
428
  }
429
+ // Resolve glob patterns into concrete file paths
253
430
  const resolvedPaths = new Set();
254
431
  for (const pattern of extraPatterns) {
255
432
  if (pattern.includes("*") || pattern.includes("?") || pattern.includes("{")) {
@@ -260,6 +437,7 @@ export async function loadExtraBootstrapFiles(dir, extraPatterns) {
260
437
  }
261
438
  }
262
439
  catch {
440
+ // glob not available or pattern error — fall back to literal
263
441
  resolvedPaths.add(pattern);
264
442
  }
265
443
  }
@@ -270,20 +448,23 @@ export async function loadExtraBootstrapFiles(dir, extraPatterns) {
270
448
  const result = [];
271
449
  for (const relPath of resolvedPaths) {
272
450
  const filePath = path.resolve(resolvedDir, relPath);
451
+ // Guard against path traversal — resolved path must stay within workspace
273
452
  if (!filePath.startsWith(resolvedDir + path.sep) && filePath !== resolvedDir) {
274
453
  continue;
275
454
  }
276
455
  try {
456
+ // Resolve symlinks and verify the real path is still within workspace
277
457
  const realFilePath = await fs.realpath(filePath);
278
458
  if (!realFilePath.startsWith(realResolvedDir + path.sep) &&
279
459
  realFilePath !== realResolvedDir) {
280
460
  continue;
281
461
  }
462
+ // Only load files whose basename is a recognized bootstrap filename
282
463
  const baseName = path.basename(relPath);
283
464
  if (!VALID_BOOTSTRAP_NAMES.has(baseName)) {
284
465
  continue;
285
466
  }
286
- const content = await fs.readFile(realFilePath, "utf-8");
467
+ const content = await readFileWithCache(realFilePath);
287
468
  result.push({
288
469
  name: baseName,
289
470
  path: filePath,
@@ -297,8 +478,3 @@ export async function loadExtraBootstrapFiles(dir, extraPatterns) {
297
478
  }
298
479
  return result;
299
480
  }
300
- export function filterBootstrapFilesForSession(files, sessionKey) {
301
- if (!sessionKey || !isSubagentSessionKey(sessionKey))
302
- return files;
303
- return files.filter((file) => SUBAGENT_BOOTSTRAP_ALLOWLIST.has(file.name));
304
- }
@@ -231,6 +231,28 @@ function buildChatCommands() {
231
231
  textAlias: "/whoami",
232
232
  category: "status",
233
233
  }),
234
+ defineChatCommand({
235
+ key: "session",
236
+ nativeName: "session",
237
+ description: "Manage session-level settings (for example /session ttl).",
238
+ textAlias: "/session",
239
+ category: "session",
240
+ args: [
241
+ {
242
+ name: "action",
243
+ description: "ttl",
244
+ type: "string",
245
+ choices: ["ttl"],
246
+ },
247
+ {
248
+ name: "value",
249
+ description: "Duration (24h, 90m) or off",
250
+ type: "string",
251
+ captureRemaining: true,
252
+ },
253
+ ],
254
+ argsMenu: "auto",
255
+ }),
234
256
  defineChatCommand({
235
257
  key: "subagents",
236
258
  nativeName: "subagents",
@@ -258,6 +280,35 @@ function buildChatCommands() {
258
280
  ],
259
281
  argsMenu: "auto",
260
282
  }),
283
+ defineChatCommand({
284
+ key: "focus",
285
+ nativeName: "focus",
286
+ description: "Bind this Discord thread (or a new one) to a session target.",
287
+ textAlias: "/focus",
288
+ category: "management",
289
+ args: [
290
+ {
291
+ name: "target",
292
+ description: "Subagent label/index or session key/id/label",
293
+ type: "string",
294
+ captureRemaining: true,
295
+ },
296
+ ],
297
+ }),
298
+ defineChatCommand({
299
+ key: "unfocus",
300
+ nativeName: "unfocus",
301
+ description: "Remove the current Discord thread binding.",
302
+ textAlias: "/unfocus",
303
+ category: "management",
304
+ }),
305
+ defineChatCommand({
306
+ key: "agents",
307
+ nativeName: "agents",
308
+ description: "List thread-bound agents for this session.",
309
+ textAlias: "/agents",
310
+ category: "management",
311
+ }),
261
312
  defineChatCommand({
262
313
  key: "kill",
263
314
  nativeName: "kill",
@@ -1,6 +1,8 @@
1
- import { getChatCommands, getNativeCommandSurfaces } from "./commands-registry.data.js";
2
1
  import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js";
3
2
  import { resolveConfiguredModelRef } from "../agents/model-selection.js";
3
+ import { isCommandFlagEnabled } from "../config/commands.js";
4
+ import { escapeRegExp } from "../utils.js";
5
+ import { getChatCommands, getNativeCommandSurfaces } from "./commands-registry.data.js";
4
6
  let cachedTextAliasMap = null;
5
7
  let cachedTextAliasCommands = null;
6
8
  let cachedDetection;
@@ -29,9 +31,6 @@ function getTextAliasMap() {
29
31
  cachedTextAliasCommands = commands;
30
32
  return map;
31
33
  }
32
- function escapeRegExp(value) {
33
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
34
- }
35
34
  function buildSkillCommandDefinitions(skillCommands) {
36
35
  if (!skillCommands || skillCommands.length === 0)
37
36
  return [];
@@ -53,11 +52,11 @@ export function listChatCommands(params) {
53
52
  }
54
53
  export function isCommandEnabled(cfg, commandKey) {
55
54
  if (commandKey === "config")
56
- return cfg.commands?.config === true;
55
+ return isCommandFlagEnabled(cfg, "config");
57
56
  if (commandKey === "debug")
58
- return cfg.commands?.debug === true;
57
+ return isCommandFlagEnabled(cfg, "debug");
59
58
  if (commandKey === "bash")
60
- return cfg.commands?.bash === true;
59
+ return isCommandFlagEnabled(cfg, "bash");
61
60
  return true;
62
61
  }
63
62
  export function listChatCommandsForConfig(cfg, params) {
@@ -81,25 +80,24 @@ function resolveNativeName(command, provider) {
81
80
  }
82
81
  return command.nativeName;
83
82
  }
84
- export function listNativeCommandSpecs(params) {
85
- return listChatCommands({ skillCommands: params?.skillCommands })
86
- .filter((command) => command.scope !== "text" && command.nativeName)
87
- .map((command) => ({
88
- name: resolveNativeName(command, params?.provider) ?? command.key,
83
+ function toNativeCommandSpec(command, provider) {
84
+ return {
85
+ name: resolveNativeName(command, provider) ?? command.key,
89
86
  description: command.description,
90
87
  acceptsArgs: Boolean(command.acceptsArgs),
91
88
  args: command.args,
92
- }));
89
+ };
93
90
  }
94
- export function listNativeCommandSpecsForConfig(cfg, params) {
95
- return listChatCommandsForConfig(cfg, params)
91
+ function listNativeSpecsFromCommands(commands, provider) {
92
+ return commands
96
93
  .filter((command) => command.scope !== "text" && command.nativeName)
97
- .map((command) => ({
98
- name: resolveNativeName(command, params?.provider) ?? command.key,
99
- description: command.description,
100
- acceptsArgs: Boolean(command.acceptsArgs),
101
- args: command.args,
102
- }));
94
+ .map((command) => toNativeCommandSpec(command, provider));
95
+ }
96
+ export function listNativeCommandSpecs(params) {
97
+ return listNativeSpecsFromCommands(listChatCommands({ skillCommands: params?.skillCommands }), params?.provider);
98
+ }
99
+ export function listNativeCommandSpecsForConfig(cfg, params) {
100
+ return listNativeSpecsFromCommands(listChatCommandsForConfig(cfg, params), params?.provider);
103
101
  }
104
102
  export function findCommandByNativeName(name, provider) {
105
103
  const normalized = name.trim().toLowerCase();
@@ -0,0 +1,114 @@
1
+ import { formatProviderModelRef } from "./model-runtime.js";
2
+ const FALLBACK_REASON_PART_MAX = 80;
3
+ export function normalizeFallbackModelRef(value) {
4
+ const trimmed = String(value ?? "").trim();
5
+ return trimmed || undefined;
6
+ }
7
+ function truncateFallbackReasonPart(value, max = FALLBACK_REASON_PART_MAX) {
8
+ const text = String(value ?? "")
9
+ .replace(/\s+/g, " ")
10
+ .trim();
11
+ if (text.length <= max) {
12
+ return text;
13
+ }
14
+ return `${text.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
15
+ }
16
+ export function formatFallbackAttemptReason(attempt) {
17
+ const reason = attempt.reason?.trim();
18
+ if (reason) {
19
+ return reason.replace(/_/g, " ");
20
+ }
21
+ const code = attempt.code?.trim();
22
+ if (code) {
23
+ return code;
24
+ }
25
+ if (typeof attempt.status === "number") {
26
+ return `HTTP ${attempt.status}`;
27
+ }
28
+ return truncateFallbackReasonPart(attempt.error || "error");
29
+ }
30
+ function formatFallbackAttemptSummary(attempt) {
31
+ return `${formatProviderModelRef(attempt.provider, attempt.model)} ${formatFallbackAttemptReason(attempt)}`;
32
+ }
33
+ export function buildFallbackReasonSummary(attempts) {
34
+ const firstAttempt = attempts[0];
35
+ const firstReason = firstAttempt
36
+ ? formatFallbackAttemptReason(firstAttempt)
37
+ : "selected model unavailable";
38
+ const moreAttempts = attempts.length > 1 ? ` (+${attempts.length - 1} more attempts)` : "";
39
+ return `${truncateFallbackReasonPart(firstReason)}${moreAttempts}`;
40
+ }
41
+ export function buildFallbackAttemptSummaries(attempts) {
42
+ return attempts.map((attempt) => truncateFallbackReasonPart(formatFallbackAttemptSummary(attempt)));
43
+ }
44
+ export function buildFallbackNotice(params) {
45
+ const selected = formatProviderModelRef(params.selectedProvider, params.selectedModel);
46
+ const active = formatProviderModelRef(params.activeProvider, params.activeModel);
47
+ if (selected === active) {
48
+ return null;
49
+ }
50
+ const reasonSummary = buildFallbackReasonSummary(params.attempts);
51
+ return `↪️ Model Fallback: ${active} (selected ${selected}; ${reasonSummary})`;
52
+ }
53
+ export function buildFallbackClearedNotice(params) {
54
+ const selected = formatProviderModelRef(params.selectedProvider, params.selectedModel);
55
+ const previous = normalizeFallbackModelRef(params.previousActiveModel);
56
+ if (previous && previous !== selected) {
57
+ return `↪️ Model Fallback cleared: ${selected} (was ${previous})`;
58
+ }
59
+ return `↪️ Model Fallback cleared: ${selected}`;
60
+ }
61
+ export function resolveActiveFallbackState(params) {
62
+ const selected = normalizeFallbackModelRef(params.state?.fallbackNoticeSelectedModel);
63
+ const active = normalizeFallbackModelRef(params.state?.fallbackNoticeActiveModel);
64
+ const reason = normalizeFallbackModelRef(params.state?.fallbackNoticeReason);
65
+ const fallbackActive = params.selectedModelRef !== params.activeModelRef &&
66
+ selected === params.selectedModelRef &&
67
+ active === params.activeModelRef;
68
+ return {
69
+ active: fallbackActive,
70
+ reason: fallbackActive ? reason : undefined,
71
+ };
72
+ }
73
+ export function resolveFallbackTransition(params) {
74
+ const selectedModelRef = formatProviderModelRef(params.selectedProvider, params.selectedModel);
75
+ const activeModelRef = formatProviderModelRef(params.activeProvider, params.activeModel);
76
+ const previousState = {
77
+ selectedModel: normalizeFallbackModelRef(params.state?.fallbackNoticeSelectedModel),
78
+ activeModel: normalizeFallbackModelRef(params.state?.fallbackNoticeActiveModel),
79
+ reason: normalizeFallbackModelRef(params.state?.fallbackNoticeReason),
80
+ };
81
+ const fallbackActive = selectedModelRef !== activeModelRef;
82
+ const fallbackTransitioned = fallbackActive &&
83
+ (previousState.selectedModel !== selectedModelRef ||
84
+ previousState.activeModel !== activeModelRef);
85
+ const fallbackCleared = !fallbackActive && Boolean(previousState.selectedModel || previousState.activeModel);
86
+ const reasonSummary = buildFallbackReasonSummary(params.attempts);
87
+ const attemptSummaries = buildFallbackAttemptSummaries(params.attempts);
88
+ const nextState = fallbackActive
89
+ ? {
90
+ selectedModel: selectedModelRef,
91
+ activeModel: activeModelRef,
92
+ reason: reasonSummary,
93
+ }
94
+ : {
95
+ selectedModel: undefined,
96
+ activeModel: undefined,
97
+ reason: undefined,
98
+ };
99
+ const stateChanged = previousState.selectedModel !== nextState.selectedModel ||
100
+ previousState.activeModel !== nextState.activeModel ||
101
+ previousState.reason !== nextState.reason;
102
+ return {
103
+ selectedModelRef,
104
+ activeModelRef,
105
+ fallbackActive,
106
+ fallbackTransitioned,
107
+ fallbackCleared,
108
+ reasonSummary,
109
+ attemptSummaries,
110
+ previousState,
111
+ nextState,
112
+ stateChanged,
113
+ };
114
+ }