@poolzin/pool-bot 2026.3.25 → 2026.3.27

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 (271) hide show
  1. package/dist/agents/model-fallback.js +5 -4
  2. package/dist/agents/tools/common.js +16 -201
  3. package/dist/auto-reply/auto-reply/reply/agent-runner-execution.js +502 -0
  4. package/dist/auto-reply/auto-reply/reply/agent-runner-helpers.js +65 -0
  5. package/dist/auto-reply/auto-reply/reply/agent-runner-memory.js +160 -0
  6. package/dist/auto-reply/auto-reply/reply/agent-runner-payloads.js +85 -0
  7. package/dist/auto-reply/auto-reply/reply/agent-runner-utils.js +101 -0
  8. package/dist/auto-reply/auto-reply/reply/bash-command.js +338 -0
  9. package/dist/auto-reply/auto-reply/reply/block-streaming.js +91 -0
  10. package/dist/auto-reply/auto-reply/reply/commands-approve.js +88 -0
  11. package/dist/auto-reply/auto-reply/reply/commands-bash.js +26 -0
  12. package/dist/auto-reply/auto-reply/reply/commands-compact.js +107 -0
  13. package/dist/auto-reply/auto-reply/reply/commands-config.js +241 -0
  14. package/dist/auto-reply/auto-reply/reply/commands-context-report.js +295 -0
  15. package/dist/auto-reply/auto-reply/reply/commands-context.js +30 -0
  16. package/dist/auto-reply/auto-reply/reply/commands-core.js +151 -0
  17. package/dist/auto-reply/auto-reply/reply/commands-export-session.js +163 -0
  18. package/dist/auto-reply/auto-reply/reply/commands-info.js +184 -0
  19. package/dist/auto-reply/auto-reply/reply/commands-models.js +299 -0
  20. package/dist/auto-reply/auto-reply/reply/commands-plugin.js +35 -0
  21. package/dist/auto-reply/auto-reply/reply/commands-ptt.js +171 -0
  22. package/dist/auto-reply/auto-reply/reply/commands-setunset-standard.js +13 -0
  23. package/dist/auto-reply/auto-reply/reply/commands-setunset.js +73 -0
  24. package/dist/auto-reply/auto-reply/reply/commands-slash-parse.js +31 -0
  25. package/dist/auto-reply/auto-reply/reply/commands-status.js +178 -0
  26. package/dist/auto-reply/auto-reply/reply/commands-subagents.js +73 -0
  27. package/dist/auto-reply/auto-reply/reply/commands-system-prompt.js +117 -0
  28. package/dist/auto-reply/auto-reply/reply/commands-tts.js +231 -0
  29. package/dist/auto-reply/auto-reply/reply/directive-handling.impl.js +380 -0
  30. package/dist/auto-reply/auto-reply/reply/followup-runner.js +227 -0
  31. package/dist/auto-reply/auto-reply/reply/get-reply-directives-apply.js +201 -0
  32. package/dist/auto-reply/auto-reply/reply/get-reply-directives-utils.js +54 -0
  33. package/dist/auto-reply/auto-reply/reply/get-reply-directives.js +332 -0
  34. package/dist/auto-reply/auto-reply/reply/get-reply-inline-actions.js +258 -0
  35. package/dist/auto-reply/auto-reply/reply/get-reply-run.js +297 -0
  36. package/dist/auto-reply/auto-reply/reply/groups.js +102 -0
  37. package/dist/auto-reply/auto-reply/reply/mentions.js +129 -0
  38. package/dist/auto-reply/auto-reply/reply/reply-delivery.js +92 -0
  39. package/dist/auto-reply/auto-reply/reply/reply-directives.js +30 -0
  40. package/dist/auto-reply/auto-reply/reply/reply-dispatcher.js +152 -0
  41. package/dist/auto-reply/auto-reply/reply/reply-elevated.js +166 -0
  42. package/dist/auto-reply/auto-reply/reply/reply-inline.js +28 -0
  43. package/dist/auto-reply/auto-reply/reply/reply-payloads.js +114 -0
  44. package/dist/auto-reply/auto-reply/reply/reply-reference.js +36 -0
  45. package/dist/auto-reply/auto-reply/reply/reply-tags.js +13 -0
  46. package/dist/auto-reply/auto-reply/reply/reply-threading.js +41 -0
  47. package/dist/auto-reply/auto-reply/reply/session-updates.js +233 -0
  48. package/dist/auto-reply/auto-reply/reply/stage-sandbox-media.js +146 -0
  49. package/dist/build-info.json +3 -3
  50. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  51. package/dist/canvas-host/a2ui/a2ui.bundle.js +2 -17772
  52. package/dist/canvas-host/a2ui/index.html +1 -307
  53. package/dist/channels/channels/directory-config.js +185 -0
  54. package/dist/channels/channels/discord/handle-action.guild-admin.js +332 -0
  55. package/dist/channels/channels/discord/handle-action.js +165 -0
  56. package/dist/channels/channels/discord.js +413 -0
  57. package/dist/channels/channels/dock.js +436 -0
  58. package/dist/channels/channels/index.js +51 -0
  59. package/dist/channels/channels/plugins/outbound/discord.js +101 -0
  60. package/dist/channels/channels/whatsapp.js +17 -0
  61. package/dist/channels/plugins/types.js +1 -1
  62. package/dist/channels/run-state-machine.js +7 -0
  63. package/dist/commands/models/auth.js +47 -1
  64. package/dist/commands-subagents/action-agents.js +44 -0
  65. package/dist/commands-subagents/action-focus.js +64 -0
  66. package/dist/commands-subagents/action-help.js +4 -0
  67. package/dist/commands-subagents/action-info.js +45 -0
  68. package/dist/commands-subagents/action-kill.js +60 -0
  69. package/dist/commands-subagents/action-list.js +44 -0
  70. package/dist/commands-subagents/action-log.js +29 -0
  71. package/dist/commands-subagents/action-send.js +119 -0
  72. package/dist/commands-subagents/action-spawn.js +52 -0
  73. package/dist/commands-subagents/action-unfocus.js +30 -0
  74. package/dist/commands-subagents/shared.js +303 -0
  75. package/dist/config/config.js +1 -8
  76. package/dist/config/types.secrets.js +61 -0
  77. package/dist/control-ui/assets/{index-D7shnQwQ.js → index-umCsvrWy.js} +884 -741
  78. package/dist/control-ui/assets/index-umCsvrWy.js.map +1 -0
  79. package/dist/control-ui/assets/pt-BR-DedEVAvY.js +2 -0
  80. package/dist/control-ui/assets/pt-BR-DedEVAvY.js.map +1 -0
  81. package/dist/control-ui/assets/zh-CN-CDzeklK-.js +2 -0
  82. package/dist/control-ui/assets/zh-CN-CDzeklK-.js.map +1 -0
  83. package/dist/control-ui/assets/zh-TW-BJCRYNWH.js +2 -0
  84. package/dist/control-ui/assets/zh-TW-BJCRYNWH.js.map +1 -0
  85. package/dist/control-ui/index.html +1 -1
  86. package/dist/gateway/method-scopes.js +9 -1
  87. package/dist/gateway/node-pending-work.js +142 -0
  88. package/dist/gateway/protocol/index.js +5 -1
  89. package/dist/gateway/protocol/schema/nodes.js +18 -0
  90. package/dist/gateway/server-methods/nodes-pending.js +96 -0
  91. package/dist/gateway/server-methods-list.js +4 -0
  92. package/dist/gateway/server-methods.js +2 -0
  93. package/dist/imessage/channel.js +253 -0
  94. package/dist/imessage/monitor/echo-cache.js +70 -0
  95. package/dist/imessage/monitor/loop-rate-limiter.js +51 -0
  96. package/dist/imessage/monitor/reflection-guard.js +50 -0
  97. package/dist/imessage/monitor/sanitize-outbound.js +25 -0
  98. package/dist/imessage/monitor/self-chat-cache.js +75 -0
  99. package/dist/imessage/runtime.js +3 -0
  100. package/dist/infra/exec-approval-reply.js +7 -0
  101. package/dist/infra/tmp-openclaw-dir.js +84 -0
  102. package/dist/pairing/pairing-challenge.js +15 -0
  103. package/dist/plugin-sdk/account-id.d.ts +1 -0
  104. package/dist/plugin-sdk/agent-media-payload.d.ts +12 -0
  105. package/dist/plugin-sdk/allow-from.d.ts +27 -0
  106. package/dist/plugin-sdk/command-auth.d.ts +25 -0
  107. package/dist/plugin-sdk/command-auth.js +3 -1
  108. package/dist/plugin-sdk/config-paths.d.ts +6 -0
  109. package/dist/plugin-sdk/file-lock.d.ts +16 -0
  110. package/dist/plugin-sdk/index.d.ts +428 -0
  111. package/dist/plugin-sdk/index.js +237 -103
  112. package/dist/plugin-sdk/json-store.d.ts +5 -0
  113. package/dist/plugin-sdk/keyed-async-queue.d.ts +12 -0
  114. package/dist/plugin-sdk/onboarding.d.ts +11 -0
  115. package/dist/plugin-sdk/provider-auth-result.d.ts +14 -0
  116. package/dist/plugin-sdk/slack-message-actions.d.ts +11 -0
  117. package/dist/plugin-sdk/status-helpers.d.ts +25 -0
  118. package/dist/plugin-sdk/temp-path.d.ts +12 -0
  119. package/dist/plugin-sdk/text-chunking.d.ts +1 -0
  120. package/dist/plugin-sdk/tool-send.d.ts +4 -0
  121. package/dist/plugin-sdk/webhook-path.d.ts +6 -0
  122. package/dist/plugin-sdk/webhook-targets.d.ts +23 -0
  123. package/dist/plugin-sdk/windows-spawn.d.ts +39 -0
  124. package/dist/plugin-sdk-internal/accounts.js +6 -0
  125. package/dist/plugin-sdk-internal/discord.js +23 -0
  126. package/dist/plugin-sdk-internal/imessage.js +13 -0
  127. package/dist/plugin-sdk-internal/setup.js +9 -0
  128. package/dist/plugin-sdk-internal/signal.js +13 -0
  129. package/dist/plugin-sdk-internal/slack.js +22 -0
  130. package/dist/plugin-sdk-internal/telegram.js +32 -0
  131. package/dist/plugin-sdk-internal/whatsapp.js +29 -0
  132. package/dist/routing/session-key.js +4 -185
  133. package/dist/shared/pid-alive.js +2 -61
  134. package/dist/shared/process-scoped-map.js +5 -7
  135. package/dist/signal/channel.js +264 -0
  136. package/dist/signal/monitor/access-policy.js +60 -0
  137. package/dist/signal/runtime.js +3 -0
  138. package/dist/slack/account-inspect.js +135 -0
  139. package/dist/slack/blocks-input.js +7 -38
  140. package/dist/slack/channel.js +394 -0
  141. package/dist/slack/interactive-replies.js +28 -0
  142. package/dist/slack/monitor/channel-type.js +31 -0
  143. package/dist/slack/monitor/dm-auth.js +49 -0
  144. package/dist/slack/monitor/events/interactions.modal.js +137 -0
  145. package/dist/slack/monitor/events/message-subtype-handlers.js +68 -0
  146. package/dist/slack/monitor/events/system-event-context.js +29 -0
  147. package/dist/slack/monitor/events/system-event-test-harness.js +41 -0
  148. package/dist/slack/monitor/external-arg-menu-store.js +46 -0
  149. package/dist/slack/monitor/message-handler/prepare-content.js +69 -0
  150. package/dist/slack/monitor/message-handler/prepare-thread-context.js +91 -0
  151. package/dist/slack/monitor/message-handler/prepare.test-helpers.js +55 -0
  152. package/dist/slack/monitor/reconnect-policy.js +78 -0
  153. package/dist/slack/monitor/slash-commands.runtime.js +1 -0
  154. package/dist/slack/monitor/slash-dispatch.runtime.js +9 -0
  155. package/dist/slack/monitor/slash-skill-commands.runtime.js +1 -0
  156. package/dist/slack/resolve-allowlist-common.js +36 -0
  157. package/dist/slack/runtime.js +3 -0
  158. package/dist/slack/sent-thread-cache.js +61 -0
  159. package/dist/slack/truncate.js +10 -0
  160. package/dist/telegram/account-inspect.js +175 -0
  161. package/dist/telegram/allow-from.js +10 -0
  162. package/dist/telegram/api-fetch.js +18 -0
  163. package/dist/telegram/approval-buttons.js +30 -0
  164. package/dist/telegram/audit-membership-runtime.js +61 -0
  165. package/dist/telegram/bot/delivery.replies.js +508 -0
  166. package/dist/telegram/bot/delivery.resolve-media.js +227 -0
  167. package/dist/telegram/bot/delivery.send.js +132 -0
  168. package/dist/telegram/bot/reply-threading.js +46 -0
  169. package/dist/telegram/bot-message-context.body.js +186 -0
  170. package/dist/telegram/bot-message-context.session.js +207 -0
  171. package/dist/telegram/bot-message-context.types.js +1 -0
  172. package/dist/telegram/bot-native-commands.test-helpers.js +117 -0
  173. package/dist/telegram/bot.media.e2e-harness.js +81 -0
  174. package/dist/telegram/bot.media.test-utils.js +81 -0
  175. package/dist/telegram/channel-actions.js +225 -0
  176. package/dist/telegram/channel.js +515 -0
  177. package/dist/telegram/conversation-route.js +107 -0
  178. package/dist/telegram/delivery.js +2 -0
  179. package/dist/telegram/delivery.replies.js +508 -0
  180. package/dist/telegram/dm-access.js +86 -0
  181. package/dist/telegram/draft-stream.test-helpers.js +62 -0
  182. package/dist/telegram/exec-approvals-handler.js +281 -0
  183. package/dist/telegram/exec-approvals.js +62 -0
  184. package/dist/telegram/forum-service-message.js +22 -0
  185. package/dist/telegram/group-config-helpers.js +10 -0
  186. package/dist/telegram/lane-delivery-state.js +19 -0
  187. package/dist/telegram/lane-delivery-text-deliverer.js +357 -0
  188. package/dist/telegram/lane-delivery.js +2 -0
  189. package/dist/telegram/normalize.js +37 -0
  190. package/dist/telegram/onboarding.js +192 -0
  191. package/dist/telegram/outbound-adapter.js +100 -0
  192. package/dist/telegram/polling-session.js +275 -0
  193. package/dist/telegram/runtime.js +3 -0
  194. package/dist/telegram/sendchataction-401-backoff.js +71 -0
  195. package/dist/telegram/sequential-key.js +46 -0
  196. package/dist/telegram/status-issues.js +105 -0
  197. package/dist/telegram/target-writeback.js +165 -0
  198. package/dist/telegram/thread-bindings.js +560 -0
  199. package/dist/utils.js +32 -257
  200. package/dist/wizard/prompts.js +5 -5
  201. package/extensions/feishu/src/policy.ts +1 -1
  202. package/extensions/firecrawl/index.test.ts +82 -0
  203. package/extensions/firecrawl/index.ts +20 -0
  204. package/extensions/firecrawl/openclaw.plugin.json +8 -0
  205. package/extensions/firecrawl/package.json +12 -0
  206. package/extensions/firecrawl/src/config.ts +159 -0
  207. package/extensions/firecrawl/src/firecrawl-client.ts +446 -0
  208. package/extensions/firecrawl/src/firecrawl-scrape-tool.ts +89 -0
  209. package/extensions/firecrawl/src/firecrawl-search-provider.ts +63 -0
  210. package/extensions/firecrawl/src/firecrawl-search-tool.ts +76 -0
  211. package/package.json +1 -1
  212. package/dist/.buildstamp +0 -1
  213. package/dist/acp/bindings-store.js +0 -209
  214. package/dist/acp/control-plane/runtime-cache.js +0 -54
  215. package/dist/acp/control-plane/runtime-options.js +0 -215
  216. package/dist/acp/control-plane/session-actor-queue.js +0 -36
  217. package/dist/acp/index.js +0 -2
  218. package/dist/acp/runtime/errors.js +0 -47
  219. package/dist/acp/runtime/registry.js +0 -86
  220. package/dist/acp/secret-file.js +0 -22
  221. package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +0 -23
  222. package/dist/agents/bash-process-registry.test-helpers.js +0 -29
  223. package/dist/agents/bash-tools.exec-approval-request.js +0 -20
  224. package/dist/agents/bash-tools.exec-host-gateway.js +0 -240
  225. package/dist/agents/bash-tools.exec-host-node.js +0 -235
  226. package/dist/agents/checkpoint-manager.js +0 -290
  227. package/dist/agents/claude-cli-runner.js +0 -3
  228. package/dist/agents/error-classifier.js +0 -251
  229. package/dist/agents/live-model-filter.js +0 -84
  230. package/dist/agents/nvidia-models.js +0 -228
  231. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +0 -34
  232. package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +0 -156
  233. package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +0 -30
  234. package/dist/agents/provider/config-loader.js +0 -76
  235. package/dist/agents/provider/index.js +0 -15
  236. package/dist/agents/provider/models-dev.js +0 -129
  237. package/dist/agents/provider/session-binding.js +0 -376
  238. package/dist/agents/queued-file-writer.js +0 -22
  239. package/dist/agents/skills/bundled-context.js +0 -23
  240. package/dist/agents/skills/security.js +0 -211
  241. package/dist/agents/skills/tools-dir.js +0 -9
  242. package/dist/agents/skills-install-download.js +0 -290
  243. package/dist/agents/skills-install-output.js +0 -30
  244. package/dist/agents/skills-install.download-test-utils.js +0 -36
  245. package/dist/agents/skills.test-helpers.js +0 -13
  246. package/dist/agents/subagent-announce-reliability.js +0 -160
  247. package/dist/agents/subagent-registry.mocks.shared.js +0 -12
  248. package/dist/agents/test-helpers/assistant-message-fixtures.js +0 -29
  249. package/dist/agents/test-helpers/fast-coding-tools.js +0 -1
  250. package/dist/agents/test-helpers/fast-core-tools.js +0 -8
  251. package/dist/agents/test-helpers/fast-tool-stubs.js +0 -18
  252. package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +0 -74
  253. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +0 -27
  254. package/dist/agents/tool-display-common.js +0 -915
  255. package/dist/agents/tool-policy-shared.js +0 -108
  256. package/dist/agents/tool-policy.conformance.js +0 -14
  257. package/dist/agents/tool-result-truncation.js +0 -299
  258. package/dist/agents/tools/cron-tool.test-helpers.js +0 -12
  259. package/dist/agents/tools/discord-actions-moderation-shared.js +0 -27
  260. package/dist/agents/tools/discord-actions-presence.js +0 -78
  261. package/dist/control-ui/assets/index-D7shnQwQ.js.map +0 -1
  262. package/dist/discord/discord-improvements.js +0 -167
  263. package/dist/discord/index.js +0 -2
  264. package/dist/hooks/bundled/boot-md/HOOK.md +0 -19
  265. package/dist/hooks/bundled/command-logger/HOOK.md +0 -122
  266. package/dist/hooks/bundled/session-memory/HOOK.md +0 -86
  267. package/dist/hooks/bundled/soul-evil/HOOK.md +0 -71
  268. package/dist/whatsapp/normalize.js +0 -66
  269. package/dist/whatsapp/resolve-outbound-target.js +0 -42
  270. /package/dist/{acp/runtime/types.js → auto-reply/auto-reply/reply/commands-types.js} +0 -0
  271. /package/dist/{agents/pi-embedded-payloads.js → slack/account-surface-fields.js} +0 -0
