@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,152 @@
|
|
|
1
|
+
import { sleep } from "../../utils.js";
|
|
2
|
+
import { registerDispatcher } from "./dispatcher-registry.js";
|
|
3
|
+
import { normalizeReplyPayload } from "./normalize-reply.js";
|
|
4
|
+
const DEFAULT_HUMAN_DELAY_MIN_MS = 800;
|
|
5
|
+
const DEFAULT_HUMAN_DELAY_MAX_MS = 2500;
|
|
6
|
+
/** Generate a random delay within the configured range. */
|
|
7
|
+
function getHumanDelay(config) {
|
|
8
|
+
const mode = config?.mode ?? "off";
|
|
9
|
+
if (mode === "off") {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
const min = mode === "custom" ? (config?.minMs ?? DEFAULT_HUMAN_DELAY_MIN_MS) : DEFAULT_HUMAN_DELAY_MIN_MS;
|
|
13
|
+
const max = mode === "custom" ? (config?.maxMs ?? DEFAULT_HUMAN_DELAY_MAX_MS) : DEFAULT_HUMAN_DELAY_MAX_MS;
|
|
14
|
+
if (max <= min) {
|
|
15
|
+
return min;
|
|
16
|
+
}
|
|
17
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
18
|
+
}
|
|
19
|
+
function normalizeReplyPayloadInternal(payload, opts) {
|
|
20
|
+
// Prefer dynamic context provider over static context
|
|
21
|
+
const prefixContext = opts.responsePrefixContextProvider?.() ?? opts.responsePrefixContext;
|
|
22
|
+
return normalizeReplyPayload(payload, {
|
|
23
|
+
responsePrefix: opts.responsePrefix,
|
|
24
|
+
responsePrefixContext: prefixContext,
|
|
25
|
+
onHeartbeatStrip: opts.onHeartbeatStrip,
|
|
26
|
+
onSkip: opts.onSkip,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function createReplyDispatcher(options) {
|
|
30
|
+
let sendChain = Promise.resolve();
|
|
31
|
+
// Track in-flight deliveries so we can emit a reliable "idle" signal.
|
|
32
|
+
// Start with pending=1 as a "reservation" to prevent premature gateway restart.
|
|
33
|
+
// This is decremented when markComplete() is called to signal no more replies will come.
|
|
34
|
+
let pending = 1;
|
|
35
|
+
let completeCalled = false;
|
|
36
|
+
// Track whether we've sent a block reply (for human delay - skip delay on first block).
|
|
37
|
+
let sentFirstBlock = false;
|
|
38
|
+
// Serialize outbound replies to preserve tool/block/final order.
|
|
39
|
+
const queuedCounts = {
|
|
40
|
+
tool: 0,
|
|
41
|
+
block: 0,
|
|
42
|
+
final: 0,
|
|
43
|
+
};
|
|
44
|
+
// Register this dispatcher globally for gateway restart coordination.
|
|
45
|
+
const { unregister } = registerDispatcher({
|
|
46
|
+
pending: () => pending,
|
|
47
|
+
waitForIdle: () => sendChain,
|
|
48
|
+
});
|
|
49
|
+
const enqueue = (kind, payload) => {
|
|
50
|
+
const normalized = normalizeReplyPayloadInternal(payload, {
|
|
51
|
+
responsePrefix: options.responsePrefix,
|
|
52
|
+
responsePrefixContext: options.responsePrefixContext,
|
|
53
|
+
responsePrefixContextProvider: options.responsePrefixContextProvider,
|
|
54
|
+
onHeartbeatStrip: options.onHeartbeatStrip,
|
|
55
|
+
onSkip: (reason) => options.onSkip?.(payload, { kind, reason }),
|
|
56
|
+
});
|
|
57
|
+
if (!normalized) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
queuedCounts[kind] += 1;
|
|
61
|
+
pending += 1;
|
|
62
|
+
// Determine if we should add human-like delay (only for block replies after the first).
|
|
63
|
+
const shouldDelay = kind === "block" && sentFirstBlock;
|
|
64
|
+
if (kind === "block") {
|
|
65
|
+
sentFirstBlock = true;
|
|
66
|
+
}
|
|
67
|
+
sendChain = sendChain
|
|
68
|
+
.then(async () => {
|
|
69
|
+
// Add human-like delay between block replies for natural rhythm.
|
|
70
|
+
if (shouldDelay) {
|
|
71
|
+
const delayMs = getHumanDelay(options.humanDelay);
|
|
72
|
+
if (delayMs > 0) {
|
|
73
|
+
await sleep(delayMs);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Safe: deliver is called inside an async .then() callback, so even a synchronous
|
|
77
|
+
// throw becomes a rejection that flows through .catch()/.finally(), ensuring cleanup.
|
|
78
|
+
await options.deliver(normalized, { kind });
|
|
79
|
+
})
|
|
80
|
+
.catch((err) => {
|
|
81
|
+
options.onError?.(err, { kind });
|
|
82
|
+
})
|
|
83
|
+
.finally(() => {
|
|
84
|
+
pending -= 1;
|
|
85
|
+
// Clear reservation if:
|
|
86
|
+
// 1. pending is now 1 (just the reservation left)
|
|
87
|
+
// 2. markComplete has been called
|
|
88
|
+
// 3. No more replies will be enqueued
|
|
89
|
+
if (pending === 1 && completeCalled) {
|
|
90
|
+
pending -= 1; // Clear the reservation
|
|
91
|
+
}
|
|
92
|
+
if (pending === 0) {
|
|
93
|
+
// Unregister from global tracking when idle.
|
|
94
|
+
unregister();
|
|
95
|
+
options.onIdle?.();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return true;
|
|
99
|
+
};
|
|
100
|
+
const markComplete = () => {
|
|
101
|
+
if (completeCalled) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
completeCalled = true;
|
|
105
|
+
// If no replies were enqueued (pending is still 1 = just the reservation),
|
|
106
|
+
// schedule clearing the reservation after current microtasks complete.
|
|
107
|
+
// This gives any in-flight enqueue() calls a chance to increment pending.
|
|
108
|
+
void Promise.resolve().then(() => {
|
|
109
|
+
if (pending === 1 && completeCalled) {
|
|
110
|
+
// Still just the reservation, no replies were enqueued
|
|
111
|
+
pending -= 1;
|
|
112
|
+
if (pending === 0) {
|
|
113
|
+
unregister();
|
|
114
|
+
options.onIdle?.();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
sendToolResult: (payload) => enqueue("tool", payload),
|
|
121
|
+
sendBlockReply: (payload) => enqueue("block", payload),
|
|
122
|
+
sendFinalReply: (payload) => enqueue("final", payload),
|
|
123
|
+
waitForIdle: () => sendChain,
|
|
124
|
+
getQueuedCounts: () => ({ ...queuedCounts }),
|
|
125
|
+
markComplete,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export function createReplyDispatcherWithTyping(options) {
|
|
129
|
+
const { onReplyStart, onIdle, onCleanup, ...dispatcherOptions } = options;
|
|
130
|
+
let typingController;
|
|
131
|
+
const dispatcher = createReplyDispatcher({
|
|
132
|
+
...dispatcherOptions,
|
|
133
|
+
onIdle: () => {
|
|
134
|
+
typingController?.markDispatchIdle();
|
|
135
|
+
onIdle?.();
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
dispatcher,
|
|
140
|
+
replyOptions: {
|
|
141
|
+
onReplyStart,
|
|
142
|
+
onTypingCleanup: onCleanup,
|
|
143
|
+
onTypingController: (typing) => {
|
|
144
|
+
typingController = typing;
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
markDispatchIdle: () => {
|
|
148
|
+
typingController?.markDispatchIdle();
|
|
149
|
+
onIdle?.();
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { resolveAgentConfig } from "../../agents/agent-scope.js";
|
|
2
|
+
import { getChannelDock } from "../../channels/dock.js";
|
|
3
|
+
import { normalizeChannelId } from "../../channels/plugins/index.js";
|
|
4
|
+
import { CHAT_CHANNEL_ORDER } from "../../channels/registry.js";
|
|
5
|
+
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
|
|
6
|
+
import { formatCliCommand } from "../../cli/command-format.js";
|
|
7
|
+
function normalizeAllowToken(value) {
|
|
8
|
+
if (!value)
|
|
9
|
+
return "";
|
|
10
|
+
return value.trim().toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
function slugAllowToken(value) {
|
|
13
|
+
if (!value)
|
|
14
|
+
return "";
|
|
15
|
+
let text = value.trim().toLowerCase();
|
|
16
|
+
if (!text)
|
|
17
|
+
return "";
|
|
18
|
+
text = text.replace(/^[@#]+/, "");
|
|
19
|
+
text = text.replace(/[\s_]+/g, "-");
|
|
20
|
+
text = text.replace(/[^a-z0-9-]+/g, "-");
|
|
21
|
+
return text.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
|
22
|
+
}
|
|
23
|
+
const SENDER_PREFIXES = [
|
|
24
|
+
...CHAT_CHANNEL_ORDER,
|
|
25
|
+
INTERNAL_MESSAGE_CHANNEL,
|
|
26
|
+
"user",
|
|
27
|
+
"group",
|
|
28
|
+
"channel",
|
|
29
|
+
];
|
|
30
|
+
const SENDER_PREFIX_RE = new RegExp(`^(${SENDER_PREFIXES.join("|")}):`, "i");
|
|
31
|
+
function stripSenderPrefix(value) {
|
|
32
|
+
if (!value)
|
|
33
|
+
return "";
|
|
34
|
+
const trimmed = value.trim();
|
|
35
|
+
return trimmed.replace(SENDER_PREFIX_RE, "");
|
|
36
|
+
}
|
|
37
|
+
function resolveElevatedAllowList(allowFrom, provider, fallbackAllowFrom) {
|
|
38
|
+
if (!allowFrom)
|
|
39
|
+
return fallbackAllowFrom;
|
|
40
|
+
const value = allowFrom[provider];
|
|
41
|
+
return Array.isArray(value) ? value : fallbackAllowFrom;
|
|
42
|
+
}
|
|
43
|
+
function isApprovedElevatedSender(params) {
|
|
44
|
+
const rawAllow = resolveElevatedAllowList(params.allowFrom, params.provider, params.fallbackAllowFrom);
|
|
45
|
+
if (!rawAllow || rawAllow.length === 0)
|
|
46
|
+
return false;
|
|
47
|
+
const allowTokens = rawAllow.map((entry) => String(entry).trim()).filter(Boolean);
|
|
48
|
+
if (allowTokens.length === 0)
|
|
49
|
+
return false;
|
|
50
|
+
if (allowTokens.some((entry) => entry === "*"))
|
|
51
|
+
return true;
|
|
52
|
+
const tokens = new Set();
|
|
53
|
+
const addToken = (value) => {
|
|
54
|
+
if (!value)
|
|
55
|
+
return;
|
|
56
|
+
const trimmed = value.trim();
|
|
57
|
+
if (!trimmed)
|
|
58
|
+
return;
|
|
59
|
+
tokens.add(trimmed);
|
|
60
|
+
const normalized = normalizeAllowToken(trimmed);
|
|
61
|
+
if (normalized)
|
|
62
|
+
tokens.add(normalized);
|
|
63
|
+
const slugged = slugAllowToken(trimmed);
|
|
64
|
+
if (slugged)
|
|
65
|
+
tokens.add(slugged);
|
|
66
|
+
};
|
|
67
|
+
addToken(params.ctx.SenderName);
|
|
68
|
+
addToken(params.ctx.SenderUsername);
|
|
69
|
+
addToken(params.ctx.SenderTag);
|
|
70
|
+
addToken(params.ctx.SenderE164);
|
|
71
|
+
addToken(params.ctx.From);
|
|
72
|
+
addToken(stripSenderPrefix(params.ctx.From));
|
|
73
|
+
addToken(params.ctx.To);
|
|
74
|
+
addToken(stripSenderPrefix(params.ctx.To));
|
|
75
|
+
for (const rawEntry of allowTokens) {
|
|
76
|
+
const entry = rawEntry.trim();
|
|
77
|
+
if (!entry)
|
|
78
|
+
continue;
|
|
79
|
+
const stripped = stripSenderPrefix(entry);
|
|
80
|
+
if (tokens.has(entry) || tokens.has(stripped))
|
|
81
|
+
return true;
|
|
82
|
+
const normalized = normalizeAllowToken(stripped);
|
|
83
|
+
if (normalized && tokens.has(normalized))
|
|
84
|
+
return true;
|
|
85
|
+
const slugged = slugAllowToken(stripped);
|
|
86
|
+
if (slugged && tokens.has(slugged))
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
export function resolveElevatedPermissions(params) {
|
|
92
|
+
const globalConfig = params.cfg.tools?.elevated;
|
|
93
|
+
const agentConfig = resolveAgentConfig(params.cfg, params.agentId)?.tools?.elevated;
|
|
94
|
+
const globalEnabled = globalConfig?.enabled !== false;
|
|
95
|
+
const agentEnabled = agentConfig?.enabled !== false;
|
|
96
|
+
const enabled = globalEnabled && agentEnabled;
|
|
97
|
+
const failures = [];
|
|
98
|
+
if (!globalEnabled)
|
|
99
|
+
failures.push({ gate: "enabled", key: "tools.elevated.enabled" });
|
|
100
|
+
if (!agentEnabled)
|
|
101
|
+
failures.push({
|
|
102
|
+
gate: "enabled",
|
|
103
|
+
key: "agents.list[].tools.elevated.enabled",
|
|
104
|
+
});
|
|
105
|
+
if (!enabled)
|
|
106
|
+
return { enabled, allowed: false, failures };
|
|
107
|
+
if (!params.provider) {
|
|
108
|
+
failures.push({ gate: "provider", key: "ctx.Provider" });
|
|
109
|
+
return { enabled, allowed: false, failures };
|
|
110
|
+
}
|
|
111
|
+
const normalizedProvider = normalizeChannelId(params.provider);
|
|
112
|
+
const dockFallbackAllowFrom = normalizedProvider
|
|
113
|
+
? getChannelDock(normalizedProvider)?.elevated?.allowFromFallback?.({
|
|
114
|
+
cfg: params.cfg,
|
|
115
|
+
accountId: params.ctx.AccountId,
|
|
116
|
+
})
|
|
117
|
+
: undefined;
|
|
118
|
+
const fallbackAllowFrom = dockFallbackAllowFrom;
|
|
119
|
+
const globalAllowed = isApprovedElevatedSender({
|
|
120
|
+
provider: params.provider,
|
|
121
|
+
ctx: params.ctx,
|
|
122
|
+
allowFrom: globalConfig?.allowFrom,
|
|
123
|
+
fallbackAllowFrom,
|
|
124
|
+
});
|
|
125
|
+
if (!globalAllowed) {
|
|
126
|
+
failures.push({
|
|
127
|
+
gate: "allowFrom",
|
|
128
|
+
key: `tools.elevated.allowFrom.${params.provider}`,
|
|
129
|
+
});
|
|
130
|
+
return { enabled, allowed: false, failures };
|
|
131
|
+
}
|
|
132
|
+
const agentAllowed = agentConfig?.allowFrom
|
|
133
|
+
? isApprovedElevatedSender({
|
|
134
|
+
provider: params.provider,
|
|
135
|
+
ctx: params.ctx,
|
|
136
|
+
allowFrom: agentConfig.allowFrom,
|
|
137
|
+
fallbackAllowFrom,
|
|
138
|
+
})
|
|
139
|
+
: true;
|
|
140
|
+
if (!agentAllowed) {
|
|
141
|
+
failures.push({
|
|
142
|
+
gate: "allowFrom",
|
|
143
|
+
key: `agents.list[].tools.elevated.allowFrom.${params.provider}`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return { enabled, allowed: globalAllowed && agentAllowed, failures };
|
|
147
|
+
}
|
|
148
|
+
export function formatElevatedUnavailableMessage(params) {
|
|
149
|
+
const lines = [];
|
|
150
|
+
lines.push(`elevated is not available right now (runtime=${params.runtimeSandboxed ? "sandboxed" : "direct"}).`);
|
|
151
|
+
if (params.failures.length > 0) {
|
|
152
|
+
lines.push(`Failing gates: ${params.failures.map((f) => `${f.gate} (${f.key})`).join(", ")}`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
lines.push("Failing gates: enabled (tools.elevated.enabled / agents.list[].tools.elevated.enabled), allowFrom (tools.elevated.allowFrom.<provider>).");
|
|
156
|
+
}
|
|
157
|
+
lines.push("Fix-it keys:");
|
|
158
|
+
lines.push("- tools.elevated.enabled");
|
|
159
|
+
lines.push("- tools.elevated.allowFrom.<provider>");
|
|
160
|
+
lines.push("- agents.list[].tools.elevated.enabled");
|
|
161
|
+
lines.push("- agents.list[].tools.elevated.allowFrom.<provider>");
|
|
162
|
+
if (params.sessionKey) {
|
|
163
|
+
lines.push(`See: ${formatCliCommand(`poolbot sandbox explain --session ${params.sessionKey}`)}`);
|
|
164
|
+
}
|
|
165
|
+
return lines.join("\n");
|
|
166
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const INLINE_SIMPLE_COMMAND_ALIASES = new Map([
|
|
2
|
+
["/help", "/help"],
|
|
3
|
+
["/commands", "/commands"],
|
|
4
|
+
["/whoami", "/whoami"],
|
|
5
|
+
["/id", "/whoami"],
|
|
6
|
+
]);
|
|
7
|
+
const INLINE_SIMPLE_COMMAND_RE = /(?:^|\s)\/(help|commands|whoami|id)(?=$|\s|:)/i;
|
|
8
|
+
const INLINE_STATUS_RE = /(?:^|\s)\/status(?=$|\s|:)(?:\s*:\s*)?/gi;
|
|
9
|
+
export function extractInlineSimpleCommand(body) {
|
|
10
|
+
if (!body)
|
|
11
|
+
return null;
|
|
12
|
+
const match = body.match(INLINE_SIMPLE_COMMAND_RE);
|
|
13
|
+
if (!match || match.index === undefined)
|
|
14
|
+
return null;
|
|
15
|
+
const alias = `/${match[1].toLowerCase()}`;
|
|
16
|
+
const command = INLINE_SIMPLE_COMMAND_ALIASES.get(alias);
|
|
17
|
+
if (!command)
|
|
18
|
+
return null;
|
|
19
|
+
const cleaned = body.replace(match[0], " ").replace(/\s+/g, " ").trim();
|
|
20
|
+
return { command, cleaned };
|
|
21
|
+
}
|
|
22
|
+
export function stripInlineStatus(body) {
|
|
23
|
+
const trimmed = body.trim();
|
|
24
|
+
if (!trimmed)
|
|
25
|
+
return { cleaned: "", didStrip: false };
|
|
26
|
+
const cleaned = trimmed.replace(INLINE_STATUS_RE, " ").replace(/\s+/g, " ").trim();
|
|
27
|
+
return { cleaned, didStrip: cleaned !== trimmed };
|
|
28
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { isMessagingToolDuplicate } from "../../agents/pi-embedded-helpers.js";
|
|
2
|
+
import { normalizeTargetForProvider } from "../../infra/outbound/target-normalization.js";
|
|
3
|
+
import { normalizeOptionalAccountId } from "../../routing/account-id.js";
|
|
4
|
+
import { extractReplyToTag } from "./reply-tags.js";
|
|
5
|
+
import { createReplyToModeFilterForChannel } from "./reply-threading.js";
|
|
6
|
+
function resolveReplyThreadingForPayload(params) {
|
|
7
|
+
const implicitReplyToId = params.implicitReplyToId?.trim() || undefined;
|
|
8
|
+
const currentMessageId = params.currentMessageId?.trim() || undefined;
|
|
9
|
+
// 1) Apply implicit reply threading first (replyToMode will strip later if needed).
|
|
10
|
+
let resolved = params.payload.replyToId || params.payload.replyToCurrent === false || !implicitReplyToId
|
|
11
|
+
? params.payload
|
|
12
|
+
: { ...params.payload, replyToId: implicitReplyToId };
|
|
13
|
+
// 2) Parse explicit reply tags from text (if present) and clean them.
|
|
14
|
+
if (typeof resolved.text === "string" && resolved.text.includes("[[")) {
|
|
15
|
+
const { cleaned, replyToId, replyToCurrent, hasTag } = extractReplyToTag(resolved.text, currentMessageId);
|
|
16
|
+
resolved = {
|
|
17
|
+
...resolved,
|
|
18
|
+
text: cleaned ? cleaned : undefined,
|
|
19
|
+
replyToId: replyToId ?? resolved.replyToId,
|
|
20
|
+
replyToTag: hasTag || resolved.replyToTag,
|
|
21
|
+
replyToCurrent: replyToCurrent || resolved.replyToCurrent,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// 3) If replyToCurrent was set out-of-band (e.g. tags already stripped upstream),
|
|
25
|
+
// ensure replyToId is set to the current message id when available.
|
|
26
|
+
if (resolved.replyToCurrent && !resolved.replyToId && currentMessageId) {
|
|
27
|
+
resolved = {
|
|
28
|
+
...resolved,
|
|
29
|
+
replyToId: currentMessageId,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return resolved;
|
|
33
|
+
}
|
|
34
|
+
// Backward-compatible helper: apply explicit reply tags/directives to a single payload.
|
|
35
|
+
// This intentionally does not apply implicit threading.
|
|
36
|
+
export function applyReplyTagsToPayload(payload, currentMessageId) {
|
|
37
|
+
return resolveReplyThreadingForPayload({ payload, currentMessageId });
|
|
38
|
+
}
|
|
39
|
+
export function isRenderablePayload(payload) {
|
|
40
|
+
return Boolean(payload.text ||
|
|
41
|
+
payload.mediaUrl ||
|
|
42
|
+
(payload.mediaUrls && payload.mediaUrls.length > 0) ||
|
|
43
|
+
payload.audioAsVoice ||
|
|
44
|
+
payload.channelData);
|
|
45
|
+
}
|
|
46
|
+
export function applyReplyThreading(params) {
|
|
47
|
+
const { payloads, replyToMode, replyToChannel, currentMessageId } = params;
|
|
48
|
+
const applyReplyToMode = createReplyToModeFilterForChannel(replyToMode, replyToChannel);
|
|
49
|
+
const implicitReplyToId = currentMessageId?.trim() || undefined;
|
|
50
|
+
return payloads
|
|
51
|
+
.map((payload) => resolveReplyThreadingForPayload({ payload, implicitReplyToId, currentMessageId }))
|
|
52
|
+
.filter(isRenderablePayload)
|
|
53
|
+
.map(applyReplyToMode);
|
|
54
|
+
}
|
|
55
|
+
export function filterMessagingToolDuplicates(params) {
|
|
56
|
+
const { payloads, sentTexts } = params;
|
|
57
|
+
if (sentTexts.length === 0) {
|
|
58
|
+
return payloads;
|
|
59
|
+
}
|
|
60
|
+
return payloads.filter((payload) => !isMessagingToolDuplicate(payload.text ?? "", sentTexts));
|
|
61
|
+
}
|
|
62
|
+
export function filterMessagingToolMediaDuplicates(params) {
|
|
63
|
+
const { payloads, sentMediaUrls } = params;
|
|
64
|
+
if (sentMediaUrls.length === 0) {
|
|
65
|
+
return payloads;
|
|
66
|
+
}
|
|
67
|
+
const sentSet = new Set(sentMediaUrls);
|
|
68
|
+
return payloads.map((payload) => {
|
|
69
|
+
const mediaUrl = payload.mediaUrl;
|
|
70
|
+
const mediaUrls = payload.mediaUrls;
|
|
71
|
+
const stripSingle = mediaUrl && sentSet.has(mediaUrl);
|
|
72
|
+
const filteredUrls = mediaUrls?.filter((u) => !sentSet.has(u));
|
|
73
|
+
if (!stripSingle && (!mediaUrls || filteredUrls?.length === mediaUrls.length)) {
|
|
74
|
+
return payload; // No change
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
...payload,
|
|
78
|
+
mediaUrl: stripSingle ? undefined : mediaUrl,
|
|
79
|
+
mediaUrls: filteredUrls?.length ? filteredUrls : undefined,
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
export function shouldSuppressMessagingToolReplies(params) {
|
|
84
|
+
const provider = params.messageProvider?.trim().toLowerCase();
|
|
85
|
+
if (!provider) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const originTarget = normalizeTargetForProvider(provider, params.originatingTo);
|
|
89
|
+
if (!originTarget) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const originAccount = normalizeOptionalAccountId(params.accountId);
|
|
93
|
+
const sentTargets = params.messagingToolSentTargets ?? [];
|
|
94
|
+
if (sentTargets.length === 0) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return sentTargets.some((target) => {
|
|
98
|
+
if (!target?.provider) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (target.provider.trim().toLowerCase() !== provider) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
const targetKey = normalizeTargetForProvider(provider, target.to);
|
|
105
|
+
if (!targetKey) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const targetAccount = normalizeOptionalAccountId(target.accountId);
|
|
109
|
+
if (originAccount && targetAccount && originAccount !== targetAccount) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return targetKey === originTarget;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function createReplyReferencePlanner(options) {
|
|
2
|
+
let hasReplied = options.hasReplied ?? false;
|
|
3
|
+
const allowReference = options.allowReference !== false;
|
|
4
|
+
const existingId = options.existingId?.trim();
|
|
5
|
+
const startId = options.startId?.trim();
|
|
6
|
+
const use = () => {
|
|
7
|
+
if (!allowReference) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
if (options.replyToMode === "off") {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const id = existingId ?? startId;
|
|
14
|
+
if (!id) {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
if (options.replyToMode === "all") {
|
|
18
|
+
hasReplied = true;
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
// "first": only the first reply gets a reference.
|
|
22
|
+
if (!hasReplied) {
|
|
23
|
+
hasReplied = true;
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
return undefined;
|
|
27
|
+
};
|
|
28
|
+
const markSent = () => {
|
|
29
|
+
hasReplied = true;
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
use,
|
|
33
|
+
markSent,
|
|
34
|
+
hasReplied: () => hasReplied,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { parseInlineDirectives } from "../../utils/directive-tags.js";
|
|
2
|
+
export function extractReplyToTag(text, currentMessageId) {
|
|
3
|
+
const result = parseInlineDirectives(text, {
|
|
4
|
+
currentMessageId,
|
|
5
|
+
stripAudioTag: false,
|
|
6
|
+
});
|
|
7
|
+
return {
|
|
8
|
+
cleaned: result.text,
|
|
9
|
+
replyToId: result.replyToId,
|
|
10
|
+
replyToCurrent: result.replyToCurrent,
|
|
11
|
+
hasTag: result.hasReplyTag,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { getChannelDock } from "../../channels/dock.js";
|
|
2
|
+
import { normalizeChannelId } from "../../channels/plugins/index.js";
|
|
3
|
+
export function resolveReplyToMode(cfg, channel, accountId, chatType) {
|
|
4
|
+
const provider = normalizeChannelId(channel);
|
|
5
|
+
if (!provider)
|
|
6
|
+
return "all";
|
|
7
|
+
const resolved = getChannelDock(provider)?.threading?.resolveReplyToMode?.({
|
|
8
|
+
cfg,
|
|
9
|
+
accountId,
|
|
10
|
+
chatType,
|
|
11
|
+
});
|
|
12
|
+
return resolved ?? "all";
|
|
13
|
+
}
|
|
14
|
+
export function createReplyToModeFilter(mode, opts = {}) {
|
|
15
|
+
let hasThreaded = false;
|
|
16
|
+
return (payload) => {
|
|
17
|
+
if (!payload.replyToId)
|
|
18
|
+
return payload;
|
|
19
|
+
if (mode === "off") {
|
|
20
|
+
if (opts.allowTagsWhenOff && payload.replyToTag)
|
|
21
|
+
return payload;
|
|
22
|
+
return { ...payload, replyToId: undefined };
|
|
23
|
+
}
|
|
24
|
+
if (mode === "all")
|
|
25
|
+
return payload;
|
|
26
|
+
if (hasThreaded) {
|
|
27
|
+
return { ...payload, replyToId: undefined };
|
|
28
|
+
}
|
|
29
|
+
hasThreaded = true;
|
|
30
|
+
return payload;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function createReplyToModeFilterForChannel(mode, channel) {
|
|
34
|
+
const provider = normalizeChannelId(channel);
|
|
35
|
+
const allowTagsWhenOff = provider
|
|
36
|
+
? Boolean(getChannelDock(provider)?.threading?.allowTagsWhenOff)
|
|
37
|
+
: false;
|
|
38
|
+
return createReplyToModeFilter(mode, {
|
|
39
|
+
allowTagsWhenOff,
|
|
40
|
+
});
|
|
41
|
+
}
|