@poolzin/pool-bot 2026.2.21 → 2026.2.23
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-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/device-pair/index.ts +2 -2
- 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/irc/src/accounts.ts +1 -1
- package/extensions/irc/src/onboarding.ts +4 -4
- 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 +10 -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 +10 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +10 -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 +10 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +10 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +10 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +10 -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
- package/dist/agents/openclaw-tools.js +0 -151
- package/dist/agents/tool-security.js +0 -96
- package/dist/gateway/url-validation.js +0 -94
- package/dist/infra/openclaw-root.js +0 -109
- package/dist/infra/tmp-openclaw-dir.js +0 -81
- package/dist/media/path-sanitization.js +0 -78
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { loadConfig } from "../config/config.js";
|
|
2
2
|
import { callGateway } from "../gateway/call.js";
|
|
3
3
|
import { onAgentEvent } from "../infra/agent-events.js";
|
|
4
|
+
import { defaultRuntime } from "../runtime.js";
|
|
4
5
|
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
|
5
6
|
import { resetAnnounceQueuesForTests } from "./subagent-announce-queue.js";
|
|
6
7
|
import { runSubagentAnnounceFlow } from "./subagent-announce.js";
|
|
@@ -13,6 +14,8 @@ let listenerStop = null;
|
|
|
13
14
|
// Use var to avoid TDZ when init runs across circular imports during bootstrap.
|
|
14
15
|
var restoreAttempted = false;
|
|
15
16
|
const SUBAGENT_ANNOUNCE_TIMEOUT_MS = 120_000;
|
|
17
|
+
const MIN_ANNOUNCE_RETRY_DELAY_MS = 1_000;
|
|
18
|
+
const MAX_ANNOUNCE_RETRY_DELAY_MS = 8_000;
|
|
16
19
|
/**
|
|
17
20
|
* Maximum number of announce delivery attempts before giving up.
|
|
18
21
|
* Prevents infinite retry loops when `runSubagentAnnounceFlow` repeatedly
|
|
@@ -24,7 +27,19 @@ const MAX_ANNOUNCE_RETRY_COUNT = 3;
|
|
|
24
27
|
* succeeded. Guards against stale registry entries surviving gateway restarts.
|
|
25
28
|
*/
|
|
26
29
|
const ANNOUNCE_EXPIRY_MS = 5 * 60_000; // 5 minutes
|
|
27
|
-
|
|
30
|
+
function resolveAnnounceRetryDelayMs(retryCount) {
|
|
31
|
+
const boundedRetryCount = Math.max(0, Math.min(retryCount, 10));
|
|
32
|
+
// retryCount is "attempts already made", so retry #1 waits 1s, then 2s, 4s...
|
|
33
|
+
const backoffExponent = Math.max(0, boundedRetryCount - 1);
|
|
34
|
+
const baseDelay = MIN_ANNOUNCE_RETRY_DELAY_MS * 2 ** backoffExponent;
|
|
35
|
+
return Math.min(baseDelay, MAX_ANNOUNCE_RETRY_DELAY_MS);
|
|
36
|
+
}
|
|
37
|
+
function logAnnounceGiveUp(entry, reason) {
|
|
38
|
+
const retryCount = entry.announceRetryCount ?? 0;
|
|
39
|
+
const endedAgoMs = typeof entry.endedAt === "number" ? Math.max(0, Date.now() - entry.endedAt) : undefined;
|
|
40
|
+
const endedAgoLabel = endedAgoMs != null ? `${Math.round(endedAgoMs / 1000)}s` : "n/a";
|
|
41
|
+
defaultRuntime.log(`[warn] Subagent announce give up (${reason}) run=${entry.runId} child=${entry.childSessionKey} requester=${entry.requesterSessionKey} retries=${retryCount} endedAgo=${endedAgoLabel}`);
|
|
42
|
+
}
|
|
28
43
|
function persistSubagentRuns() {
|
|
29
44
|
try {
|
|
30
45
|
saveSubagentRegistryToDisk(subagentRuns);
|
|
@@ -49,6 +64,7 @@ function startSubagentAnnounceCleanupFlow(runId, entry) {
|
|
|
49
64
|
requesterOrigin,
|
|
50
65
|
requesterDisplayKey: entry.requesterDisplayKey,
|
|
51
66
|
task: entry.task,
|
|
67
|
+
expectsCompletionMessage: entry.expectsCompletionMessage,
|
|
52
68
|
timeoutMs: SUBAGENT_ANNOUNCE_TIMEOUT_MS,
|
|
53
69
|
cleanup: entry.cleanup,
|
|
54
70
|
waitForCompletion: false,
|
|
@@ -74,15 +90,30 @@ function resumeSubagentRun(runId) {
|
|
|
74
90
|
}
|
|
75
91
|
// Skip entries that have exhausted their retry budget or expired (#18264).
|
|
76
92
|
if ((entry.announceRetryCount ?? 0) >= MAX_ANNOUNCE_RETRY_COUNT) {
|
|
93
|
+
logAnnounceGiveUp(entry, "retry-limit");
|
|
77
94
|
entry.cleanupCompletedAt = Date.now();
|
|
78
95
|
persistSubagentRuns();
|
|
79
96
|
return;
|
|
80
97
|
}
|
|
81
98
|
if (typeof entry.endedAt === "number" && Date.now() - entry.endedAt > ANNOUNCE_EXPIRY_MS) {
|
|
99
|
+
logAnnounceGiveUp(entry, "expiry");
|
|
82
100
|
entry.cleanupCompletedAt = Date.now();
|
|
83
101
|
persistSubagentRuns();
|
|
84
102
|
return;
|
|
85
103
|
}
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
const delayMs = resolveAnnounceRetryDelayMs(entry.announceRetryCount ?? 0);
|
|
106
|
+
const earliestRetryAt = (entry.lastAnnounceRetryAt ?? 0) + delayMs;
|
|
107
|
+
if (entry.expectsCompletionMessage === true &&
|
|
108
|
+
entry.lastAnnounceRetryAt &&
|
|
109
|
+
now < earliestRetryAt) {
|
|
110
|
+
const waitMs = Math.max(1, earliestRetryAt - now);
|
|
111
|
+
setTimeout(() => {
|
|
112
|
+
resumeSubagentRun(runId);
|
|
113
|
+
}, waitMs).unref?.();
|
|
114
|
+
resumedRuns.add(runId);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
86
117
|
if (typeof entry.endedAt === "number" && entry.endedAt > 0) {
|
|
87
118
|
if (suppressAnnounceForSteerRestart(entry)) {
|
|
88
119
|
resumedRuns.add(runId);
|
|
@@ -238,14 +269,16 @@ function finalizeSubagentCleanup(runId, cleanup, didAnnounce) {
|
|
|
238
269
|
return;
|
|
239
270
|
}
|
|
240
271
|
if (!didAnnounce) {
|
|
272
|
+
const now = Date.now();
|
|
241
273
|
const retryCount = (entry.announceRetryCount ?? 0) + 1;
|
|
242
274
|
entry.announceRetryCount = retryCount;
|
|
243
|
-
entry.lastAnnounceRetryAt =
|
|
275
|
+
entry.lastAnnounceRetryAt = now;
|
|
244
276
|
// Check if the announce has exceeded retry limits or expired (#18264).
|
|
245
|
-
const endedAgo = typeof entry.endedAt === "number" ?
|
|
277
|
+
const endedAgo = typeof entry.endedAt === "number" ? now - entry.endedAt : 0;
|
|
246
278
|
if (retryCount >= MAX_ANNOUNCE_RETRY_COUNT || endedAgo > ANNOUNCE_EXPIRY_MS) {
|
|
247
279
|
// Give up: mark as completed to break the infinite retry loop.
|
|
248
|
-
entry
|
|
280
|
+
logAnnounceGiveUp(entry, retryCount >= MAX_ANNOUNCE_RETRY_COUNT ? "retry-limit" : "expiry");
|
|
281
|
+
entry.cleanupCompletedAt = now;
|
|
249
282
|
persistSubagentRuns();
|
|
250
283
|
retryDeferredCompletedAnnounces(runId);
|
|
251
284
|
return;
|
|
@@ -254,6 +287,12 @@ function finalizeSubagentCleanup(runId, cleanup, didAnnounce) {
|
|
|
254
287
|
entry.cleanupHandled = false;
|
|
255
288
|
resumedRuns.delete(runId);
|
|
256
289
|
persistSubagentRuns();
|
|
290
|
+
if (entry.expectsCompletionMessage !== true) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
setTimeout(() => {
|
|
294
|
+
resumeSubagentRun(runId);
|
|
295
|
+
}, resolveAnnounceRetryDelayMs(entry.announceRetryCount ?? 0)).unref?.();
|
|
257
296
|
return;
|
|
258
297
|
}
|
|
259
298
|
if (cleanup === "delete") {
|
|
@@ -284,6 +323,7 @@ function retryDeferredCompletedAnnounces(excludeRunId) {
|
|
|
284
323
|
// Force-expire announces that have been pending too long (#18264).
|
|
285
324
|
const endedAgo = now - (entry.endedAt ?? now);
|
|
286
325
|
if (endedAgo > ANNOUNCE_EXPIRY_MS) {
|
|
326
|
+
logAnnounceGiveUp(entry, "expiry");
|
|
287
327
|
entry.cleanupCompletedAt = now;
|
|
288
328
|
persistSubagentRuns();
|
|
289
329
|
continue;
|
|
@@ -405,6 +445,7 @@ export function registerSubagentRun(params) {
|
|
|
405
445
|
requesterDisplayKey: params.requesterDisplayKey,
|
|
406
446
|
task: params.task,
|
|
407
447
|
cleanup: params.cleanup,
|
|
448
|
+
expectsCompletionMessage: params.expectsCompletionMessage,
|
|
408
449
|
label: params.label,
|
|
409
450
|
model: params.model,
|
|
410
451
|
runTimeoutSeconds,
|
|
@@ -6,12 +6,13 @@ import { normalizeAgentId, parseAgentSessionKey } from "../routing/session-key.j
|
|
|
6
6
|
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
|
7
7
|
import { resolveAgentConfig } from "./agent-scope.js";
|
|
8
8
|
import { AGENT_LANE_SUBAGENT } from "./lanes.js";
|
|
9
|
-
import {
|
|
9
|
+
import { resolveSubagentSpawnModelSelection } from "./model-selection.js";
|
|
10
10
|
import { buildSubagentSystemPrompt } from "./subagent-announce.js";
|
|
11
11
|
import { getSubagentDepthFromSessionStore } from "./subagent-depth.js";
|
|
12
12
|
import { countActiveRunsForSession, registerSubagentRun } from "./subagent-registry.js";
|
|
13
13
|
import { readStringParam } from "./tools/common.js";
|
|
14
14
|
import { resolveDisplaySessionKey, resolveInternalSessionKey, resolveMainSessionAlias, } from "./tools/sessions-helpers.js";
|
|
15
|
+
export const SUBAGENT_SPAWN_ACCEPTED_NOTE = "auto-announces on completion, do not poll/sleep. The response will be sent back as an agent message.";
|
|
15
16
|
export function splitModelRef(ref) {
|
|
16
17
|
if (!ref) {
|
|
17
18
|
return { provider: undefined, model: undefined };
|
|
@@ -26,20 +27,6 @@ export function splitModelRef(ref) {
|
|
|
26
27
|
}
|
|
27
28
|
return { provider: undefined, model: trimmed };
|
|
28
29
|
}
|
|
29
|
-
export function normalizeModelSelection(value) {
|
|
30
|
-
if (typeof value === "string") {
|
|
31
|
-
const trimmed = value.trim();
|
|
32
|
-
return trimmed || undefined;
|
|
33
|
-
}
|
|
34
|
-
if (!value || typeof value !== "object") {
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
const primary = value.primary;
|
|
38
|
-
if (typeof primary === "string" && primary.trim()) {
|
|
39
|
-
return primary.trim();
|
|
40
|
-
}
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
30
|
export async function spawnSubagentDirect(params, ctx) {
|
|
44
31
|
const task = params.task;
|
|
45
32
|
const label = params.label?.trim() || "";
|
|
@@ -56,7 +43,6 @@ export async function spawnSubagentDirect(params, ctx) {
|
|
|
56
43
|
const runTimeoutSeconds = typeof params.runTimeoutSeconds === "number" && Number.isFinite(params.runTimeoutSeconds)
|
|
57
44
|
? Math.max(0, Math.floor(params.runTimeoutSeconds))
|
|
58
45
|
: 0;
|
|
59
|
-
let modelWarning;
|
|
60
46
|
let modelApplied = false;
|
|
61
47
|
const cfg = loadConfig();
|
|
62
48
|
const { mainKey, alias } = resolveMainSessionAlias(cfg);
|
|
@@ -110,15 +96,11 @@ export async function spawnSubagentDirect(params, ctx) {
|
|
|
110
96
|
const childDepth = callerDepth + 1;
|
|
111
97
|
const spawnedByKey = requesterInternalKey;
|
|
112
98
|
const targetAgentConfig = resolveAgentConfig(cfg, targetAgentId);
|
|
113
|
-
const
|
|
99
|
+
const resolvedModel = resolveSubagentSpawnModelSelection({
|
|
114
100
|
cfg,
|
|
115
101
|
agentId: targetAgentId,
|
|
102
|
+
modelOverride,
|
|
116
103
|
});
|
|
117
|
-
const resolvedModel = normalizeModelSelection(modelOverride) ??
|
|
118
|
-
normalizeModelSelection(targetAgentConfig?.subagents?.model) ??
|
|
119
|
-
normalizeModelSelection(cfg.agents?.defaults?.subagents?.model) ??
|
|
120
|
-
normalizeModelSelection(cfg.agents?.defaults?.model?.primary) ??
|
|
121
|
-
normalizeModelSelection(`${runtimeDefaultModel.provider}/${runtimeDefaultModel.model}`);
|
|
122
104
|
const resolvedThinkingDefaultRaw = readStringParam(targetAgentConfig?.subagents ?? {}, "thinking") ??
|
|
123
105
|
readStringParam(cfg.agents?.defaults?.subagents ?? {}, "thinking");
|
|
124
106
|
let thinkingOverride;
|
|
@@ -161,15 +143,11 @@ export async function spawnSubagentDirect(params, ctx) {
|
|
|
161
143
|
}
|
|
162
144
|
catch (err) {
|
|
163
145
|
const messageText = err instanceof Error ? err.message : typeof err === "string" ? err : "error";
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
childSessionKey,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
modelWarning = messageText;
|
|
146
|
+
return {
|
|
147
|
+
status: "error",
|
|
148
|
+
error: messageText,
|
|
149
|
+
childSessionKey,
|
|
150
|
+
};
|
|
173
151
|
}
|
|
174
152
|
}
|
|
175
153
|
if (thinkingOverride !== undefined) {
|
|
@@ -201,13 +179,17 @@ export async function spawnSubagentDirect(params, ctx) {
|
|
|
201
179
|
childDepth,
|
|
202
180
|
maxSpawnDepth,
|
|
203
181
|
});
|
|
182
|
+
const childTaskMessage = [
|
|
183
|
+
`[Subagent Context] You are running as a subagent (depth ${childDepth}/${maxSpawnDepth}). Results auto-announce to your requester; do not busy-poll for status.`,
|
|
184
|
+
`[Subagent Task]: ${task}`,
|
|
185
|
+
].join("\n\n");
|
|
204
186
|
const childIdem = crypto.randomUUID();
|
|
205
187
|
let childRunId = childIdem;
|
|
206
188
|
try {
|
|
207
189
|
const response = await callGateway({
|
|
208
190
|
method: "agent",
|
|
209
191
|
params: {
|
|
210
|
-
message:
|
|
192
|
+
message: childTaskMessage,
|
|
211
193
|
sessionKey: childSessionKey,
|
|
212
194
|
channel: requesterOrigin?.channel,
|
|
213
195
|
to: requesterOrigin?.to ?? undefined,
|
|
@@ -251,12 +233,13 @@ export async function spawnSubagentDirect(params, ctx) {
|
|
|
251
233
|
label: label || undefined,
|
|
252
234
|
model: resolvedModel,
|
|
253
235
|
runTimeoutSeconds,
|
|
236
|
+
expectsCompletionMessage: params.expectsCompletionMessage === true,
|
|
254
237
|
});
|
|
255
238
|
return {
|
|
256
239
|
status: "accepted",
|
|
257
240
|
childSessionKey,
|
|
258
241
|
runId: childRunId,
|
|
242
|
+
note: SUBAGENT_SPAWN_ACCEPTED_NOTE,
|
|
259
243
|
modelApplied: resolvedModel ? modelApplied : undefined,
|
|
260
|
-
warning: modelWarning,
|
|
261
244
|
};
|
|
262
245
|
}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
function extractBetween(input, startMarker, endMarker) {
|
|
2
3
|
const start = input.indexOf(startMarker);
|
|
3
|
-
if (start === -1)
|
|
4
|
+
if (start === -1) {
|
|
4
5
|
return { text: "", found: false };
|
|
6
|
+
}
|
|
5
7
|
const end = input.indexOf(endMarker, start + startMarker.length);
|
|
6
|
-
if (end === -1)
|
|
8
|
+
if (end === -1) {
|
|
7
9
|
return { text: input.slice(start), found: true };
|
|
10
|
+
}
|
|
8
11
|
return { text: input.slice(start, end), found: true };
|
|
9
12
|
}
|
|
10
13
|
function parseSkillBlocks(skillsPrompt) {
|
|
11
14
|
const prompt = skillsPrompt.trim();
|
|
12
|
-
if (!prompt)
|
|
15
|
+
if (!prompt) {
|
|
13
16
|
return [];
|
|
17
|
+
}
|
|
14
18
|
const blocks = Array.from(prompt.matchAll(/<skill>[\s\S]*?<\/skill>/gi)).map((match) => match[0] ?? "");
|
|
15
19
|
return blocks
|
|
16
20
|
.map((block) => {
|
|
@@ -20,12 +24,22 @@ function parseSkillBlocks(skillsPrompt) {
|
|
|
20
24
|
.filter((b) => b.blockChars > 0);
|
|
21
25
|
}
|
|
22
26
|
function buildInjectedWorkspaceFiles(params) {
|
|
23
|
-
const
|
|
27
|
+
const injectedByPath = new Map(params.injectedFiles.map((f) => [f.path, f.content]));
|
|
28
|
+
const injectedByBaseName = new Map();
|
|
29
|
+
for (const file of params.injectedFiles) {
|
|
30
|
+
const normalizedPath = file.path.replace(/\\/g, "/");
|
|
31
|
+
const baseName = path.posix.basename(normalizedPath);
|
|
32
|
+
if (!injectedByBaseName.has(baseName)) {
|
|
33
|
+
injectedByBaseName.set(baseName, file.content);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
24
36
|
return params.bootstrapFiles.map((file) => {
|
|
25
37
|
const rawChars = file.missing ? 0 : (file.content ?? "").trimEnd().length;
|
|
26
|
-
const injected =
|
|
38
|
+
const injected = injectedByPath.get(file.path) ??
|
|
39
|
+
injectedByPath.get(file.name) ??
|
|
40
|
+
injectedByBaseName.get(file.name);
|
|
27
41
|
const injectedChars = injected ? injected.length : 0;
|
|
28
|
-
const truncated = !file.missing &&
|
|
42
|
+
const truncated = !file.missing && injectedChars < rawChars;
|
|
29
43
|
return {
|
|
30
44
|
name: file.name,
|
|
31
45
|
path: file.path,
|
|
@@ -42,8 +56,9 @@ function buildToolsEntries(tools) {
|
|
|
42
56
|
const summary = tool.description?.trim() || tool.label?.trim() || "";
|
|
43
57
|
const summaryChars = summary.length;
|
|
44
58
|
const schemaChars = (() => {
|
|
45
|
-
if (!tool.parameters || typeof tool.parameters !== "object")
|
|
59
|
+
if (!tool.parameters || typeof tool.parameters !== "object") {
|
|
46
60
|
return 0;
|
|
61
|
+
}
|
|
47
62
|
try {
|
|
48
63
|
return JSON.stringify(tool.parameters).length;
|
|
49
64
|
}
|
|
@@ -56,8 +71,9 @@ function buildToolsEntries(tools) {
|
|
|
56
71
|
? tool.parameters
|
|
57
72
|
: null;
|
|
58
73
|
const props = schema && typeof schema.properties === "object" ? schema.properties : null;
|
|
59
|
-
if (!props || typeof props !== "object")
|
|
74
|
+
if (!props || typeof props !== "object") {
|
|
60
75
|
return null;
|
|
76
|
+
}
|
|
61
77
|
return Object.keys(props).length;
|
|
62
78
|
})();
|
|
63
79
|
return { name, summaryChars, schemaChars, propertiesCount };
|
|
@@ -67,8 +83,9 @@ function extractToolListText(systemPrompt) {
|
|
|
67
83
|
const markerA = "Tool names are case-sensitive. Call tools exactly as listed.\n";
|
|
68
84
|
const markerB = "\nTOOLS.md does not control tool availability; it is user guidance for how to use external tools.";
|
|
69
85
|
const extracted = extractBetween(systemPrompt, markerA, markerB);
|
|
70
|
-
if (!extracted.found)
|
|
86
|
+
if (!extracted.found) {
|
|
71
87
|
return "";
|
|
88
|
+
}
|
|
72
89
|
return extracted.text.replace(markerA, "").trim();
|
|
73
90
|
}
|
|
74
91
|
export function buildSystemPromptReport(params) {
|
|
@@ -89,6 +106,7 @@ export function buildSystemPromptReport(params) {
|
|
|
89
106
|
model: params.model,
|
|
90
107
|
workspaceDir: params.workspaceDir,
|
|
91
108
|
bootstrapMaxChars: params.bootstrapMaxChars,
|
|
109
|
+
bootstrapTotalMaxChars: params.bootstrapTotalMaxChars,
|
|
92
110
|
sandbox: params.sandbox,
|
|
93
111
|
systemPrompt: {
|
|
94
112
|
chars: systemPrompt.length,
|
|
@@ -98,7 +116,6 @@ export function buildSystemPromptReport(params) {
|
|
|
98
116
|
injectedWorkspaceFiles: buildInjectedWorkspaceFiles({
|
|
99
117
|
bootstrapFiles: params.bootstrapFiles,
|
|
100
118
|
injectedFiles: params.injectedFiles,
|
|
101
|
-
bootstrapMaxChars: params.bootstrapMaxChars,
|
|
102
119
|
}),
|
|
103
120
|
skills: {
|
|
104
121
|
promptChars: params.skillsPrompt.length,
|
|
@@ -2,11 +2,13 @@ import { SILENT_REPLY_TOKEN } from "../auto-reply/tokens.js";
|
|
|
2
2
|
import { listDeliverableMessageChannels } from "../utils/message-channel.js";
|
|
3
3
|
import { sanitizeForPromptLiteral } from "./sanitize-for-prompt.js";
|
|
4
4
|
function buildSkillsSection(params) {
|
|
5
|
-
if (params.isMinimal)
|
|
5
|
+
if (params.isMinimal) {
|
|
6
6
|
return [];
|
|
7
|
+
}
|
|
7
8
|
const trimmed = params.skillsPrompt?.trim();
|
|
8
|
-
if (!trimmed)
|
|
9
|
+
if (!trimmed) {
|
|
9
10
|
return [];
|
|
11
|
+
}
|
|
10
12
|
return [
|
|
11
13
|
"## Skills (mandatory)",
|
|
12
14
|
"Before replying: scan <available_skills> <description> entries.",
|
|
@@ -19,8 +21,9 @@ function buildSkillsSection(params) {
|
|
|
19
21
|
];
|
|
20
22
|
}
|
|
21
23
|
function buildMemorySection(params) {
|
|
22
|
-
if (params.isMinimal)
|
|
24
|
+
if (params.isMinimal) {
|
|
23
25
|
return [];
|
|
26
|
+
}
|
|
24
27
|
if (!params.availableTools.has("memory_search") && !params.availableTools.has("memory_get")) {
|
|
25
28
|
return [];
|
|
26
29
|
}
|
|
@@ -38,21 +41,25 @@ function buildMemorySection(params) {
|
|
|
38
41
|
return lines;
|
|
39
42
|
}
|
|
40
43
|
function buildUserIdentitySection(ownerLine, isMinimal) {
|
|
41
|
-
if (!ownerLine || isMinimal)
|
|
44
|
+
if (!ownerLine || isMinimal) {
|
|
42
45
|
return [];
|
|
46
|
+
}
|
|
43
47
|
return ["## User Identity", ownerLine, ""];
|
|
44
48
|
}
|
|
45
49
|
function buildTimeSection(params) {
|
|
46
|
-
if (!params.userTimezone)
|
|
50
|
+
if (!params.userTimezone) {
|
|
47
51
|
return [];
|
|
52
|
+
}
|
|
48
53
|
return ["## Current Date & Time", `Time zone: ${params.userTimezone}`, ""];
|
|
49
54
|
}
|
|
50
55
|
function buildReplyTagsSection(isMinimal) {
|
|
51
|
-
if (isMinimal)
|
|
56
|
+
if (isMinimal) {
|
|
52
57
|
return [];
|
|
58
|
+
}
|
|
53
59
|
return [
|
|
54
60
|
"## Reply Tags",
|
|
55
61
|
"To request a native reply/quote on supported surfaces, include one tag in your reply:",
|
|
62
|
+
"- Reply tags must be the very first token in the message (no leading text/newlines): [[reply_to_current]] your reply.",
|
|
56
63
|
"- [[reply_to_current]] replies to the triggering message.",
|
|
57
64
|
"- Prefer [[reply_to_current]]. Use [[reply_to:<id>]] only when an id was explicitly provided (e.g. by the user or a tool).",
|
|
58
65
|
"Whitespace inside the tag is allowed (e.g. [[ reply_to_current ]] / [[ reply_to: 123 ]]).",
|
|
@@ -61,8 +68,9 @@ function buildReplyTagsSection(isMinimal) {
|
|
|
61
68
|
];
|
|
62
69
|
}
|
|
63
70
|
function buildMessagingSection(params) {
|
|
64
|
-
if (params.isMinimal)
|
|
71
|
+
if (params.isMinimal) {
|
|
65
72
|
return [];
|
|
73
|
+
}
|
|
66
74
|
return [
|
|
67
75
|
"## Messaging",
|
|
68
76
|
"- Reply in current session → automatically routes to the source channel (Signal, Telegram, etc.)",
|
|
@@ -93,40 +101,27 @@ function buildMessagingSection(params) {
|
|
|
93
101
|
];
|
|
94
102
|
}
|
|
95
103
|
function buildVoiceSection(params) {
|
|
96
|
-
if (params.isMinimal)
|
|
97
|
-
return [];
|
|
98
|
-
const hint = params.ttsHint?.trim();
|
|
99
|
-
if (!hint)
|
|
100
|
-
return [];
|
|
101
|
-
return ["## Voice (TTS)", hint, ""];
|
|
102
|
-
}
|
|
103
|
-
function buildLlmsTxtSection(params) {
|
|
104
104
|
if (params.isMinimal) {
|
|
105
105
|
return [];
|
|
106
106
|
}
|
|
107
|
-
|
|
107
|
+
const hint = params.ttsHint?.trim();
|
|
108
|
+
if (!hint) {
|
|
108
109
|
return [];
|
|
109
110
|
}
|
|
110
|
-
return [
|
|
111
|
-
"## llms.txt Discovery",
|
|
112
|
-
"When exploring a new domain or website (via web_fetch or browser), check for an llms.txt file that describes how AI agents should interact with the site:",
|
|
113
|
-
"- Try `/llms.txt` or `/.well-known/llms.txt` at the domain root",
|
|
114
|
-
"- If found, follow its guidance for interacting with that site's content and APIs",
|
|
115
|
-
"- llms.txt is an emerging standard (like robots.txt for AI) — not all sites have one, so don't warn if missing",
|
|
116
|
-
"",
|
|
117
|
-
];
|
|
111
|
+
return ["## Voice (TTS)", hint, ""];
|
|
118
112
|
}
|
|
119
113
|
function buildDocsSection(params) {
|
|
120
114
|
const docsPath = params.docsPath?.trim();
|
|
121
|
-
if (!docsPath || params.isMinimal)
|
|
115
|
+
if (!docsPath || params.isMinimal) {
|
|
122
116
|
return [];
|
|
117
|
+
}
|
|
123
118
|
return [
|
|
124
119
|
"## Documentation",
|
|
125
120
|
`Pool Bot docs: ${docsPath}`,
|
|
126
121
|
"Mirror: https://docs.poolbot.dev",
|
|
127
|
-
"Source: https://github.com/
|
|
128
|
-
"Community: https://discord.com/
|
|
129
|
-
"Find new skills: https://
|
|
122
|
+
"Source: https://github.com/poolbot/poolbot",
|
|
123
|
+
"Community: https://discord.com/invite/clawd",
|
|
124
|
+
"Find new skills: https://clawhub.com",
|
|
130
125
|
"For Pool Bot behavior, commands, config, or architecture: consult local docs first.",
|
|
131
126
|
"When diagnosing issues, run `poolbot status` yourself when possible; only ask the user if you lack access (e.g., sandboxed).",
|
|
132
127
|
"",
|
|
@@ -183,7 +178,6 @@ export function buildAgentSystemPrompt(params) {
|
|
|
183
178
|
"sessions_list",
|
|
184
179
|
"sessions_history",
|
|
185
180
|
"sessions_send",
|
|
186
|
-
"sessions_spawn",
|
|
187
181
|
"subagents",
|
|
188
182
|
"session_status",
|
|
189
183
|
"image",
|
|
@@ -204,8 +198,9 @@ export function buildAgentSystemPrompt(params) {
|
|
|
204
198
|
const externalToolSummaries = new Map();
|
|
205
199
|
for (const [key, value] of Object.entries(params.toolSummaries ?? {})) {
|
|
206
200
|
const normalized = key.trim().toLowerCase();
|
|
207
|
-
if (!normalized || !value?.trim())
|
|
201
|
+
if (!normalized || !value?.trim()) {
|
|
208
202
|
continue;
|
|
203
|
+
}
|
|
209
204
|
externalToolSummaries.set(normalized, value.trim());
|
|
210
205
|
}
|
|
211
206
|
const extraTools = Array.from(new Set(normalizedTools.filter((tool) => !toolOrder.includes(tool))));
|
|
@@ -215,7 +210,7 @@ export function buildAgentSystemPrompt(params) {
|
|
|
215
210
|
const name = resolveToolName(tool);
|
|
216
211
|
return summary ? `- ${name}: ${summary}` : `- ${name}`;
|
|
217
212
|
});
|
|
218
|
-
for (const tool of extraTools.
|
|
213
|
+
for (const tool of extraTools.toSorted()) {
|
|
219
214
|
const summary = coreToolSummaries[tool] ?? externalToolSummaries.get(tool);
|
|
220
215
|
const name = resolveToolName(tool);
|
|
221
216
|
toolLines.push(summary ? `- ${name}: ${summary}` : `- ${name}`);
|
|
@@ -437,7 +432,6 @@ export function buildAgentSystemPrompt(params) {
|
|
|
437
432
|
messageToolHints: params.messageToolHints,
|
|
438
433
|
}),
|
|
439
434
|
...buildVoiceSection({ isMinimal, ttsHint: params.ttsHint }),
|
|
440
|
-
...buildLlmsTxtSection({ isMinimal, availableTools }),
|
|
441
435
|
];
|
|
442
436
|
if (extraSystemPrompt) {
|
|
443
437
|
// Use "Subagent Context" header for minimal mode (subagents), otherwise "Group Chat Context"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
const STRICT9_LEN = 9;
|
|
3
|
+
const TOOL_CALL_TYPES = new Set(["toolCall", "toolUse", "functionCall"]);
|
|
3
4
|
/**
|
|
4
5
|
* Sanitize a tool call ID to be compatible with various providers.
|
|
5
6
|
*
|
|
@@ -8,25 +9,63 @@ const STRICT9_LEN = 9;
|
|
|
8
9
|
*/
|
|
9
10
|
export function sanitizeToolCallId(id, mode = "strict") {
|
|
10
11
|
if (!id || typeof id !== "string") {
|
|
11
|
-
if (mode === "strict9")
|
|
12
|
+
if (mode === "strict9") {
|
|
12
13
|
return "defaultid";
|
|
14
|
+
}
|
|
13
15
|
return "defaulttoolid";
|
|
14
16
|
}
|
|
15
17
|
if (mode === "strict9") {
|
|
16
18
|
const alphanumericOnly = id.replace(/[^a-zA-Z0-9]/g, "");
|
|
17
|
-
if (alphanumericOnly.length >= STRICT9_LEN)
|
|
19
|
+
if (alphanumericOnly.length >= STRICT9_LEN) {
|
|
18
20
|
return alphanumericOnly.slice(0, STRICT9_LEN);
|
|
19
|
-
|
|
21
|
+
}
|
|
22
|
+
if (alphanumericOnly.length > 0) {
|
|
20
23
|
return shortHash(alphanumericOnly, STRICT9_LEN);
|
|
24
|
+
}
|
|
21
25
|
return shortHash("sanitized", STRICT9_LEN);
|
|
22
26
|
}
|
|
23
27
|
// Some providers require strictly alphanumeric tool call IDs.
|
|
24
28
|
const alphanumericOnly = id.replace(/[^a-zA-Z0-9]/g, "");
|
|
25
29
|
return alphanumericOnly.length > 0 ? alphanumericOnly : "sanitizedtoolid";
|
|
26
30
|
}
|
|
31
|
+
export function extractToolCallsFromAssistant(msg) {
|
|
32
|
+
const content = msg.content;
|
|
33
|
+
if (!Array.isArray(content)) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
const toolCalls = [];
|
|
37
|
+
for (const block of content) {
|
|
38
|
+
if (!block || typeof block !== "object") {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const rec = block;
|
|
42
|
+
if (typeof rec.id !== "string" || !rec.id) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (typeof rec.type === "string" && TOOL_CALL_TYPES.has(rec.type)) {
|
|
46
|
+
toolCalls.push({
|
|
47
|
+
id: rec.id,
|
|
48
|
+
name: typeof rec.name === "string" ? rec.name : undefined,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return toolCalls;
|
|
53
|
+
}
|
|
54
|
+
export function extractToolResultId(msg) {
|
|
55
|
+
const toolCallId = msg.toolCallId;
|
|
56
|
+
if (typeof toolCallId === "string" && toolCallId) {
|
|
57
|
+
return toolCallId;
|
|
58
|
+
}
|
|
59
|
+
const toolUseId = msg.toolUseId;
|
|
60
|
+
if (typeof toolUseId === "string" && toolUseId) {
|
|
61
|
+
return toolUseId;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
27
65
|
export function isValidCloudCodeAssistToolId(id, mode = "strict") {
|
|
28
|
-
if (!id || typeof id !== "string")
|
|
66
|
+
if (!id || typeof id !== "string") {
|
|
29
67
|
return false;
|
|
68
|
+
}
|
|
30
69
|
if (mode === "strict9") {
|
|
31
70
|
return /^[a-zA-Z0-9]{9}$/.test(id);
|
|
32
71
|
}
|
|
@@ -40,44 +79,51 @@ function makeUniqueToolId(params) {
|
|
|
40
79
|
if (params.mode === "strict9") {
|
|
41
80
|
const base = sanitizeToolCallId(params.id, params.mode);
|
|
42
81
|
const candidate = base.length >= STRICT9_LEN ? base.slice(0, STRICT9_LEN) : "";
|
|
43
|
-
if (candidate && !params.used.has(candidate))
|
|
82
|
+
if (candidate && !params.used.has(candidate)) {
|
|
44
83
|
return candidate;
|
|
84
|
+
}
|
|
45
85
|
for (let i = 0; i < 1000; i += 1) {
|
|
46
86
|
const hashed = shortHash(`${params.id}:${i}`, STRICT9_LEN);
|
|
47
|
-
if (!params.used.has(hashed))
|
|
87
|
+
if (!params.used.has(hashed)) {
|
|
48
88
|
return hashed;
|
|
89
|
+
}
|
|
49
90
|
}
|
|
50
91
|
return shortHash(`${params.id}:${Date.now()}`, STRICT9_LEN);
|
|
51
92
|
}
|
|
52
93
|
const MAX_LEN = 40;
|
|
53
94
|
const base = sanitizeToolCallId(params.id, params.mode).slice(0, MAX_LEN);
|
|
54
|
-
if (!params.used.has(base))
|
|
95
|
+
if (!params.used.has(base)) {
|
|
55
96
|
return base;
|
|
97
|
+
}
|
|
56
98
|
const hash = shortHash(params.id);
|
|
57
99
|
// Use separator based on mode: none for strict, underscore for non-strict variants
|
|
58
100
|
const separator = params.mode === "strict" ? "" : "_";
|
|
59
101
|
const maxBaseLen = MAX_LEN - separator.length - hash.length;
|
|
60
102
|
const clippedBase = base.length > maxBaseLen ? base.slice(0, maxBaseLen) : base;
|
|
61
103
|
const candidate = `${clippedBase}${separator}${hash}`;
|
|
62
|
-
if (!params.used.has(candidate))
|
|
104
|
+
if (!params.used.has(candidate)) {
|
|
63
105
|
return candidate;
|
|
106
|
+
}
|
|
64
107
|
for (let i = 2; i < 1000; i += 1) {
|
|
65
108
|
const suffix = params.mode === "strict" ? `x${i}` : `_${i}`;
|
|
66
109
|
const next = `${candidate.slice(0, MAX_LEN - suffix.length)}${suffix}`;
|
|
67
|
-
if (!params.used.has(next))
|
|
110
|
+
if (!params.used.has(next)) {
|
|
68
111
|
return next;
|
|
112
|
+
}
|
|
69
113
|
}
|
|
70
114
|
const ts = params.mode === "strict" ? `t${Date.now()}` : `_${Date.now()}`;
|
|
71
115
|
return `${candidate.slice(0, MAX_LEN - ts.length)}${ts}`;
|
|
72
116
|
}
|
|
73
117
|
function rewriteAssistantToolCallIds(params) {
|
|
74
118
|
const content = params.message.content;
|
|
75
|
-
if (!Array.isArray(content))
|
|
119
|
+
if (!Array.isArray(content)) {
|
|
76
120
|
return params.message;
|
|
121
|
+
}
|
|
77
122
|
let changed = false;
|
|
78
123
|
const next = content.map((block) => {
|
|
79
|
-
if (!block || typeof block !== "object")
|
|
124
|
+
if (!block || typeof block !== "object") {
|
|
80
125
|
return block;
|
|
126
|
+
}
|
|
81
127
|
const rec = block;
|
|
82
128
|
const type = rec.type;
|
|
83
129
|
const id = rec.id;
|
|
@@ -87,13 +133,15 @@ function rewriteAssistantToolCallIds(params) {
|
|
|
87
133
|
return block;
|
|
88
134
|
}
|
|
89
135
|
const nextId = params.resolve(id);
|
|
90
|
-
if (nextId === id)
|
|
136
|
+
if (nextId === id) {
|
|
91
137
|
return block;
|
|
138
|
+
}
|
|
92
139
|
changed = true;
|
|
93
140
|
return { ...block, id: nextId };
|
|
94
141
|
});
|
|
95
|
-
if (!changed)
|
|
142
|
+
if (!changed) {
|
|
96
143
|
return params.message;
|
|
144
|
+
}
|
|
97
145
|
return { ...params.message, content: next };
|
|
98
146
|
}
|
|
99
147
|
function rewriteToolResultIds(params) {
|
|
@@ -128,8 +176,9 @@ export function sanitizeToolCallIdsForCloudCodeAssist(messages, mode = "strict")
|
|
|
128
176
|
const used = new Set();
|
|
129
177
|
const resolve = (id) => {
|
|
130
178
|
const existing = map.get(id);
|
|
131
|
-
if (existing)
|
|
179
|
+
if (existing) {
|
|
132
180
|
return existing;
|
|
181
|
+
}
|
|
133
182
|
const next = makeUniqueToolId({ id, used, mode });
|
|
134
183
|
map.set(id, next);
|
|
135
184
|
used.add(next);
|
|
@@ -137,16 +186,18 @@ export function sanitizeToolCallIdsForCloudCodeAssist(messages, mode = "strict")
|
|
|
137
186
|
};
|
|
138
187
|
let changed = false;
|
|
139
188
|
const out = messages.map((msg) => {
|
|
140
|
-
if (!msg || typeof msg !== "object")
|
|
189
|
+
if (!msg || typeof msg !== "object") {
|
|
141
190
|
return msg;
|
|
191
|
+
}
|
|
142
192
|
const role = msg.role;
|
|
143
193
|
if (role === "assistant") {
|
|
144
194
|
const next = rewriteAssistantToolCallIds({
|
|
145
195
|
message: msg,
|
|
146
196
|
resolve,
|
|
147
197
|
});
|
|
148
|
-
if (next !== msg)
|
|
198
|
+
if (next !== msg) {
|
|
149
199
|
changed = true;
|
|
200
|
+
}
|
|
150
201
|
return next;
|
|
151
202
|
}
|
|
152
203
|
if (role === "toolResult") {
|
|
@@ -154,8 +205,9 @@ export function sanitizeToolCallIdsForCloudCodeAssist(messages, mode = "strict")
|
|
|
154
205
|
message: msg,
|
|
155
206
|
resolve,
|
|
156
207
|
});
|
|
157
|
-
if (next !== msg)
|
|
208
|
+
if (next !== msg) {
|
|
158
209
|
changed = true;
|
|
210
|
+
}
|
|
159
211
|
return next;
|
|
160
212
|
}
|
|
161
213
|
return msg;
|