@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,253 @@
|
|
|
1
|
+
import { buildAccountScopedDmSecurityPolicy, collectAllowlistProviderRestrictSendersWarnings, } from "openclaw/plugin-sdk/compat";
|
|
2
|
+
import { applyAccountNameToChannelSection, buildChannelConfigSchema, collectStatusIssuesFromLastError, DEFAULT_ACCOUNT_ID, deleteAccountFromConfigSection, formatTrimmedAllowFromEntries, getChatChannelMeta, imessageOnboardingAdapter, IMessageConfigSchema, listIMessageAccountIds, looksLikeIMessageTargetId, migrateBaseNameToDefaultAccount, normalizeAccountId, normalizeIMessageMessagingTarget, PAIRING_APPROVED_MESSAGE, resolveChannelMediaMaxBytes, resolveDefaultIMessageAccountId, resolveIMessageAccount, resolveIMessageConfigAllowFrom, resolveIMessageConfigDefaultTo, resolveIMessageGroupRequireMention, resolveIMessageGroupToolPolicy, setAccountEnabledInConfigSection, } from "openclaw/plugin-sdk/imessage";
|
|
3
|
+
import { resolveOutboundSendDep } from "../../../src/infra/outbound/send-deps.js";
|
|
4
|
+
import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js";
|
|
5
|
+
import { getIMessageRuntime } from "./runtime.js";
|
|
6
|
+
const meta = getChatChannelMeta("imessage");
|
|
7
|
+
function buildIMessageSetupPatch(input) {
|
|
8
|
+
return {
|
|
9
|
+
...(input.cliPath ? { cliPath: input.cliPath } : {}),
|
|
10
|
+
...(input.dbPath ? { dbPath: input.dbPath } : {}),
|
|
11
|
+
...(input.service ? { service: input.service } : {}),
|
|
12
|
+
...(input.region ? { region: input.region } : {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function sendIMessageOutbound(params) {
|
|
16
|
+
const send = resolveOutboundSendDep(params.deps, "imessage") ??
|
|
17
|
+
getIMessageRuntime().channel.imessage.sendMessageIMessage;
|
|
18
|
+
const maxBytes = resolveChannelMediaMaxBytes({
|
|
19
|
+
cfg: params.cfg,
|
|
20
|
+
resolveChannelLimitMb: ({ cfg, accountId }) => cfg.channels?.imessage?.accounts?.[accountId]?.mediaMaxMb ??
|
|
21
|
+
cfg.channels?.imessage?.mediaMaxMb,
|
|
22
|
+
accountId: params.accountId,
|
|
23
|
+
});
|
|
24
|
+
return await send(params.to, params.text, {
|
|
25
|
+
config: params.cfg,
|
|
26
|
+
...(params.mediaUrl ? { mediaUrl: params.mediaUrl } : {}),
|
|
27
|
+
...(params.mediaLocalRoots?.length ? { mediaLocalRoots: params.mediaLocalRoots } : {}),
|
|
28
|
+
maxBytes,
|
|
29
|
+
accountId: params.accountId ?? undefined,
|
|
30
|
+
replyToId: params.replyToId ?? undefined,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export const imessagePlugin = {
|
|
34
|
+
id: "imessage",
|
|
35
|
+
meta: {
|
|
36
|
+
...meta,
|
|
37
|
+
aliases: ["imsg"],
|
|
38
|
+
showConfigured: false,
|
|
39
|
+
},
|
|
40
|
+
onboarding: imessageOnboardingAdapter,
|
|
41
|
+
pairing: {
|
|
42
|
+
idLabel: "imessageSenderId",
|
|
43
|
+
notifyApproval: async ({ id }) => {
|
|
44
|
+
await getIMessageRuntime().channel.imessage.sendMessageIMessage(id, PAIRING_APPROVED_MESSAGE);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
capabilities: {
|
|
48
|
+
chatTypes: ["direct", "group"],
|
|
49
|
+
media: true,
|
|
50
|
+
},
|
|
51
|
+
reload: { configPrefixes: ["channels.imessage"] },
|
|
52
|
+
configSchema: buildChannelConfigSchema(IMessageConfigSchema),
|
|
53
|
+
config: {
|
|
54
|
+
listAccountIds: (cfg) => listIMessageAccountIds(cfg),
|
|
55
|
+
resolveAccount: (cfg, accountId) => resolveIMessageAccount({ cfg, accountId }),
|
|
56
|
+
defaultAccountId: (cfg) => resolveDefaultIMessageAccountId(cfg),
|
|
57
|
+
setAccountEnabled: ({ cfg, accountId, enabled }) => setAccountEnabledInConfigSection({
|
|
58
|
+
cfg,
|
|
59
|
+
sectionKey: "imessage",
|
|
60
|
+
accountId,
|
|
61
|
+
enabled,
|
|
62
|
+
allowTopLevel: true,
|
|
63
|
+
}),
|
|
64
|
+
deleteAccount: ({ cfg, accountId }) => deleteAccountFromConfigSection({
|
|
65
|
+
cfg,
|
|
66
|
+
sectionKey: "imessage",
|
|
67
|
+
accountId,
|
|
68
|
+
clearBaseFields: ["cliPath", "dbPath", "service", "region", "name"],
|
|
69
|
+
}),
|
|
70
|
+
isConfigured: (account) => account.configured,
|
|
71
|
+
describeAccount: (account) => ({
|
|
72
|
+
accountId: account.accountId,
|
|
73
|
+
name: account.name,
|
|
74
|
+
enabled: account.enabled,
|
|
75
|
+
configured: account.configured,
|
|
76
|
+
}),
|
|
77
|
+
resolveAllowFrom: ({ cfg, accountId }) => resolveIMessageConfigAllowFrom({ cfg, accountId }),
|
|
78
|
+
formatAllowFrom: ({ allowFrom }) => formatTrimmedAllowFromEntries(allowFrom),
|
|
79
|
+
resolveDefaultTo: ({ cfg, accountId }) => resolveIMessageConfigDefaultTo({ cfg, accountId }),
|
|
80
|
+
},
|
|
81
|
+
security: {
|
|
82
|
+
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
|
83
|
+
return buildAccountScopedDmSecurityPolicy({
|
|
84
|
+
cfg,
|
|
85
|
+
channelKey: "imessage",
|
|
86
|
+
accountId,
|
|
87
|
+
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
88
|
+
policy: account.config.dmPolicy,
|
|
89
|
+
allowFrom: account.config.allowFrom ?? [],
|
|
90
|
+
policyPathSuffix: "dmPolicy",
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
collectWarnings: ({ account, cfg }) => {
|
|
94
|
+
return collectAllowlistProviderRestrictSendersWarnings({
|
|
95
|
+
cfg,
|
|
96
|
+
providerConfigPresent: cfg.channels?.imessage !== undefined,
|
|
97
|
+
configuredGroupPolicy: account.config.groupPolicy,
|
|
98
|
+
surface: "iMessage groups",
|
|
99
|
+
openScope: "any member",
|
|
100
|
+
groupPolicyPath: "channels.imessage.groupPolicy",
|
|
101
|
+
groupAllowFromPath: "channels.imessage.groupAllowFrom",
|
|
102
|
+
mentionGated: false,
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
groups: {
|
|
107
|
+
resolveRequireMention: resolveIMessageGroupRequireMention,
|
|
108
|
+
resolveToolPolicy: resolveIMessageGroupToolPolicy,
|
|
109
|
+
},
|
|
110
|
+
messaging: {
|
|
111
|
+
normalizeTarget: normalizeIMessageMessagingTarget,
|
|
112
|
+
targetResolver: {
|
|
113
|
+
looksLikeId: looksLikeIMessageTargetId,
|
|
114
|
+
hint: "<handle|chat_id:ID>",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
setup: {
|
|
118
|
+
resolveAccountId: ({ accountId }) => normalizeAccountId(accountId),
|
|
119
|
+
applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
|
|
120
|
+
cfg,
|
|
121
|
+
channelKey: "imessage",
|
|
122
|
+
accountId,
|
|
123
|
+
name,
|
|
124
|
+
}),
|
|
125
|
+
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
126
|
+
const namedConfig = applyAccountNameToChannelSection({
|
|
127
|
+
cfg,
|
|
128
|
+
channelKey: "imessage",
|
|
129
|
+
accountId,
|
|
130
|
+
name: input.name,
|
|
131
|
+
});
|
|
132
|
+
const next = (accountId !== DEFAULT_ACCOUNT_ID
|
|
133
|
+
? migrateBaseNameToDefaultAccount({
|
|
134
|
+
cfg: namedConfig,
|
|
135
|
+
channelKey: "imessage",
|
|
136
|
+
})
|
|
137
|
+
: namedConfig);
|
|
138
|
+
if (accountId === DEFAULT_ACCOUNT_ID) {
|
|
139
|
+
return {
|
|
140
|
+
...next,
|
|
141
|
+
channels: {
|
|
142
|
+
...next.channels,
|
|
143
|
+
imessage: {
|
|
144
|
+
...next.channels?.imessage,
|
|
145
|
+
enabled: true,
|
|
146
|
+
...buildIMessageSetupPatch(input),
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
...next,
|
|
153
|
+
channels: {
|
|
154
|
+
...next.channels,
|
|
155
|
+
imessage: {
|
|
156
|
+
...next.channels?.imessage,
|
|
157
|
+
enabled: true,
|
|
158
|
+
accounts: {
|
|
159
|
+
...next.channels?.imessage?.accounts,
|
|
160
|
+
[accountId]: {
|
|
161
|
+
...next.channels?.imessage?.accounts?.[accountId],
|
|
162
|
+
enabled: true,
|
|
163
|
+
...buildIMessageSetupPatch(input),
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
outbound: {
|
|
172
|
+
deliveryMode: "direct",
|
|
173
|
+
chunker: (text, limit) => getIMessageRuntime().channel.text.chunkText(text, limit),
|
|
174
|
+
chunkerMode: "text",
|
|
175
|
+
textChunkLimit: 4000,
|
|
176
|
+
sendText: async ({ cfg, to, text, accountId, deps, replyToId }) => {
|
|
177
|
+
const result = await sendIMessageOutbound({
|
|
178
|
+
cfg,
|
|
179
|
+
to,
|
|
180
|
+
text,
|
|
181
|
+
accountId: accountId ?? undefined,
|
|
182
|
+
deps,
|
|
183
|
+
replyToId: replyToId ?? undefined,
|
|
184
|
+
});
|
|
185
|
+
return { channel: "imessage", ...result };
|
|
186
|
+
},
|
|
187
|
+
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, deps, replyToId }) => {
|
|
188
|
+
const result = await sendIMessageOutbound({
|
|
189
|
+
cfg,
|
|
190
|
+
to,
|
|
191
|
+
text,
|
|
192
|
+
mediaUrl,
|
|
193
|
+
mediaLocalRoots,
|
|
194
|
+
accountId: accountId ?? undefined,
|
|
195
|
+
deps,
|
|
196
|
+
replyToId: replyToId ?? undefined,
|
|
197
|
+
});
|
|
198
|
+
return { channel: "imessage", ...result };
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
status: {
|
|
202
|
+
defaultRuntime: {
|
|
203
|
+
accountId: DEFAULT_ACCOUNT_ID,
|
|
204
|
+
running: false,
|
|
205
|
+
lastStartAt: null,
|
|
206
|
+
lastStopAt: null,
|
|
207
|
+
lastError: null,
|
|
208
|
+
cliPath: null,
|
|
209
|
+
dbPath: null,
|
|
210
|
+
},
|
|
211
|
+
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("imessage", accounts),
|
|
212
|
+
buildChannelSummary: ({ snapshot }) => buildPassiveProbedChannelStatusSummary(snapshot, {
|
|
213
|
+
cliPath: snapshot.cliPath ?? null,
|
|
214
|
+
dbPath: snapshot.dbPath ?? null,
|
|
215
|
+
}),
|
|
216
|
+
probeAccount: async ({ timeoutMs }) => getIMessageRuntime().channel.imessage.probeIMessage(timeoutMs),
|
|
217
|
+
buildAccountSnapshot: ({ account, runtime, probe }) => ({
|
|
218
|
+
accountId: account.accountId,
|
|
219
|
+
name: account.name,
|
|
220
|
+
enabled: account.enabled,
|
|
221
|
+
configured: account.configured,
|
|
222
|
+
running: runtime?.running ?? false,
|
|
223
|
+
lastStartAt: runtime?.lastStartAt ?? null,
|
|
224
|
+
lastStopAt: runtime?.lastStopAt ?? null,
|
|
225
|
+
lastError: runtime?.lastError ?? null,
|
|
226
|
+
cliPath: runtime?.cliPath ?? account.config.cliPath ?? null,
|
|
227
|
+
dbPath: runtime?.dbPath ?? account.config.dbPath ?? null,
|
|
228
|
+
probe,
|
|
229
|
+
lastInboundAt: runtime?.lastInboundAt ?? null,
|
|
230
|
+
lastOutboundAt: runtime?.lastOutboundAt ?? null,
|
|
231
|
+
}),
|
|
232
|
+
resolveAccountState: ({ enabled }) => (enabled ? "enabled" : "disabled"),
|
|
233
|
+
},
|
|
234
|
+
gateway: {
|
|
235
|
+
startAccount: async (ctx) => {
|
|
236
|
+
const account = ctx.account;
|
|
237
|
+
const cliPath = account.config.cliPath?.trim() || "imsg";
|
|
238
|
+
const dbPath = account.config.dbPath?.trim();
|
|
239
|
+
ctx.setStatus({
|
|
240
|
+
accountId: account.accountId,
|
|
241
|
+
cliPath,
|
|
242
|
+
dbPath: dbPath ?? null,
|
|
243
|
+
});
|
|
244
|
+
ctx.log?.info(`[${account.accountId}] starting provider (${cliPath}${dbPath ? ` db=${dbPath}` : ""})`);
|
|
245
|
+
return getIMessageRuntime().channel.imessage.monitorIMessageProvider({
|
|
246
|
+
accountId: account.accountId,
|
|
247
|
+
config: ctx.cfg,
|
|
248
|
+
runtime: ctx.runtime,
|
|
249
|
+
abortSignal: ctx.abortSignal,
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Keep the text fallback short so repeated user replies like "ok" are not
|
|
2
|
+
// suppressed for long; delayed reflections should match the stronger message-id key.
|
|
3
|
+
const SENT_MESSAGE_TEXT_TTL_MS = 5_000;
|
|
4
|
+
const SENT_MESSAGE_ID_TTL_MS = 60_000;
|
|
5
|
+
function normalizeEchoTextKey(text) {
|
|
6
|
+
if (!text) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const normalized = text.replace(/\r\n?/g, "\n").trim();
|
|
10
|
+
return normalized ? normalized : null;
|
|
11
|
+
}
|
|
12
|
+
function normalizeEchoMessageIdKey(messageId) {
|
|
13
|
+
if (!messageId) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const normalized = messageId.trim();
|
|
17
|
+
if (!normalized || normalized === "ok" || normalized === "unknown") {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
22
|
+
class DefaultSentMessageCache {
|
|
23
|
+
textCache = new Map();
|
|
24
|
+
messageIdCache = new Map();
|
|
25
|
+
remember(scope, lookup) {
|
|
26
|
+
const textKey = normalizeEchoTextKey(lookup.text);
|
|
27
|
+
if (textKey) {
|
|
28
|
+
this.textCache.set(`${scope}:${textKey}`, Date.now());
|
|
29
|
+
}
|
|
30
|
+
const messageIdKey = normalizeEchoMessageIdKey(lookup.messageId);
|
|
31
|
+
if (messageIdKey) {
|
|
32
|
+
this.messageIdCache.set(`${scope}:${messageIdKey}`, Date.now());
|
|
33
|
+
}
|
|
34
|
+
this.cleanup();
|
|
35
|
+
}
|
|
36
|
+
has(scope, lookup) {
|
|
37
|
+
this.cleanup();
|
|
38
|
+
const messageIdKey = normalizeEchoMessageIdKey(lookup.messageId);
|
|
39
|
+
if (messageIdKey) {
|
|
40
|
+
const idTimestamp = this.messageIdCache.get(`${scope}:${messageIdKey}`);
|
|
41
|
+
if (idTimestamp && Date.now() - idTimestamp <= SENT_MESSAGE_ID_TTL_MS) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const textKey = normalizeEchoTextKey(lookup.text);
|
|
46
|
+
if (textKey) {
|
|
47
|
+
const textTimestamp = this.textCache.get(`${scope}:${textKey}`);
|
|
48
|
+
if (textTimestamp && Date.now() - textTimestamp <= SENT_MESSAGE_TEXT_TTL_MS) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
cleanup() {
|
|
55
|
+
const now = Date.now();
|
|
56
|
+
for (const [key, timestamp] of this.textCache.entries()) {
|
|
57
|
+
if (now - timestamp > SENT_MESSAGE_TEXT_TTL_MS) {
|
|
58
|
+
this.textCache.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const [key, timestamp] of this.messageIdCache.entries()) {
|
|
62
|
+
if (now - timestamp > SENT_MESSAGE_ID_TTL_MS) {
|
|
63
|
+
this.messageIdCache.delete(key);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function createSentMessageCache() {
|
|
69
|
+
return new DefaultSentMessageCache();
|
|
70
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-conversation rate limiter that detects rapid-fire identical echo
|
|
3
|
+
* patterns and suppresses them before they amplify into queue overflow.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_WINDOW_MS = 60_000;
|
|
6
|
+
const DEFAULT_MAX_HITS = 5;
|
|
7
|
+
const CLEANUP_INTERVAL_MS = 120_000;
|
|
8
|
+
export function createLoopRateLimiter(opts) {
|
|
9
|
+
const windowMs = opts?.windowMs ?? DEFAULT_WINDOW_MS;
|
|
10
|
+
const maxHits = opts?.maxHits ?? DEFAULT_MAX_HITS;
|
|
11
|
+
const conversations = new Map();
|
|
12
|
+
let lastCleanup = Date.now();
|
|
13
|
+
function cleanup() {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
if (now - lastCleanup < CLEANUP_INTERVAL_MS) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
lastCleanup = now;
|
|
19
|
+
for (const [key, win] of conversations.entries()) {
|
|
20
|
+
const recent = win.timestamps.filter((ts) => now - ts <= windowMs);
|
|
21
|
+
if (recent.length === 0) {
|
|
22
|
+
conversations.delete(key);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
win.timestamps = recent;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
record(conversationKey) {
|
|
31
|
+
cleanup();
|
|
32
|
+
let win = conversations.get(conversationKey);
|
|
33
|
+
if (!win) {
|
|
34
|
+
win = { timestamps: [] };
|
|
35
|
+
conversations.set(conversationKey, win);
|
|
36
|
+
}
|
|
37
|
+
win.timestamps.push(Date.now());
|
|
38
|
+
},
|
|
39
|
+
isRateLimited(conversationKey) {
|
|
40
|
+
cleanup();
|
|
41
|
+
const win = conversations.get(conversationKey);
|
|
42
|
+
if (!win) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const recent = win.timestamps.filter((ts) => now - ts <= windowMs);
|
|
47
|
+
win.timestamps = recent;
|
|
48
|
+
return recent.length >= maxHits;
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects inbound messages that are reflections of assistant-originated content.
|
|
3
|
+
* These patterns indicate internal metadata leaked into a channel and then
|
|
4
|
+
* bounced back as a new inbound message — creating an echo loop.
|
|
5
|
+
*/
|
|
6
|
+
import { findCodeRegions, isInsideCode } from "../../../../src/shared/text/code-regions.js";
|
|
7
|
+
const INTERNAL_SEPARATOR_RE = /(?:#\+){2,}#?/;
|
|
8
|
+
const ASSISTANT_ROLE_MARKER_RE = /\bassistant\s+to\s*=\s*\w+/i;
|
|
9
|
+
// Require closing `>` to avoid false-positives on phrases like "<thought experiment>".
|
|
10
|
+
const THINKING_TAG_RE = /<\s*\/?\s*(?:think(?:ing)?|thought|antthinking)\b[^<>]*>/i;
|
|
11
|
+
const RELEVANT_MEMORIES_TAG_RE = /<\s*\/?\s*relevant[-_]memories\b[^<>]*>/i;
|
|
12
|
+
// Require closing `>` to avoid false-positives on phrases like "<final answer>".
|
|
13
|
+
const FINAL_TAG_RE = /<\s*\/?\s*final\b[^<>]*>/i;
|
|
14
|
+
const REFLECTION_PATTERNS = [
|
|
15
|
+
{ re: INTERNAL_SEPARATOR_RE, label: "internal-separator" },
|
|
16
|
+
{ re: ASSISTANT_ROLE_MARKER_RE, label: "assistant-role-marker" },
|
|
17
|
+
{ re: THINKING_TAG_RE, label: "thinking-tag" },
|
|
18
|
+
{ re: RELEVANT_MEMORIES_TAG_RE, label: "relevant-memories-tag" },
|
|
19
|
+
{ re: FINAL_TAG_RE, label: "final-tag" },
|
|
20
|
+
];
|
|
21
|
+
function hasMatchOutsideCode(text, re) {
|
|
22
|
+
const codeRegions = findCodeRegions(text);
|
|
23
|
+
const globalRe = new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`);
|
|
24
|
+
for (const match of text.matchAll(globalRe)) {
|
|
25
|
+
const start = match.index ?? -1;
|
|
26
|
+
if (start >= 0 && !isInsideCode(start, codeRegions)) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check whether an inbound message appears to be a reflection of
|
|
34
|
+
* assistant-originated content. Returns matched pattern labels for telemetry.
|
|
35
|
+
*/
|
|
36
|
+
export function detectReflectedContent(text) {
|
|
37
|
+
if (!text) {
|
|
38
|
+
return { isReflection: false, matchedLabels: [] };
|
|
39
|
+
}
|
|
40
|
+
const matchedLabels = [];
|
|
41
|
+
for (const { re, label } of REFLECTION_PATTERNS) {
|
|
42
|
+
if (hasMatchOutsideCode(text, re)) {
|
|
43
|
+
matchedLabels.push(label);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
isReflection: matchedLabels.length > 0,
|
|
48
|
+
matchedLabels,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { stripAssistantInternalScaffolding } from "../../../../src/shared/text/assistant-visible-text.js";
|
|
2
|
+
/**
|
|
3
|
+
* Patterns that indicate assistant-internal metadata leaked into text.
|
|
4
|
+
* These must never reach a user-facing channel.
|
|
5
|
+
*/
|
|
6
|
+
const INTERNAL_SEPARATOR_RE = /(?:#\+){2,}#?/g;
|
|
7
|
+
const ASSISTANT_ROLE_MARKER_RE = /\bassistant\s+to\s*=\s*\w+/gi;
|
|
8
|
+
const ROLE_TURN_MARKER_RE = /\b(?:user|system|assistant)\s*:\s*$/gm;
|
|
9
|
+
/**
|
|
10
|
+
* Strip all assistant-internal scaffolding from outbound text before delivery.
|
|
11
|
+
* Applies reasoning/thinking tag removal, memory tag removal, and
|
|
12
|
+
* model-specific internal separator stripping.
|
|
13
|
+
*/
|
|
14
|
+
export function sanitizeOutboundText(text) {
|
|
15
|
+
if (!text) {
|
|
16
|
+
return text;
|
|
17
|
+
}
|
|
18
|
+
let cleaned = stripAssistantInternalScaffolding(text);
|
|
19
|
+
cleaned = cleaned.replace(INTERNAL_SEPARATOR_RE, "");
|
|
20
|
+
cleaned = cleaned.replace(ASSISTANT_ROLE_MARKER_RE, "");
|
|
21
|
+
cleaned = cleaned.replace(ROLE_TURN_MARKER_RE, "");
|
|
22
|
+
// Collapse excessive blank lines left after stripping.
|
|
23
|
+
cleaned = cleaned.replace(/\n{3,}/g, "\n\n").trim();
|
|
24
|
+
return cleaned;
|
|
25
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { formatIMessageChatTarget } from "../targets.js";
|
|
3
|
+
const SELF_CHAT_TTL_MS = 10_000;
|
|
4
|
+
const MAX_SELF_CHAT_CACHE_ENTRIES = 512;
|
|
5
|
+
const CLEANUP_MIN_INTERVAL_MS = 1_000;
|
|
6
|
+
function normalizeText(text) {
|
|
7
|
+
if (!text) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const normalized = text.replace(/\r\n?/g, "\n").trim();
|
|
11
|
+
return normalized ? normalized : null;
|
|
12
|
+
}
|
|
13
|
+
function isUsableTimestamp(createdAt) {
|
|
14
|
+
return typeof createdAt === "number" && Number.isFinite(createdAt);
|
|
15
|
+
}
|
|
16
|
+
function digestText(text) {
|
|
17
|
+
return createHash("sha256").update(text).digest("hex");
|
|
18
|
+
}
|
|
19
|
+
function buildScope(parts) {
|
|
20
|
+
if (!parts.isGroup) {
|
|
21
|
+
return `${parts.accountId}:imessage:${parts.sender}`;
|
|
22
|
+
}
|
|
23
|
+
const chatTarget = formatIMessageChatTarget(parts.chatId) || "chat_id:unknown";
|
|
24
|
+
return `${parts.accountId}:${chatTarget}:imessage:${parts.sender}`;
|
|
25
|
+
}
|
|
26
|
+
class DefaultSelfChatCache {
|
|
27
|
+
cache = new Map();
|
|
28
|
+
lastCleanupAt = 0;
|
|
29
|
+
buildKey(lookup) {
|
|
30
|
+
const text = normalizeText(lookup.text);
|
|
31
|
+
if (!text || !isUsableTimestamp(lookup.createdAt)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return `${buildScope(lookup)}:${lookup.createdAt}:${digestText(text)}`;
|
|
35
|
+
}
|
|
36
|
+
remember(lookup) {
|
|
37
|
+
const key = this.buildKey(lookup);
|
|
38
|
+
if (!key) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.cache.set(key, Date.now());
|
|
42
|
+
this.maybeCleanup();
|
|
43
|
+
}
|
|
44
|
+
has(lookup) {
|
|
45
|
+
this.maybeCleanup();
|
|
46
|
+
const key = this.buildKey(lookup);
|
|
47
|
+
if (!key) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const timestamp = this.cache.get(key);
|
|
51
|
+
return typeof timestamp === "number" && Date.now() - timestamp <= SELF_CHAT_TTL_MS;
|
|
52
|
+
}
|
|
53
|
+
maybeCleanup() {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
if (now - this.lastCleanupAt < CLEANUP_MIN_INTERVAL_MS) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.lastCleanupAt = now;
|
|
59
|
+
for (const [key, timestamp] of this.cache.entries()) {
|
|
60
|
+
if (now - timestamp > SELF_CHAT_TTL_MS) {
|
|
61
|
+
this.cache.delete(key);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
while (this.cache.size > MAX_SELF_CHAT_CACHE_ENTRIES) {
|
|
65
|
+
const oldestKey = this.cache.keys().next().value;
|
|
66
|
+
if (typeof oldestKey !== "string") {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
this.cache.delete(oldestKey);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function createSelfChatCache() {
|
|
74
|
+
return new DefaultSelfChatCache();
|
|
75
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export const POSIX_POOLBOT_TMP_DIR = "/tmp/poolbot";
|
|
5
|
+
function isNodeErrorWithCode(err, code) {
|
|
6
|
+
return (typeof err === "object" &&
|
|
7
|
+
err !== null &&
|
|
8
|
+
"code" in err &&
|
|
9
|
+
err.code === code);
|
|
10
|
+
}
|
|
11
|
+
export function resolvePreferredPoolBotTmpDir(options = {}) {
|
|
12
|
+
const accessSync = options.accessSync ?? fs.accessSync;
|
|
13
|
+
const lstatSync = options.lstatSync ?? fs.lstatSync;
|
|
14
|
+
const mkdirSync = options.mkdirSync ?? fs.mkdirSync;
|
|
15
|
+
const getuid = options.getuid ??
|
|
16
|
+
(() => {
|
|
17
|
+
try {
|
|
18
|
+
return typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const tmpdir = options.tmpdir ?? os.tmpdir;
|
|
25
|
+
const uid = getuid();
|
|
26
|
+
const isSecureDirForUser = (st) => {
|
|
27
|
+
if (uid === undefined) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
if (typeof st.uid === "number" && st.uid !== uid) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// Avoid group/other writable dirs when running on multi-user hosts.
|
|
34
|
+
if (typeof st.mode === "number" && (st.mode & 0o022) !== 0) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
39
|
+
const fallback = () => {
|
|
40
|
+
const base = tmpdir();
|
|
41
|
+
const suffix = uid === undefined ? "poolbot" : `poolbot-${uid}`;
|
|
42
|
+
return path.join(base, suffix);
|
|
43
|
+
};
|
|
44
|
+
const isTrustedPreferredDir = (st) => {
|
|
45
|
+
return st.isDirectory() && !st.isSymbolicLink() && isSecureDirForUser(st);
|
|
46
|
+
};
|
|
47
|
+
const resolvePreferredState = (requireWritableAccess) => {
|
|
48
|
+
try {
|
|
49
|
+
const preferred = lstatSync(POSIX_POOLBOT_TMP_DIR);
|
|
50
|
+
if (!isTrustedPreferredDir(preferred)) {
|
|
51
|
+
return "invalid";
|
|
52
|
+
}
|
|
53
|
+
if (requireWritableAccess) {
|
|
54
|
+
accessSync(POSIX_POOLBOT_TMP_DIR, fs.constants.W_OK | fs.constants.X_OK);
|
|
55
|
+
}
|
|
56
|
+
return "available";
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
if (isNodeErrorWithCode(err, "ENOENT")) {
|
|
60
|
+
return "missing";
|
|
61
|
+
}
|
|
62
|
+
return "invalid";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const existingPreferredState = resolvePreferredState(true);
|
|
66
|
+
if (existingPreferredState === "available") {
|
|
67
|
+
return POSIX_POOLBOT_TMP_DIR;
|
|
68
|
+
}
|
|
69
|
+
if (existingPreferredState === "invalid") {
|
|
70
|
+
return fallback();
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
accessSync("/tmp", fs.constants.W_OK | fs.constants.X_OK);
|
|
74
|
+
// Create with a safe default; subsequent callers expect it exists.
|
|
75
|
+
mkdirSync(POSIX_POOLBOT_TMP_DIR, { recursive: true, mode: 0o700 });
|
|
76
|
+
if (resolvePreferredState(true) !== "available") {
|
|
77
|
+
return fallback();
|
|
78
|
+
}
|
|
79
|
+
return POSIX_POOLBOT_TMP_DIR;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return fallback();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pairing challenge utilities for channel authorization.
|
|
3
|
+
*/
|
|
4
|
+
function generateRandomCode() {
|
|
5
|
+
// Generate a 6-digit random code using Math.random
|
|
6
|
+
return Math.floor(100000 + Math.random() * 900000).toString();
|
|
7
|
+
}
|
|
8
|
+
export async function issuePairingChallenge(_params) {
|
|
9
|
+
const code = generateRandomCode();
|
|
10
|
+
const expiresAt = new Date(Date.now() + 10 * 60 * 1000); // 10 minutes
|
|
11
|
+
return {
|
|
12
|
+
code,
|
|
13
|
+
expiresAt,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../routing/session-key.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type AgentMediaPayload = {
|
|
2
|
+
MediaPath?: string;
|
|
3
|
+
MediaType?: string;
|
|
4
|
+
MediaUrl?: string;
|
|
5
|
+
MediaPaths?: string[];
|
|
6
|
+
MediaUrls?: string[];
|
|
7
|
+
MediaTypes?: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function buildAgentMediaPayload(mediaList: Array<{
|
|
10
|
+
path: string;
|
|
11
|
+
contentType?: string | null;
|
|
12
|
+
}>): AgentMediaPayload;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare function formatAllowFromLowercase(params: {
|
|
2
|
+
allowFrom: Array<string | number>;
|
|
3
|
+
stripPrefixRe?: RegExp;
|
|
4
|
+
}): string[];
|
|
5
|
+
type ParsedChatAllowTarget = {
|
|
6
|
+
kind: "chat_id";
|
|
7
|
+
chatId: number;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "chat_guid";
|
|
10
|
+
chatGuid: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "chat_identifier";
|
|
13
|
+
chatIdentifier: string;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "handle";
|
|
16
|
+
handle: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params: {
|
|
19
|
+
allowFrom: Array<string | number>;
|
|
20
|
+
sender: string;
|
|
21
|
+
chatId?: number | null;
|
|
22
|
+
chatGuid?: string | null;
|
|
23
|
+
chatIdentifier?: string | null;
|
|
24
|
+
normalizeSender: (sender: string) => string;
|
|
25
|
+
parseAllowTarget: (entry: string) => TParsed;
|
|
26
|
+
}): boolean;
|
|
27
|
+
export {};
|