@poolzin/pool-bot 2026.2.11 → 2026.2.18
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 +34 -0
- package/dist/agents/agent-scope.js +4 -0
- package/dist/agents/announce-idempotency.js +14 -0
- package/dist/agents/auth-profiles/usage.js +22 -0
- package/dist/agents/auth-profiles.js +1 -1
- package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +23 -0
- package/dist/agents/bash-tools.exec-runtime.js +438 -0
- package/dist/agents/bash-tools.shared.js +6 -0
- package/dist/agents/cli-runner/reliability.js +61 -0
- package/dist/agents/cli-watchdog-defaults.js +11 -0
- package/dist/agents/command-poll-backoff.js +63 -0
- package/dist/agents/current-time.js +16 -0
- package/dist/agents/glob-pattern.js +42 -0
- package/dist/agents/memory-search.js +33 -0
- package/dist/agents/model-alias-lines.js +18 -0
- package/dist/agents/model-auth-label.js +61 -0
- package/dist/agents/model-fallback.js +59 -8
- package/dist/agents/models-config.e2e-harness.js +115 -0
- package/dist/agents/ollama-stream.js +11 -3
- package/dist/agents/openclaw-tools.js +135 -0
- package/dist/agents/pi-auth-json.js +118 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +147 -0
- package/dist/agents/pi-embedded-subscribe.e2e-harness.js +90 -0
- package/dist/agents/pi-embedded-subscribe.handlers.compaction.js +63 -0
- package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +30 -0
- package/dist/agents/pi-extensions/session-manager-runtime-registry.js +23 -0
- package/dist/agents/pi-tools.before-tool-call.js +145 -4
- package/dist/agents/pi-tools.js +29 -9
- package/dist/agents/pi-tools.policy.js +85 -92
- package/dist/agents/pi-tools.schema.js +54 -27
- package/dist/agents/queued-file-writer.js +22 -0
- package/dist/agents/sandbox/docker.js +133 -40
- package/dist/agents/sandbox/fs-bridge.js +146 -0
- package/dist/agents/sandbox/fs-paths.js +205 -0
- package/dist/agents/sandbox/hash.js +4 -0
- package/dist/agents/sandbox/validate-sandbox-security.js +157 -0
- package/dist/agents/sandbox-paths.js +3 -0
- package/dist/agents/sandbox-tool-policy.js +26 -0
- package/dist/agents/sanitize-for-prompt.js +18 -0
- package/dist/agents/session-dirs.js +20 -0
- package/dist/agents/session-write-lock.js +203 -39
- package/dist/agents/skills/filter.js +24 -0
- package/dist/agents/skills/tools-dir.js +9 -0
- package/dist/agents/skills-install-download.js +290 -0
- package/dist/agents/skills-install-output.js +30 -0
- package/dist/agents/skills-install.download-test-utils.js +36 -0
- package/dist/agents/skills.e2e-test-helpers.js +13 -0
- package/dist/agents/subagent-announce-queue.js +59 -15
- package/dist/agents/subagent-depth.js +137 -0
- package/dist/agents/subagent-registry.js +448 -96
- package/dist/agents/subagent-spawn.js +262 -0
- package/dist/agents/system-prompt.js +52 -10
- package/dist/agents/test-helpers/fast-tool-stubs.js +18 -0
- package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +74 -0
- package/dist/agents/tool-display-common.js +782 -0
- package/dist/agents/tool-loop-detection.js +466 -0
- package/dist/agents/tool-policy.js +6 -0
- package/dist/agents/tools/image-tool.js +1 -1
- package/dist/agents/tools/sessions-access.js +178 -0
- package/dist/agents/tools/sessions-resolution.js +206 -0
- package/dist/agents/tools/subagents-tool.js +616 -0
- package/dist/agents/workspace-dir.js +18 -0
- package/dist/agents/workspace-dirs.js +14 -0
- package/dist/agents/workspace.js +70 -0
- package/dist/auto-reply/heartbeat-reply-payload.js +18 -0
- package/dist/auto-reply/reply/commands-export-session.js +163 -0
- package/dist/auto-reply/reply/commands-mesh.js +245 -0
- package/dist/auto-reply/reply/commands-setunset.js +28 -0
- package/dist/auto-reply/reply/commands-slash-parse.js +31 -0
- package/dist/auto-reply/reply/commands-system-prompt.js +117 -0
- package/dist/auto-reply/reply/directive-handling.levels.js +17 -0
- package/dist/auto-reply/reply/directive-handling.params.js +1 -0
- package/dist/auto-reply/reply/directive-parsing.js +36 -0
- package/dist/auto-reply/reply/dispatcher-registry.js +43 -0
- package/dist/auto-reply/reply/elevated-unavailable.js +20 -0
- package/dist/auto-reply/reply/post-compaction-audit.js +96 -0
- package/dist/auto-reply/reply/post-compaction-context.js +98 -0
- package/dist/auto-reply/reply/reply-delivery.js +92 -0
- package/dist/auto-reply/reply/session-reset-prompt.js +1 -0
- package/dist/auto-reply/reply/session-run-accounting.js +33 -0
- package/dist/auto-reply/reply.directive.directive-behavior.e2e-harness.js +115 -0
- package/dist/auto-reply/reply.directive.directive-behavior.e2e-mocks.js +12 -0
- package/dist/browser/bridge-auth-registry.js +26 -0
- package/dist/browser/client-actions-url.js +10 -0
- package/dist/browser/control-auth.js +73 -0
- package/dist/browser/csrf.js +64 -0
- package/dist/browser/http-auth.js +52 -0
- package/dist/browser/paths.js +37 -0
- package/dist/browser/proxy-files.js +32 -0
- package/dist/browser/pw-ai-state.js +7 -0
- package/dist/browser/resolved-config-refresh.js +42 -0
- package/dist/browser/routes/path-output.js +1 -0
- package/dist/browser/server-context.chrome-test-harness.js +20 -0
- package/dist/browser/server-middleware.js +31 -0
- package/dist/browser/test-port.js +16 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/file-resolver.js +43 -0
- package/dist/channels/account-summary.js +19 -0
- package/dist/channels/draft-stream-loop.js +77 -0
- package/dist/channels/plugins/account-helpers.js +26 -0
- package/dist/channels/telegram/allow-from.js +10 -0
- package/dist/cli/browser-cli-resize.js +22 -0
- package/dist/cli/browser-cli-shared.js +8 -0
- package/dist/cli/clawbot-cli.js +5 -0
- package/dist/cli/completion-cli.js +566 -0
- package/dist/cli/config-cli.js +63 -5
- package/dist/cli/daemon-cli/lifecycle-core.js +256 -0
- package/dist/cli/daemon-cli/register-service-commands.js +60 -0
- package/dist/cli/daemon-cli-compat.js +80 -0
- package/dist/cli/nodes-cli/pairing-render.js +26 -0
- package/dist/cli/program/action-reparse.js +17 -0
- package/dist/cli/program/command-registry.js +17 -0
- package/dist/cli/program/program-context.js +8 -0
- package/dist/cli/program/register.subclis.js +7 -0
- package/dist/cli/program/routes.js +233 -0
- package/dist/cli/qr-cli.js +132 -0
- package/dist/cli/requirements-test-fixtures.js +17 -0
- package/dist/cli/respawn-policy.js +4 -0
- package/dist/cli/shared/parse-port.js +18 -0
- package/dist/cli/skills-cli.format.js +241 -0
- package/dist/cli/update-cli/progress.js +121 -0
- package/dist/cli/update-cli/restart-helper.js +108 -0
- package/dist/cli/update-cli/shared.js +196 -0
- package/dist/cli/update-cli/status.js +97 -0
- package/dist/cli/update-cli/suppress-deprecations.js +17 -0
- package/dist/cli/update-cli/update-command.js +506 -0
- package/dist/cli/update-cli/wizard.js +130 -0
- package/dist/cli/update-cli.js +3 -9
- package/dist/cli/windows-argv.js +69 -0
- package/dist/commands/auth-choice-legacy.js +20 -0
- package/dist/commands/auth-choice.apply-helpers.js +8 -0
- package/dist/commands/channel-test-helpers.js +19 -0
- package/dist/commands/cleanup-plan.js +10 -0
- package/dist/commands/cleanup-utils.js +7 -0
- package/dist/commands/config-validation.js +15 -0
- package/dist/commands/doctor-completion.js +112 -0
- package/dist/commands/doctor-memory-search.js +119 -0
- package/dist/commands/doctor-session-locks.js +73 -0
- package/dist/commands/doctor.e2e-harness.js +364 -0
- package/dist/commands/gateway-presence.js +19 -0
- package/dist/commands/model-default.js +35 -0
- package/dist/commands/models/fallbacks-shared.js +102 -0
- package/dist/commands/models/shared.js +24 -0
- package/dist/commands/onboard-auth.config-gateways.js +64 -0
- package/dist/commands/onboard-auth.config-litellm.js +45 -0
- package/dist/commands/onboard-auth.config-shared.js +116 -0
- package/dist/commands/onboard-config.js +16 -0
- package/dist/commands/onboard-non-interactive.test-helpers.js +31 -0
- package/dist/commands/onboard-provider-auth-flags.js +136 -0
- package/dist/commands/openai-codex-oauth.js +40 -0
- package/dist/commands/test-runtime-config-helpers.js +21 -0
- package/dist/commands/test-wizard-helpers.js +68 -0
- package/dist/commands/vllm-setup.js +66 -0
- package/dist/compat/legacy-names.js +2 -0
- package/dist/config/backup-rotation.js +19 -0
- package/dist/config/env-preserve.js +122 -0
- package/dist/config/includes-scan.js +78 -0
- package/dist/config/plugins-allowlist.js +13 -0
- package/dist/config/schema.help.js +256 -0
- package/dist/config/schema.hints.js +189 -0
- package/dist/config/schema.irc.js +20 -0
- package/dist/config/schema.labels.js +317 -0
- package/dist/config/sessions/delivery-info.js +40 -0
- package/dist/config/types.irc.js +1 -0
- package/dist/config/zod-schema.agent-defaults.js +14 -0
- package/dist/config/zod-schema.agent-model.js +10 -0
- package/dist/config/zod-schema.agent-runtime.js +14 -0
- package/dist/config/zod-schema.allowdeny.js +35 -0
- package/dist/config/zod-schema.sensitive.js +4 -0
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -1
- package/dist/cron/isolated-agent/skills-snapshot.js +26 -0
- package/dist/cron/isolated-agent/subagent-followup.js +127 -0
- package/dist/cron/isolated-agent.mocks.js +12 -0
- package/dist/cron/isolated-agent.test-setup.js +22 -0
- package/dist/cron/legacy-delivery.js +43 -0
- package/dist/cron/webhook-url.js +22 -0
- package/dist/daemon/arg-split.js +40 -0
- package/dist/daemon/exec-file.js +23 -0
- package/dist/daemon/output.js +6 -0
- package/dist/daemon/runtime-format.js +31 -0
- package/dist/daemon/schtasks-exec.js +4 -0
- package/dist/daemon/service-audit.js +22 -0
- package/dist/discord/client.js +41 -0
- package/dist/discord/components-registry.js +57 -0
- package/dist/discord/components.js +816 -0
- package/dist/discord/guilds.js +12 -0
- package/dist/discord/monitor/gateway-plugin.js +48 -0
- package/dist/discord/monitor/presence.js +30 -0
- package/dist/discord/send.components.js +115 -0
- package/dist/discord/send.shared.js +4 -0
- package/dist/discord/ui.js +26 -0
- package/dist/discord/voice-message.js +254 -0
- package/dist/gateway/agent-event-assistant-text.js +5 -0
- package/dist/gateway/agent-prompt.js +33 -0
- package/dist/gateway/auth-rate-limit.js +136 -0
- package/dist/gateway/channel-health-monitor.js +114 -0
- package/dist/gateway/control-ui-contract.js +1 -0
- package/dist/gateway/control-ui-csp.js +15 -0
- package/dist/gateway/gateway-config-prompts.shared.js +25 -0
- package/dist/gateway/http-auth-helpers.js +18 -0
- package/dist/gateway/http-common.js +18 -0
- package/dist/gateway/http-endpoint-helpers.js +27 -0
- package/dist/gateway/node-invoke-sanitize.js +11 -0
- package/dist/gateway/node-invoke-system-run-approval.js +205 -0
- package/dist/gateway/probe-auth.js +21 -0
- package/dist/gateway/protocol/index.js +7 -2
- package/dist/gateway/protocol/schema/mesh.js +54 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +7 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server/ws-connection/auth-messages.js +54 -0
- package/dist/gateway/server-channels.js +11 -0
- package/dist/gateway/server-methods/attachment-normalize.js +16 -0
- package/dist/gateway/server-methods/base-hash.js +8 -0
- package/dist/gateway/server-methods/mesh.js +700 -0
- package/dist/gateway/server-methods/nodes.handlers.invoke-result.js +55 -0
- package/dist/gateway/server-methods/restart-request.js +13 -0
- package/dist/gateway/server-methods/validation.js +8 -0
- package/dist/gateway/server.agent.gateway-server-agent.mocks.js +35 -0
- package/dist/gateway/server.e2e-registry-helpers.js +1 -0
- package/dist/gateway/server.e2e-ws-harness.js +20 -0
- package/dist/gateway/test-helpers.js +2 -0
- package/dist/gateway/test-helpers.server.js +3 -1
- package/dist/gateway/test-http-response.js +12 -0
- package/dist/gateway/test-openai-responses-model.js +20 -0
- package/dist/gateway/test-temp-config.js +30 -0
- package/dist/gateway/test-with-server.js +32 -0
- package/dist/hooks/bundled/bootstrap-extra-files/handler.js +46 -0
- package/dist/imessage/monitor/abort-handler.js +23 -0
- package/dist/imessage/monitor/inbound-processing.js +346 -0
- package/dist/imessage/monitor/parse-notification.js +64 -0
- package/dist/imessage/target-parsing-helpers.js +92 -0
- package/dist/infra/archive.js +244 -20
- package/dist/infra/detect-package-manager.js +26 -0
- package/dist/infra/exec-approvals-allowlist.js +257 -0
- package/dist/infra/exec-approvals-analysis.js +770 -0
- package/dist/infra/exec-approvals.js +13 -0
- package/dist/infra/file-lock.js +1 -0
- package/dist/infra/gemini-auth.js +39 -0
- package/dist/infra/heartbeat-active-hours.js +85 -0
- package/dist/infra/heartbeat-events-filter.js +50 -0
- package/dist/infra/heartbeat-runner.test-utils.js +39 -0
- package/dist/infra/http-body.js +265 -0
- package/dist/infra/install-package-dir.js +50 -0
- package/dist/infra/install-safe-path.js +49 -0
- package/dist/infra/json-files.js +49 -0
- package/dist/infra/jsonl-socket.js +52 -0
- package/dist/infra/map-size.js +14 -0
- package/dist/infra/net/hostname.js +7 -0
- package/dist/infra/npm-registry-spec.js +39 -0
- package/dist/infra/openclaw-root.js +109 -0
- package/dist/infra/outbound/delivery-queue.js +214 -0
- package/dist/infra/outbound/identity.js +23 -0
- package/dist/infra/outbound/message-action-params.js +307 -0
- package/dist/infra/outbound/tool-payload.js +21 -0
- package/dist/infra/package-json.js +23 -0
- package/dist/infra/pairing-files.js +19 -0
- package/dist/infra/pairing-token.js +9 -0
- package/dist/infra/path-prepend.js +51 -0
- package/dist/infra/path-safety.js +16 -0
- package/dist/infra/process-respawn.js +49 -0
- package/dist/infra/runtime-status.js +16 -0
- package/dist/infra/session-cost-usage.types.js +1 -0
- package/dist/infra/session-maintenance-warning.js +89 -0
- package/dist/infra/system-run-command.js +78 -0
- package/dist/infra/tmp-openclaw-dir.js +81 -0
- package/dist/infra/tmp-poolbot-dir.js +2 -0
- package/dist/infra/update-channels.js +19 -0
- package/dist/line/actions.js +45 -0
- package/dist/line/channel-access-token.js +9 -0
- package/dist/line/flex-templates/basic-cards.js +332 -0
- package/dist/line/flex-templates/common.js +18 -0
- package/dist/line/flex-templates/media-control-cards.js +453 -0
- package/dist/line/flex-templates/message.js +10 -0
- package/dist/line/flex-templates/schedule-cards.js +399 -0
- package/dist/line/flex-templates/types.js +1 -0
- package/dist/line/webhook-node.js +100 -0
- package/dist/line/webhook-utils.js +11 -0
- package/dist/logging/diagnostic-session-state.js +73 -0
- package/dist/logging/diagnostic.js +22 -0
- package/dist/logging/timestamps.js +14 -0
- package/dist/markdown/whatsapp.js +62 -0
- package/dist/media/base64.js +34 -0
- package/dist/media/local-roots.js +32 -0
- package/dist/media/outbound-attachment.js +10 -0
- package/dist/media/read-response-with-limit.js +41 -0
- package/dist/media/sniff-mime-from-base64.js +19 -0
- package/dist/media-understanding/audio-preflight.js +67 -0
- package/dist/media-understanding/fs.js +13 -0
- package/dist/media-understanding/output-extract.js +26 -0
- package/dist/media-understanding/providers/audio.test-helpers.js +34 -0
- package/dist/media-understanding/providers/google/inline-data.js +64 -0
- package/dist/media-understanding/providers/shared.js +7 -0
- package/dist/media-understanding/runner.entries.js +459 -0
- package/dist/memory/batch-error-utils.js +11 -0
- package/dist/memory/batch-http.js +27 -0
- package/dist/memory/batch-output.js +29 -0
- package/dist/memory/batch-runner.js +22 -0
- package/dist/memory/batch-upload.js +23 -0
- package/dist/memory/batch-utils.js +26 -0
- package/dist/memory/embeddings-debug.js +11 -0
- package/dist/memory/embeddings-remote-client.js +22 -0
- package/dist/memory/embeddings-remote-fetch.js +14 -0
- package/dist/memory/embeddings.js +36 -9
- package/dist/memory/hybrid.js +24 -5
- package/dist/memory/manager-embedding-ops.js +616 -0
- package/dist/memory/manager-sync-ops.js +953 -0
- package/dist/memory/manager.js +76 -28
- package/dist/memory/mmr.js +164 -0
- package/dist/memory/qmd-manager.js +1061 -0
- package/dist/memory/qmd-query-parser.js +107 -0
- package/dist/memory/qmd-scope.js +93 -0
- package/dist/memory/query-expansion.js +331 -0
- package/dist/memory/search-manager.js +0 -1
- package/dist/memory/sync-index.js +21 -0
- package/dist/memory/sync-progress.js +22 -0
- package/dist/memory/sync-stale.js +30 -0
- package/dist/memory/temporal-decay.js +119 -0
- package/dist/memory/test-embeddings-mock.js +16 -0
- package/dist/memory/test-manager-helpers.js +14 -0
- package/dist/memory/test-runtime-mocks.js +11 -0
- package/dist/node-host/invoke-browser.js +177 -0
- package/dist/node-host/invoke.js +685 -0
- package/dist/pairing/setup-code.js +285 -0
- package/dist/plugin-sdk/account-id.js +1 -0
- package/dist/plugin-sdk/agent-media-payload.js +13 -0
- package/dist/plugin-sdk/allow-from.js +47 -0
- package/dist/plugin-sdk/command-auth.js +23 -0
- package/dist/plugin-sdk/config-paths.js +9 -0
- package/dist/plugin-sdk/file-lock.js +116 -0
- package/dist/plugin-sdk/json-store.js +31 -0
- package/dist/plugin-sdk/onboarding.js +28 -0
- package/dist/plugin-sdk/provider-auth-result.js +29 -0
- package/dist/plugin-sdk/slack-message-actions.js +133 -0
- package/dist/plugin-sdk/status-helpers.js +35 -0
- package/dist/plugin-sdk/text-chunking.js +31 -0
- package/dist/plugin-sdk/tool-send.js +12 -0
- package/dist/plugin-sdk/webhook-path.js +27 -0
- package/dist/plugin-sdk/webhook-targets.js +34 -0
- package/dist/plugins/hooks.test-helpers.js +21 -0
- package/dist/plugins/uninstall.js +171 -0
- package/dist/process/kill-tree.js +98 -0
- package/dist/process/supervisor/adapters/child.js +143 -0
- package/dist/process/supervisor/adapters/env.js +13 -0
- package/dist/process/supervisor/adapters/pty.js +148 -0
- package/dist/process/supervisor/index.js +10 -0
- package/dist/process/supervisor/registry.js +117 -0
- package/dist/process/supervisor/supervisor.js +244 -0
- package/dist/process/supervisor/types.js +1 -0
- package/dist/providers/google-shared.test-helpers.js +75 -0
- package/dist/security/audit-channel.js +419 -0
- package/dist/security/audit-tool-policy.js +1 -0
- package/dist/security/scan-paths.js +12 -0
- package/dist/sessions/input-provenance.js +55 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/shared/chat-content.js +31 -0
- package/dist/shared/chat-envelope.js +45 -0
- package/dist/shared/config-eval.js +117 -0
- package/dist/shared/device-auth.js +16 -0
- package/dist/shared/entry-metadata.js +9 -0
- package/dist/shared/entry-status.js +25 -0
- package/dist/shared/frontmatter.js +98 -0
- package/dist/shared/model-param-b.js +19 -0
- package/dist/shared/net/ipv4.js +17 -0
- package/dist/shared/node-match.js +53 -0
- package/dist/shared/pid-alive.js +12 -0
- package/dist/shared/process-scoped-map.js +10 -0
- package/dist/shared/requirements.js +128 -0
- package/dist/shared/subagents-format.js +84 -0
- package/dist/shared/usage-aggregates.js +28 -0
- package/dist/signal/monitor/mentions.js +45 -0
- package/dist/signal/rpc-context.js +19 -0
- package/dist/slack/blocks-fallback.js +76 -0
- package/dist/slack/blocks-input.js +40 -0
- package/dist/slack/draft-stream.js +106 -0
- package/dist/slack/message-actions.js +51 -0
- package/dist/slack/modal-metadata.js +32 -0
- package/dist/slack/monitor/events/interactions.js +462 -0
- package/dist/slack/monitor/room-context.js +17 -0
- package/dist/slack/stream-mode.js +41 -0
- package/dist/telegram/bot-native-command-menu.js +64 -0
- package/dist/telegram/bot.media.e2e-harness.js +81 -0
- package/dist/telegram/button-types.js +1 -0
- package/dist/telegram/group-access.js +65 -0
- package/dist/telegram/outbound-params.js +21 -0
- package/dist/telegram/poll-vote-cache.js +21 -0
- package/dist/terminal/health-style.js +36 -0
- package/dist/test-utils/chunk-test-helpers.js +21 -0
- package/dist/test-utils/env.js +72 -0
- package/dist/test-utils/exec-assertions.js +12 -0
- package/dist/test-utils/imessage-test-plugin.js +54 -0
- package/dist/test-utils/mock-http-response.js +17 -0
- package/dist/test-utils/vitest-mock-fn.js +1 -0
- package/dist/tts/tts-core.js +550 -0
- package/dist/utils/chunk-items.js +10 -0
- package/dist/utils/reaction-level.js +52 -0
- package/dist/utils/safe-json.js +22 -0
- package/dist/utils/with-timeout.js +14 -0
- package/dist/web/media.js +17 -5
- package/dist/whatsapp/resolve-outbound-target.js +42 -0
- package/dist/wizard/onboarding.completion.js +74 -0
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/bluebubbles/src/account-resolve.ts +29 -0
- package/extensions/bluebubbles/src/monitor-normalize.ts +796 -0
- package/extensions/bluebubbles/src/monitor-processing.ts +1007 -0
- package/extensions/bluebubbles/src/monitor-reply-cache.ts +185 -0
- package/extensions/bluebubbles/src/monitor-shared.ts +51 -0
- package/extensions/bluebubbles/src/multipart.ts +32 -0
- package/extensions/bluebubbles/src/send-helpers.ts +53 -0
- package/extensions/bluebubbles/src/test-harness.ts +50 -0
- package/extensions/bluebubbles/src/test-mocks.ts +11 -0
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/device-pair/index.ts +554 -0
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/discord/src/channel.js +366 -0
- package/extensions/discord/src/runtime.js +10 -0
- package/extensions/feishu/index.ts +63 -0
- package/extensions/feishu/src/accounts.ts +114 -0
- package/extensions/feishu/src/bitable.ts +739 -0
- package/extensions/feishu/src/bot.ts +965 -0
- package/extensions/feishu/src/channel.ts +351 -0
- package/extensions/feishu/src/client.ts +118 -0
- package/extensions/feishu/src/config-schema.ts +206 -0
- package/extensions/feishu/src/dedup.ts +33 -0
- package/extensions/feishu/src/directory.ts +177 -0
- package/extensions/feishu/src/doc-schema.ts +47 -0
- package/extensions/feishu/src/docx.ts +536 -0
- package/extensions/feishu/src/drive-schema.ts +46 -0
- package/extensions/feishu/src/drive.ts +227 -0
- package/extensions/feishu/src/dynamic-agent.ts +131 -0
- package/extensions/feishu/src/media.ts +449 -0
- package/extensions/feishu/src/mention.ts +126 -0
- package/extensions/feishu/src/monitor.ts +330 -0
- package/extensions/feishu/src/onboarding.ts +359 -0
- package/extensions/feishu/src/outbound.ts +55 -0
- package/extensions/feishu/src/perm-schema.ts +52 -0
- package/extensions/feishu/src/perm.ts +173 -0
- package/extensions/feishu/src/policy.ts +84 -0
- package/extensions/feishu/src/probe.ts +44 -0
- package/extensions/feishu/src/reactions.ts +160 -0
- package/extensions/feishu/src/reply-dispatcher.ts +239 -0
- package/extensions/feishu/src/runtime.ts +14 -0
- package/extensions/feishu/src/send-result.ts +29 -0
- package/extensions/feishu/src/send.ts +335 -0
- package/extensions/feishu/src/streaming-card.ts +223 -0
- package/extensions/feishu/src/targets.ts +78 -0
- package/extensions/feishu/src/tools-config.ts +21 -0
- package/extensions/feishu/src/types.ts +81 -0
- package/extensions/feishu/src/typing.ts +80 -0
- package/extensions/feishu/src/wiki-schema.ts +55 -0
- package/extensions/feishu/src/wiki.ts +232 -0
- 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/imessage/src/channel.js +253 -0
- package/extensions/imessage/src/runtime.js +10 -0
- package/extensions/irc/index.ts +17 -0
- package/extensions/irc/src/accounts.ts +268 -0
- package/extensions/irc/src/channel.ts +367 -0
- package/extensions/irc/src/client.ts +439 -0
- package/extensions/irc/src/config-schema.ts +97 -0
- package/extensions/irc/src/connect-options.ts +30 -0
- package/extensions/irc/src/control-chars.ts +22 -0
- package/extensions/irc/src/inbound.ts +334 -0
- package/extensions/irc/src/monitor.ts +147 -0
- package/extensions/irc/src/normalize.ts +117 -0
- package/extensions/irc/src/onboarding.ts +479 -0
- package/extensions/irc/src/policy.ts +157 -0
- package/extensions/irc/src/probe.ts +53 -0
- package/extensions/irc/src/protocol.ts +169 -0
- package/extensions/irc/src/runtime.ts +14 -0
- package/extensions/irc/src/send.ts +88 -0
- package/extensions/irc/src/types.ts +93 -0
- 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/matrix/src/matrix/client-bootstrap.ts +39 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/monitor-onchar.ts +25 -0
- package/extensions/mattermost/src/mattermost/monitor-websocket.ts +221 -0
- package/extensions/mattermost/src/mattermost/reactions.ts +130 -0
- package/extensions/mattermost/src/mattermost/reconnect.ts +103 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/index.ts +161 -0
- package/extensions/minimax-portal-auth/oauth.ts +247 -0
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/msteams/src/file-lock.ts +1 -0
- package/extensions/msteams/src/graph.ts +92 -0
- package/extensions/msteams/src/mentions.ts +114 -0
- package/extensions/msteams/src/test-runtime.ts +16 -0
- 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/index.ts +177 -0
- package/extensions/phone-control/index.ts +421 -0
- package/extensions/shared/resolve-target-test-helpers.ts +66 -0
- package/extensions/signal/package.json +1 -1
- package/extensions/signal/src/channel.js +273 -0
- package/extensions/signal/src/runtime.js +10 -0
- package/extensions/slack/package.json +1 -1
- package/extensions/slack/src/channel.js +489 -0
- package/extensions/slack/src/runtime.js +10 -0
- package/extensions/talk-voice/index.ts +150 -0
- package/extensions/telegram/package.json +1 -1
- package/extensions/telegram/src/channel.js +424 -0
- package/extensions/telegram/src/runtime.js +10 -0
- package/extensions/thread-ownership/index.ts +133 -0
- package/extensions/tlon/package.json +1 -1
- package/extensions/tlon/src/account-fields.ts +25 -0
- package/extensions/tlon/src/urbit/base-url.ts +57 -0
- package/extensions/tlon/src/urbit/channel-client.ts +157 -0
- package/extensions/tlon/src/urbit/channel-ops.ts +164 -0
- package/extensions/tlon/src/urbit/context.ts +47 -0
- package/extensions/tlon/src/urbit/errors.ts +51 -0
- package/extensions/tlon/src/urbit/fetch.ts +39 -0
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/twitch/src/test-fixtures.ts +30 -0
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/voice-call/src/allowlist.ts +19 -0
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/whatsapp/src/channel.js +429 -0
- package/extensions/whatsapp/src/runtime.js +10 -0
- 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
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
3
|
+
import { isPlainObject } from "../utils.js";
|
|
4
|
+
const log = createSubsystemLogger("agents/loop-detection");
|
|
5
|
+
export const TOOL_CALL_HISTORY_SIZE = 30;
|
|
6
|
+
export const WARNING_THRESHOLD = 10;
|
|
7
|
+
export const CRITICAL_THRESHOLD = 20;
|
|
8
|
+
export const GLOBAL_CIRCUIT_BREAKER_THRESHOLD = 30;
|
|
9
|
+
const DEFAULT_LOOP_DETECTION_CONFIG = {
|
|
10
|
+
enabled: false,
|
|
11
|
+
historySize: TOOL_CALL_HISTORY_SIZE,
|
|
12
|
+
warningThreshold: WARNING_THRESHOLD,
|
|
13
|
+
criticalThreshold: CRITICAL_THRESHOLD,
|
|
14
|
+
globalCircuitBreakerThreshold: GLOBAL_CIRCUIT_BREAKER_THRESHOLD,
|
|
15
|
+
detectors: {
|
|
16
|
+
genericRepeat: true,
|
|
17
|
+
knownPollNoProgress: true,
|
|
18
|
+
pingPong: true,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
function asPositiveInt(value, fallback) {
|
|
22
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
function resolveLoopDetectionConfig(config) {
|
|
28
|
+
let warningThreshold = asPositiveInt(config?.warningThreshold, DEFAULT_LOOP_DETECTION_CONFIG.warningThreshold);
|
|
29
|
+
let criticalThreshold = asPositiveInt(config?.criticalThreshold, DEFAULT_LOOP_DETECTION_CONFIG.criticalThreshold);
|
|
30
|
+
let globalCircuitBreakerThreshold = asPositiveInt(config?.globalCircuitBreakerThreshold, DEFAULT_LOOP_DETECTION_CONFIG.globalCircuitBreakerThreshold);
|
|
31
|
+
if (criticalThreshold <= warningThreshold) {
|
|
32
|
+
criticalThreshold = warningThreshold + 1;
|
|
33
|
+
}
|
|
34
|
+
if (globalCircuitBreakerThreshold <= criticalThreshold) {
|
|
35
|
+
globalCircuitBreakerThreshold = criticalThreshold + 1;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
enabled: config?.enabled ?? DEFAULT_LOOP_DETECTION_CONFIG.enabled,
|
|
39
|
+
historySize: asPositiveInt(config?.historySize, DEFAULT_LOOP_DETECTION_CONFIG.historySize),
|
|
40
|
+
warningThreshold,
|
|
41
|
+
criticalThreshold,
|
|
42
|
+
globalCircuitBreakerThreshold,
|
|
43
|
+
detectors: {
|
|
44
|
+
genericRepeat: config?.detectors?.genericRepeat ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.genericRepeat,
|
|
45
|
+
knownPollNoProgress: config?.detectors?.knownPollNoProgress ??
|
|
46
|
+
DEFAULT_LOOP_DETECTION_CONFIG.detectors.knownPollNoProgress,
|
|
47
|
+
pingPong: config?.detectors?.pingPong ?? DEFAULT_LOOP_DETECTION_CONFIG.detectors.pingPong,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Hash a tool call for pattern matching.
|
|
53
|
+
* Uses tool name + deterministic JSON serialization digest of params.
|
|
54
|
+
*/
|
|
55
|
+
export function hashToolCall(toolName, params) {
|
|
56
|
+
return `${toolName}:${digestStable(params)}`;
|
|
57
|
+
}
|
|
58
|
+
function stableStringify(value) {
|
|
59
|
+
if (value === null || typeof value !== "object") {
|
|
60
|
+
return JSON.stringify(value);
|
|
61
|
+
}
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
return `[${value.map(stableStringify).join(",")}]`;
|
|
64
|
+
}
|
|
65
|
+
const obj = value;
|
|
66
|
+
const keys = Object.keys(obj).toSorted();
|
|
67
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`).join(",")}}`;
|
|
68
|
+
}
|
|
69
|
+
function digestStable(value) {
|
|
70
|
+
const serialized = stableStringifyFallback(value);
|
|
71
|
+
return createHash("sha256").update(serialized).digest("hex");
|
|
72
|
+
}
|
|
73
|
+
function stableStringifyFallback(value) {
|
|
74
|
+
try {
|
|
75
|
+
return stableStringify(value);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
if (value === null || value === undefined) {
|
|
79
|
+
return `${value}`;
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === "string") {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
85
|
+
return `${value}`;
|
|
86
|
+
}
|
|
87
|
+
if (value instanceof Error) {
|
|
88
|
+
return `${value.name}:${value.message}`;
|
|
89
|
+
}
|
|
90
|
+
return Object.prototype.toString.call(value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function isKnownPollToolCall(toolName, params) {
|
|
94
|
+
if (toolName === "command_status") {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (toolName !== "process" || !isPlainObject(params)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const action = params.action;
|
|
101
|
+
return action === "poll" || action === "log";
|
|
102
|
+
}
|
|
103
|
+
function extractTextContent(result) {
|
|
104
|
+
if (!isPlainObject(result) || !Array.isArray(result.content)) {
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
return result.content
|
|
108
|
+
.filter((entry) => isPlainObject(entry) && typeof entry.type === "string" && typeof entry.text === "string")
|
|
109
|
+
.map((entry) => entry.text)
|
|
110
|
+
.join("\n")
|
|
111
|
+
.trim();
|
|
112
|
+
}
|
|
113
|
+
function formatErrorForHash(error) {
|
|
114
|
+
if (error instanceof Error) {
|
|
115
|
+
return error.message || error.name;
|
|
116
|
+
}
|
|
117
|
+
if (typeof error === "string") {
|
|
118
|
+
return error;
|
|
119
|
+
}
|
|
120
|
+
if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") {
|
|
121
|
+
return `${error}`;
|
|
122
|
+
}
|
|
123
|
+
return stableStringify(error);
|
|
124
|
+
}
|
|
125
|
+
function hashToolOutcome(toolName, params, result, error) {
|
|
126
|
+
if (error !== undefined) {
|
|
127
|
+
return `error:${digestStable(formatErrorForHash(error))}`;
|
|
128
|
+
}
|
|
129
|
+
if (!isPlainObject(result)) {
|
|
130
|
+
return result === undefined ? undefined : digestStable(result);
|
|
131
|
+
}
|
|
132
|
+
const details = isPlainObject(result.details) ? result.details : {};
|
|
133
|
+
const text = extractTextContent(result);
|
|
134
|
+
if (isKnownPollToolCall(toolName, params) && toolName === "process" && isPlainObject(params)) {
|
|
135
|
+
const action = params.action;
|
|
136
|
+
if (action === "poll") {
|
|
137
|
+
return digestStable({
|
|
138
|
+
action,
|
|
139
|
+
status: details.status,
|
|
140
|
+
exitCode: details.exitCode ?? null,
|
|
141
|
+
exitSignal: details.exitSignal ?? null,
|
|
142
|
+
aggregated: details.aggregated ?? null,
|
|
143
|
+
text,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (action === "log") {
|
|
147
|
+
return digestStable({
|
|
148
|
+
action,
|
|
149
|
+
status: details.status,
|
|
150
|
+
totalLines: details.totalLines ?? null,
|
|
151
|
+
totalChars: details.totalChars ?? null,
|
|
152
|
+
truncated: details.truncated ?? null,
|
|
153
|
+
exitCode: details.exitCode ?? null,
|
|
154
|
+
exitSignal: details.exitSignal ?? null,
|
|
155
|
+
text,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return digestStable({
|
|
160
|
+
details,
|
|
161
|
+
text,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function getNoProgressStreak(history, toolName, argsHash) {
|
|
165
|
+
let streak = 0;
|
|
166
|
+
let latestResultHash;
|
|
167
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
168
|
+
const record = history[i];
|
|
169
|
+
if (!record || record.toolName !== toolName || record.argsHash !== argsHash) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (typeof record.resultHash !== "string" || !record.resultHash) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (!latestResultHash) {
|
|
176
|
+
latestResultHash = record.resultHash;
|
|
177
|
+
streak = 1;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (record.resultHash !== latestResultHash) {
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
streak += 1;
|
|
184
|
+
}
|
|
185
|
+
return { count: streak, latestResultHash };
|
|
186
|
+
}
|
|
187
|
+
function getPingPongStreak(history, currentSignature) {
|
|
188
|
+
const last = history.at(-1);
|
|
189
|
+
if (!last) {
|
|
190
|
+
return { count: 0, noProgressEvidence: false };
|
|
191
|
+
}
|
|
192
|
+
let otherSignature;
|
|
193
|
+
let otherToolName;
|
|
194
|
+
for (let i = history.length - 2; i >= 0; i -= 1) {
|
|
195
|
+
const call = history[i];
|
|
196
|
+
if (!call) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (call.argsHash !== last.argsHash) {
|
|
200
|
+
otherSignature = call.argsHash;
|
|
201
|
+
otherToolName = call.toolName;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (!otherSignature || !otherToolName) {
|
|
206
|
+
return { count: 0, noProgressEvidence: false };
|
|
207
|
+
}
|
|
208
|
+
let alternatingTailCount = 0;
|
|
209
|
+
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
210
|
+
const call = history[i];
|
|
211
|
+
if (!call) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const expected = alternatingTailCount % 2 === 0 ? last.argsHash : otherSignature;
|
|
215
|
+
if (call.argsHash !== expected) {
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
alternatingTailCount += 1;
|
|
219
|
+
}
|
|
220
|
+
if (alternatingTailCount < 2) {
|
|
221
|
+
return { count: 0, noProgressEvidence: false };
|
|
222
|
+
}
|
|
223
|
+
const expectedCurrentSignature = otherSignature;
|
|
224
|
+
if (currentSignature !== expectedCurrentSignature) {
|
|
225
|
+
return { count: 0, noProgressEvidence: false };
|
|
226
|
+
}
|
|
227
|
+
const tailStart = Math.max(0, history.length - alternatingTailCount);
|
|
228
|
+
let firstHashA;
|
|
229
|
+
let firstHashB;
|
|
230
|
+
let noProgressEvidence = true;
|
|
231
|
+
for (let i = tailStart; i < history.length; i += 1) {
|
|
232
|
+
const call = history[i];
|
|
233
|
+
if (!call) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (!call.resultHash) {
|
|
237
|
+
noProgressEvidence = false;
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
if (call.argsHash === last.argsHash) {
|
|
241
|
+
if (!firstHashA) {
|
|
242
|
+
firstHashA = call.resultHash;
|
|
243
|
+
}
|
|
244
|
+
else if (firstHashA !== call.resultHash) {
|
|
245
|
+
noProgressEvidence = false;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (call.argsHash === otherSignature) {
|
|
251
|
+
if (!firstHashB) {
|
|
252
|
+
firstHashB = call.resultHash;
|
|
253
|
+
}
|
|
254
|
+
else if (firstHashB !== call.resultHash) {
|
|
255
|
+
noProgressEvidence = false;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
noProgressEvidence = false;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
// Need repeated stable outcomes on both sides before treating ping-pong as no-progress.
|
|
264
|
+
if (!firstHashA || !firstHashB) {
|
|
265
|
+
noProgressEvidence = false;
|
|
266
|
+
}
|
|
267
|
+
return {
|
|
268
|
+
count: alternatingTailCount + 1,
|
|
269
|
+
pairedToolName: last.toolName,
|
|
270
|
+
pairedSignature: last.argsHash,
|
|
271
|
+
noProgressEvidence,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function canonicalPairKey(signatureA, signatureB) {
|
|
275
|
+
return [signatureA, signatureB].toSorted().join("|");
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Detect if an agent is stuck in a repetitive tool call loop.
|
|
279
|
+
* Checks if the same tool+params combination has been called excessively.
|
|
280
|
+
*/
|
|
281
|
+
export function detectToolCallLoop(state, toolName, params, config) {
|
|
282
|
+
const resolvedConfig = resolveLoopDetectionConfig(config);
|
|
283
|
+
if (!resolvedConfig.enabled) {
|
|
284
|
+
return { stuck: false };
|
|
285
|
+
}
|
|
286
|
+
const history = state.toolCallHistory ?? [];
|
|
287
|
+
const currentHash = hashToolCall(toolName, params);
|
|
288
|
+
const noProgress = getNoProgressStreak(history, toolName, currentHash);
|
|
289
|
+
const noProgressStreak = noProgress.count;
|
|
290
|
+
const knownPollTool = isKnownPollToolCall(toolName, params);
|
|
291
|
+
const pingPong = getPingPongStreak(history, currentHash);
|
|
292
|
+
if (noProgressStreak >= resolvedConfig.globalCircuitBreakerThreshold) {
|
|
293
|
+
log.error(`Global circuit breaker triggered: ${toolName} repeated ${noProgressStreak} times with no progress`);
|
|
294
|
+
return {
|
|
295
|
+
stuck: true,
|
|
296
|
+
level: "critical",
|
|
297
|
+
detector: "global_circuit_breaker",
|
|
298
|
+
count: noProgressStreak,
|
|
299
|
+
message: `CRITICAL: ${toolName} has repeated identical no-progress outcomes ${noProgressStreak} times. Session execution blocked by global circuit breaker to prevent runaway loops.`,
|
|
300
|
+
warningKey: `global:${toolName}:${currentHash}:${noProgress.latestResultHash ?? "none"}`,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (knownPollTool &&
|
|
304
|
+
resolvedConfig.detectors.knownPollNoProgress &&
|
|
305
|
+
noProgressStreak >= resolvedConfig.criticalThreshold) {
|
|
306
|
+
log.error(`Critical polling loop detected: ${toolName} repeated ${noProgressStreak} times`);
|
|
307
|
+
return {
|
|
308
|
+
stuck: true,
|
|
309
|
+
level: "critical",
|
|
310
|
+
detector: "known_poll_no_progress",
|
|
311
|
+
count: noProgressStreak,
|
|
312
|
+
message: `CRITICAL: Called ${toolName} with identical arguments and no progress ${noProgressStreak} times. This appears to be a stuck polling loop. Session execution blocked to prevent resource waste.`,
|
|
313
|
+
warningKey: `poll:${toolName}:${currentHash}:${noProgress.latestResultHash ?? "none"}`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
if (knownPollTool &&
|
|
317
|
+
resolvedConfig.detectors.knownPollNoProgress &&
|
|
318
|
+
noProgressStreak >= resolvedConfig.warningThreshold) {
|
|
319
|
+
log.warn(`Polling loop warning: ${toolName} repeated ${noProgressStreak} times`);
|
|
320
|
+
return {
|
|
321
|
+
stuck: true,
|
|
322
|
+
level: "warning",
|
|
323
|
+
detector: "known_poll_no_progress",
|
|
324
|
+
count: noProgressStreak,
|
|
325
|
+
message: `WARNING: You have called ${toolName} ${noProgressStreak} times with identical arguments and no progress. Stop polling and either (1) increase wait time between checks, or (2) report the task as failed if the process is stuck.`,
|
|
326
|
+
warningKey: `poll:${toolName}:${currentHash}:${noProgress.latestResultHash ?? "none"}`,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const pingPongWarningKey = pingPong.pairedSignature
|
|
330
|
+
? `pingpong:${canonicalPairKey(currentHash, pingPong.pairedSignature)}`
|
|
331
|
+
: `pingpong:${toolName}:${currentHash}`;
|
|
332
|
+
if (resolvedConfig.detectors.pingPong &&
|
|
333
|
+
pingPong.count >= resolvedConfig.criticalThreshold &&
|
|
334
|
+
pingPong.noProgressEvidence) {
|
|
335
|
+
log.error(`Critical ping-pong loop detected: alternating calls count=${pingPong.count} currentTool=${toolName}`);
|
|
336
|
+
return {
|
|
337
|
+
stuck: true,
|
|
338
|
+
level: "critical",
|
|
339
|
+
detector: "ping_pong",
|
|
340
|
+
count: pingPong.count,
|
|
341
|
+
message: `CRITICAL: You are alternating between repeated tool-call patterns (${pingPong.count} consecutive calls) with no progress. This appears to be a stuck ping-pong loop. Session execution blocked to prevent resource waste.`,
|
|
342
|
+
pairedToolName: pingPong.pairedToolName,
|
|
343
|
+
warningKey: pingPongWarningKey,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
if (resolvedConfig.detectors.pingPong && pingPong.count >= resolvedConfig.warningThreshold) {
|
|
347
|
+
log.warn(`Ping-pong loop warning: alternating calls count=${pingPong.count} currentTool=${toolName}`);
|
|
348
|
+
return {
|
|
349
|
+
stuck: true,
|
|
350
|
+
level: "warning",
|
|
351
|
+
detector: "ping_pong",
|
|
352
|
+
count: pingPong.count,
|
|
353
|
+
message: `WARNING: You are alternating between repeated tool-call patterns (${pingPong.count} consecutive calls). This looks like a ping-pong loop; stop retrying and report the task as failed.`,
|
|
354
|
+
pairedToolName: pingPong.pairedToolName,
|
|
355
|
+
warningKey: pingPongWarningKey,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
// Generic detector: warn-only for repeated identical calls.
|
|
359
|
+
const recentCount = history.filter((h) => h.toolName === toolName && h.argsHash === currentHash).length;
|
|
360
|
+
if (!knownPollTool &&
|
|
361
|
+
resolvedConfig.detectors.genericRepeat &&
|
|
362
|
+
recentCount >= resolvedConfig.warningThreshold) {
|
|
363
|
+
log.warn(`Loop warning: ${toolName} called ${recentCount} times with identical arguments`);
|
|
364
|
+
return {
|
|
365
|
+
stuck: true,
|
|
366
|
+
level: "warning",
|
|
367
|
+
detector: "generic_repeat",
|
|
368
|
+
count: recentCount,
|
|
369
|
+
message: `WARNING: You have called ${toolName} ${recentCount} times with identical arguments. If this is not making progress, stop retrying and report the task as failed.`,
|
|
370
|
+
warningKey: `generic:${toolName}:${currentHash}`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return { stuck: false };
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Record a tool call in the session's history for loop detection.
|
|
377
|
+
* Maintains sliding window of last N calls.
|
|
378
|
+
*/
|
|
379
|
+
export function recordToolCall(state, toolName, params, toolCallId, config) {
|
|
380
|
+
const resolvedConfig = resolveLoopDetectionConfig(config);
|
|
381
|
+
if (!state.toolCallHistory) {
|
|
382
|
+
state.toolCallHistory = [];
|
|
383
|
+
}
|
|
384
|
+
state.toolCallHistory.push({
|
|
385
|
+
toolName,
|
|
386
|
+
argsHash: hashToolCall(toolName, params),
|
|
387
|
+
toolCallId,
|
|
388
|
+
timestamp: Date.now(),
|
|
389
|
+
});
|
|
390
|
+
if (state.toolCallHistory.length > resolvedConfig.historySize) {
|
|
391
|
+
state.toolCallHistory.shift();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Record a completed tool call outcome so loop detection can identify no-progress repeats.
|
|
396
|
+
*/
|
|
397
|
+
export function recordToolCallOutcome(state, params) {
|
|
398
|
+
const resolvedConfig = resolveLoopDetectionConfig(params.config);
|
|
399
|
+
const resultHash = hashToolOutcome(params.toolName, params.toolParams, params.result, params.error);
|
|
400
|
+
if (!resultHash) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (!state.toolCallHistory) {
|
|
404
|
+
state.toolCallHistory = [];
|
|
405
|
+
}
|
|
406
|
+
const argsHash = hashToolCall(params.toolName, params.toolParams);
|
|
407
|
+
let matched = false;
|
|
408
|
+
for (let i = state.toolCallHistory.length - 1; i >= 0; i -= 1) {
|
|
409
|
+
const call = state.toolCallHistory[i];
|
|
410
|
+
if (!call) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (params.toolCallId && call.toolCallId !== params.toolCallId) {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (call.toolName !== params.toolName || call.argsHash !== argsHash) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (call.resultHash !== undefined) {
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
call.resultHash = resultHash;
|
|
423
|
+
matched = true;
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
if (!matched) {
|
|
427
|
+
state.toolCallHistory.push({
|
|
428
|
+
toolName: params.toolName,
|
|
429
|
+
argsHash,
|
|
430
|
+
toolCallId: params.toolCallId,
|
|
431
|
+
resultHash,
|
|
432
|
+
timestamp: Date.now(),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
if (state.toolCallHistory.length > resolvedConfig.historySize) {
|
|
436
|
+
state.toolCallHistory.splice(0, state.toolCallHistory.length - resolvedConfig.historySize);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Get current tool call statistics for a session (for debugging/monitoring).
|
|
441
|
+
*/
|
|
442
|
+
export function getToolCallStats(state) {
|
|
443
|
+
const history = state.toolCallHistory ?? [];
|
|
444
|
+
const patterns = new Map();
|
|
445
|
+
for (const call of history) {
|
|
446
|
+
const key = call.argsHash;
|
|
447
|
+
const existing = patterns.get(key);
|
|
448
|
+
if (existing) {
|
|
449
|
+
existing.count += 1;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
patterns.set(key, { toolName: call.toolName, count: 1 });
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
let mostFrequent = null;
|
|
456
|
+
for (const pattern of patterns.values()) {
|
|
457
|
+
if (!mostFrequent || pattern.count > mostFrequent.count) {
|
|
458
|
+
mostFrequent = pattern;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return {
|
|
462
|
+
totalCalls: history.length,
|
|
463
|
+
uniquePatterns: patterns.size,
|
|
464
|
+
mostFrequent,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
@@ -221,3 +221,9 @@ export function resolveToolProfilePolicy(profile) {
|
|
|
221
221
|
deny: resolved.deny ? [...resolved.deny] : undefined,
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
|
+
export function mergeAlsoAllowPolicy(policy, alsoAllow) {
|
|
225
|
+
if (!policy?.allow || !Array.isArray(alsoAllow) || alsoAllow.length === 0) {
|
|
226
|
+
return policy;
|
|
227
|
+
}
|
|
228
|
+
return { ...policy, allow: Array.from(new Set([...policy.allow, ...alsoAllow])) };
|
|
229
|
+
}
|
|
@@ -310,7 +310,7 @@ export function createImageTool(options) {
|
|
|
310
310
|
const modelOverride = typeof record.model === "string" && record.model.trim() ? record.model.trim() : undefined;
|
|
311
311
|
const maxBytesMb = typeof record.maxBytesMb === "number" ? record.maxBytesMb : undefined;
|
|
312
312
|
const maxBytes = pickMaxBytes(options?.config, maxBytesMb);
|
|
313
|
-
const sandboxRoot = options?.sandboxRoot?.trim();
|
|
313
|
+
const sandboxRoot = options?.sandbox?.root?.trim() ?? options?.sandboxRoot?.trim();
|
|
314
314
|
const isUrl = isHttpUrl;
|
|
315
315
|
if (sandboxRoot && isUrl) {
|
|
316
316
|
throw new Error("Sandboxed image tool does not allow remote URLs.");
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { isSubagentSessionKey, resolveAgentIdFromSessionKey } from "../../routing/session-key.js";
|
|
2
|
+
import { listSpawnedSessionKeys, resolveInternalSessionKey, resolveMainSessionAlias, } from "./sessions-resolution.js";
|
|
3
|
+
export function resolveSessionToolsVisibility(cfg) {
|
|
4
|
+
const raw = cfg.tools?.sessions
|
|
5
|
+
?.visibility;
|
|
6
|
+
const value = typeof raw === "string" ? raw.trim().toLowerCase() : "";
|
|
7
|
+
if (value === "self" || value === "tree" || value === "agent" || value === "all") {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
return "tree";
|
|
11
|
+
}
|
|
12
|
+
export function resolveEffectiveSessionToolsVisibility(params) {
|
|
13
|
+
const visibility = resolveSessionToolsVisibility(params.cfg);
|
|
14
|
+
if (!params.sandboxed) {
|
|
15
|
+
return visibility;
|
|
16
|
+
}
|
|
17
|
+
const sandboxClamp = params.cfg.agents?.defaults?.sandbox?.sessionToolsVisibility ?? "spawned";
|
|
18
|
+
if (sandboxClamp === "spawned" && visibility !== "tree") {
|
|
19
|
+
return "tree";
|
|
20
|
+
}
|
|
21
|
+
return visibility;
|
|
22
|
+
}
|
|
23
|
+
export function resolveSandboxSessionToolsVisibility(cfg) {
|
|
24
|
+
return cfg.agents?.defaults?.sandbox?.sessionToolsVisibility ?? "spawned";
|
|
25
|
+
}
|
|
26
|
+
export function resolveSandboxedSessionToolContext(params) {
|
|
27
|
+
const { mainKey, alias } = resolveMainSessionAlias(params.cfg);
|
|
28
|
+
const visibility = resolveSandboxSessionToolsVisibility(params.cfg);
|
|
29
|
+
const requesterInternalKey = typeof params.agentSessionKey === "string" && params.agentSessionKey.trim()
|
|
30
|
+
? resolveInternalSessionKey({
|
|
31
|
+
key: params.agentSessionKey,
|
|
32
|
+
alias,
|
|
33
|
+
mainKey,
|
|
34
|
+
})
|
|
35
|
+
: undefined;
|
|
36
|
+
const effectiveRequesterKey = requesterInternalKey ?? alias;
|
|
37
|
+
const restrictToSpawned = params.sandboxed === true &&
|
|
38
|
+
visibility === "spawned" &&
|
|
39
|
+
!!requesterInternalKey &&
|
|
40
|
+
!isSubagentSessionKey(requesterInternalKey);
|
|
41
|
+
return {
|
|
42
|
+
mainKey,
|
|
43
|
+
alias,
|
|
44
|
+
visibility,
|
|
45
|
+
requesterInternalKey,
|
|
46
|
+
effectiveRequesterKey,
|
|
47
|
+
restrictToSpawned,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function createAgentToAgentPolicy(cfg) {
|
|
51
|
+
const routingA2A = cfg.tools?.agentToAgent;
|
|
52
|
+
const enabled = routingA2A?.enabled === true;
|
|
53
|
+
const allowPatterns = Array.isArray(routingA2A?.allow) ? routingA2A.allow : [];
|
|
54
|
+
const matchesAllow = (agentId) => {
|
|
55
|
+
if (allowPatterns.length === 0) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return allowPatterns.some((pattern) => {
|
|
59
|
+
const raw = String(pattern ?? "").trim();
|
|
60
|
+
if (!raw) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
if (raw === "*") {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
if (!raw.includes("*")) {
|
|
67
|
+
return raw === agentId;
|
|
68
|
+
}
|
|
69
|
+
const escaped = raw.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
70
|
+
const re = new RegExp(`^${escaped.replaceAll("\\*", ".*")}$`, "i");
|
|
71
|
+
return re.test(agentId);
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const isAllowed = (requesterAgentId, targetAgentId) => {
|
|
75
|
+
if (requesterAgentId === targetAgentId) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (!enabled) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
return matchesAllow(requesterAgentId) && matchesAllow(targetAgentId);
|
|
82
|
+
};
|
|
83
|
+
return { enabled, matchesAllow, isAllowed };
|
|
84
|
+
}
|
|
85
|
+
function actionPrefix(action) {
|
|
86
|
+
if (action === "history") {
|
|
87
|
+
return "Session history";
|
|
88
|
+
}
|
|
89
|
+
if (action === "send") {
|
|
90
|
+
return "Session send";
|
|
91
|
+
}
|
|
92
|
+
return "Session list";
|
|
93
|
+
}
|
|
94
|
+
function a2aDisabledMessage(action) {
|
|
95
|
+
if (action === "history") {
|
|
96
|
+
return "Agent-to-agent history is disabled. Set tools.agentToAgent.enabled=true to allow cross-agent access.";
|
|
97
|
+
}
|
|
98
|
+
if (action === "send") {
|
|
99
|
+
return "Agent-to-agent messaging is disabled. Set tools.agentToAgent.enabled=true to allow cross-agent sends.";
|
|
100
|
+
}
|
|
101
|
+
return "Agent-to-agent listing is disabled. Set tools.agentToAgent.enabled=true to allow cross-agent visibility.";
|
|
102
|
+
}
|
|
103
|
+
function a2aDeniedMessage(action) {
|
|
104
|
+
if (action === "history") {
|
|
105
|
+
return "Agent-to-agent history denied by tools.agentToAgent.allow.";
|
|
106
|
+
}
|
|
107
|
+
if (action === "send") {
|
|
108
|
+
return "Agent-to-agent messaging denied by tools.agentToAgent.allow.";
|
|
109
|
+
}
|
|
110
|
+
return "Agent-to-agent listing denied by tools.agentToAgent.allow.";
|
|
111
|
+
}
|
|
112
|
+
function crossVisibilityMessage(action) {
|
|
113
|
+
if (action === "history") {
|
|
114
|
+
return "Session history visibility is restricted. Set tools.sessions.visibility=all to allow cross-agent access.";
|
|
115
|
+
}
|
|
116
|
+
if (action === "send") {
|
|
117
|
+
return "Session send visibility is restricted. Set tools.sessions.visibility=all to allow cross-agent access.";
|
|
118
|
+
}
|
|
119
|
+
return "Session list visibility is restricted. Set tools.sessions.visibility=all to allow cross-agent access.";
|
|
120
|
+
}
|
|
121
|
+
function selfVisibilityMessage(action) {
|
|
122
|
+
return `${actionPrefix(action)} visibility is restricted to the current session (tools.sessions.visibility=self).`;
|
|
123
|
+
}
|
|
124
|
+
function treeVisibilityMessage(action) {
|
|
125
|
+
return `${actionPrefix(action)} visibility is restricted to the current session tree (tools.sessions.visibility=tree).`;
|
|
126
|
+
}
|
|
127
|
+
export async function createSessionVisibilityGuard(params) {
|
|
128
|
+
const requesterAgentId = resolveAgentIdFromSessionKey(params.requesterSessionKey);
|
|
129
|
+
const spawnedKeys = params.visibility === "tree"
|
|
130
|
+
? await listSpawnedSessionKeys({ requesterSessionKey: params.requesterSessionKey })
|
|
131
|
+
: null;
|
|
132
|
+
const check = (targetSessionKey) => {
|
|
133
|
+
const targetAgentId = resolveAgentIdFromSessionKey(targetSessionKey);
|
|
134
|
+
const isCrossAgent = targetAgentId !== requesterAgentId;
|
|
135
|
+
if (isCrossAgent) {
|
|
136
|
+
if (params.visibility !== "all") {
|
|
137
|
+
return {
|
|
138
|
+
allowed: false,
|
|
139
|
+
status: "forbidden",
|
|
140
|
+
error: crossVisibilityMessage(params.action),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (!params.a2aPolicy.enabled) {
|
|
144
|
+
return {
|
|
145
|
+
allowed: false,
|
|
146
|
+
status: "forbidden",
|
|
147
|
+
error: a2aDisabledMessage(params.action),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (!params.a2aPolicy.isAllowed(requesterAgentId, targetAgentId)) {
|
|
151
|
+
return {
|
|
152
|
+
allowed: false,
|
|
153
|
+
status: "forbidden",
|
|
154
|
+
error: a2aDeniedMessage(params.action),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return { allowed: true };
|
|
158
|
+
}
|
|
159
|
+
if (params.visibility === "self" && targetSessionKey !== params.requesterSessionKey) {
|
|
160
|
+
return {
|
|
161
|
+
allowed: false,
|
|
162
|
+
status: "forbidden",
|
|
163
|
+
error: selfVisibilityMessage(params.action),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (params.visibility === "tree" &&
|
|
167
|
+
targetSessionKey !== params.requesterSessionKey &&
|
|
168
|
+
!spawnedKeys?.has(targetSessionKey)) {
|
|
169
|
+
return {
|
|
170
|
+
allowed: false,
|
|
171
|
+
status: "forbidden",
|
|
172
|
+
error: treeVisibilityMessage(params.action),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return { allowed: true };
|
|
176
|
+
};
|
|
177
|
+
return { check };
|
|
178
|
+
}
|