@poolzin/pool-bot 2026.3.25 → 2026.3.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 (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 +10 -276
  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,44 @@
1
+ import { getThreadBindingManager } from "../../../discord/monitor/thread-bindings.js";
2
+ import { formatRunLabel, sortSubagentRuns } from "../subagents-utils.js";
3
+ import { isDiscordSurface, resolveDiscordAccountId, stopWithText, } from "./shared.js";
4
+ export function handleSubagentsAgentsAction(ctx) {
5
+ const { params, requesterKey, runs } = ctx;
6
+ const isDiscord = isDiscordSurface(params);
7
+ const accountId = isDiscord ? resolveDiscordAccountId(params) : undefined;
8
+ const threadBindings = accountId ? getThreadBindingManager(accountId) : null;
9
+ const visibleRuns = sortSubagentRuns(runs).filter((entry) => {
10
+ if (!entry.endedAt) {
11
+ return true;
12
+ }
13
+ return Boolean(threadBindings?.listBySessionKey(entry.childSessionKey)[0]);
14
+ });
15
+ const lines = ["agents:", "-----"];
16
+ if (visibleRuns.length === 0) {
17
+ lines.push("(none)");
18
+ }
19
+ else {
20
+ let index = 1;
21
+ for (const entry of visibleRuns) {
22
+ const threadBinding = threadBindings?.listBySessionKey(entry.childSessionKey)[0];
23
+ const bindingText = threadBinding
24
+ ? `thread:${threadBinding.threadId}`
25
+ : isDiscord
26
+ ? "unbound"
27
+ : "bindings available on discord";
28
+ lines.push(`${index}. ${formatRunLabel(entry)} (${bindingText})`);
29
+ index += 1;
30
+ }
31
+ }
32
+ if (threadBindings) {
33
+ const acpBindings = threadBindings
34
+ .listBindings()
35
+ .filter((entry) => entry.targetKind === "acp" && entry.targetSessionKey === requesterKey);
36
+ if (acpBindings.length > 0) {
37
+ lines.push("", "acp/session bindings:", "-----");
38
+ for (const binding of acpBindings) {
39
+ lines.push(`- ${binding.label ?? binding.targetSessionKey} (thread:${binding.threadId}, session:${binding.targetSessionKey})`);
40
+ }
41
+ }
42
+ }
43
+ return stopWithText(lines.join("\n"));
44
+ }
@@ -0,0 +1,64 @@
1
+ import { getThreadBindingManager, resolveThreadBindingIntroText, resolveThreadBindingThreadName, } from "../../../discord/monitor/thread-bindings.js";
2
+ import { isDiscordSurface, resolveDiscordAccountId, resolveDiscordChannelIdForFocus, resolveFocusTargetSession, stopWithText, } from "./shared.js";
3
+ export async function handleSubagentsFocusAction(ctx) {
4
+ const { params, runs, restTokens } = ctx;
5
+ if (!isDiscordSurface(params)) {
6
+ return stopWithText("⚠️ /focus is only available on Discord.");
7
+ }
8
+ const token = restTokens.join(" ").trim();
9
+ if (!token) {
10
+ return stopWithText("Usage: /focus <subagent-label|session-key|session-id|session-label>");
11
+ }
12
+ const accountId = resolveDiscordAccountId(params);
13
+ const threadBindings = getThreadBindingManager(accountId);
14
+ if (!threadBindings) {
15
+ return stopWithText("⚠️ Discord thread bindings are unavailable for this account.");
16
+ }
17
+ const focusTarget = await resolveFocusTargetSession({ runs, token });
18
+ if (!focusTarget) {
19
+ return stopWithText(`⚠️ Unable to resolve focus target: ${token}`);
20
+ }
21
+ const currentThreadId = params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId).trim() : "";
22
+ const parentChannelId = currentThreadId ? undefined : resolveDiscordChannelIdForFocus(params);
23
+ if (!currentThreadId && !parentChannelId) {
24
+ return stopWithText("⚠️ Could not resolve a Discord channel for /focus.");
25
+ }
26
+ const senderId = params.command.senderId?.trim() || "";
27
+ if (currentThreadId) {
28
+ const existingBinding = threadBindings.getByThreadId(currentThreadId);
29
+ if (existingBinding &&
30
+ existingBinding.boundBy &&
31
+ existingBinding.boundBy !== "system" &&
32
+ senderId &&
33
+ senderId !== existingBinding.boundBy) {
34
+ return stopWithText(`⚠️ Only ${existingBinding.boundBy} can refocus this thread.`);
35
+ }
36
+ }
37
+ const label = focusTarget.label || token;
38
+ const binding = await threadBindings.bindTarget({
39
+ threadId: currentThreadId || undefined,
40
+ channelId: parentChannelId,
41
+ createThread: !currentThreadId,
42
+ threadName: resolveThreadBindingThreadName({
43
+ agentId: focusTarget.agentId,
44
+ label,
45
+ }),
46
+ targetKind: focusTarget.targetKind,
47
+ targetSessionKey: focusTarget.targetSessionKey,
48
+ agentId: focusTarget.agentId,
49
+ label,
50
+ boundBy: senderId || "unknown",
51
+ introText: resolveThreadBindingIntroText({
52
+ agentId: focusTarget.agentId,
53
+ label,
54
+ sessionTtlMs: threadBindings.getSessionTtlMs(),
55
+ }),
56
+ });
57
+ if (!binding) {
58
+ return stopWithText("⚠️ Failed to bind a Discord thread to the target session.");
59
+ }
60
+ const actionText = currentThreadId
61
+ ? `bound this thread to ${binding.targetSessionKey}`
62
+ : `created thread ${binding.threadId} and bound it to ${binding.targetSessionKey}`;
63
+ return stopWithText(`✅ ${actionText} (${binding.targetKind}).`);
64
+ }
@@ -0,0 +1,4 @@
1
+ import { buildSubagentsHelp, stopWithText } from "./shared.js";
2
+ export function handleSubagentsHelpAction() {
3
+ return stopWithText(buildSubagentsHelp());
4
+ }
@@ -0,0 +1,45 @@
1
+ import { loadSessionStore, resolveStorePath } from "../../../config/sessions.js";
2
+ import { formatDurationCompact } from "../../../shared/subagents-format.js";
3
+ import { formatRunLabel } from "../subagents-utils.js";
4
+ import { formatTimestampWithAge, loadSubagentSessionEntry, resolveDisplayStatus, resolveSubagentEntryForToken, stopWithText, } from "./shared.js";
5
+ export function handleSubagentsInfoAction(ctx) {
6
+ const { params, runs, restTokens } = ctx;
7
+ const target = restTokens[0];
8
+ if (!target) {
9
+ return stopWithText("ℹ️ Usage: /subagents info <id|#>");
10
+ }
11
+ const targetResolution = resolveSubagentEntryForToken(runs, target);
12
+ if ("reply" in targetResolution) {
13
+ return targetResolution.reply;
14
+ }
15
+ const run = targetResolution.entry;
16
+ const { entry: sessionEntry } = loadSubagentSessionEntry(params, run.childSessionKey, {
17
+ loadSessionStore,
18
+ resolveStorePath,
19
+ });
20
+ const runtime = run.startedAt && Number.isFinite(run.startedAt)
21
+ ? (formatDurationCompact((run.endedAt ?? Date.now()) - run.startedAt) ?? "n/a")
22
+ : "n/a";
23
+ const outcome = run.outcome
24
+ ? `${run.outcome.status}${run.outcome.error ? ` (${run.outcome.error})` : ""}`
25
+ : "n/a";
26
+ const lines = [
27
+ "ℹ️ Subagent info",
28
+ `Status: ${resolveDisplayStatus(run)}`,
29
+ `Label: ${formatRunLabel(run)}`,
30
+ `Task: ${run.task}`,
31
+ `Run: ${run.runId}`,
32
+ `Session: ${run.childSessionKey}`,
33
+ `SessionId: ${sessionEntry?.sessionId ?? "n/a"}`,
34
+ `Transcript: ${sessionEntry?.sessionFile ?? "n/a"}`,
35
+ `Runtime: ${runtime}`,
36
+ `Created: ${formatTimestampWithAge(run.createdAt)}`,
37
+ `Started: ${formatTimestampWithAge(run.startedAt)}`,
38
+ `Ended: ${formatTimestampWithAge(run.endedAt)}`,
39
+ `Cleanup: ${run.cleanup}`,
40
+ run.archiveAtMs ? `Archive: ${formatTimestampWithAge(run.archiveAtMs)}` : undefined,
41
+ run.cleanupHandled ? "Cleanup handled: yes" : undefined,
42
+ `Outcome: ${outcome}`,
43
+ ].filter(Boolean);
44
+ return stopWithText(lines.join("\n"));
45
+ }
@@ -0,0 +1,60 @@
1
+ import { abortEmbeddedPiRun } from "../../../agents/pi-embedded.js";
2
+ import { markSubagentRunTerminated } from "../../../agents/subagent-registry.js";
3
+ import { loadSessionStore, resolveStorePath, updateSessionStore, } from "../../../config/sessions.js";
4
+ import { logVerbose } from "../../../globals.js";
5
+ import { stopSubagentsForRequester } from "../abort.js";
6
+ import { clearSessionQueues } from "../queue.js";
7
+ import { formatRunLabel } from "../subagents-utils.js";
8
+ import { COMMAND, loadSubagentSessionEntry, resolveSubagentEntryForToken, stopWithText, } from "./shared.js";
9
+ export async function handleSubagentsKillAction(ctx) {
10
+ const { params, handledPrefix, requesterKey, runs, restTokens } = ctx;
11
+ const target = restTokens[0];
12
+ if (!target) {
13
+ return stopWithText(handledPrefix === COMMAND ? "Usage: /subagents kill <id|#|all>" : "Usage: /kill <id|#|all>");
14
+ }
15
+ if (target === "all" || target === "*") {
16
+ stopSubagentsForRequester({
17
+ cfg: params.cfg,
18
+ requesterSessionKey: requesterKey,
19
+ });
20
+ return { shouldContinue: false };
21
+ }
22
+ const targetResolution = resolveSubagentEntryForToken(runs, target);
23
+ if ("reply" in targetResolution) {
24
+ return targetResolution.reply;
25
+ }
26
+ if (targetResolution.entry.endedAt) {
27
+ return stopWithText(`${formatRunLabel(targetResolution.entry)} is already finished.`);
28
+ }
29
+ const childKey = targetResolution.entry.childSessionKey;
30
+ const { storePath, store, entry } = loadSubagentSessionEntry(params, childKey, {
31
+ loadSessionStore,
32
+ resolveStorePath,
33
+ });
34
+ const sessionId = entry?.sessionId;
35
+ if (sessionId) {
36
+ abortEmbeddedPiRun(sessionId);
37
+ }
38
+ const cleared = clearSessionQueues([childKey, sessionId]);
39
+ if (cleared.followupCleared > 0 || cleared.laneCleared > 0) {
40
+ logVerbose(`subagents kill: cleared followups=${cleared.followupCleared} lane=${cleared.laneCleared} keys=${cleared.keys.join(",")}`);
41
+ }
42
+ if (entry) {
43
+ entry.abortedLastRun = true;
44
+ entry.updatedAt = Date.now();
45
+ store[childKey] = entry;
46
+ await updateSessionStore(storePath, (nextStore) => {
47
+ nextStore[childKey] = entry;
48
+ });
49
+ }
50
+ markSubagentRunTerminated({
51
+ runId: targetResolution.entry.runId,
52
+ childSessionKey: childKey,
53
+ reason: "killed",
54
+ });
55
+ stopSubagentsForRequester({
56
+ cfg: params.cfg,
57
+ requesterSessionKey: childKey,
58
+ });
59
+ return { shouldContinue: false };
60
+ }
@@ -0,0 +1,44 @@
1
+ import { loadSessionStore, resolveStorePath } from "../../../config/sessions.js";
2
+ import { sortSubagentRuns } from "../subagents-utils.js";
3
+ import { RECENT_WINDOW_MINUTES, formatSubagentListLine, loadSubagentSessionEntry, stopWithText, } from "./shared.js";
4
+ export function handleSubagentsListAction(ctx) {
5
+ const { params, runs } = ctx;
6
+ const sorted = sortSubagentRuns(runs);
7
+ const now = Date.now();
8
+ const recentCutoff = now - RECENT_WINDOW_MINUTES * 60_000;
9
+ const storeCache = new Map();
10
+ let index = 1;
11
+ const mapRuns = (entries, runtimeMs) => entries.map((entry) => {
12
+ const { entry: sessionEntry } = loadSubagentSessionEntry(params, entry.childSessionKey, {
13
+ loadSessionStore,
14
+ resolveStorePath,
15
+ }, storeCache);
16
+ const line = formatSubagentListLine({
17
+ entry,
18
+ index,
19
+ runtimeMs: runtimeMs(entry),
20
+ sessionEntry,
21
+ });
22
+ index += 1;
23
+ return line;
24
+ });
25
+ const activeEntries = sorted.filter((entry) => !entry.endedAt);
26
+ const activeLines = mapRuns(activeEntries, (entry) => now - (entry.startedAt ?? entry.createdAt));
27
+ const recentEntries = sorted.filter((entry) => !!entry.endedAt && (entry.endedAt ?? 0) >= recentCutoff);
28
+ const recentLines = mapRuns(recentEntries, (entry) => (entry.endedAt ?? now) - (entry.startedAt ?? entry.createdAt));
29
+ const lines = ["active subagents:", "-----"];
30
+ if (activeLines.length === 0) {
31
+ lines.push("(none)");
32
+ }
33
+ else {
34
+ lines.push(activeLines.join("\n"));
35
+ }
36
+ lines.push("", `recent subagents (last ${RECENT_WINDOW_MINUTES}m):`, "-----");
37
+ if (recentLines.length === 0) {
38
+ lines.push("(none)");
39
+ }
40
+ else {
41
+ lines.push(recentLines.join("\n"));
42
+ }
43
+ return stopWithText(lines.join("\n"));
44
+ }
@@ -0,0 +1,29 @@
1
+ import { callGateway } from "../../../gateway/call.js";
2
+ import { formatRunLabel } from "../subagents-utils.js";
3
+ import { formatLogLines, resolveSubagentEntryForToken, stopWithText, stripToolMessages, } from "./shared.js";
4
+ export async function handleSubagentsLogAction(ctx) {
5
+ const { runs, restTokens } = ctx;
6
+ const target = restTokens[0];
7
+ if (!target) {
8
+ return stopWithText("📜 Usage: /subagents log <id|#> [limit]");
9
+ }
10
+ const includeTools = restTokens.some((token) => token.toLowerCase() === "tools");
11
+ const limitToken = restTokens.find((token) => /^\d+$/.test(token));
12
+ const limit = limitToken ? Math.min(200, Math.max(1, Number.parseInt(limitToken, 10))) : 20;
13
+ const targetResolution = resolveSubagentEntryForToken(runs, target);
14
+ if ("reply" in targetResolution) {
15
+ return targetResolution.reply;
16
+ }
17
+ const history = await callGateway({
18
+ method: "chat.history",
19
+ params: { sessionKey: targetResolution.entry.childSessionKey, limit },
20
+ });
21
+ const rawMessages = Array.isArray(history?.messages) ? history.messages : [];
22
+ const filtered = includeTools ? rawMessages : stripToolMessages(rawMessages);
23
+ const lines = formatLogLines(filtered);
24
+ const header = `📜 Subagent log: ${formatRunLabel(targetResolution.entry)}`;
25
+ if (lines.length === 0) {
26
+ return stopWithText(`${header}\n(no messages)`);
27
+ }
28
+ return stopWithText([header, ...lines].join("\n"));
29
+ }
@@ -0,0 +1,119 @@
1
+ import crypto from "node:crypto";
2
+ import { AGENT_LANE_SUBAGENT } from "../../../agents/lanes.js";
3
+ import { abortEmbeddedPiRun } from "../../../agents/pi-embedded.js";
4
+ import { clearSubagentRunSteerRestart, replaceSubagentRunAfterSteer, markSubagentRunForSteerRestart, } from "../../../agents/subagent-registry.js";
5
+ import { loadSessionStore, resolveStorePath } from "../../../config/sessions.js";
6
+ import { callGateway } from "../../../gateway/call.js";
7
+ import { logVerbose } from "../../../globals.js";
8
+ import { INTERNAL_MESSAGE_CHANNEL } from "../../../utils/message-channel.js";
9
+ import { clearSessionQueues } from "../queue.js";
10
+ import { formatRunLabel } from "../subagents-utils.js";
11
+ import { COMMAND, STEER_ABORT_SETTLE_TIMEOUT_MS, extractAssistantText, loadSubagentSessionEntry, resolveSubagentEntryForToken, stopWithText, stripToolMessages, } from "./shared.js";
12
+ export async function handleSubagentsSendAction(ctx, steerRequested) {
13
+ const { params, handledPrefix, runs, restTokens } = ctx;
14
+ const target = restTokens[0];
15
+ const message = restTokens.slice(1).join(" ").trim();
16
+ if (!target || !message) {
17
+ return stopWithText(steerRequested
18
+ ? handledPrefix === COMMAND
19
+ ? "Usage: /subagents steer <id|#> <message>"
20
+ : `Usage: ${handledPrefix} <id|#> <message>`
21
+ : "Usage: /subagents send <id|#> <message>");
22
+ }
23
+ const targetResolution = resolveSubagentEntryForToken(runs, target);
24
+ if ("reply" in targetResolution) {
25
+ return targetResolution.reply;
26
+ }
27
+ if (steerRequested && targetResolution.entry.endedAt) {
28
+ return stopWithText(`${formatRunLabel(targetResolution.entry)} is already finished.`);
29
+ }
30
+ const { entry: targetSessionEntry } = loadSubagentSessionEntry(params, targetResolution.entry.childSessionKey, {
31
+ loadSessionStore,
32
+ resolveStorePath,
33
+ });
34
+ const targetSessionId = typeof targetSessionEntry?.sessionId === "string" && targetSessionEntry.sessionId.trim()
35
+ ? targetSessionEntry.sessionId.trim()
36
+ : undefined;
37
+ if (steerRequested) {
38
+ markSubagentRunForSteerRestart(targetResolution.entry.runId);
39
+ if (targetSessionId) {
40
+ abortEmbeddedPiRun(targetSessionId);
41
+ }
42
+ const cleared = clearSessionQueues([targetResolution.entry.childSessionKey, targetSessionId]);
43
+ if (cleared.followupCleared > 0 || cleared.laneCleared > 0) {
44
+ logVerbose(`subagents steer: cleared followups=${cleared.followupCleared} lane=${cleared.laneCleared} keys=${cleared.keys.join(",")}`);
45
+ }
46
+ try {
47
+ await callGateway({
48
+ method: "agent.wait",
49
+ params: {
50
+ runId: targetResolution.entry.runId,
51
+ timeoutMs: STEER_ABORT_SETTLE_TIMEOUT_MS,
52
+ },
53
+ timeoutMs: STEER_ABORT_SETTLE_TIMEOUT_MS + 2_000,
54
+ });
55
+ }
56
+ catch {
57
+ // Continue even if wait fails; steer should still be attempted.
58
+ }
59
+ }
60
+ const idempotencyKey = crypto.randomUUID();
61
+ let runId = idempotencyKey;
62
+ try {
63
+ const response = await callGateway({
64
+ method: "agent",
65
+ params: {
66
+ message,
67
+ sessionKey: targetResolution.entry.childSessionKey,
68
+ sessionId: targetSessionId,
69
+ idempotencyKey,
70
+ deliver: false,
71
+ channel: INTERNAL_MESSAGE_CHANNEL,
72
+ lane: AGENT_LANE_SUBAGENT,
73
+ timeout: 0,
74
+ },
75
+ timeoutMs: 10_000,
76
+ });
77
+ const responseRunId = typeof response?.runId === "string" ? response.runId : undefined;
78
+ if (responseRunId) {
79
+ runId = responseRunId;
80
+ }
81
+ }
82
+ catch (err) {
83
+ if (steerRequested) {
84
+ clearSubagentRunSteerRestart(targetResolution.entry.runId);
85
+ }
86
+ const messageText = err instanceof Error ? err.message : typeof err === "string" ? err : "error";
87
+ return stopWithText(`send failed: ${messageText}`);
88
+ }
89
+ if (steerRequested) {
90
+ replaceSubagentRunAfterSteer({
91
+ previousRunId: targetResolution.entry.runId,
92
+ nextRunId: runId,
93
+ fallback: targetResolution.entry,
94
+ runTimeoutSeconds: targetResolution.entry.runTimeoutSeconds ?? 0,
95
+ });
96
+ return stopWithText(`steered ${formatRunLabel(targetResolution.entry)} (run ${runId.slice(0, 8)}).`);
97
+ }
98
+ const waitMs = 30_000;
99
+ const wait = await callGateway({
100
+ method: "agent.wait",
101
+ params: { runId, timeoutMs: waitMs },
102
+ timeoutMs: waitMs + 2000,
103
+ });
104
+ if (wait?.status === "timeout") {
105
+ return stopWithText(`⏳ Subagent still running (run ${runId.slice(0, 8)}).`);
106
+ }
107
+ if (wait?.status === "error") {
108
+ const waitError = typeof wait.error === "string" ? wait.error : "unknown error";
109
+ return stopWithText(`⚠️ Subagent error: ${waitError} (run ${runId.slice(0, 8)}).`);
110
+ }
111
+ const history = await callGateway({
112
+ method: "chat.history",
113
+ params: { sessionKey: targetResolution.entry.childSessionKey, limit: 50 },
114
+ });
115
+ const filtered = stripToolMessages(Array.isArray(history?.messages) ? history.messages : []);
116
+ const last = filtered.length > 0 ? filtered[filtered.length - 1] : undefined;
117
+ const replyText = last ? extractAssistantText(last) : undefined;
118
+ return stopWithText(replyText ?? `✅ Sent to ${formatRunLabel(targetResolution.entry)} (run ${runId.slice(0, 8)}).`);
119
+ }
@@ -0,0 +1,52 @@
1
+ import { spawnSubagentDirect } from "../../../agents/subagent-spawn.js";
2
+ import { stopWithText } from "./shared.js";
3
+ export async function handleSubagentsSpawnAction(ctx) {
4
+ const { params, requesterKey, restTokens } = ctx;
5
+ const agentId = restTokens[0];
6
+ const taskParts = [];
7
+ let model;
8
+ let thinking;
9
+ for (let i = 1; i < restTokens.length; i++) {
10
+ if (restTokens[i] === "--model" && i + 1 < restTokens.length) {
11
+ i += 1;
12
+ model = restTokens[i];
13
+ }
14
+ else if (restTokens[i] === "--thinking" && i + 1 < restTokens.length) {
15
+ i += 1;
16
+ thinking = restTokens[i];
17
+ }
18
+ else {
19
+ taskParts.push(restTokens[i]);
20
+ }
21
+ }
22
+ const task = taskParts.join(" ").trim();
23
+ if (!agentId || !task) {
24
+ return stopWithText("Usage: /subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]");
25
+ }
26
+ const commandTo = typeof params.command.to === "string" ? params.command.to.trim() : "";
27
+ const originatingTo = typeof params.ctx.OriginatingTo === "string" ? params.ctx.OriginatingTo.trim() : "";
28
+ const fallbackTo = typeof params.ctx.To === "string" ? params.ctx.To.trim() : "";
29
+ const normalizedTo = originatingTo || commandTo || fallbackTo || undefined;
30
+ const result = await spawnSubagentDirect({
31
+ task,
32
+ agentId,
33
+ model,
34
+ thinking,
35
+ mode: "run",
36
+ cleanup: "keep",
37
+ expectsCompletionMessage: true,
38
+ }, {
39
+ agentSessionKey: requesterKey,
40
+ agentChannel: params.ctx.OriginatingChannel ?? params.command.channel,
41
+ agentAccountId: params.ctx.AccountId,
42
+ agentTo: normalizedTo,
43
+ agentThreadId: params.ctx.MessageThreadId,
44
+ agentGroupId: params.sessionEntry?.groupId ?? null,
45
+ agentGroupChannel: params.sessionEntry?.groupChannel ?? null,
46
+ agentGroupSpace: params.sessionEntry?.space ?? null,
47
+ });
48
+ if (result.status === "accepted") {
49
+ return stopWithText(`Spawned subagent ${agentId} (session ${result.childSessionKey}, run ${result.runId?.slice(0, 8)}).`);
50
+ }
51
+ return stopWithText(`Spawn failed: ${result.error ?? result.status}`);
52
+ }
@@ -0,0 +1,30 @@
1
+ import { getThreadBindingManager } from "../../../discord/monitor/thread-bindings.js";
2
+ import { isDiscordSurface, resolveDiscordAccountId, stopWithText, } from "./shared.js";
3
+ export function handleSubagentsUnfocusAction(ctx) {
4
+ const { params } = ctx;
5
+ if (!isDiscordSurface(params)) {
6
+ return stopWithText("⚠️ /unfocus is only available on Discord.");
7
+ }
8
+ const threadId = params.ctx.MessageThreadId != null ? String(params.ctx.MessageThreadId) : "";
9
+ if (!threadId.trim()) {
10
+ return stopWithText("⚠️ /unfocus must be run inside a Discord thread.");
11
+ }
12
+ const threadBindings = getThreadBindingManager(resolveDiscordAccountId(params));
13
+ if (!threadBindings) {
14
+ return stopWithText("⚠️ Discord thread bindings are unavailable for this account.");
15
+ }
16
+ const binding = threadBindings.getByThreadId(threadId);
17
+ if (!binding) {
18
+ return stopWithText("ℹ️ This thread is not currently focused.");
19
+ }
20
+ const senderId = params.command.senderId?.trim() || "";
21
+ if (binding.boundBy && binding.boundBy !== "system" && senderId && senderId !== binding.boundBy) {
22
+ return stopWithText(`⚠️ Only ${binding.boundBy} can unfocus this thread.`);
23
+ }
24
+ threadBindings.unbindThread({
25
+ threadId,
26
+ reason: "manual",
27
+ sendFarewell: true,
28
+ });
29
+ return stopWithText("✅ Thread unfocused.");
30
+ }