@poolzin/pool-bot 2026.2.20 → 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 +25 -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-auth.js +12 -0
- package/dist/agents/model-catalog.js +40 -9
- package/dist/agents/model-fallback.js +24 -0
- 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 -80
- 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/provider/config-loader.js +76 -0
- package/dist/agents/provider/index.js +15 -0
- package/dist/agents/provider/integration.js +136 -0
- package/dist/agents/provider/models-dev.js +129 -0
- package/dist/agents/provider/rate-limits.js +458 -0
- package/dist/agents/provider/request-monitor.js +449 -0
- package/dist/agents/provider/session-binding.js +376 -0
- package/dist/agents/provider/token-pool.js +541 -0
- 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/plcode-controller/SKILL.md +156 -0
- package/skills/plcode-controller/assets/operator-prompts.md +65 -0
- package/skills/plcode-controller/references/command-cheatsheet.md +53 -0
- package/skills/plcode-controller/references/failure-handling.md +60 -0
- package/skills/plcode-controller/references/model-selection.md +57 -0
- package/skills/plcode-controller/references/plan-vs-build.md +52 -0
- package/skills/plcode-controller/references/question-handling.md +40 -0
- package/skills/plcode-controller/references/session-management.md +63 -0
- package/skills/plcode-controller/references/workflow.md +35 -0
- package/skills/tmux/SKILL.md +111 -79
- package/skills/weather/SKILL.md +88 -25
|
@@ -2,32 +2,39 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import { createAgentSession, estimateTokens, SessionManager, SettingsManager, } from "@mariozechner/pi-coding-agent";
|
|
4
4
|
import { resolveHeartbeatPrompt } from "../../auto-reply/heartbeat.js";
|
|
5
|
-
import { listChannelSupportedActions, resolveChannelMessageToolHints } from "../channel-tools.js";
|
|
6
5
|
import { resolveChannelCapabilities } from "../../config/channel-capabilities.js";
|
|
7
6
|
import { getMachineDisplayName } from "../../infra/machine-name.js";
|
|
7
|
+
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
|
8
|
+
import { enqueueCommandInLane } from "../../process/command-queue.js";
|
|
9
|
+
import { isCronSessionKey, isSubagentSessionKey } from "../../routing/session-key.js";
|
|
10
|
+
import { resolveSignalReactionLevel } from "../../signal/reaction-level.js";
|
|
8
11
|
import { resolveTelegramInlineButtonsScope } from "../../telegram/inline-buttons.js";
|
|
9
12
|
import { resolveTelegramReactionLevel } from "../../telegram/reaction-level.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
13
|
+
import { buildTtsSystemPromptHint } from "../../tts/tts.js";
|
|
14
|
+
import { resolveUserPath } from "../../utils.js";
|
|
12
15
|
import { normalizeMessageChannel } from "../../utils/message-channel.js";
|
|
13
|
-
import { isSubagentSessionKey } from "../../routing/session-key.js";
|
|
14
16
|
import { isReasoningTagProvider } from "../../utils/provider-utils.js";
|
|
15
|
-
import { resolveUserPath } from "../../utils.js";
|
|
16
17
|
import { resolvePoolbotAgentDir } from "../agent-paths.js";
|
|
17
18
|
import { resolveSessionAgentIds } from "../agent-scope.js";
|
|
18
19
|
import { makeBootstrapWarn, resolveBootstrapContextForRun } from "../bootstrap-files.js";
|
|
19
|
-
import {
|
|
20
|
+
import { listChannelSupportedActions, resolveChannelMessageToolHints } from "../channel-tools.js";
|
|
21
|
+
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
|
|
20
22
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../defaults.js";
|
|
23
|
+
import { resolvePoolbotDocsPath } from "../docs-path.js";
|
|
21
24
|
import { getApiKeyForModel, resolveModelAuthMode } from "../model-auth.js";
|
|
22
25
|
import { ensurePoolbotModelsJson } from "../models-config.js";
|
|
23
26
|
import { ensureSessionHeader, validateAnthropicTurns, validateGeminiTurns, } from "../pi-embedded-helpers.js";
|
|
24
27
|
import { ensurePiCompactionReserveTokens, resolveCompactionReserveTokensFloor, } from "../pi-settings.js";
|
|
25
28
|
import { createPoolbotCodingTools } from "../pi-tools.js";
|
|
26
29
|
import { resolveSandboxContext } from "../sandbox.js";
|
|
30
|
+
import { repairSessionFileIfNeeded } from "../session-file-repair.js";
|
|
27
31
|
import { guardSessionManager } from "../session-tool-result-guard-wrapper.js";
|
|
28
|
-
import {
|
|
29
|
-
import { acquireSessionWriteLock } from "../session-write-lock.js";
|
|
32
|
+
import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js";
|
|
33
|
+
import { acquireSessionWriteLock, resolveSessionLockMaxHoldFromTimeout, } from "../session-write-lock.js";
|
|
34
|
+
import { detectRuntimeShell } from "../shell-utils.js";
|
|
30
35
|
import { applySkillEnvOverrides, applySkillEnvOverridesFromSnapshot, loadWorkspaceSkillEntries, resolveSkillsPromptForRun, } from "../skills.js";
|
|
36
|
+
import { resolveTranscriptPolicy } from "../transcript-policy.js";
|
|
37
|
+
import { compactWithSafetyTimeout, EMBEDDED_COMPACTION_TIMEOUT_MS, } from "./compaction-safety-timeout.js";
|
|
31
38
|
import { buildEmbeddedExtensionPaths } from "./extensions.js";
|
|
32
39
|
import { logToolSchemasForGoogle, sanitizeSessionHistory, sanitizeToolsForGoogle, } from "./google.js";
|
|
33
40
|
import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js";
|
|
@@ -38,27 +45,137 @@ import { buildEmbeddedSandboxInfo } from "./sandbox-info.js";
|
|
|
38
45
|
import { prewarmSessionFile, trackSessionManagerAccess } from "./session-manager-cache.js";
|
|
39
46
|
import { applySystemPromptOverrideToSession, buildEmbeddedSystemPrompt, createSystemPromptOverride, } from "./system-prompt.js";
|
|
40
47
|
import { splitSdkTools } from "./tool-split.js";
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
|
|
48
|
+
import { describeUnknownError, mapThinkingLevel } from "./utils.js";
|
|
49
|
+
import { flushPendingToolResultsAfterIdle } from "./wait-for-idle-before-flush.js";
|
|
50
|
+
function createCompactionDiagId() {
|
|
51
|
+
return `cmp-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
52
|
+
}
|
|
53
|
+
function getMessageTextChars(msg) {
|
|
54
|
+
const content = msg.content;
|
|
55
|
+
if (typeof content === "string") {
|
|
56
|
+
return content.length;
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(content)) {
|
|
59
|
+
return 0;
|
|
60
|
+
}
|
|
61
|
+
let total = 0;
|
|
62
|
+
for (const block of content) {
|
|
63
|
+
if (!block || typeof block !== "object") {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const text = block.text;
|
|
67
|
+
if (typeof text === "string") {
|
|
68
|
+
total += text.length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return total;
|
|
72
|
+
}
|
|
73
|
+
function resolveMessageToolLabel(msg) {
|
|
74
|
+
const candidate = msg.toolName ??
|
|
75
|
+
msg.name ??
|
|
76
|
+
msg.tool;
|
|
77
|
+
return typeof candidate === "string" && candidate.trim().length > 0 ? candidate : undefined;
|
|
78
|
+
}
|
|
79
|
+
function summarizeCompactionMessages(messages) {
|
|
80
|
+
let historyTextChars = 0;
|
|
81
|
+
let toolResultChars = 0;
|
|
82
|
+
const contributors = [];
|
|
83
|
+
let estTokens = 0;
|
|
84
|
+
let tokenEstimationFailed = false;
|
|
85
|
+
for (const msg of messages) {
|
|
86
|
+
const role = typeof msg.role === "string" ? msg.role : "unknown";
|
|
87
|
+
const chars = getMessageTextChars(msg);
|
|
88
|
+
historyTextChars += chars;
|
|
89
|
+
if (role === "toolResult") {
|
|
90
|
+
toolResultChars += chars;
|
|
91
|
+
}
|
|
92
|
+
contributors.push({ role, chars, tool: resolveMessageToolLabel(msg) });
|
|
93
|
+
if (!tokenEstimationFailed) {
|
|
94
|
+
try {
|
|
95
|
+
estTokens += estimateTokens(msg);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
tokenEstimationFailed = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
messages: messages.length,
|
|
104
|
+
historyTextChars,
|
|
105
|
+
toolResultChars,
|
|
106
|
+
estTokens: tokenEstimationFailed ? undefined : estTokens,
|
|
107
|
+
contributors: contributors.toSorted((a, b) => b.chars - a.chars).slice(0, 3),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function classifyCompactionReason(reason) {
|
|
111
|
+
const text = (reason ?? "").trim().toLowerCase();
|
|
112
|
+
if (!text) {
|
|
113
|
+
return "unknown";
|
|
114
|
+
}
|
|
115
|
+
if (text.includes("nothing to compact")) {
|
|
116
|
+
return "no_compactable_entries";
|
|
117
|
+
}
|
|
118
|
+
if (text.includes("below threshold")) {
|
|
119
|
+
return "below_threshold";
|
|
120
|
+
}
|
|
121
|
+
if (text.includes("already compacted")) {
|
|
122
|
+
return "already_compacted_recently";
|
|
123
|
+
}
|
|
124
|
+
if (text.includes("guard")) {
|
|
125
|
+
return "guard_blocked";
|
|
126
|
+
}
|
|
127
|
+
if (text.includes("summary")) {
|
|
128
|
+
return "summary_failed";
|
|
129
|
+
}
|
|
130
|
+
if (text.includes("timed out") || text.includes("timeout")) {
|
|
131
|
+
return "timeout";
|
|
132
|
+
}
|
|
133
|
+
if (text.includes("400") ||
|
|
134
|
+
text.includes("401") ||
|
|
135
|
+
text.includes("403") ||
|
|
136
|
+
text.includes("429")) {
|
|
137
|
+
return "provider_error_4xx";
|
|
138
|
+
}
|
|
139
|
+
if (text.includes("500") ||
|
|
140
|
+
text.includes("502") ||
|
|
141
|
+
text.includes("503") ||
|
|
142
|
+
text.includes("504")) {
|
|
143
|
+
return "provider_error_5xx";
|
|
144
|
+
}
|
|
145
|
+
return "unknown";
|
|
146
|
+
}
|
|
44
147
|
/**
|
|
45
148
|
* Core compaction logic without lane queueing.
|
|
46
149
|
* Use this when already inside a session/global lane to avoid deadlocks.
|
|
47
150
|
*/
|
|
48
151
|
export async function compactEmbeddedPiSessionDirect(params) {
|
|
152
|
+
const startedAt = Date.now();
|
|
153
|
+
const diagId = params.diagId?.trim() || createCompactionDiagId();
|
|
154
|
+
const trigger = params.trigger ?? "manual";
|
|
155
|
+
const attempt = params.attempt ?? 1;
|
|
156
|
+
const maxAttempts = params.maxAttempts ?? 1;
|
|
157
|
+
const runId = params.runId ?? params.sessionId;
|
|
49
158
|
const resolvedWorkspace = resolveUserPath(params.workspaceDir);
|
|
50
159
|
const prevCwd = process.cwd();
|
|
51
160
|
const provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
|
|
52
161
|
const modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
162
|
+
const fail = (reason) => {
|
|
163
|
+
log.warn(`[compaction-diag] end runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
|
|
164
|
+
`diagId=${diagId} trigger=${trigger} provider=${provider}/${modelId} ` +
|
|
165
|
+
`attempt=${attempt} maxAttempts=${maxAttempts} outcome=failed reason=${classifyCompactionReason(reason)} ` +
|
|
166
|
+
`durationMs=${Date.now() - startedAt}`);
|
|
57
167
|
return {
|
|
58
168
|
ok: false,
|
|
59
169
|
compacted: false,
|
|
60
|
-
reason
|
|
170
|
+
reason,
|
|
61
171
|
};
|
|
172
|
+
};
|
|
173
|
+
const agentDir = params.agentDir ?? resolvePoolbotAgentDir();
|
|
174
|
+
await ensurePoolbotModelsJson(params.config, agentDir);
|
|
175
|
+
const { model, error, authStorage, modelRegistry } = resolveModel(provider, modelId, agentDir, params.config);
|
|
176
|
+
if (!model) {
|
|
177
|
+
const reason = error ?? `Unknown model: ${provider}/${modelId}`;
|
|
178
|
+
return fail(reason);
|
|
62
179
|
}
|
|
63
180
|
try {
|
|
64
181
|
const apiKeyInfo = await getApiKeyForModel({
|
|
@@ -84,11 +201,8 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
84
201
|
}
|
|
85
202
|
}
|
|
86
203
|
catch (err) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
compacted: false,
|
|
90
|
-
reason: describeUnknownError(err),
|
|
91
|
-
};
|
|
204
|
+
const reason = describeUnknownError(err);
|
|
205
|
+
return fail(reason);
|
|
92
206
|
}
|
|
93
207
|
await fs.mkdir(resolvedWorkspace, { recursive: true });
|
|
94
208
|
const sandboxSessionKey = params.sessionKey?.trim() || params.sessionId;
|
|
@@ -141,7 +255,6 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
141
255
|
const runAbortController = new AbortController();
|
|
142
256
|
const toolsRaw = createPoolbotCodingTools({
|
|
143
257
|
exec: {
|
|
144
|
-
...resolveExecToolDefaults(params.config),
|
|
145
258
|
elevated: params.bashElevated,
|
|
146
259
|
},
|
|
147
260
|
sandbox,
|
|
@@ -159,6 +272,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
159
272
|
abortSignal: runAbortController.signal,
|
|
160
273
|
modelProvider: model.provider,
|
|
161
274
|
modelId,
|
|
275
|
+
modelContextWindowTokens: model.contextWindow,
|
|
162
276
|
modelAuthMode: resolveModelAuthMode(model.provider, params.config),
|
|
163
277
|
});
|
|
164
278
|
const tools = sanitizeToolsForGoogle({ tools: toolsRaw, provider });
|
|
@@ -178,8 +292,9 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
178
292
|
accountId: params.agentAccountId ?? undefined,
|
|
179
293
|
});
|
|
180
294
|
if (inlineButtonsScope !== "off") {
|
|
181
|
-
if (!runtimeCapabilities)
|
|
295
|
+
if (!runtimeCapabilities) {
|
|
182
296
|
runtimeCapabilities = [];
|
|
297
|
+
}
|
|
183
298
|
if (!runtimeCapabilities.some((cap) => String(cap).trim().toLowerCase() === "inlinebuttons")) {
|
|
184
299
|
runtimeCapabilities.push("inlineButtons");
|
|
185
300
|
}
|
|
@@ -226,6 +341,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
226
341
|
arch: os.arch(),
|
|
227
342
|
node: process.version,
|
|
228
343
|
model: `${provider}/${modelId}`,
|
|
344
|
+
shell: detectRuntimeShell(),
|
|
229
345
|
channel: runtimeChannel,
|
|
230
346
|
capabilities: runtimeCapabilities,
|
|
231
347
|
channelActions,
|
|
@@ -240,7 +356,9 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
240
356
|
config: params.config,
|
|
241
357
|
});
|
|
242
358
|
const isDefaultAgent = sessionAgentId === defaultAgentId;
|
|
243
|
-
const promptMode = isSubagentSessionKey(params.sessionKey)
|
|
359
|
+
const promptMode = isSubagentSessionKey(params.sessionKey) || isCronSessionKey(params.sessionKey)
|
|
360
|
+
? "minimal"
|
|
361
|
+
: "full";
|
|
244
362
|
const docsPath = await resolvePoolbotDocsPath({
|
|
245
363
|
workspaceDir: effectiveWorkspace,
|
|
246
364
|
argv1: process.argv[1],
|
|
@@ -272,12 +390,20 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
272
390
|
userTime,
|
|
273
391
|
userTimeFormat,
|
|
274
392
|
contextFiles,
|
|
393
|
+
memoryCitationsMode: params.config?.memory?.citations,
|
|
275
394
|
});
|
|
276
395
|
const systemPromptOverride = createSystemPromptOverride(appendPrompt);
|
|
277
396
|
const sessionLock = await acquireSessionWriteLock({
|
|
278
397
|
sessionFile: params.sessionFile,
|
|
398
|
+
maxHoldMs: resolveSessionLockMaxHoldFromTimeout({
|
|
399
|
+
timeoutMs: EMBEDDED_COMPACTION_TIMEOUT_MS,
|
|
400
|
+
}),
|
|
279
401
|
});
|
|
280
402
|
try {
|
|
403
|
+
await repairSessionFileIfNeeded({
|
|
404
|
+
sessionFile: params.sessionFile,
|
|
405
|
+
warn: (message) => log.warn(message),
|
|
406
|
+
});
|
|
281
407
|
await prewarmSessionFile(params.sessionFile);
|
|
282
408
|
const transcriptPolicy = resolveTranscriptPolicy({
|
|
283
409
|
modelApi: model.api,
|
|
@@ -326,6 +452,7 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
326
452
|
modelApi: model.api,
|
|
327
453
|
modelId,
|
|
328
454
|
provider,
|
|
455
|
+
config: params.config,
|
|
329
456
|
sessionManager,
|
|
330
457
|
sessionId: params.sessionId,
|
|
331
458
|
policy: transcriptPolicy,
|
|
@@ -336,11 +463,54 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
336
463
|
const validated = transcriptPolicy.validateAnthropicTurns
|
|
337
464
|
? validateAnthropicTurns(validatedGemini)
|
|
338
465
|
: validatedGemini;
|
|
339
|
-
|
|
466
|
+
// Capture full message history BEFORE limiting — plugins need the complete conversation
|
|
467
|
+
const preCompactionMessages = [...session.messages];
|
|
468
|
+
const truncated = limitHistoryTurns(validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config));
|
|
469
|
+
// Re-run tool_use/tool_result pairing repair after truncation, since
|
|
470
|
+
// limitHistoryTurns can orphan tool_result blocks by removing the
|
|
471
|
+
// assistant message that contained the matching tool_use.
|
|
472
|
+
const limited = transcriptPolicy.repairToolUseResultPairing
|
|
473
|
+
? sanitizeToolUseResultPairing(truncated)
|
|
474
|
+
: truncated;
|
|
340
475
|
if (limited.length > 0) {
|
|
341
476
|
session.agent.replaceMessages(limited);
|
|
342
477
|
}
|
|
343
|
-
|
|
478
|
+
// Run before_compaction hooks (fire-and-forget).
|
|
479
|
+
// The session JSONL already contains all messages on disk, so plugins
|
|
480
|
+
// can read sessionFile asynchronously and process in parallel with
|
|
481
|
+
// the compaction LLM call — no need to block or wait for after_compaction.
|
|
482
|
+
const hookRunner = getGlobalHookRunner();
|
|
483
|
+
const hookCtx = {
|
|
484
|
+
agentId: params.sessionKey?.split(":")[0] ?? "main",
|
|
485
|
+
sessionKey: params.sessionKey,
|
|
486
|
+
sessionId: params.sessionId,
|
|
487
|
+
workspaceDir: params.workspaceDir,
|
|
488
|
+
messageProvider: params.messageChannel ?? params.messageProvider,
|
|
489
|
+
};
|
|
490
|
+
if (hookRunner?.hasHooks("before_compaction")) {
|
|
491
|
+
hookRunner
|
|
492
|
+
.runBeforeCompaction({
|
|
493
|
+
messageCount: preCompactionMessages.length,
|
|
494
|
+
compactingCount: limited.length,
|
|
495
|
+
messages: preCompactionMessages,
|
|
496
|
+
sessionFile: params.sessionFile,
|
|
497
|
+
}, hookCtx)
|
|
498
|
+
.catch((hookErr) => {
|
|
499
|
+
log.warn(`before_compaction hook failed: ${String(hookErr)}`);
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
const diagEnabled = log.isEnabled("debug");
|
|
503
|
+
const preMetrics = diagEnabled ? summarizeCompactionMessages(session.messages) : undefined;
|
|
504
|
+
if (diagEnabled && preMetrics) {
|
|
505
|
+
log.debug(`[compaction-diag] start runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
|
|
506
|
+
`diagId=${diagId} trigger=${trigger} provider=${provider}/${modelId} ` +
|
|
507
|
+
`attempt=${attempt} maxAttempts=${maxAttempts} ` +
|
|
508
|
+
`pre.messages=${preMetrics.messages} pre.historyTextChars=${preMetrics.historyTextChars} ` +
|
|
509
|
+
`pre.toolResultChars=${preMetrics.toolResultChars} pre.estTokens=${preMetrics.estTokens ?? "unknown"}`);
|
|
510
|
+
log.debug(`[compaction-diag] contributors diagId=${diagId} top=${JSON.stringify(preMetrics.contributors)}`);
|
|
511
|
+
}
|
|
512
|
+
const compactStartedAt = Date.now();
|
|
513
|
+
const result = await compactWithSafetyTimeout(() => session.compact(params.customInstructions));
|
|
344
514
|
// Estimate tokens after compaction by summing token estimates for remaining messages
|
|
345
515
|
let tokensAfter;
|
|
346
516
|
try {
|
|
@@ -357,6 +527,34 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
357
527
|
// If estimation fails, leave tokensAfter undefined
|
|
358
528
|
tokensAfter = undefined;
|
|
359
529
|
}
|
|
530
|
+
// Run after_compaction hooks (fire-and-forget).
|
|
531
|
+
// Also includes sessionFile for plugins that only need to act after
|
|
532
|
+
// compaction completes (e.g. analytics, cleanup).
|
|
533
|
+
if (hookRunner?.hasHooks("after_compaction")) {
|
|
534
|
+
hookRunner
|
|
535
|
+
.runAfterCompaction({
|
|
536
|
+
messageCount: session.messages.length,
|
|
537
|
+
tokenCount: tokensAfter,
|
|
538
|
+
compactedCount: limited.length - session.messages.length,
|
|
539
|
+
sessionFile: params.sessionFile,
|
|
540
|
+
}, hookCtx)
|
|
541
|
+
.catch((hookErr) => {
|
|
542
|
+
log.warn(`after_compaction hook failed: ${hookErr}`);
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const postMetrics = diagEnabled ? summarizeCompactionMessages(session.messages) : undefined;
|
|
546
|
+
if (diagEnabled && preMetrics && postMetrics) {
|
|
547
|
+
log.debug(`[compaction-diag] end runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
|
|
548
|
+
`diagId=${diagId} trigger=${trigger} provider=${provider}/${modelId} ` +
|
|
549
|
+
`attempt=${attempt} maxAttempts=${maxAttempts} outcome=compacted reason=none ` +
|
|
550
|
+
`durationMs=${Date.now() - compactStartedAt} retrying=false ` +
|
|
551
|
+
`post.messages=${postMetrics.messages} post.historyTextChars=${postMetrics.historyTextChars} ` +
|
|
552
|
+
`post.toolResultChars=${postMetrics.toolResultChars} post.estTokens=${postMetrics.estTokens ?? "unknown"} ` +
|
|
553
|
+
`delta.messages=${postMetrics.messages - preMetrics.messages} ` +
|
|
554
|
+
`delta.historyTextChars=${postMetrics.historyTextChars - preMetrics.historyTextChars} ` +
|
|
555
|
+
`delta.toolResultChars=${postMetrics.toolResultChars - preMetrics.toolResultChars} ` +
|
|
556
|
+
`delta.estTokens=${typeof preMetrics.estTokens === "number" && typeof postMetrics.estTokens === "number" ? postMetrics.estTokens - preMetrics.estTokens : "unknown"}`);
|
|
557
|
+
}
|
|
360
558
|
return {
|
|
361
559
|
ok: true,
|
|
362
560
|
compacted: true,
|
|
@@ -370,7 +568,10 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
370
568
|
};
|
|
371
569
|
}
|
|
372
570
|
finally {
|
|
373
|
-
|
|
571
|
+
await flushPendingToolResultsAfterIdle({
|
|
572
|
+
agent: session?.agent,
|
|
573
|
+
sessionManager,
|
|
574
|
+
});
|
|
374
575
|
session.dispose();
|
|
375
576
|
}
|
|
376
577
|
}
|
|
@@ -379,11 +580,8 @@ export async function compactEmbeddedPiSessionDirect(params) {
|
|
|
379
580
|
}
|
|
380
581
|
}
|
|
381
582
|
catch (err) {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
compacted: false,
|
|
385
|
-
reason: describeUnknownError(err),
|
|
386
|
-
};
|
|
583
|
+
const reason = describeUnknownError(err);
|
|
584
|
+
return fail(reason);
|
|
387
585
|
}
|
|
388
586
|
finally {
|
|
389
587
|
restoreSkillEnv?.();
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { streamSimple } from "@mariozechner/pi-ai";
|
|
2
2
|
import { log } from "./logger.js";
|
|
3
|
+
const OPENROUTER_APP_HEADERS = {
|
|
4
|
+
"HTTP-Referer": "https://poolbot.dev",
|
|
5
|
+
"X-Title": "PoolBot",
|
|
6
|
+
};
|
|
7
|
+
const ANTHROPIC_CONTEXT_1M_BETA = "context-1m-2025-08-07";
|
|
8
|
+
const ANTHROPIC_1M_MODEL_PREFIXES = ["claude-opus-4", "claude-sonnet-4"];
|
|
9
|
+
// NOTE: We only force `store=true` for *direct* OpenAI Responses.
|
|
10
|
+
// Codex responses (chatgpt.com/backend-api/codex/responses) require `store=false`.
|
|
11
|
+
const OPENAI_RESPONSES_APIS = new Set(["openai-responses"]);
|
|
12
|
+
const OPENAI_RESPONSES_PROVIDERS = new Set(["openai"]);
|
|
3
13
|
/**
|
|
4
14
|
* Resolve provider-specific extra params from model config.
|
|
5
15
|
* Used to pass through stream params like temperature/maxTokens.
|
|
@@ -11,17 +21,35 @@ export function resolveExtraParams(params) {
|
|
|
11
21
|
const modelConfig = params.cfg?.agents?.defaults?.models?.[modelKey];
|
|
12
22
|
return modelConfig?.params ? { ...modelConfig.params } : undefined;
|
|
13
23
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Resolve cacheRetention from extraParams, supporting both new `cacheRetention`
|
|
26
|
+
* and legacy `cacheControlTtl` values for backwards compatibility.
|
|
27
|
+
*
|
|
28
|
+
* Mapping: "5m" → "short", "1h" → "long"
|
|
29
|
+
*
|
|
30
|
+
* Only applies to Anthropic provider (OpenRouter uses openai-completions API
|
|
31
|
+
* with hardcoded cache_control, not the cacheRetention stream option).
|
|
32
|
+
*/
|
|
33
|
+
function resolveCacheRetention(extraParams, provider) {
|
|
34
|
+
if (provider !== "anthropic") {
|
|
17
35
|
return undefined;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
36
|
+
}
|
|
37
|
+
// Prefer new cacheRetention if present
|
|
38
|
+
const newVal = extraParams?.cacheRetention;
|
|
39
|
+
if (newVal === "none" || newVal === "short" || newVal === "long") {
|
|
40
|
+
return newVal;
|
|
41
|
+
}
|
|
42
|
+
// Fall back to legacy cacheControlTtl with mapping
|
|
43
|
+
const legacy = extraParams?.cacheControlTtl;
|
|
44
|
+
if (legacy === "5m") {
|
|
45
|
+
return "short";
|
|
46
|
+
}
|
|
47
|
+
if (legacy === "1h") {
|
|
48
|
+
return "long";
|
|
49
|
+
}
|
|
22
50
|
return undefined;
|
|
23
51
|
}
|
|
24
|
-
function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider
|
|
52
|
+
function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider) {
|
|
25
53
|
if (!extraParams || Object.keys(extraParams).length === 0) {
|
|
26
54
|
return undefined;
|
|
27
55
|
}
|
|
@@ -32,9 +60,9 @@ function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider, mode
|
|
|
32
60
|
if (typeof extraParams.maxTokens === "number") {
|
|
33
61
|
streamParams.maxTokens = extraParams.maxTokens;
|
|
34
62
|
}
|
|
35
|
-
const
|
|
36
|
-
if (
|
|
37
|
-
streamParams.
|
|
63
|
+
const cacheRetention = resolveCacheRetention(extraParams, provider);
|
|
64
|
+
if (cacheRetention) {
|
|
65
|
+
streamParams.cacheRetention = cacheRetention;
|
|
38
66
|
}
|
|
39
67
|
if (Object.keys(streamParams).length === 0) {
|
|
40
68
|
return undefined;
|
|
@@ -47,8 +75,149 @@ function createStreamFnWithExtraParams(baseStreamFn, extraParams, provider, mode
|
|
|
47
75
|
});
|
|
48
76
|
return wrappedStreamFn;
|
|
49
77
|
}
|
|
78
|
+
function isDirectOpenAIBaseUrl(baseUrl) {
|
|
79
|
+
if (typeof baseUrl !== "string" || !baseUrl.trim()) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
84
|
+
return host === "api.openai.com" || host === "chatgpt.com";
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
const normalized = baseUrl.toLowerCase();
|
|
88
|
+
return normalized.includes("api.openai.com") || normalized.includes("chatgpt.com");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function shouldForceResponsesStore(model) {
|
|
92
|
+
if (typeof model.api !== "string" || typeof model.provider !== "string") {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (!OPENAI_RESPONSES_APIS.has(model.api)) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
if (!OPENAI_RESPONSES_PROVIDERS.has(model.provider)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
return isDirectOpenAIBaseUrl(model.baseUrl);
|
|
102
|
+
}
|
|
103
|
+
function createOpenAIResponsesStoreWrapper(baseStreamFn) {
|
|
104
|
+
const underlying = baseStreamFn ?? streamSimple;
|
|
105
|
+
return (model, context, options) => {
|
|
106
|
+
if (!shouldForceResponsesStore(model)) {
|
|
107
|
+
return underlying(model, context, options);
|
|
108
|
+
}
|
|
109
|
+
const originalOnPayload = options?.onPayload;
|
|
110
|
+
return underlying(model, context, {
|
|
111
|
+
...options,
|
|
112
|
+
onPayload: (payload) => {
|
|
113
|
+
if (payload && typeof payload === "object") {
|
|
114
|
+
payload.store = true;
|
|
115
|
+
}
|
|
116
|
+
originalOnPayload?.(payload);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function isAnthropic1MModel(modelId) {
|
|
122
|
+
const normalized = modelId.trim().toLowerCase();
|
|
123
|
+
return ANTHROPIC_1M_MODEL_PREFIXES.some((prefix) => normalized.startsWith(prefix));
|
|
124
|
+
}
|
|
125
|
+
function parseHeaderList(value) {
|
|
126
|
+
if (typeof value !== "string") {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
return value
|
|
130
|
+
.split(",")
|
|
131
|
+
.map((item) => item.trim())
|
|
132
|
+
.filter(Boolean);
|
|
133
|
+
}
|
|
134
|
+
function resolveAnthropicBetas(extraParams, provider, modelId) {
|
|
135
|
+
if (provider !== "anthropic") {
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
const betas = new Set();
|
|
139
|
+
const configured = extraParams?.anthropicBeta;
|
|
140
|
+
if (typeof configured === "string" && configured.trim()) {
|
|
141
|
+
betas.add(configured.trim());
|
|
142
|
+
}
|
|
143
|
+
else if (Array.isArray(configured)) {
|
|
144
|
+
for (const beta of configured) {
|
|
145
|
+
if (typeof beta === "string" && beta.trim()) {
|
|
146
|
+
betas.add(beta.trim());
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (extraParams?.context1m === true) {
|
|
151
|
+
if (isAnthropic1MModel(modelId)) {
|
|
152
|
+
betas.add(ANTHROPIC_CONTEXT_1M_BETA);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
log.warn(`ignoring context1m for non-opus/sonnet model: ${provider}/${modelId}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return betas.size > 0 ? [...betas] : undefined;
|
|
159
|
+
}
|
|
160
|
+
function mergeAnthropicBetaHeader(headers, betas) {
|
|
161
|
+
const merged = { ...headers };
|
|
162
|
+
const existingKey = Object.keys(merged).find((key) => key.toLowerCase() === "anthropic-beta");
|
|
163
|
+
const existing = existingKey ? parseHeaderList(merged[existingKey]) : [];
|
|
164
|
+
const values = Array.from(new Set([...existing, ...betas]));
|
|
165
|
+
const key = existingKey ?? "anthropic-beta";
|
|
166
|
+
merged[key] = values.join(",");
|
|
167
|
+
return merged;
|
|
168
|
+
}
|
|
169
|
+
function createAnthropicBetaHeadersWrapper(baseStreamFn, betas) {
|
|
170
|
+
const underlying = baseStreamFn ?? streamSimple;
|
|
171
|
+
return (model, context, options) => underlying(model, context, {
|
|
172
|
+
...options,
|
|
173
|
+
headers: mergeAnthropicBetaHeader(options?.headers, betas),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create a streamFn wrapper that adds OpenRouter app attribution headers.
|
|
178
|
+
* These headers allow Pool Bot to appear on OpenRouter's leaderboard.
|
|
179
|
+
*/
|
|
180
|
+
function createOpenRouterHeadersWrapper(baseStreamFn) {
|
|
181
|
+
const underlying = baseStreamFn ?? streamSimple;
|
|
182
|
+
return (model, context, options) => underlying(model, context, {
|
|
183
|
+
...options,
|
|
184
|
+
headers: {
|
|
185
|
+
...OPENROUTER_APP_HEADERS,
|
|
186
|
+
...options?.headers,
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Create a streamFn wrapper that injects tool_stream=true for Z.AI providers.
|
|
192
|
+
*
|
|
193
|
+
* Z.AI's API supports the `tool_stream` parameter to enable real-time streaming
|
|
194
|
+
* of tool call arguments and reasoning content. When enabled, the API returns
|
|
195
|
+
* progressive tool_call deltas, allowing users to see tool execution in real-time.
|
|
196
|
+
*
|
|
197
|
+
* @see https://docs.z.ai/api-reference#streaming
|
|
198
|
+
*/
|
|
199
|
+
function createZaiToolStreamWrapper(baseStreamFn, enabled) {
|
|
200
|
+
const underlying = baseStreamFn ?? streamSimple;
|
|
201
|
+
return (model, context, options) => {
|
|
202
|
+
if (!enabled) {
|
|
203
|
+
return underlying(model, context, options);
|
|
204
|
+
}
|
|
205
|
+
const originalOnPayload = options?.onPayload;
|
|
206
|
+
return underlying(model, context, {
|
|
207
|
+
...options,
|
|
208
|
+
onPayload: (payload) => {
|
|
209
|
+
if (payload && typeof payload === "object") {
|
|
210
|
+
// Inject tool_stream: true for Z.AI API
|
|
211
|
+
payload.tool_stream = true;
|
|
212
|
+
}
|
|
213
|
+
originalOnPayload?.(payload);
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
}
|
|
50
218
|
/**
|
|
51
219
|
* Apply extra params (like temperature) to an agent's streamFn.
|
|
220
|
+
* Also adds OpenRouter app attribution headers when using the OpenRouter provider.
|
|
52
221
|
*
|
|
53
222
|
* @internal Exported for testing
|
|
54
223
|
*/
|
|
@@ -62,9 +231,31 @@ export function applyExtraParamsToAgent(agent, cfg, provider, modelId, extraPara
|
|
|
62
231
|
? Object.fromEntries(Object.entries(extraParamsOverride).filter(([, value]) => value !== undefined))
|
|
63
232
|
: undefined;
|
|
64
233
|
const merged = Object.assign({}, extraParams, override);
|
|
65
|
-
const wrappedStreamFn = createStreamFnWithExtraParams(agent.streamFn, merged, provider
|
|
234
|
+
const wrappedStreamFn = createStreamFnWithExtraParams(agent.streamFn, merged, provider);
|
|
66
235
|
if (wrappedStreamFn) {
|
|
67
236
|
log.debug(`applying extraParams to agent streamFn for ${provider}/${modelId}`);
|
|
68
237
|
agent.streamFn = wrappedStreamFn;
|
|
69
238
|
}
|
|
239
|
+
const anthropicBetas = resolveAnthropicBetas(merged, provider, modelId);
|
|
240
|
+
if (anthropicBetas?.length) {
|
|
241
|
+
log.debug(`applying Anthropic beta header for ${provider}/${modelId}: ${anthropicBetas.join(",")}`);
|
|
242
|
+
agent.streamFn = createAnthropicBetaHeadersWrapper(agent.streamFn, anthropicBetas);
|
|
243
|
+
}
|
|
244
|
+
if (provider === "openrouter") {
|
|
245
|
+
log.debug(`applying OpenRouter app attribution headers for ${provider}/${modelId}`);
|
|
246
|
+
agent.streamFn = createOpenRouterHeadersWrapper(agent.streamFn);
|
|
247
|
+
}
|
|
248
|
+
// Enable Z.AI tool_stream for real-time tool call streaming.
|
|
249
|
+
// Enabled by default for Z.AI provider, can be disabled via params.tool_stream: false
|
|
250
|
+
if (provider === "zai" || provider === "z-ai") {
|
|
251
|
+
const toolStreamEnabled = merged?.tool_stream !== false;
|
|
252
|
+
if (toolStreamEnabled) {
|
|
253
|
+
log.debug(`enabling Z.AI tool_stream for ${provider}/${modelId}`);
|
|
254
|
+
agent.streamFn = createZaiToolStreamWrapper(agent.streamFn, true);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Work around upstream pi-ai hardcoding `store: false` for Responses API.
|
|
258
|
+
// Force `store=true` for direct OpenAI/OpenAI Codex providers so multi-turn
|
|
259
|
+
// server-side conversation state is preserved.
|
|
260
|
+
agent.streamFn = createOpenAIResponsesStoreWrapper(agent.streamFn);
|
|
70
261
|
}
|