@poolzin/pool-bot 2026.2.21 → 2026.2.22
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/CHANGELOG.md +17 -0
- package/dist/agents/api-key-rotation.js +47 -0
- package/dist/agents/apply-patch-update.js +19 -9
- package/dist/agents/apply-patch.js +72 -47
- package/dist/agents/bash-tools.exec.js +141 -559
- package/dist/agents/cli-backends.js +49 -6
- package/dist/agents/cli-runner/helpers.js +69 -152
- package/dist/agents/cli-runner.js +70 -19
- package/dist/agents/identity.js +20 -1
- package/dist/agents/image-sanitization.js +9 -0
- package/dist/agents/live-auth-keys.js +123 -26
- package/dist/agents/live-model-filter.js +13 -4
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-forward-compat.js +60 -23
- package/dist/agents/model-selection.js +134 -41
- package/dist/agents/pi-auth-json.js +2 -2
- package/dist/agents/pi-embedded-helpers/bootstrap.js +65 -15
- package/dist/agents/pi-embedded-helpers/errors.js +140 -15
- package/dist/agents/pi-embedded-helpers/images.js +22 -12
- package/dist/agents/pi-embedded-helpers.js +2 -2
- package/dist/agents/pi-embedded-runner/abort.js +10 -3
- package/dist/agents/pi-embedded-runner/compact.js +230 -32
- package/dist/agents/pi-embedded-runner/extra-params.js +203 -12
- package/dist/agents/pi-embedded-runner/google.js +109 -19
- package/dist/agents/pi-embedded-runner/history.js +35 -17
- package/dist/agents/pi-embedded-runner/run/attempt.js +386 -95
- package/dist/agents/pi-embedded-runner/run/images.js +81 -55
- package/dist/agents/pi-embedded-runner/run/payloads.js +89 -39
- package/dist/agents/pi-embedded-runner/run.js +193 -25
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +2 -2
- package/dist/agents/pi-embedded-runner/runs.js +17 -8
- package/dist/agents/pi-embedded-runner/tool-result-context-guard.js +262 -0
- package/dist/agents/pi-embedded-runner.js +1 -1
- package/dist/agents/pi-embedded-subscribe.handlers.tools.js +180 -10
- package/dist/agents/pi-embedded-subscribe.js +37 -0
- package/dist/agents/pi-embedded-subscribe.tools.js +127 -30
- package/dist/agents/pi-model-discovery.js +9 -2
- package/dist/agents/pi-tool-definition-adapter.js +60 -8
- package/dist/agents/pi-tools.before-tool-call.js +1 -1
- package/dist/agents/pi-tools.js +113 -94
- package/dist/agents/pi-tools.read.js +337 -38
- package/dist/agents/poolbot-tools.js +14 -5
- package/dist/agents/sandbox/docker.js +10 -5
- package/dist/agents/sandbox/registry.js +96 -46
- package/dist/agents/sandbox/sanitize-env-vars.js +82 -0
- package/dist/agents/sandbox-paths.js +43 -10
- package/dist/agents/session-tool-result-guard-wrapper.js +23 -11
- package/dist/agents/session-tool-result-guard.js +39 -39
- package/dist/agents/session-transcript-repair.js +36 -33
- package/dist/agents/session-write-lock.js +62 -44
- package/dist/agents/skills/frontmatter.js +49 -88
- package/dist/agents/skills/workspace.js +335 -28
- package/dist/agents/subagent-announce.js +508 -174
- package/dist/agents/subagent-registry.js +45 -4
- package/dist/agents/subagent-spawn.js +16 -33
- package/dist/agents/system-prompt-report.js +27 -10
- package/dist/agents/system-prompt.js +26 -32
- package/dist/agents/tool-call-id.js +69 -17
- package/dist/agents/tool-display-common.js +1 -1
- package/dist/agents/tool-images.js +64 -31
- package/dist/agents/tools/canvas-tool.js +17 -11
- package/dist/agents/tools/common.js +37 -19
- package/dist/agents/tools/cron-tool.js +40 -38
- package/dist/agents/tools/gateway.js +70 -2
- package/dist/agents/tools/message-tool.js +181 -40
- package/dist/agents/tools/nodes-tool.js +128 -36
- package/dist/agents/tools/nodes-utils.js +12 -38
- package/dist/agents/tools/session-status-tool.js +24 -71
- package/dist/agents/tools/sessions-helpers.js +38 -210
- package/dist/agents/tools/sessions-spawn-tool.js +28 -198
- package/dist/agents/tools/telegram-actions.js +58 -7
- package/dist/agents/tools/web-fetch-utils.js +112 -7
- package/dist/agents/tools/web-fetch.js +279 -175
- package/dist/agents/tools/web-shared.js +71 -8
- package/dist/agents/usage.js +25 -16
- package/dist/auto-reply/commands-registry.data.js +85 -11
- package/dist/auto-reply/dispatch.js +40 -21
- package/dist/auto-reply/reply/abort.js +102 -33
- package/dist/auto-reply/reply/commands-core.js +82 -33
- package/dist/auto-reply/reply/commands-export-session.js +1 -1
- package/dist/auto-reply/reply/commands-info.js +41 -12
- package/dist/auto-reply/reply/commands-subagents.js +352 -100
- package/dist/auto-reply/reply/commands-system-prompt.js +2 -2
- package/dist/auto-reply/reply/dispatch-from-config.js +100 -29
- package/dist/auto-reply/reply/elevated-unavailable.js +1 -1
- package/dist/auto-reply/reply/inbound-meta.js +12 -1
- package/dist/auto-reply/reply/mentions.js +18 -11
- package/dist/auto-reply/reply/normalize-reply.js +17 -8
- package/dist/auto-reply/reply/reply-dispatcher.js +62 -10
- package/dist/auto-reply/reply/session.js +102 -21
- package/dist/auto-reply/reply/streaming-directives.js +16 -5
- package/dist/auto-reply/status.js +73 -50
- package/dist/browser/extension-relay.js +3 -3
- package/dist/browser/http-auth.js +1 -1
- package/dist/browser/paths.js +2 -2
- package/dist/build-info.json +3 -3
- package/dist/channels/allowlist-match.js +20 -0
- package/dist/channels/allowlists/resolve-utils.js +65 -2
- package/dist/channels/chat-type.js +8 -4
- package/dist/channels/dock.js +127 -35
- package/dist/channels/draft-stream-loop.js +6 -2
- package/dist/channels/plugins/actions/telegram.js +42 -18
- package/dist/channels/plugins/allowlist-match.js +1 -1
- package/dist/channels/plugins/group-mentions.js +51 -41
- package/dist/channels/plugins/message-action-names.js +2 -0
- package/dist/channels/plugins/message-actions.js +24 -5
- package/dist/channels/plugins/normalize/discord.js +26 -4
- package/dist/channels/plugins/normalize/signal.js +35 -22
- package/dist/channels/plugins/onboarding/helpers.js +8 -26
- package/dist/channels/plugins/outbound/imessage.js +15 -14
- package/dist/channels/registry.js +20 -7
- package/dist/cli/acp-cli.js +7 -5
- package/dist/cli/browser-cli-extension.js +25 -12
- package/dist/cli/browser-cli-state.cookies-storage.js +25 -6
- package/dist/cli/browser-cli-state.js +101 -145
- package/dist/cli/command-options.js +28 -0
- package/dist/cli/completion-cli.js +6 -6
- package/dist/cli/cron-cli/register.cron-add.js +25 -1
- package/dist/cli/cron-cli/register.cron-edit.js +44 -0
- package/dist/cli/cron-cli/shared.js +7 -1
- package/dist/cli/daemon-cli/lifecycle-core.js +23 -21
- package/dist/cli/daemon-cli/lifecycle.js +23 -247
- package/dist/cli/daemon-cli/register-service-commands.js +25 -4
- package/dist/cli/daemon-cli.js +1 -0
- package/dist/cli/devices-cli.js +33 -20
- package/dist/cli/gateway-cli/register.js +37 -105
- package/dist/cli/gateway-cli/run.js +49 -11
- package/dist/cli/nodes-camera.js +59 -4
- package/dist/cli/nodes-cli/register.camera.js +27 -24
- package/dist/cli/nodes-cli/rpc.js +21 -38
- package/dist/cli/qr-cli.js +2 -2
- package/dist/cli/skills-cli.format.js +2 -2
- package/dist/cli/update-cli/progress.js +2 -2
- package/dist/cli/update-cli/restart-helper.js +28 -7
- package/dist/cli/update-cli/shared.js +7 -7
- package/dist/cli/update-cli/status.js +1 -1
- package/dist/cli/update-cli/update-command.js +14 -8
- package/dist/cli/update-cli/wizard.js +2 -2
- package/dist/cli/update-cli.js +21 -1027
- package/dist/commands/auth-choice.apply.anthropic.js +10 -2
- package/dist/commands/channels/add-mutators.js +3 -35
- package/dist/commands/channels/add.js +39 -51
- package/dist/commands/config-validation.js +1 -1
- package/dist/commands/configure.gateway-auth.js +52 -15
- package/dist/commands/configure.gateway.js +84 -40
- package/dist/commands/doctor-completion.js +3 -3
- package/dist/commands/doctor-config-flow.js +536 -16
- package/dist/commands/doctor-gateway-services.js +103 -79
- package/dist/commands/doctor-memory-search.js +9 -9
- package/dist/commands/doctor-platform-notes.js +57 -30
- package/dist/commands/doctor-prompter.js +26 -15
- package/dist/commands/doctor-session-locks.js +1 -1
- package/dist/commands/doctor.js +21 -9
- package/dist/commands/model-picker.js +120 -95
- package/dist/commands/models/set.js +2 -21
- package/dist/commands/models/shared.js +65 -37
- package/dist/commands/onboard-helpers.js +81 -39
- package/dist/commands/openai-codex-oauth.js +1 -1
- package/dist/commands/sessions.js +52 -53
- package/dist/commands/status.summary.js +52 -34
- package/dist/commands/test-wizard-helpers.js +2 -2
- package/dist/config/defaults.js +79 -42
- package/dist/config/group-policy.js +50 -18
- package/dist/config/includes.js +37 -10
- package/dist/config/schema.help.js +5 -4
- package/dist/config/schema.hints.js +2 -2
- package/dist/config/schema.labels.js +1 -0
- package/dist/config/sessions/group.js +12 -11
- package/dist/config/sessions/paths.js +137 -11
- package/dist/config/sessions/store.js +185 -65
- package/dist/config/sessions/types.js +15 -1
- package/dist/config/sessions.js +1 -0
- package/dist/config/telegram-custom-commands.js +3 -2
- package/dist/config/types.js +2 -0
- package/dist/config/zod-schema.agent-defaults.js +6 -27
- package/dist/config/zod-schema.agent-runtime.js +171 -79
- package/dist/config/zod-schema.providers-core.js +138 -65
- package/dist/config/zod-schema.session.js +49 -22
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/run.js +224 -57
- package/dist/cron/normalize.js +48 -45
- package/dist/cron/run-log.js +14 -0
- package/dist/cron/service/jobs.js +190 -28
- package/dist/cron/service/normalize.js +29 -11
- package/dist/cron/service/store.js +30 -44
- package/dist/cron/service/timer.js +182 -96
- package/dist/cron/service.js +3 -0
- package/dist/cron/stagger.js +37 -0
- package/dist/daemon/inspect.js +132 -92
- package/dist/daemon/runtime-paths.js +25 -4
- package/dist/daemon/service-audit.js +47 -16
- package/dist/discord/accounts.js +23 -20
- package/dist/discord/monitor/agent-components.js +1115 -219
- package/dist/discord/monitor/allow-list.js +114 -34
- package/dist/discord/monitor/listeners.js +204 -97
- package/dist/discord/monitor/message-handler.js +21 -10
- package/dist/discord/monitor/message-handler.preflight.js +195 -101
- package/dist/discord/monitor/message-handler.process.js +384 -123
- package/dist/discord/monitor/message-utils.js +86 -23
- package/dist/discord/monitor/native-command.js +77 -57
- package/dist/discord/monitor/provider.js +122 -117
- package/dist/discord/monitor/reply-context.js +20 -16
- package/dist/discord/monitor/reply-delivery.js +40 -8
- package/dist/discord/monitor/rest-fetch.js +22 -0
- package/dist/discord/monitor/threading.js +117 -24
- package/dist/discord/send.js +2 -1
- package/dist/discord/send.outbound.js +124 -11
- package/dist/discord/send.shared.js +112 -72
- package/dist/discord/voice-message.js +3 -3
- package/dist/gateway/auth.js +119 -44
- package/dist/gateway/call.js +76 -34
- package/dist/gateway/channel-health-monitor.js +57 -50
- package/dist/gateway/client.js +63 -29
- package/dist/gateway/control-ui-contract.js +1 -1
- package/dist/gateway/gateway-config-prompts.shared.js +2 -2
- package/dist/gateway/net.js +109 -1
- package/dist/gateway/protocol/index.js +5 -8
- package/dist/gateway/protocol/schema/agent.js +19 -1
- package/dist/gateway/protocol/schema/channels.js +21 -0
- package/dist/gateway/protocol/schema/cron.js +43 -30
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -11
- package/dist/gateway/protocol/schema/sessions.js +5 -1
- package/dist/gateway/protocol/schema.js +0 -1
- package/dist/gateway/server/presence-events.js +12 -0
- package/dist/gateway/server/ws-connection/message-handler.js +203 -212
- package/dist/gateway/server/ws-connection.js +58 -21
- package/dist/gateway/server-broadcast.js +18 -13
- package/dist/gateway/server-cron.js +177 -10
- package/dist/gateway/server-methods/agent-job.js +131 -38
- package/dist/gateway/server-methods/send.js +60 -14
- package/dist/gateway/server-methods/sessions.js +160 -96
- package/dist/gateway/server-methods/system.js +5 -7
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +24 -8
- package/dist/gateway/server-node-events.js +278 -68
- package/dist/gateway/session-utils.fs.js +316 -75
- package/dist/gateway/session-utils.js +224 -70
- package/dist/gateway/sessions-patch.js +63 -20
- package/dist/gateway/test-temp-config.js +1 -1
- package/dist/gateway/tools-invoke-http.js +118 -70
- package/dist/gateway/ws-log.js +135 -107
- package/dist/hooks/frontmatter.js +36 -82
- package/dist/hooks/install.js +149 -139
- package/dist/hooks/internal-hooks.js +29 -4
- package/dist/hooks/plugin-hooks.js +2 -1
- package/dist/imessage/monitor/deliver.js +10 -4
- package/dist/imessage/monitor/monitor-provider.js +138 -375
- package/dist/imessage/monitor/runtime.js +4 -8
- package/dist/imessage/send.js +65 -19
- package/dist/infra/exec-approvals-allowlist.js +7 -0
- package/dist/infra/exec-approvals.js +35 -920
- package/dist/infra/exec-safe-bin-trust.js +64 -0
- package/dist/infra/heartbeat-runner.js +207 -134
- package/dist/infra/heartbeat-wake.js +183 -22
- package/dist/infra/install-source-utils.js +47 -0
- package/dist/infra/net/ssrf.js +170 -36
- package/dist/infra/outbound/deliver.js +224 -58
- package/dist/infra/outbound/message-action-spec.js +12 -5
- package/dist/infra/outbound/outbound-session.js +27 -25
- package/dist/infra/poolbot-root.js +32 -22
- package/dist/infra/ports.js +14 -11
- package/dist/infra/skills-remote.js +48 -37
- package/dist/infra/system-events.js +25 -11
- package/dist/infra/system-presence.js +26 -33
- package/dist/infra/tmp-poolbot-dir.js +81 -2
- package/dist/infra/wsl.js +37 -1
- package/dist/line/bot-message-context.js +163 -191
- package/dist/logging/subsystem.js +59 -22
- package/dist/markdown/ir.js +124 -50
- package/dist/media/store.js +1 -1
- package/dist/media-understanding/runner.entries.js +42 -25
- package/dist/media-understanding/runner.js +53 -488
- package/dist/memory/embeddings-gemini.js +53 -38
- package/dist/memory/manager-embedding-ops.js +48 -69
- package/dist/pairing/pairing-store.js +178 -119
- package/dist/plugin-sdk/index.js +34 -6
- package/dist/plugins/hooks.js +135 -14
- package/dist/plugins/install.js +190 -152
- package/dist/polls.js +11 -0
- package/dist/routing/resolve-route.js +190 -56
- package/dist/routing/session-key.js +38 -22
- package/dist/runtime.js +35 -9
- package/dist/security/audit-channel.js +1 -1
- package/dist/sessions/session-key-utils.js +29 -11
- package/dist/shared/frontmatter.js +5 -5
- package/dist/shared/node-list-types.js +1 -0
- package/dist/shared/string-normalization.js +15 -0
- package/dist/signal/monitor/event-handler.js +68 -36
- package/dist/signal/send.js +29 -37
- package/dist/slack/monitor/allow-list.js +10 -11
- package/dist/slack/monitor/commands.js +14 -3
- package/dist/slack/monitor/events/interactions.js +4 -4
- package/dist/slack/monitor/media.js +224 -16
- package/dist/slack/monitor/message-handler/dispatch.js +247 -13
- package/dist/slack/monitor/message-handler/prepare.js +128 -45
- package/dist/slack/monitor/slash.js +357 -144
- package/dist/slack/streaming.js +77 -0
- package/dist/telegram/accounts.js +40 -13
- package/dist/telegram/allowed-updates.js +3 -0
- package/dist/telegram/bot/delivery.js +129 -66
- package/dist/telegram/bot/helpers.js +136 -122
- package/dist/telegram/bot-handlers.js +600 -339
- package/dist/telegram/bot-message-context.js +115 -73
- package/dist/telegram/bot-message-dispatch.js +235 -104
- package/dist/telegram/bot-native-command-menu.js +3 -1
- package/dist/telegram/bot-native-commands.js +213 -193
- package/dist/telegram/bot.js +24 -132
- package/dist/telegram/draft-stream.js +84 -75
- package/dist/telegram/format.js +150 -6
- package/dist/telegram/send.js +415 -255
- package/dist/telegram/targets.js +21 -2
- package/dist/telegram/update-offset-store.js +19 -3
- package/dist/terminal/restore.js +5 -2
- package/dist/test-utils/fetch-mock.js +5 -0
- package/dist/version.js +18 -5
- package/dist/web/auto-reply/monitor/broadcast.js +7 -3
- package/dist/web/auto-reply/monitor/on-message.js +6 -3
- package/dist/web/inbound/media.js +34 -8
- package/dist/web/inbound/monitor.js +34 -17
- package/dist/web/inbound/send-api.js +18 -17
- package/dist/web/outbound.js +12 -5
- package/dist/wizard/clack-prompter.js +40 -7
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
- package/skills/apple-reminders/SKILL.md +100 -49
- package/skills/coding-agent/SKILL.md +34 -28
- package/skills/github/SKILL.md +131 -16
- package/skills/imsg/SKILL.md +112 -15
- package/skills/openhue/SKILL.md +101 -19
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
|
@@ -2,34 +2,29 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import { resolveHumanDelayConfig } from "../../agents/identity.js";
|
|
3
3
|
import { resolveTextChunkLimit } from "../../auto-reply/chunk.js";
|
|
4
4
|
import { hasControlCommand } from "../../auto-reply/command-detection.js";
|
|
5
|
-
import { formatInboundEnvelope, formatInboundFromLabel, resolveEnvelopeFormatOptions, } from "../../auto-reply/envelope.js";
|
|
6
|
-
import { createInboundDebouncer, resolveInboundDebounceMs, } from "../../auto-reply/inbound-debounce.js";
|
|
7
5
|
import { dispatchInboundMessage } from "../../auto-reply/dispatch.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { buildMentionRegexes, matchesMentionPatterns } from "../../auto-reply/reply/mentions.js";
|
|
6
|
+
import { createInboundDebouncer, resolveInboundDebounceMs, } from "../../auto-reply/inbound-debounce.js";
|
|
7
|
+
import { clearHistoryEntriesIfEnabled, DEFAULT_GROUP_HISTORY_LIMIT, } from "../../auto-reply/reply/history.js";
|
|
11
8
|
import { createReplyDispatcher } from "../../auto-reply/reply/reply-dispatcher.js";
|
|
12
|
-
import {
|
|
13
|
-
import { createReplyPrefixContext } from "../../channels/reply-prefix.js";
|
|
9
|
+
import { createReplyPrefixOptions } from "../../channels/reply-prefix.js";
|
|
14
10
|
import { recordInboundSession } from "../../channels/session.js";
|
|
15
11
|
import { loadConfig } from "../../config/config.js";
|
|
16
|
-
import { resolveChannelGroupPolicy, resolveChannelGroupRequireMention, } from "../../config/group-policy.js";
|
|
17
12
|
import { readSessionUpdatedAt, resolveStorePath } from "../../config/sessions.js";
|
|
18
13
|
import { danger, logVerbose, shouldLogVerbose } from "../../globals.js";
|
|
19
14
|
import { waitForTransportReady } from "../../infra/transport-ready.js";
|
|
20
15
|
import { mediaKindFromMime } from "../../media/constants.js";
|
|
21
16
|
import { buildPairingReply } from "../../pairing/pairing-messages.js";
|
|
22
17
|
import { readChannelAllowFromStore, upsertChannelPairingRequest, } from "../../pairing/pairing-store.js";
|
|
23
|
-
import { resolveAgentRoute } from "../../routing/resolve-route.js";
|
|
24
18
|
import { truncateUtf16Safe } from "../../utils.js";
|
|
25
|
-
import { resolveControlCommandGate } from "../../channels/command-gating.js";
|
|
26
19
|
import { resolveIMessageAccount } from "../accounts.js";
|
|
27
20
|
import { createIMessageRpcClient } from "../client.js";
|
|
28
21
|
import { DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS } from "../constants.js";
|
|
29
22
|
import { probeIMessage } from "../probe.js";
|
|
30
23
|
import { sendMessageIMessage } from "../send.js";
|
|
31
|
-
import {
|
|
24
|
+
import { attachIMessageMonitorAbortHandler } from "./abort-handler.js";
|
|
32
25
|
import { deliverReplies } from "./deliver.js";
|
|
26
|
+
import { buildIMessageInboundContext, resolveIMessageInboundDecision, } from "./inbound-processing.js";
|
|
27
|
+
import { parseIMessageNotification } from "./parse-notification.js";
|
|
33
28
|
import { normalizeAllowList, resolveRuntime } from "./runtime.js";
|
|
34
29
|
/**
|
|
35
30
|
* Try to detect remote host from an SSH wrapper script like:
|
|
@@ -46,8 +41,9 @@ async function detectRemoteHostFromCliPath(cliPath) {
|
|
|
46
41
|
const content = await fs.readFile(expanded, "utf8");
|
|
47
42
|
// Match user@host pattern first (e.g., poolbot@192.168.64.3)
|
|
48
43
|
const userHostMatch = content.match(/\bssh\b[^\n]*?\s+([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+)/);
|
|
49
|
-
if (userHostMatch)
|
|
44
|
+
if (userHostMatch) {
|
|
50
45
|
return userHostMatch[1];
|
|
46
|
+
}
|
|
51
47
|
// Fallback: match host-only before imsg command (e.g., ssh -T mac-mini imsg)
|
|
52
48
|
const hostOnlyMatch = content.match(/\bssh\b[^\n]*?\s+([a-zA-Z][a-zA-Z0-9._-]*)\s+\S*\bimsg\b/);
|
|
53
49
|
return hostOnlyMatch?.[1];
|
|
@@ -56,40 +52,31 @@ async function detectRemoteHostFromCliPath(cliPath) {
|
|
|
56
52
|
return undefined;
|
|
57
53
|
}
|
|
58
54
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if (typeof value === "number")
|
|
65
|
-
return String(value);
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
function describeReplyContext(message) {
|
|
69
|
-
const body = normalizeReplyField(message.reply_to_text);
|
|
70
|
-
if (!body)
|
|
71
|
-
return null;
|
|
72
|
-
const id = normalizeReplyField(message.reply_to_id);
|
|
73
|
-
const sender = normalizeReplyField(message.reply_to_sender);
|
|
74
|
-
return { body, id, sender };
|
|
75
|
-
}
|
|
55
|
+
/**
|
|
56
|
+
* Cache for recently sent messages, used for echo detection.
|
|
57
|
+
* Keys are scoped by conversation (accountId:target) so the same text in different chats is not conflated.
|
|
58
|
+
* Entries expire after 5 seconds; we do not forget on match so multiple echo deliveries are all filtered.
|
|
59
|
+
*/
|
|
76
60
|
class SentMessageCache {
|
|
77
61
|
cache = new Map();
|
|
78
|
-
ttlMs = 5000;
|
|
62
|
+
ttlMs = 5000; // 5 seconds
|
|
79
63
|
remember(scope, text) {
|
|
80
|
-
if (!text?.trim())
|
|
64
|
+
if (!text?.trim()) {
|
|
81
65
|
return;
|
|
66
|
+
}
|
|
82
67
|
const key = `${scope}:${text.trim()}`;
|
|
83
68
|
this.cache.set(key, Date.now());
|
|
84
69
|
this.cleanup();
|
|
85
70
|
}
|
|
86
71
|
has(scope, text) {
|
|
87
|
-
if (!text?.trim())
|
|
72
|
+
if (!text?.trim()) {
|
|
88
73
|
return false;
|
|
74
|
+
}
|
|
89
75
|
const key = `${scope}:${text.trim()}`;
|
|
90
76
|
const timestamp = this.cache.get(key);
|
|
91
|
-
if (!timestamp)
|
|
77
|
+
if (!timestamp) {
|
|
92
78
|
return false;
|
|
79
|
+
}
|
|
93
80
|
const age = Date.now() - timestamp;
|
|
94
81
|
if (age > this.ttlMs) {
|
|
95
82
|
this.cache.delete(key);
|
|
@@ -100,8 +87,9 @@ class SentMessageCache {
|
|
|
100
87
|
cleanup() {
|
|
101
88
|
const now = Date.now();
|
|
102
89
|
for (const [text, timestamp] of this.cache.entries()) {
|
|
103
|
-
if (now - timestamp > this.ttlMs)
|
|
90
|
+
if (now - timestamp > this.ttlMs) {
|
|
104
91
|
this.cache.delete(text);
|
|
92
|
+
}
|
|
105
93
|
}
|
|
106
94
|
}
|
|
107
95
|
}
|
|
@@ -143,18 +131,10 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
143
131
|
const inboundDebouncer = createInboundDebouncer({
|
|
144
132
|
debounceMs: inboundDebounceMs,
|
|
145
133
|
buildKey: (entry) => {
|
|
146
|
-
// Use message-specific key to preserve attachments
|
|
147
|
-
// This prevents different messages from being merged incorrectly
|
|
148
134
|
const sender = entry.message.sender?.trim();
|
|
149
|
-
if (!sender)
|
|
135
|
+
if (!sender) {
|
|
150
136
|
return null;
|
|
151
|
-
// Prefer message id for unique identification (prevents cross-message contamination)
|
|
152
|
-
// If id is not available, fall back to conversation-based key
|
|
153
|
-
const messageId = entry.message.id;
|
|
154
|
-
if (messageId != null) {
|
|
155
|
-
return `imessage:${accountInfo.accountId}:msg:${messageId}`;
|
|
156
137
|
}
|
|
157
|
-
// Fallback to conversation-based key only when id is unavailable
|
|
158
138
|
const conversationId = entry.message.chat_id != null
|
|
159
139
|
? `chat:${entry.message.chat_id}`
|
|
160
140
|
: (entry.message.chat_guid ?? entry.message.chat_identifier ?? "unknown");
|
|
@@ -162,18 +142,19 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
162
142
|
},
|
|
163
143
|
shouldDebounce: (entry) => {
|
|
164
144
|
const text = entry.message.text?.trim() ?? "";
|
|
165
|
-
if (!text)
|
|
145
|
+
if (!text) {
|
|
166
146
|
return false;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (entry.message.attachments && entry.message.attachments.length > 0)
|
|
147
|
+
}
|
|
148
|
+
if (entry.message.attachments && entry.message.attachments.length > 0) {
|
|
170
149
|
return false;
|
|
150
|
+
}
|
|
171
151
|
return !hasControlCommand(text, cfg);
|
|
172
152
|
},
|
|
173
153
|
onFlush: async (entries) => {
|
|
174
154
|
const last = entries.at(-1);
|
|
175
|
-
if (!last)
|
|
155
|
+
if (!last) {
|
|
176
156
|
return;
|
|
157
|
+
}
|
|
177
158
|
if (entries.length === 1) {
|
|
178
159
|
await handleMessageNow(last.message);
|
|
179
160
|
return;
|
|
@@ -185,9 +166,7 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
185
166
|
const syntheticMessage = {
|
|
186
167
|
...last.message,
|
|
187
168
|
text: combinedText,
|
|
188
|
-
|
|
189
|
-
// (should always be null here due to shouldDebounce guard, but keep for safety)
|
|
190
|
-
attachments: last.message.attachments ?? null,
|
|
169
|
+
attachments: null,
|
|
191
170
|
};
|
|
192
171
|
await handleMessageNow(syntheticMessage);
|
|
193
172
|
},
|
|
@@ -196,140 +175,7 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
196
175
|
},
|
|
197
176
|
});
|
|
198
177
|
async function handleMessageNow(message) {
|
|
199
|
-
const senderRaw = message.sender ?? "";
|
|
200
|
-
const sender = senderRaw.trim();
|
|
201
|
-
if (!sender)
|
|
202
|
-
return;
|
|
203
|
-
const senderNormalized = normalizeIMessageHandle(sender);
|
|
204
|
-
if (message.is_from_me)
|
|
205
|
-
return;
|
|
206
|
-
const chatId = message.chat_id ?? undefined;
|
|
207
|
-
const chatGuid = message.chat_guid ?? undefined;
|
|
208
|
-
const chatIdentifier = message.chat_identifier ?? undefined;
|
|
209
|
-
const groupIdCandidate = chatId !== undefined ? String(chatId) : undefined;
|
|
210
|
-
const groupListPolicy = groupIdCandidate
|
|
211
|
-
? resolveChannelGroupPolicy({
|
|
212
|
-
cfg,
|
|
213
|
-
channel: "imessage",
|
|
214
|
-
accountId: accountInfo.accountId,
|
|
215
|
-
groupId: groupIdCandidate,
|
|
216
|
-
})
|
|
217
|
-
: {
|
|
218
|
-
allowlistEnabled: false,
|
|
219
|
-
allowed: true,
|
|
220
|
-
groupConfig: undefined,
|
|
221
|
-
defaultConfig: undefined,
|
|
222
|
-
};
|
|
223
|
-
// Some iMessage threads can have multiple participants but still report
|
|
224
|
-
// is_group=false depending on how Messages stores the identifier.
|
|
225
|
-
// If the owner explicitly configures a chat_id under imessage.groups, treat
|
|
226
|
-
// that thread as a "group" for permission gating and session isolation.
|
|
227
|
-
const treatAsGroupByConfig = Boolean(groupIdCandidate && groupListPolicy.allowlistEnabled && groupListPolicy.groupConfig);
|
|
228
|
-
const isGroup = Boolean(message.is_group) || treatAsGroupByConfig;
|
|
229
|
-
if (isGroup && !chatId)
|
|
230
|
-
return;
|
|
231
|
-
const groupId = isGroup ? groupIdCandidate : undefined;
|
|
232
|
-
const storeAllowFrom = await readChannelAllowFromStore("imessage").catch(() => []);
|
|
233
|
-
const effectiveDmAllowFrom = Array.from(new Set([...allowFrom, ...storeAllowFrom]))
|
|
234
|
-
.map((v) => String(v).trim())
|
|
235
|
-
.filter(Boolean);
|
|
236
|
-
const effectiveGroupAllowFrom = Array.from(new Set([...groupAllowFrom, ...storeAllowFrom]))
|
|
237
|
-
.map((v) => String(v).trim())
|
|
238
|
-
.filter(Boolean);
|
|
239
|
-
if (isGroup) {
|
|
240
|
-
if (groupPolicy === "disabled") {
|
|
241
|
-
logVerbose("Blocked iMessage group message (groupPolicy: disabled)");
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (groupPolicy === "allowlist") {
|
|
245
|
-
if (effectiveGroupAllowFrom.length === 0) {
|
|
246
|
-
logVerbose("Blocked iMessage group message (groupPolicy: allowlist, no groupAllowFrom)");
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const allowed = isAllowedIMessageSender({
|
|
250
|
-
allowFrom: effectiveGroupAllowFrom,
|
|
251
|
-
sender,
|
|
252
|
-
chatId: chatId ?? undefined,
|
|
253
|
-
chatGuid,
|
|
254
|
-
chatIdentifier,
|
|
255
|
-
});
|
|
256
|
-
if (!allowed) {
|
|
257
|
-
logVerbose(`Blocked iMessage sender ${sender} (not in groupAllowFrom)`);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
if (groupListPolicy.allowlistEnabled && !groupListPolicy.allowed) {
|
|
262
|
-
logVerbose(`imessage: skipping group message (${groupId ?? "unknown"}) not in allowlist`);
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
const dmHasWildcard = effectiveDmAllowFrom.includes("*");
|
|
267
|
-
const dmAuthorized = dmPolicy === "open"
|
|
268
|
-
? true
|
|
269
|
-
: dmHasWildcard ||
|
|
270
|
-
(effectiveDmAllowFrom.length > 0 &&
|
|
271
|
-
isAllowedIMessageSender({
|
|
272
|
-
allowFrom: effectiveDmAllowFrom,
|
|
273
|
-
sender,
|
|
274
|
-
chatId: chatId ?? undefined,
|
|
275
|
-
chatGuid,
|
|
276
|
-
chatIdentifier,
|
|
277
|
-
}));
|
|
278
|
-
if (!isGroup) {
|
|
279
|
-
if (dmPolicy === "disabled")
|
|
280
|
-
return;
|
|
281
|
-
if (!dmAuthorized) {
|
|
282
|
-
if (dmPolicy === "pairing") {
|
|
283
|
-
const senderId = normalizeIMessageHandle(sender);
|
|
284
|
-
const { code, created } = await upsertChannelPairingRequest({
|
|
285
|
-
channel: "imessage",
|
|
286
|
-
id: senderId,
|
|
287
|
-
meta: {
|
|
288
|
-
sender: senderId,
|
|
289
|
-
chatId: chatId ? String(chatId) : undefined,
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
if (created) {
|
|
293
|
-
logVerbose(`imessage pairing request sender=${senderId}`);
|
|
294
|
-
try {
|
|
295
|
-
await sendMessageIMessage(sender, buildPairingReply({
|
|
296
|
-
channel: "imessage",
|
|
297
|
-
idLine: `Your iMessage sender id: ${senderId}`,
|
|
298
|
-
code,
|
|
299
|
-
}), {
|
|
300
|
-
client,
|
|
301
|
-
maxBytes: mediaMaxBytes,
|
|
302
|
-
accountId: accountInfo.accountId,
|
|
303
|
-
...(chatId ? { chatId } : {}),
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
catch (err) {
|
|
307
|
-
logVerbose(`imessage pairing reply failed for ${senderId}: ${String(err)}`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
logVerbose(`Blocked iMessage sender ${sender} (dmPolicy=${dmPolicy})`);
|
|
313
|
-
}
|
|
314
|
-
return;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
const route = resolveAgentRoute({
|
|
318
|
-
cfg,
|
|
319
|
-
channel: "imessage",
|
|
320
|
-
accountId: accountInfo.accountId,
|
|
321
|
-
peer: {
|
|
322
|
-
kind: isGroup ? "group" : "dm",
|
|
323
|
-
id: isGroup ? String(chatId ?? "unknown") : normalizeIMessageHandle(sender),
|
|
324
|
-
},
|
|
325
|
-
});
|
|
326
|
-
const mentionRegexes = buildMentionRegexes(cfg, route.agentId);
|
|
327
178
|
const messageText = (message.text ?? "").trim();
|
|
328
|
-
const echoScope = `${accountInfo.accountId}:${isGroup ? formatIMessageChatTarget(chatId) : `imessage:${sender}`}`;
|
|
329
|
-
if (messageText && sentMessageCache.has(echoScope, messageText)) {
|
|
330
|
-
logVerbose(`imessage: skipping echo message (matches recently sent text within 5s): "${truncateUtf16Safe(messageText, 50)}"`);
|
|
331
|
-
return;
|
|
332
|
-
}
|
|
333
179
|
const attachments = includeAttachments ? (message.attachments ?? []) : [];
|
|
334
180
|
// Filter to valid attachments with paths
|
|
335
181
|
const validAttachments = attachments.filter((entry) => entry?.original_path && !entry?.missing);
|
|
@@ -342,184 +188,94 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
342
188
|
const kind = mediaKindFromMime(mediaType ?? undefined);
|
|
343
189
|
const placeholder = kind ? `<media:${kind}>` : attachments?.length ? "<media:attachment>" : "";
|
|
344
190
|
const bodyText = messageText || placeholder;
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const replyContext = describeReplyContext(message);
|
|
348
|
-
const createdAt = message.created_at ? Date.parse(message.created_at) : undefined;
|
|
349
|
-
const historyKey = isGroup
|
|
350
|
-
? String(chatId ?? chatGuid ?? chatIdentifier ?? "unknown")
|
|
351
|
-
: undefined;
|
|
352
|
-
const mentioned = isGroup ? matchesMentionPatterns(messageText, mentionRegexes) : true;
|
|
353
|
-
const requireMention = resolveChannelGroupRequireMention({
|
|
191
|
+
const storeAllowFrom = await readChannelAllowFromStore("imessage").catch(() => []);
|
|
192
|
+
const decision = resolveIMessageInboundDecision({
|
|
354
193
|
cfg,
|
|
355
|
-
channel: "imessage",
|
|
356
194
|
accountId: accountInfo.accountId,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
195
|
+
message,
|
|
196
|
+
opts,
|
|
197
|
+
messageText,
|
|
198
|
+
bodyText,
|
|
199
|
+
allowFrom,
|
|
200
|
+
groupAllowFrom,
|
|
201
|
+
groupPolicy,
|
|
202
|
+
dmPolicy,
|
|
203
|
+
storeAllowFrom,
|
|
204
|
+
historyLimit,
|
|
205
|
+
groupHistories,
|
|
206
|
+
echoCache: sentMessageCache,
|
|
207
|
+
logVerbose,
|
|
360
208
|
});
|
|
361
|
-
|
|
362
|
-
const useAccessGroups = cfg.commands?.useAccessGroups !== false;
|
|
363
|
-
const ownerAllowedForCommands = effectiveDmAllowFrom.length > 0
|
|
364
|
-
? isAllowedIMessageSender({
|
|
365
|
-
allowFrom: effectiveDmAllowFrom,
|
|
366
|
-
sender,
|
|
367
|
-
chatId: chatId ?? undefined,
|
|
368
|
-
chatGuid,
|
|
369
|
-
chatIdentifier,
|
|
370
|
-
})
|
|
371
|
-
: false;
|
|
372
|
-
const groupAllowedForCommands = effectiveGroupAllowFrom.length > 0
|
|
373
|
-
? isAllowedIMessageSender({
|
|
374
|
-
allowFrom: effectiveGroupAllowFrom,
|
|
375
|
-
sender,
|
|
376
|
-
chatId: chatId ?? undefined,
|
|
377
|
-
chatGuid,
|
|
378
|
-
chatIdentifier,
|
|
379
|
-
})
|
|
380
|
-
: false;
|
|
381
|
-
const hasControlCommandInMessage = hasControlCommand(messageText, cfg);
|
|
382
|
-
const commandGate = resolveControlCommandGate({
|
|
383
|
-
useAccessGroups,
|
|
384
|
-
authorizers: [
|
|
385
|
-
{ configured: effectiveDmAllowFrom.length > 0, allowed: ownerAllowedForCommands },
|
|
386
|
-
{ configured: effectiveGroupAllowFrom.length > 0, allowed: groupAllowedForCommands },
|
|
387
|
-
],
|
|
388
|
-
allowTextCommands: true,
|
|
389
|
-
hasControlCommand: hasControlCommandInMessage,
|
|
390
|
-
});
|
|
391
|
-
const commandAuthorized = isGroup ? commandGate.commandAuthorized : dmAuthorized;
|
|
392
|
-
if (isGroup && commandGate.shouldBlock) {
|
|
393
|
-
logInboundDrop({
|
|
394
|
-
log: logVerbose,
|
|
395
|
-
channel: "imessage",
|
|
396
|
-
reason: "control command (unauthorized)",
|
|
397
|
-
target: sender,
|
|
398
|
-
});
|
|
209
|
+
if (decision.kind === "drop") {
|
|
399
210
|
return;
|
|
400
211
|
}
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
messageId: message.id ? String(message.id) : undefined,
|
|
415
|
-
}
|
|
416
|
-
: null,
|
|
212
|
+
const chatId = message.chat_id ?? undefined;
|
|
213
|
+
if (decision.kind === "pairing") {
|
|
214
|
+
const sender = (message.sender ?? "").trim();
|
|
215
|
+
if (!sender) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const { code, created } = await upsertChannelPairingRequest({
|
|
219
|
+
channel: "imessage",
|
|
220
|
+
id: decision.senderId,
|
|
221
|
+
meta: {
|
|
222
|
+
sender: decision.senderId,
|
|
223
|
+
chatId: chatId ? String(chatId) : undefined,
|
|
224
|
+
},
|
|
417
225
|
});
|
|
226
|
+
if (created) {
|
|
227
|
+
logVerbose(`imessage pairing request sender=${decision.senderId}`);
|
|
228
|
+
try {
|
|
229
|
+
await sendMessageIMessage(sender, buildPairingReply({
|
|
230
|
+
channel: "imessage",
|
|
231
|
+
idLine: `Your iMessage sender id: ${decision.senderId}`,
|
|
232
|
+
code,
|
|
233
|
+
}), {
|
|
234
|
+
client,
|
|
235
|
+
maxBytes: mediaMaxBytes,
|
|
236
|
+
accountId: accountInfo.accountId,
|
|
237
|
+
...(chatId ? { chatId } : {}),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
logVerbose(`imessage pairing reply failed for ${decision.senderId}: ${String(err)}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
418
244
|
return;
|
|
419
245
|
}
|
|
420
|
-
const chatTarget = formatIMessageChatTarget(chatId);
|
|
421
|
-
const fromLabel = formatInboundFromLabel({
|
|
422
|
-
isGroup,
|
|
423
|
-
groupLabel: message.chat_name ?? undefined,
|
|
424
|
-
groupId: chatId !== undefined ? String(chatId) : "unknown",
|
|
425
|
-
groupFallback: "Group",
|
|
426
|
-
directLabel: senderNormalized,
|
|
427
|
-
directId: sender,
|
|
428
|
-
});
|
|
429
246
|
const storePath = resolveStorePath(cfg.session?.store, {
|
|
430
|
-
agentId: route.agentId,
|
|
247
|
+
agentId: decision.route.agentId,
|
|
431
248
|
});
|
|
432
|
-
const envelopeOptions = resolveEnvelopeFormatOptions(cfg);
|
|
433
249
|
const previousTimestamp = readSessionUpdatedAt({
|
|
434
250
|
storePath,
|
|
435
|
-
sessionKey: route.sessionKey,
|
|
251
|
+
sessionKey: decision.route.sessionKey,
|
|
436
252
|
});
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
channel: "iMessage",
|
|
442
|
-
from: fromLabel,
|
|
443
|
-
timestamp: createdAt,
|
|
444
|
-
body: `${bodyText}${replySuffix}`,
|
|
445
|
-
chatType: isGroup ? "group" : "direct",
|
|
446
|
-
sender: { name: senderNormalized, id: sender },
|
|
253
|
+
const { ctxPayload, chatTarget } = buildIMessageInboundContext({
|
|
254
|
+
cfg,
|
|
255
|
+
decision,
|
|
256
|
+
message,
|
|
447
257
|
previousTimestamp,
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
formatEntry: (entry) => formatInboundEnvelope({
|
|
458
|
-
channel: "iMessage",
|
|
459
|
-
from: fromLabel,
|
|
460
|
-
timestamp: entry.timestamp,
|
|
461
|
-
body: `${entry.body}${entry.messageId ? ` [id:${entry.messageId}]` : ""}`,
|
|
462
|
-
chatType: "group",
|
|
463
|
-
senderLabel: entry.sender,
|
|
464
|
-
envelope: envelopeOptions,
|
|
465
|
-
}),
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
const imessageTo = (isGroup ? chatTarget : undefined) || `imessage:${sender}`;
|
|
469
|
-
const inboundHistory = isGroup && historyKey && historyLimit > 0
|
|
470
|
-
? (groupHistories.get(historyKey) ?? []).map((entry) => ({
|
|
471
|
-
sender: entry.sender,
|
|
472
|
-
body: entry.body,
|
|
473
|
-
timestamp: entry.timestamp,
|
|
474
|
-
}))
|
|
475
|
-
: undefined;
|
|
476
|
-
const ctxPayload = finalizeInboundContext({
|
|
477
|
-
Body: combinedBody,
|
|
478
|
-
BodyForAgent: bodyText,
|
|
479
|
-
InboundHistory: inboundHistory,
|
|
480
|
-
RawBody: bodyText,
|
|
481
|
-
CommandBody: bodyText,
|
|
482
|
-
From: isGroup ? `imessage:group:${chatId ?? "unknown"}` : `imessage:${sender}`,
|
|
483
|
-
To: imessageTo,
|
|
484
|
-
SessionKey: route.sessionKey,
|
|
485
|
-
AccountId: route.accountId,
|
|
486
|
-
ChatType: isGroup ? "group" : "direct",
|
|
487
|
-
ConversationLabel: fromLabel,
|
|
488
|
-
GroupSubject: isGroup ? (message.chat_name ?? undefined) : undefined,
|
|
489
|
-
GroupMembers: isGroup ? (message.participants ?? []).filter(Boolean).join(", ") : undefined,
|
|
490
|
-
SenderName: senderNormalized,
|
|
491
|
-
SenderId: sender,
|
|
492
|
-
Provider: "imessage",
|
|
493
|
-
Surface: "imessage",
|
|
494
|
-
MessageSid: message.id ? String(message.id) : undefined,
|
|
495
|
-
ReplyToId: replyContext?.id,
|
|
496
|
-
ReplyToBody: replyContext?.body,
|
|
497
|
-
ReplyToSender: replyContext?.sender,
|
|
498
|
-
Timestamp: createdAt,
|
|
499
|
-
MediaPath: mediaPath,
|
|
500
|
-
MediaType: mediaType,
|
|
501
|
-
MediaUrl: mediaPath,
|
|
502
|
-
MediaPaths: mediaPaths.length > 0 ? mediaPaths : undefined,
|
|
503
|
-
MediaTypes: mediaTypes.length > 0 ? mediaTypes : undefined,
|
|
504
|
-
MediaUrls: mediaPaths.length > 0 ? mediaPaths : undefined,
|
|
505
|
-
MediaRemoteHost: remoteHost,
|
|
506
|
-
WasMentioned: effectiveWasMentioned,
|
|
507
|
-
CommandAuthorized: commandAuthorized,
|
|
508
|
-
// Originating channel for reply routing.
|
|
509
|
-
OriginatingChannel: "imessage",
|
|
510
|
-
OriginatingTo: imessageTo,
|
|
258
|
+
remoteHost,
|
|
259
|
+
historyLimit,
|
|
260
|
+
groupHistories,
|
|
261
|
+
media: {
|
|
262
|
+
path: mediaPath,
|
|
263
|
+
type: mediaType,
|
|
264
|
+
paths: mediaPaths,
|
|
265
|
+
types: mediaTypes,
|
|
266
|
+
},
|
|
511
267
|
});
|
|
512
|
-
const updateTarget =
|
|
268
|
+
const updateTarget = chatTarget || decision.sender;
|
|
513
269
|
await recordInboundSession({
|
|
514
270
|
storePath,
|
|
515
|
-
sessionKey: ctxPayload.SessionKey ?? route.sessionKey,
|
|
271
|
+
sessionKey: ctxPayload.SessionKey ?? decision.route.sessionKey,
|
|
516
272
|
ctx: ctxPayload,
|
|
517
|
-
updateLastRoute: !isGroup && updateTarget
|
|
273
|
+
updateLastRoute: !decision.isGroup && updateTarget
|
|
518
274
|
? {
|
|
519
|
-
sessionKey: route.mainSessionKey,
|
|
275
|
+
sessionKey: decision.route.mainSessionKey,
|
|
520
276
|
channel: "imessage",
|
|
521
277
|
to: updateTarget,
|
|
522
|
-
accountId: route.accountId,
|
|
278
|
+
accountId: decision.route.accountId,
|
|
523
279
|
}
|
|
524
280
|
: undefined,
|
|
525
281
|
onRecordError: (err) => {
|
|
@@ -527,18 +283,27 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
527
283
|
},
|
|
528
284
|
});
|
|
529
285
|
if (shouldLogVerbose()) {
|
|
530
|
-
const preview = truncateUtf16Safe(
|
|
531
|
-
logVerbose(`imessage inbound: chatId=${chatId ?? "unknown"} from=${ctxPayload.From} len=${
|
|
286
|
+
const preview = truncateUtf16Safe(String(ctxPayload.Body ?? ""), 200).replace(/\n/g, "\\n");
|
|
287
|
+
logVerbose(`imessage inbound: chatId=${chatId ?? "unknown"} from=${ctxPayload.From} len=${String(ctxPayload.Body ?? "").length} preview="${preview}"`);
|
|
532
288
|
}
|
|
533
|
-
const
|
|
289
|
+
const { onModelSelected, ...prefixOptions } = createReplyPrefixOptions({
|
|
290
|
+
cfg,
|
|
291
|
+
agentId: decision.route.agentId,
|
|
292
|
+
channel: "imessage",
|
|
293
|
+
accountId: decision.route.accountId,
|
|
294
|
+
});
|
|
534
295
|
const dispatcher = createReplyDispatcher({
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
humanDelay: resolveHumanDelayConfig(cfg, route.agentId),
|
|
296
|
+
...prefixOptions,
|
|
297
|
+
humanDelay: resolveHumanDelayConfig(cfg, decision.route.agentId),
|
|
538
298
|
deliver: async (payload) => {
|
|
299
|
+
const target = ctxPayload.To;
|
|
300
|
+
if (!target) {
|
|
301
|
+
runtime.error?.(danger("imessage: missing delivery target"));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
539
304
|
await deliverReplies({
|
|
540
305
|
replies: [payload],
|
|
541
|
-
target
|
|
306
|
+
target,
|
|
542
307
|
client,
|
|
543
308
|
accountId: accountInfo.accountId,
|
|
544
309
|
runtime,
|
|
@@ -559,28 +324,33 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
559
324
|
disableBlockStreaming: typeof accountInfo.config.blockStreaming === "boolean"
|
|
560
325
|
? !accountInfo.config.blockStreaming
|
|
561
326
|
: undefined,
|
|
562
|
-
onModelSelected
|
|
327
|
+
onModelSelected,
|
|
563
328
|
},
|
|
564
329
|
});
|
|
565
330
|
if (!queuedFinal) {
|
|
566
|
-
if (isGroup && historyKey) {
|
|
331
|
+
if (decision.isGroup && decision.historyKey) {
|
|
567
332
|
clearHistoryEntriesIfEnabled({
|
|
568
333
|
historyMap: groupHistories,
|
|
569
|
-
historyKey,
|
|
334
|
+
historyKey: decision.historyKey,
|
|
570
335
|
limit: historyLimit,
|
|
571
336
|
});
|
|
572
337
|
}
|
|
573
338
|
return;
|
|
574
339
|
}
|
|
575
|
-
if (isGroup && historyKey) {
|
|
576
|
-
clearHistoryEntriesIfEnabled({
|
|
340
|
+
if (decision.isGroup && decision.historyKey) {
|
|
341
|
+
clearHistoryEntriesIfEnabled({
|
|
342
|
+
historyMap: groupHistories,
|
|
343
|
+
historyKey: decision.historyKey,
|
|
344
|
+
limit: historyLimit,
|
|
345
|
+
});
|
|
577
346
|
}
|
|
578
347
|
}
|
|
579
348
|
const handleMessage = async (raw) => {
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
349
|
+
const message = parseIMessageNotification(raw);
|
|
350
|
+
if (!message) {
|
|
351
|
+
logVerbose("imessage: dropping malformed RPC message payload");
|
|
583
352
|
return;
|
|
353
|
+
}
|
|
584
354
|
await inboundDebouncer.enqueue({ message });
|
|
585
355
|
};
|
|
586
356
|
await waitForTransportReady({
|
|
@@ -593,16 +363,18 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
593
363
|
runtime,
|
|
594
364
|
check: async () => {
|
|
595
365
|
const probe = await probeIMessage(probeTimeoutMs, { cliPath, dbPath, runtime });
|
|
596
|
-
if (probe.ok)
|
|
366
|
+
if (probe.ok) {
|
|
597
367
|
return { ok: true };
|
|
368
|
+
}
|
|
598
369
|
if (probe.fatal) {
|
|
599
370
|
throw new Error(probe.error ?? "imsg rpc unavailable");
|
|
600
371
|
}
|
|
601
372
|
return { ok: false, error: probe.error ?? "unreachable" };
|
|
602
373
|
},
|
|
603
374
|
});
|
|
604
|
-
if (opts.abortSignal?.aborted)
|
|
375
|
+
if (opts.abortSignal?.aborted) {
|
|
605
376
|
return;
|
|
377
|
+
}
|
|
606
378
|
const client = await createIMessageRpcClient({
|
|
607
379
|
cliPath,
|
|
608
380
|
dbPath,
|
|
@@ -620,21 +392,11 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
620
392
|
});
|
|
621
393
|
let subscriptionId = null;
|
|
622
394
|
const abort = opts.abortSignal;
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
})
|
|
629
|
-
.catch(() => {
|
|
630
|
-
// Ignore disconnect errors during shutdown.
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
void client.stop().catch(() => {
|
|
634
|
-
// Ignore disconnect errors during shutdown.
|
|
635
|
-
});
|
|
636
|
-
};
|
|
637
|
-
abort?.addEventListener("abort", onAbort, { once: true });
|
|
395
|
+
const detachAbortHandler = attachIMessageMonitorAbortHandler({
|
|
396
|
+
abortSignal: abort,
|
|
397
|
+
client,
|
|
398
|
+
getSubscriptionId: () => subscriptionId,
|
|
399
|
+
});
|
|
638
400
|
try {
|
|
639
401
|
const result = await client.request("watch.subscribe", {
|
|
640
402
|
attachments: includeAttachments,
|
|
@@ -643,13 +405,14 @@ export async function monitorIMessageProvider(opts = {}) {
|
|
|
643
405
|
await client.waitForClose();
|
|
644
406
|
}
|
|
645
407
|
catch (err) {
|
|
646
|
-
if (abort?.aborted)
|
|
408
|
+
if (abort?.aborted) {
|
|
647
409
|
return;
|
|
410
|
+
}
|
|
648
411
|
runtime.error?.(danger(`imessage: monitor failed: ${String(err)}`));
|
|
649
412
|
throw err;
|
|
650
413
|
}
|
|
651
414
|
finally {
|
|
652
|
-
|
|
415
|
+
detachAbortHandler();
|
|
653
416
|
await client.stop();
|
|
654
417
|
}
|
|
655
418
|
}
|