@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.
- package/dist/agents/model-fallback.js +5 -4
- package/dist/agents/tools/common.js +16 -201
- package/dist/auto-reply/auto-reply/reply/agent-runner-execution.js +502 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-helpers.js +65 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-memory.js +160 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-payloads.js +85 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-utils.js +101 -0
- package/dist/auto-reply/auto-reply/reply/bash-command.js +338 -0
- package/dist/auto-reply/auto-reply/reply/block-streaming.js +91 -0
- package/dist/auto-reply/auto-reply/reply/commands-approve.js +88 -0
- package/dist/auto-reply/auto-reply/reply/commands-bash.js +26 -0
- package/dist/auto-reply/auto-reply/reply/commands-compact.js +107 -0
- package/dist/auto-reply/auto-reply/reply/commands-config.js +241 -0
- package/dist/auto-reply/auto-reply/reply/commands-context-report.js +295 -0
- package/dist/auto-reply/auto-reply/reply/commands-context.js +30 -0
- package/dist/auto-reply/auto-reply/reply/commands-core.js +151 -0
- package/dist/auto-reply/auto-reply/reply/commands-export-session.js +163 -0
- package/dist/auto-reply/auto-reply/reply/commands-info.js +184 -0
- package/dist/auto-reply/auto-reply/reply/commands-models.js +299 -0
- package/dist/auto-reply/auto-reply/reply/commands-plugin.js +35 -0
- package/dist/auto-reply/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-slash-parse.js +31 -0
- package/dist/auto-reply/auto-reply/reply/commands-status.js +178 -0
- package/dist/auto-reply/auto-reply/reply/commands-subagents.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-system-prompt.js +117 -0
- package/dist/auto-reply/auto-reply/reply/commands-tts.js +231 -0
- package/dist/auto-reply/auto-reply/reply/directive-handling.impl.js +380 -0
- package/dist/auto-reply/auto-reply/reply/followup-runner.js +227 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-apply.js +201 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-utils.js +54 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives.js +332 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-inline-actions.js +258 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-run.js +297 -0
- package/dist/auto-reply/auto-reply/reply/groups.js +102 -0
- package/dist/auto-reply/auto-reply/reply/mentions.js +129 -0
- package/dist/auto-reply/auto-reply/reply/reply-delivery.js +92 -0
- package/dist/auto-reply/auto-reply/reply/reply-directives.js +30 -0
- package/dist/auto-reply/auto-reply/reply/reply-dispatcher.js +152 -0
- package/dist/auto-reply/auto-reply/reply/reply-elevated.js +166 -0
- package/dist/auto-reply/auto-reply/reply/reply-inline.js +28 -0
- package/dist/auto-reply/auto-reply/reply/reply-payloads.js +114 -0
- package/dist/auto-reply/auto-reply/reply/reply-reference.js +36 -0
- package/dist/auto-reply/auto-reply/reply/reply-tags.js +13 -0
- package/dist/auto-reply/auto-reply/reply/reply-threading.js +41 -0
- package/dist/auto-reply/auto-reply/reply/session-updates.js +233 -0
- package/dist/auto-reply/auto-reply/reply/stage-sandbox-media.js +146 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +2 -17772
- package/dist/canvas-host/a2ui/index.html +1 -307
- package/dist/channels/channels/directory-config.js +185 -0
- package/dist/channels/channels/discord/handle-action.guild-admin.js +332 -0
- package/dist/channels/channels/discord/handle-action.js +165 -0
- package/dist/channels/channels/discord.js +413 -0
- package/dist/channels/channels/dock.js +436 -0
- package/dist/channels/channels/index.js +51 -0
- package/dist/channels/channels/plugins/outbound/discord.js +101 -0
- package/dist/channels/channels/whatsapp.js +17 -0
- package/dist/channels/plugins/types.js +1 -1
- package/dist/channels/run-state-machine.js +7 -0
- package/dist/commands/models/auth.js +47 -1
- package/dist/commands-subagents/action-agents.js +44 -0
- package/dist/commands-subagents/action-focus.js +64 -0
- package/dist/commands-subagents/action-help.js +4 -0
- package/dist/commands-subagents/action-info.js +45 -0
- package/dist/commands-subagents/action-kill.js +60 -0
- package/dist/commands-subagents/action-list.js +44 -0
- package/dist/commands-subagents/action-log.js +29 -0
- package/dist/commands-subagents/action-send.js +119 -0
- package/dist/commands-subagents/action-spawn.js +52 -0
- package/dist/commands-subagents/action-unfocus.js +30 -0
- package/dist/commands-subagents/shared.js +303 -0
- package/dist/config/config.js +1 -8
- package/dist/config/types.secrets.js +61 -0
- package/dist/control-ui/assets/{index-D7shnQwQ.js → index-umCsvrWy.js} +884 -741
- package/dist/control-ui/assets/index-umCsvrWy.js.map +1 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js +2 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js.map +1 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js +2 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js.map +1 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js +2 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/method-scopes.js +9 -1
- package/dist/gateway/node-pending-work.js +142 -0
- package/dist/gateway/protocol/index.js +5 -1
- package/dist/gateway/protocol/schema/nodes.js +18 -0
- package/dist/gateway/server-methods/nodes-pending.js +96 -0
- package/dist/gateway/server-methods-list.js +4 -0
- package/dist/gateway/server-methods.js +2 -0
- package/dist/imessage/channel.js +253 -0
- package/dist/imessage/monitor/echo-cache.js +70 -0
- package/dist/imessage/monitor/loop-rate-limiter.js +51 -0
- package/dist/imessage/monitor/reflection-guard.js +50 -0
- package/dist/imessage/monitor/sanitize-outbound.js +25 -0
- package/dist/imessage/monitor/self-chat-cache.js +75 -0
- package/dist/imessage/runtime.js +3 -0
- package/dist/infra/exec-approval-reply.js +7 -0
- package/dist/infra/tmp-openclaw-dir.js +84 -0
- package/dist/pairing/pairing-challenge.js +15 -0
- package/dist/plugin-sdk/account-id.d.ts +1 -0
- package/dist/plugin-sdk/agent-media-payload.d.ts +12 -0
- package/dist/plugin-sdk/allow-from.d.ts +27 -0
- package/dist/plugin-sdk/command-auth.d.ts +25 -0
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/config-paths.d.ts +6 -0
- package/dist/plugin-sdk/file-lock.d.ts +16 -0
- package/dist/plugin-sdk/index.d.ts +428 -0
- package/dist/plugin-sdk/index.js +237 -103
- package/dist/plugin-sdk/json-store.d.ts +5 -0
- package/dist/plugin-sdk/keyed-async-queue.d.ts +12 -0
- package/dist/plugin-sdk/onboarding.d.ts +11 -0
- package/dist/plugin-sdk/provider-auth-result.d.ts +14 -0
- package/dist/plugin-sdk/slack-message-actions.d.ts +11 -0
- package/dist/plugin-sdk/status-helpers.d.ts +25 -0
- package/dist/plugin-sdk/temp-path.d.ts +12 -0
- package/dist/plugin-sdk/text-chunking.d.ts +1 -0
- package/dist/plugin-sdk/tool-send.d.ts +4 -0
- package/dist/plugin-sdk/webhook-path.d.ts +6 -0
- package/dist/plugin-sdk/webhook-targets.d.ts +23 -0
- package/dist/plugin-sdk/windows-spawn.d.ts +39 -0
- package/dist/plugin-sdk-internal/accounts.js +6 -0
- package/dist/plugin-sdk-internal/discord.js +23 -0
- package/dist/plugin-sdk-internal/imessage.js +13 -0
- package/dist/plugin-sdk-internal/setup.js +9 -0
- package/dist/plugin-sdk-internal/signal.js +13 -0
- package/dist/plugin-sdk-internal/slack.js +22 -0
- package/dist/plugin-sdk-internal/telegram.js +32 -0
- package/dist/plugin-sdk-internal/whatsapp.js +29 -0
- package/dist/routing/session-key.js +4 -185
- package/dist/shared/pid-alive.js +2 -61
- package/dist/shared/process-scoped-map.js +5 -7
- package/dist/signal/channel.js +264 -0
- package/dist/signal/monitor/access-policy.js +60 -0
- package/dist/signal/runtime.js +3 -0
- package/dist/slack/account-inspect.js +135 -0
- package/dist/slack/blocks-input.js +7 -38
- package/dist/slack/channel.js +394 -0
- package/dist/slack/interactive-replies.js +28 -0
- package/dist/slack/monitor/channel-type.js +31 -0
- package/dist/slack/monitor/dm-auth.js +49 -0
- package/dist/slack/monitor/events/interactions.modal.js +137 -0
- package/dist/slack/monitor/events/message-subtype-handlers.js +68 -0
- package/dist/slack/monitor/events/system-event-context.js +29 -0
- package/dist/slack/monitor/events/system-event-test-harness.js +41 -0
- package/dist/slack/monitor/external-arg-menu-store.js +46 -0
- package/dist/slack/monitor/message-handler/prepare-content.js +69 -0
- package/dist/slack/monitor/message-handler/prepare-thread-context.js +91 -0
- package/dist/slack/monitor/message-handler/prepare.test-helpers.js +55 -0
- package/dist/slack/monitor/reconnect-policy.js +78 -0
- package/dist/slack/monitor/slash-commands.runtime.js +1 -0
- package/dist/slack/monitor/slash-dispatch.runtime.js +9 -0
- package/dist/slack/monitor/slash-skill-commands.runtime.js +1 -0
- package/dist/slack/resolve-allowlist-common.js +36 -0
- package/dist/slack/runtime.js +3 -0
- package/dist/slack/sent-thread-cache.js +61 -0
- package/dist/slack/truncate.js +10 -0
- package/dist/telegram/account-inspect.js +175 -0
- package/dist/telegram/allow-from.js +10 -0
- package/dist/telegram/api-fetch.js +18 -0
- package/dist/telegram/approval-buttons.js +30 -0
- package/dist/telegram/audit-membership-runtime.js +61 -0
- package/dist/telegram/bot/delivery.replies.js +508 -0
- package/dist/telegram/bot/delivery.resolve-media.js +227 -0
- package/dist/telegram/bot/delivery.send.js +132 -0
- package/dist/telegram/bot/reply-threading.js +46 -0
- package/dist/telegram/bot-message-context.body.js +186 -0
- package/dist/telegram/bot-message-context.session.js +207 -0
- package/dist/telegram/bot-message-context.types.js +1 -0
- package/dist/telegram/bot-native-commands.test-helpers.js +117 -0
- package/dist/telegram/bot.media.e2e-harness.js +81 -0
- package/dist/telegram/bot.media.test-utils.js +81 -0
- package/dist/telegram/channel-actions.js +225 -0
- package/dist/telegram/channel.js +515 -0
- package/dist/telegram/conversation-route.js +107 -0
- package/dist/telegram/delivery.js +2 -0
- package/dist/telegram/delivery.replies.js +508 -0
- package/dist/telegram/dm-access.js +86 -0
- package/dist/telegram/draft-stream.test-helpers.js +62 -0
- package/dist/telegram/exec-approvals-handler.js +281 -0
- package/dist/telegram/exec-approvals.js +62 -0
- package/dist/telegram/forum-service-message.js +22 -0
- package/dist/telegram/group-config-helpers.js +10 -0
- package/dist/telegram/lane-delivery-state.js +19 -0
- package/dist/telegram/lane-delivery-text-deliverer.js +357 -0
- package/dist/telegram/lane-delivery.js +2 -0
- package/dist/telegram/normalize.js +37 -0
- package/dist/telegram/onboarding.js +192 -0
- package/dist/telegram/outbound-adapter.js +100 -0
- package/dist/telegram/polling-session.js +275 -0
- package/dist/telegram/runtime.js +3 -0
- package/dist/telegram/sendchataction-401-backoff.js +71 -0
- package/dist/telegram/sequential-key.js +46 -0
- package/dist/telegram/status-issues.js +105 -0
- package/dist/telegram/target-writeback.js +165 -0
- package/dist/telegram/thread-bindings.js +560 -0
- package/dist/utils.js +10 -276
- package/dist/wizard/prompts.js +5 -5
- package/extensions/feishu/src/policy.ts +1 -1
- package/extensions/firecrawl/index.test.ts +82 -0
- package/extensions/firecrawl/index.ts +20 -0
- package/extensions/firecrawl/openclaw.plugin.json +8 -0
- package/extensions/firecrawl/package.json +12 -0
- package/extensions/firecrawl/src/config.ts +159 -0
- package/extensions/firecrawl/src/firecrawl-client.ts +446 -0
- package/extensions/firecrawl/src/firecrawl-scrape-tool.ts +89 -0
- package/extensions/firecrawl/src/firecrawl-search-provider.ts +63 -0
- package/extensions/firecrawl/src/firecrawl-search-tool.ts +76 -0
- package/package.json +1 -1
- package/dist/.buildstamp +0 -1
- package/dist/acp/bindings-store.js +0 -209
- package/dist/acp/control-plane/runtime-cache.js +0 -54
- package/dist/acp/control-plane/runtime-options.js +0 -215
- package/dist/acp/control-plane/session-actor-queue.js +0 -36
- package/dist/acp/index.js +0 -2
- package/dist/acp/runtime/errors.js +0 -47
- package/dist/acp/runtime/registry.js +0 -86
- package/dist/acp/secret-file.js +0 -22
- package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +0 -23
- package/dist/agents/bash-process-registry.test-helpers.js +0 -29
- package/dist/agents/bash-tools.exec-approval-request.js +0 -20
- package/dist/agents/bash-tools.exec-host-gateway.js +0 -240
- package/dist/agents/bash-tools.exec-host-node.js +0 -235
- package/dist/agents/checkpoint-manager.js +0 -290
- package/dist/agents/claude-cli-runner.js +0 -3
- package/dist/agents/error-classifier.js +0 -251
- package/dist/agents/live-model-filter.js +0 -84
- package/dist/agents/nvidia-models.js +0 -228
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +0 -34
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +0 -156
- package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +0 -30
- package/dist/agents/provider/config-loader.js +0 -76
- package/dist/agents/provider/index.js +0 -15
- package/dist/agents/provider/models-dev.js +0 -129
- package/dist/agents/provider/session-binding.js +0 -376
- package/dist/agents/queued-file-writer.js +0 -22
- package/dist/agents/skills/bundled-context.js +0 -23
- package/dist/agents/skills/security.js +0 -211
- package/dist/agents/skills/tools-dir.js +0 -9
- package/dist/agents/skills-install-download.js +0 -290
- package/dist/agents/skills-install-output.js +0 -30
- package/dist/agents/skills-install.download-test-utils.js +0 -36
- package/dist/agents/skills.test-helpers.js +0 -13
- package/dist/agents/subagent-announce-reliability.js +0 -160
- package/dist/agents/subagent-registry.mocks.shared.js +0 -12
- package/dist/agents/test-helpers/assistant-message-fixtures.js +0 -29
- package/dist/agents/test-helpers/fast-coding-tools.js +0 -1
- package/dist/agents/test-helpers/fast-core-tools.js +0 -8
- package/dist/agents/test-helpers/fast-tool-stubs.js +0 -18
- package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +0 -74
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +0 -27
- package/dist/agents/tool-display-common.js +0 -915
- package/dist/agents/tool-policy-shared.js +0 -108
- package/dist/agents/tool-policy.conformance.js +0 -14
- package/dist/agents/tool-result-truncation.js +0 -299
- package/dist/agents/tools/cron-tool.test-helpers.js +0 -12
- package/dist/agents/tools/discord-actions-moderation-shared.js +0 -27
- package/dist/agents/tools/discord-actions-presence.js +0 -78
- package/dist/control-ui/assets/index-D7shnQwQ.js.map +0 -1
- package/dist/discord/discord-improvements.js +0 -167
- package/dist/discord/index.js +0 -2
- package/dist/hooks/bundled/boot-md/HOOK.md +0 -19
- package/dist/hooks/bundled/command-logger/HOOK.md +0 -122
- package/dist/hooks/bundled/session-memory/HOOK.md +0 -86
- package/dist/hooks/bundled/soul-evil/HOOK.md +0 -71
- package/dist/whatsapp/normalize.js +0 -66
- package/dist/whatsapp/resolve-outbound-target.js +0 -42
- /package/dist/{acp/runtime/types.js → auto-reply/auto-reply/reply/commands-types.js} +0 -0
- /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,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
|
+
}
|