@@ -0,0 +1,231 @@
1
+ import { logVerbose } from "../../globals.js";
2
+ import { getLastTtsAttempt, getTtsMaxLength, getTtsProvider, isSummarizationEnabled, isTtsEnabled, isTtsProviderConfigured, resolveTtsApiKey, resolveTtsConfig, resolveTtsPrefsPath, setLastTtsAttempt, setSummarizationEnabled, setTtsEnabled, setTtsMaxLength, setTtsProvider, textToSpeech, } from "../../tts/tts.js";
3
+ function parseTtsCommand(normalized) {
4
+ // Accept `/tts` and `/tts <action> [args]` as a single control surface.
5
+ if (normalized === "/tts") {
6
+ return { action: "status", args: "" };
7
+ }
8
+ if (!normalized.startsWith("/tts ")) {
9
+ return null;
10
+ }
11
+ const rest = normalized.slice(5).trim();
12
+ if (!rest) {
13
+ return { action: "status", args: "" };
14
+ }
15
+ const [action, ...tail] = rest.split(/\s+/);
16
+ return { action: action.toLowerCase(), args: tail.join(" ").trim() };
17
+ }
18
+ function ttsUsage() {
19
+ // Keep usage in one place so help/validation stays consistent.
20
+ return {
21
+ text: `🔊 **TTS (Text-to-Speech) Help**\n\n` +
22
+ `**Commands:**\n` +
23
+ `• /tts on — Enable automatic TTS for replies\n` +
24
+ `• /tts off — Disable TTS\n` +
25
+ `• /tts status — Show current settings\n` +
26
+ `• /tts provider [name] — View/change provider\n` +
27
+ `• /tts limit [number] — View/change text limit\n` +
28
+ `• /tts summary [on|off] — View/change auto-summary\n` +
29
+ `• /tts audio <text> — Generate audio from text\n\n` +
30
+ `**Providers:**\n` +
31
+ `• edge — Free, fast (default)\n` +
32
+ `• openai — High quality (requires API key)\n` +
33
+ `• elevenlabs — Premium voices (requires API key)\n\n` +
34
+ `**Text Limit (default: 1500, max: 4096):**\n` +
35
+ `When text exceeds the limit:\n` +
36
+ `• Summary ON: AI summarizes, then generates audio\n` +
37
+ `• Summary OFF: Truncates text, then generates audio\n\n` +
38
+ `**Examples:**\n` +
39
+ `/tts provider edge\n` +
40
+ `/tts limit 2000\n` +
41
+ `/tts audio Hello, this is a test!`,
42
+ };
43
+ }
44
+ export const handleTtsCommands = async (params, allowTextCommands) => {
45
+ if (!allowTextCommands) {
46
+ return null;
47
+ }
48
+ const parsed = parseTtsCommand(params.command.commandBodyNormalized);
49
+ if (!parsed) {
50
+ return null;
51
+ }
52
+ if (!params.command.isAuthorizedSender) {
53
+ logVerbose(`Ignoring TTS command from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
54
+ return { shouldContinue: false };
55
+ }
56
+ const config = resolveTtsConfig(params.cfg);
57
+ const prefsPath = resolveTtsPrefsPath(config);
58
+ const action = parsed.action;
59
+ const args = parsed.args;
60
+ if (action === "help") {
61
+ return { shouldContinue: false, reply: ttsUsage() };
62
+ }
63
+ if (action === "on") {
64
+ setTtsEnabled(prefsPath, true);
65
+ return { shouldContinue: false, reply: { text: "🔊 TTS enabled." } };
66
+ }
67
+ if (action === "off") {
68
+ setTtsEnabled(prefsPath, false);
69
+ return { shouldContinue: false, reply: { text: "🔇 TTS disabled." } };
70
+ }
71
+ if (action === "audio") {
72
+ if (!args.trim()) {
73
+ return {
74
+ shouldContinue: false,
75
+ reply: {
76
+ text: `🎤 Generate audio from text.\n\n` +
77
+ `Usage: /tts audio <text>\n` +
78
+ `Example: /tts audio Hello, this is a test!`,
79
+ },
80
+ };
81
+ }
82
+ const start = Date.now();
83
+ const result = await textToSpeech({
84
+ text: args,
85
+ cfg: params.cfg,
86
+ channel: params.command.channel,
87
+ prefsPath,
88
+ });
89
+ if (result.success && result.audioPath) {
90
+ // Store last attempt for `/tts status`.
91
+ setLastTtsAttempt({
92
+ timestamp: Date.now(),
93
+ success: true,
94
+ textLength: args.length,
95
+ summarized: false,
96
+ provider: result.provider,
97
+ latencyMs: result.latencyMs,
98
+ });
99
+ const payload = {
100
+ mediaUrl: result.audioPath,
101
+ audioAsVoice: result.voiceCompatible === true,
102
+ };
103
+ return { shouldContinue: false, reply: payload };
104
+ }
105
+ // Store failure details for `/tts status`.
106
+ setLastTtsAttempt({
107
+ timestamp: Date.now(),
108
+ success: false,
109
+ textLength: args.length,
110
+ summarized: false,
111
+ error: result.error,
112
+ latencyMs: Date.now() - start,
113
+ });
114
+ return {
115
+ shouldContinue: false,
116
+ reply: { text: `❌ Error generating audio: ${result.error ?? "unknown error"}` },
117
+ };
118
+ }
119
+ if (action === "provider") {
120
+ const currentProvider = getTtsProvider(config, prefsPath);
121
+ if (!args.trim()) {
122
+ const hasOpenAI = Boolean(resolveTtsApiKey(config, "openai"));
123
+ const hasElevenLabs = Boolean(resolveTtsApiKey(config, "elevenlabs"));
124
+ const hasEdge = isTtsProviderConfigured(config, "edge");
125
+ return {
126
+ shouldContinue: false,
127
+ reply: {
128
+ text: `🎙️ TTS provider\n` +
129
+ `Primary: ${currentProvider}\n` +
130
+ `OpenAI key: ${hasOpenAI ? "✅" : "❌"}\n` +
131
+ `ElevenLabs key: ${hasElevenLabs ? "✅" : "❌"}\n` +
132
+ `Edge enabled: ${hasEdge ? "✅" : "❌"}\n` +
133
+ `Usage: /tts provider openai | elevenlabs | edge`,
134
+ },
135
+ };
136
+ }
137
+ const requested = args.trim().toLowerCase();
138
+ if (requested !== "openai" && requested !== "elevenlabs" && requested !== "edge") {
139
+ return { shouldContinue: false, reply: ttsUsage() };
140
+ }
141
+ setTtsProvider(prefsPath, requested);
142
+ return {
143
+ shouldContinue: false,
144
+ reply: { text: `✅ TTS provider set to ${requested}.` },
145
+ };
146
+ }
147
+ if (action === "limit") {
148
+ if (!args.trim()) {
149
+ const currentLimit = getTtsMaxLength(prefsPath);
150
+ return {
151
+ shouldContinue: false,
152
+ reply: {
153
+ text: `📏 TTS limit: ${currentLimit} characters.\n\n` +
154
+ `Text longer than this triggers summary (if enabled).\n` +
155
+ `Range: 100-4096 chars (Telegram max).\n\n` +
156
+ `To change: /tts limit <number>\n` +
157
+ `Example: /tts limit 2000`,
158
+ },
159
+ };
160
+ }
161
+ const next = Number.parseInt(args.trim(), 10);
162
+ if (!Number.isFinite(next) || next < 100 || next > 4096) {
163
+ return {
164
+ shouldContinue: false,
165
+ reply: { text: "❌ Limit must be between 100 and 4096 characters." },
166
+ };
167
+ }
168
+ setTtsMaxLength(prefsPath, next);
169
+ return {
170
+ shouldContinue: false,
171
+ reply: { text: `✅ TTS limit set to ${next} characters.` },
172
+ };
173
+ }
174
+ if (action === "summary") {
175
+ if (!args.trim()) {
176
+ const enabled = isSummarizationEnabled(prefsPath);
177
+ const maxLen = getTtsMaxLength(prefsPath);
178
+ return {
179
+ shouldContinue: false,
180
+ reply: {
181
+ text: `📝 TTS auto-summary: ${enabled ? "on" : "off"}.\n\n` +
182
+ `When text exceeds ${maxLen} chars:\n` +
183
+ `• ON: summarizes text, then generates audio\n` +
184
+ `• OFF: truncates text, then generates audio\n\n` +
185
+ `To change: /tts summary on | off`,
186
+ },
187
+ };
188
+ }
189
+ const requested = args.trim().toLowerCase();
190
+ if (requested !== "on" && requested !== "off") {
191
+ return { shouldContinue: false, reply: ttsUsage() };
192
+ }
193
+ setSummarizationEnabled(prefsPath, requested === "on");
194
+ return {
195
+ shouldContinue: false,
196
+ reply: {
197
+ text: requested === "on" ? "✅ TTS auto-summary enabled." : "❌ TTS auto-summary disabled.",
198
+ },
199
+ };
200
+ }
201
+ if (action === "status") {
202
+ const enabled = isTtsEnabled(config, prefsPath);
203
+ const provider = getTtsProvider(config, prefsPath);
204
+ const hasKey = isTtsProviderConfigured(config, provider);
205
+ const maxLength = getTtsMaxLength(prefsPath);
206
+ const summarize = isSummarizationEnabled(prefsPath);
207
+ const last = getLastTtsAttempt();
208
+ const lines = [
209
+ "📊 TTS status",
210
+ `State: ${enabled ? "✅ enabled" : "❌ disabled"}`,
211
+ `Provider: ${provider} (${hasKey ? "✅ configured" : "❌ not configured"})`,
212
+ `Text limit: ${maxLength} chars`,
213
+ `Auto-summary: ${summarize ? "on" : "off"}`,
214
+ ];
215
+ if (last) {
216
+ const timeAgo = Math.round((Date.now() - last.timestamp) / 1000);
217
+ lines.push("");
218
+ lines.push(`Last attempt (${timeAgo}s ago): ${last.success ? "✅" : "❌"}`);
219
+ lines.push(`Text: ${last.textLength} chars${last.summarized ? " (summarized)" : ""}`);
220
+ if (last.success) {
221
+ lines.push(`Provider: ${last.provider ?? "unknown"}`);
222
+ lines.push(`Latency: ${last.latencyMs ?? 0}ms`);
223
+ }
224
+ else if (last.error) {
225
+ lines.push(`Error: ${last.error}`);
226
+ }
227
+ }
228
+ return { shouldContinue: false, reply: { text: lines.join("\n") } };
229
+ }
230
+ return { shouldContinue: false, reply: ttsUsage() };
231
+ };
@@ -0,0 +1,380 @@
1
+ import { resolveAgentConfig, resolveAgentDir, resolveSessionAgentId, } from "../../agents/agent-scope.js";
2
+ import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
3
+ import { updateSessionStore } from "../../config/sessions.js";
4
+ import { enqueueSystemEvent } from "../../infra/system-events.js";
5
+ import { applyVerboseOverride } from "../../sessions/level-overrides.js";
6
+ import { applyModelOverrideToSessionEntry } from "../../sessions/model-overrides.js";
7
+ import { formatThinkingLevels, formatXHighModelHint, supportsXHighThinking } from "../thinking.js";
8
+ import { maybeHandleModelDirectiveInfo, resolveModelSelectionFromDirective, } from "./directive-handling.model.js";
9
+ import { maybeHandleQueueDirective } from "./directive-handling.queue-validation.js";
10
+ import { formatDirectiveAck, formatElevatedEvent, formatElevatedRuntimeHint, formatElevatedUnavailableText, formatReasoningEvent, withOptions, } from "./directive-handling.shared.js";
11
+ function resolveExecDefaults(params) {
12
+ const globalExec = params.cfg.tools?.exec;
13
+ const agentExec = params.agentId
14
+ ? resolveAgentConfig(params.cfg, params.agentId)?.tools?.exec
15
+ : undefined;
16
+ return {
17
+ host: params.sessionEntry?.execHost ??
18
+ agentExec?.host ??
19
+ globalExec?.host ??
20
+ "sandbox",
21
+ security: params.sessionEntry?.execSecurity ??
22
+ agentExec?.security ??
23
+ globalExec?.security ??
24
+ "deny",
25
+ ask: params.sessionEntry?.execAsk ??
26
+ agentExec?.ask ??
27
+ globalExec?.ask ??
28
+ "on-miss",
29
+ node: params.sessionEntry?.execNode ?? agentExec?.node ?? globalExec?.node,
30
+ };
31
+ }
32
+ export async function handleDirectiveOnly(params) {
33
+ const { directives, sessionEntry, sessionStore, sessionKey, storePath, elevatedEnabled, elevatedAllowed, defaultProvider, defaultModel, aliasIndex, allowedModelKeys, allowedModelCatalog, resetModelOverride, provider, model, initialModelLabel, formatModelSwitchEvent, currentThinkLevel, currentVerboseLevel, currentReasoningLevel, currentElevatedLevel, } = params;
34
+ const activeAgentId = resolveSessionAgentId({
35
+ sessionKey: params.sessionKey,
36
+ config: params.cfg,
37
+ });
38
+ const agentDir = resolveAgentDir(params.cfg, activeAgentId);
39
+ const runtimeIsSandboxed = resolveSandboxRuntimeStatus({
40
+ cfg: params.cfg,
41
+ sessionKey: params.sessionKey,
42
+ }).sandboxed;
43
+ const shouldHintDirectRuntime = directives.hasElevatedDirective && !runtimeIsSandboxed;
44
+ const modelInfo = await maybeHandleModelDirectiveInfo({
45
+ directives,
46
+ cfg: params.cfg,
47
+ agentDir,
48
+ activeAgentId,
49
+ provider,
50
+ model,
51
+ defaultProvider,
52
+ defaultModel,
53
+ aliasIndex,
54
+ allowedModelCatalog,
55
+ resetModelOverride,
56
+ });
57
+ if (modelInfo)
58
+ return modelInfo;
59
+ const modelResolution = resolveModelSelectionFromDirective({
60
+ directives,
61
+ cfg: params.cfg,
62
+ agentDir,
63
+ defaultProvider,
64
+ defaultModel,
65
+ aliasIndex,
66
+ allowedModelKeys,
67
+ allowedModelCatalog,
68
+ provider,
69
+ });
70
+ if (modelResolution.errorText)
71
+ return { text: modelResolution.errorText };
72
+ const modelSelection = modelResolution.modelSelection;
73
+ const profileOverride = modelResolution.profileOverride;
74
+ const resolvedProvider = modelSelection?.provider ?? provider;
75
+ const resolvedModel = modelSelection?.model ?? model;
76
+ if (directives.hasThinkDirective && !directives.thinkLevel) {
77
+ // If no argument was provided, show the current level
78
+ if (!directives.rawThinkLevel) {
79
+ const level = currentThinkLevel ?? "off";
80
+ return {
81
+ text: withOptions(`Current thinking level: ${level}.`, formatThinkingLevels(resolvedProvider, resolvedModel)),
82
+ };
83
+ }
84
+ return {
85
+ text: `Unrecognized thinking level "${directives.rawThinkLevel}". Valid levels: ${formatThinkingLevels(resolvedProvider, resolvedModel)}.`,
86
+ };
87
+ }
88
+ if (directives.hasVerboseDirective && !directives.verboseLevel) {
89
+ if (!directives.rawVerboseLevel) {
90
+ const level = currentVerboseLevel ?? "off";
91
+ return {
92
+ text: withOptions(`Current verbose level: ${level}.`, "on, full, off"),
93
+ };
94
+ }
95
+ return {
96
+ text: `Unrecognized verbose level "${directives.rawVerboseLevel}". Valid levels: off, on, full.`,
97
+ };
98
+ }
99
+ if (directives.hasReasoningDirective && !directives.reasoningLevel) {
100
+ if (!directives.rawReasoningLevel) {
101
+ const level = currentReasoningLevel ?? "off";
102
+ return {
103
+ text: withOptions(`Current reasoning level: ${level}.`, "on, off, stream"),
104
+ };
105
+ }
106
+ return {
107
+ text: `Unrecognized reasoning level "${directives.rawReasoningLevel}". Valid levels: on, off, stream.`,
108
+ };
109
+ }
110
+ if (directives.hasElevatedDirective && !directives.elevatedLevel) {
111
+ if (!directives.rawElevatedLevel) {
112
+ if (!elevatedEnabled || !elevatedAllowed) {
113
+ return {
114
+ text: formatElevatedUnavailableText({
115
+ runtimeSandboxed: runtimeIsSandboxed,
116
+ failures: params.elevatedFailures,
117
+ sessionKey: params.sessionKey,
118
+ }),
119
+ };
120
+ }
121
+ const level = currentElevatedLevel ?? "off";
122
+ return {
123
+ text: [
124
+ withOptions(`Current elevated level: ${level}.`, "on, off, ask, full"),
125
+ shouldHintDirectRuntime ? formatElevatedRuntimeHint() : null,
126
+ ]
127
+ .filter(Boolean)
128
+ .join("\n"),
129
+ };
130
+ }
131
+ return {
132
+ text: `Unrecognized elevated level "${directives.rawElevatedLevel}". Valid levels: off, on, ask, full.`,
133
+ };
134
+ }
135
+ if (directives.hasElevatedDirective && (!elevatedEnabled || !elevatedAllowed)) {
136
+ return {
137
+ text: formatElevatedUnavailableText({
138
+ runtimeSandboxed: runtimeIsSandboxed,
139
+ failures: params.elevatedFailures,
140
+ sessionKey: params.sessionKey,
141
+ }),
142
+ };
143
+ }
144
+ if (directives.hasExecDirective) {
145
+ if (directives.invalidExecHost) {
146
+ return {
147
+ text: `Unrecognized exec host "${directives.rawExecHost ?? ""}". Valid hosts: sandbox, gateway, node.`,
148
+ };
149
+ }
150
+ if (directives.invalidExecSecurity) {
151
+ return {
152
+ text: `Unrecognized exec security "${directives.rawExecSecurity ?? ""}". Valid: deny, allowlist, full.`,
153
+ };
154
+ }
155
+ if (directives.invalidExecAsk) {
156
+ return {
157
+ text: `Unrecognized exec ask "${directives.rawExecAsk ?? ""}". Valid: off, on-miss, always.`,
158
+ };
159
+ }
160
+ if (directives.invalidExecNode) {
161
+ return {
162
+ text: "Exec node requires a value.",
163
+ };
164
+ }
165
+ if (!directives.hasExecOptions) {
166
+ const execDefaults = resolveExecDefaults({
167
+ cfg: params.cfg,
168
+ sessionEntry,
169
+ agentId: activeAgentId,
170
+ });
171
+ const nodeLabel = execDefaults.node ? `node=${execDefaults.node}` : "node=(unset)";
172
+ return {
173
+ text: withOptions(`Current exec defaults: host=${execDefaults.host}, security=${execDefaults.security}, ask=${execDefaults.ask}, ${nodeLabel}.`, "host=sandbox|gateway|node, security=deny|allowlist|full, ask=off|on-miss|always, node=<id>"),
174
+ };
175
+ }
176
+ }
177
+ const queueAck = maybeHandleQueueDirective({
178
+ directives,
179
+ cfg: params.cfg,
180
+ channel: provider,
181
+ sessionEntry,
182
+ });
183
+ if (queueAck)
184
+ return queueAck;
185
+ if (directives.hasThinkDirective &&
186
+ directives.thinkLevel === "xhigh" &&
187
+ !supportsXHighThinking(resolvedProvider, resolvedModel)) {
188
+ return {
189
+ text: `Thinking level "xhigh" is only supported for ${formatXHighModelHint()}.`,
190
+ };
191
+ }
192
+ const nextThinkLevel = directives.hasThinkDirective
193
+ ? directives.thinkLevel
194
+ : (sessionEntry?.thinkingLevel ?? currentThinkLevel);
195
+ const shouldDowngradeXHigh = !directives.hasThinkDirective &&
196
+ nextThinkLevel === "xhigh" &&
197
+ !supportsXHighThinking(resolvedProvider, resolvedModel);
198
+ const prevElevatedLevel = currentElevatedLevel ??
199
+ sessionEntry.elevatedLevel ??
200
+ (elevatedAllowed ? "on" : "off");
201
+ const prevReasoningLevel = currentReasoningLevel ?? sessionEntry.reasoningLevel ?? "off";
202
+ let elevatedChanged = directives.hasElevatedDirective &&
203
+ directives.elevatedLevel !== undefined &&
204
+ elevatedEnabled &&
205
+ elevatedAllowed;
206
+ let reasoningChanged = directives.hasReasoningDirective && directives.reasoningLevel !== undefined;
207
+ if (directives.hasThinkDirective && directives.thinkLevel) {
208
+ if (directives.thinkLevel === "off")
209
+ delete sessionEntry.thinkingLevel;
210
+ else
211
+ sessionEntry.thinkingLevel = directives.thinkLevel;
212
+ }
213
+ if (shouldDowngradeXHigh) {
214
+ sessionEntry.thinkingLevel = "high";
215
+ }
216
+ if (directives.hasVerboseDirective && directives.verboseLevel) {
217
+ applyVerboseOverride(sessionEntry, directives.verboseLevel);
218
+ }
219
+ if (directives.hasReasoningDirective && directives.reasoningLevel) {
220
+ if (directives.reasoningLevel === "off")
221
+ delete sessionEntry.reasoningLevel;
222
+ else
223
+ sessionEntry.reasoningLevel = directives.reasoningLevel;
224
+ reasoningChanged =
225
+ directives.reasoningLevel !== prevReasoningLevel && directives.reasoningLevel !== undefined;
226
+ }
227
+ if (directives.hasElevatedDirective && directives.elevatedLevel) {
228
+ // Unlike other toggles, elevated defaults can be "on".
229
+ // Persist "off" explicitly so `/elevated off` actually overrides defaults.
230
+ sessionEntry.elevatedLevel = directives.elevatedLevel;
231
+ elevatedChanged =
232
+ elevatedChanged ||
233
+ (directives.elevatedLevel !== prevElevatedLevel && directives.elevatedLevel !== undefined);
234
+ }
235
+ if (directives.hasExecDirective && directives.hasExecOptions) {
236
+ if (directives.execHost) {
237
+ sessionEntry.execHost = directives.execHost;
238
+ }
239
+ if (directives.execSecurity) {
240
+ sessionEntry.execSecurity = directives.execSecurity;
241
+ }
242
+ if (directives.execAsk) {
243
+ sessionEntry.execAsk = directives.execAsk;
244
+ }
245
+ if (directives.execNode) {
246
+ sessionEntry.execNode = directives.execNode;
247
+ }
248
+ }
249
+ if (modelSelection) {
250
+ applyModelOverrideToSessionEntry({
251
+ entry: sessionEntry,
252
+ selection: modelSelection,
253
+ profileOverride,
254
+ });
255
+ }
256
+ if (directives.hasQueueDirective && directives.queueReset) {
257
+ delete sessionEntry.queueMode;
258
+ delete sessionEntry.queueDebounceMs;
259
+ delete sessionEntry.queueCap;
260
+ delete sessionEntry.queueDrop;
261
+ }
262
+ else if (directives.hasQueueDirective) {
263
+ if (directives.queueMode)
264
+ sessionEntry.queueMode = directives.queueMode;
265
+ if (typeof directives.debounceMs === "number") {
266
+ sessionEntry.queueDebounceMs = directives.debounceMs;
267
+ }
268
+ if (typeof directives.cap === "number") {
269
+ sessionEntry.queueCap = directives.cap;
270
+ }
271
+ if (directives.dropPolicy) {
272
+ sessionEntry.queueDrop = directives.dropPolicy;
273
+ }
274
+ }
275
+ sessionEntry.updatedAt = Date.now();
276
+ sessionStore[sessionKey] = sessionEntry;
277
+ if (storePath) {
278
+ await updateSessionStore(storePath, (store) => {
279
+ store[sessionKey] = sessionEntry;
280
+ });
281
+ }
282
+ if (modelSelection) {
283
+ const nextLabel = `${modelSelection.provider}/${modelSelection.model}`;
284
+ if (nextLabel !== initialModelLabel) {
285
+ enqueueSystemEvent(formatModelSwitchEvent(nextLabel, modelSelection.alias), {
286
+ sessionKey,
287
+ contextKey: `model:${nextLabel}`,
288
+ });
289
+ }
290
+ }
291
+ if (elevatedChanged) {
292
+ const nextElevated = (sessionEntry.elevatedLevel ?? "off");
293
+ enqueueSystemEvent(formatElevatedEvent(nextElevated), {
294
+ sessionKey,
295
+ contextKey: "mode:elevated",
296
+ });
297
+ }
298
+ if (reasoningChanged) {
299
+ const nextReasoning = (sessionEntry.reasoningLevel ?? "off");
300
+ enqueueSystemEvent(formatReasoningEvent(nextReasoning), {
301
+ sessionKey,
302
+ contextKey: "mode:reasoning",
303
+ });
304
+ }
305
+ const parts = [];
306
+ if (directives.hasThinkDirective && directives.thinkLevel) {
307
+ parts.push(directives.thinkLevel === "off"
308
+ ? "Thinking disabled."
309
+ : `Thinking level set to ${directives.thinkLevel}.`);
310
+ }
311
+ if (directives.hasVerboseDirective && directives.verboseLevel) {
312
+ parts.push(directives.verboseLevel === "off"
313
+ ? formatDirectiveAck("Verbose logging disabled.")
314
+ : directives.verboseLevel === "full"
315
+ ? formatDirectiveAck("Verbose logging set to full.")
316
+ : formatDirectiveAck("Verbose logging enabled."));
317
+ }
318
+ if (directives.hasReasoningDirective && directives.reasoningLevel) {
319
+ parts.push(directives.reasoningLevel === "off"
320
+ ? formatDirectiveAck("Reasoning visibility disabled.")
321
+ : directives.reasoningLevel === "stream"
322
+ ? formatDirectiveAck("Reasoning stream enabled (Telegram only).")
323
+ : formatDirectiveAck("Reasoning visibility enabled."));
324
+ }
325
+ if (directives.hasElevatedDirective && directives.elevatedLevel) {
326
+ parts.push(directives.elevatedLevel === "off"
327
+ ? formatDirectiveAck("Elevated mode disabled.")
328
+ : directives.elevatedLevel === "full"
329
+ ? formatDirectiveAck("Elevated mode set to full (auto-approve).")
330
+ : formatDirectiveAck("Elevated mode set to ask (approvals may still apply)."));
331
+ if (shouldHintDirectRuntime)
332
+ parts.push(formatElevatedRuntimeHint());
333
+ }
334
+ if (directives.hasExecDirective && directives.hasExecOptions) {
335
+ const execParts = [];
336
+ if (directives.execHost)
337
+ execParts.push(`host=${directives.execHost}`);
338
+ if (directives.execSecurity)
339
+ execParts.push(`security=${directives.execSecurity}`);
340
+ if (directives.execAsk)
341
+ execParts.push(`ask=${directives.execAsk}`);
342
+ if (directives.execNode)
343
+ execParts.push(`node=${directives.execNode}`);
344
+ if (execParts.length > 0) {
345
+ parts.push(formatDirectiveAck(`Exec defaults set (${execParts.join(", ")}).`));
346
+ }
347
+ }
348
+ if (shouldDowngradeXHigh) {
349
+ parts.push(`Thinking level set to high (xhigh not supported for ${resolvedProvider}/${resolvedModel}).`);
350
+ }
351
+ if (modelSelection) {
352
+ const label = `${modelSelection.provider}/${modelSelection.model}`;
353
+ const labelWithAlias = modelSelection.alias ? `${modelSelection.alias} (${label})` : label;
354
+ parts.push(modelSelection.isDefault
355
+ ? `Model reset to default (${labelWithAlias}).`
356
+ : `Model set to ${labelWithAlias}.`);
357
+ if (profileOverride) {
358
+ parts.push(`Auth profile set to ${profileOverride}.`);
359
+ }
360
+ }
361
+ if (directives.hasQueueDirective && directives.queueMode) {
362
+ parts.push(formatDirectiveAck(`Queue mode set to ${directives.queueMode}.`));
363
+ }
364
+ else if (directives.hasQueueDirective && directives.queueReset) {
365
+ parts.push(formatDirectiveAck("Queue mode reset to default."));
366
+ }
367
+ if (directives.hasQueueDirective && typeof directives.debounceMs === "number") {
368
+ parts.push(formatDirectiveAck(`Queue debounce set to ${directives.debounceMs}ms.`));
369
+ }
370
+ if (directives.hasQueueDirective && typeof directives.cap === "number") {
371
+ parts.push(formatDirectiveAck(`Queue cap set to ${directives.cap}.`));
372
+ }
373
+ if (directives.hasQueueDirective && directives.dropPolicy) {
374
+ parts.push(formatDirectiveAck(`Queue drop set to ${directives.dropPolicy}.`));
375
+ }
376
+ const ack = parts.join(" ").trim();
377
+ if (!ack && directives.hasStatusDirective)
378
+ return undefined;
379
+ return { text: ack || "OK." };
380
+ }