@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,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox security validation — blocks dangerous Docker configurations.
|
|
3
|
+
*
|
|
4
|
+
* Threat model: local-trusted config, but protect against foot-guns and config injection.
|
|
5
|
+
* Enforced at runtime when creating sandbox containers.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
8
|
+
import { posix } from "node:path";
|
|
9
|
+
// Targeted denylist: host paths that should never be exposed inside sandbox containers.
|
|
10
|
+
// Exported for reuse in security audit collectors.
|
|
11
|
+
export const BLOCKED_HOST_PATHS = [
|
|
12
|
+
"/etc",
|
|
13
|
+
"/private/etc",
|
|
14
|
+
"/proc",
|
|
15
|
+
"/sys",
|
|
16
|
+
"/dev",
|
|
17
|
+
"/root",
|
|
18
|
+
"/boot",
|
|
19
|
+
// Directories that commonly contain (or alias) the Docker socket.
|
|
20
|
+
"/run",
|
|
21
|
+
"/var/run",
|
|
22
|
+
"/private/var/run",
|
|
23
|
+
"/var/run/docker.sock",
|
|
24
|
+
"/private/var/run/docker.sock",
|
|
25
|
+
"/run/docker.sock",
|
|
26
|
+
];
|
|
27
|
+
const BLOCKED_NETWORK_MODES = new Set(["host"]);
|
|
28
|
+
const BLOCKED_SECCOMP_PROFILES = new Set(["unconfined"]);
|
|
29
|
+
const BLOCKED_APPARMOR_PROFILES = new Set(["unconfined"]);
|
|
30
|
+
/**
|
|
31
|
+
* Parse the host/source path from a Docker bind mount string.
|
|
32
|
+
* Format: `source:target[:mode]`
|
|
33
|
+
*/
|
|
34
|
+
export function parseBindSourcePath(bind) {
|
|
35
|
+
const trimmed = bind.trim();
|
|
36
|
+
const firstColon = trimmed.indexOf(":");
|
|
37
|
+
if (firstColon <= 0) {
|
|
38
|
+
// No colon or starts with colon — treat as source.
|
|
39
|
+
return trimmed;
|
|
40
|
+
}
|
|
41
|
+
return trimmed.slice(0, firstColon);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Normalize a POSIX path: resolve `.`, `..`, collapse `//`, strip trailing `/`.
|
|
45
|
+
*/
|
|
46
|
+
export function normalizeHostPath(raw) {
|
|
47
|
+
const trimmed = raw.trim();
|
|
48
|
+
return posix.normalize(trimmed).replace(/\/+$/, "") || "/";
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* String-only blocked-path check (no filesystem I/O).
|
|
52
|
+
* Blocks:
|
|
53
|
+
* - binds that target blocked paths (equal or under)
|
|
54
|
+
* - binds that cover the system root (mounting "/" is never safe)
|
|
55
|
+
* - non-absolute source paths (relative / volume names) because they are hard to validate safely
|
|
56
|
+
*/
|
|
57
|
+
export function getBlockedBindReason(bind) {
|
|
58
|
+
const sourceRaw = parseBindSourcePath(bind);
|
|
59
|
+
if (!sourceRaw.startsWith("/")) {
|
|
60
|
+
return { kind: "non_absolute", sourcePath: sourceRaw };
|
|
61
|
+
}
|
|
62
|
+
const normalized = normalizeHostPath(sourceRaw);
|
|
63
|
+
return getBlockedReasonForSourcePath(normalized);
|
|
64
|
+
}
|
|
65
|
+
export function getBlockedReasonForSourcePath(sourceNormalized) {
|
|
66
|
+
if (sourceNormalized === "/") {
|
|
67
|
+
return { kind: "covers", blockedPath: "/" };
|
|
68
|
+
}
|
|
69
|
+
for (const blocked of BLOCKED_HOST_PATHS) {
|
|
70
|
+
if (sourceNormalized === blocked || sourceNormalized.startsWith(blocked + "/")) {
|
|
71
|
+
return { kind: "targets", blockedPath: blocked };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function tryRealpathAbsolute(path) {
|
|
77
|
+
if (!path.startsWith("/")) {
|
|
78
|
+
return path;
|
|
79
|
+
}
|
|
80
|
+
if (!existsSync(path)) {
|
|
81
|
+
return path;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
// Use native when available (keeps platform semantics); normalize for prefix checks.
|
|
85
|
+
return normalizeHostPath(realpathSync.native(path));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function formatBindBlockedError(params) {
|
|
92
|
+
if (params.reason.kind === "non_absolute") {
|
|
93
|
+
return new Error(`Sandbox security: bind mount "${params.bind}" uses a non-absolute source path ` +
|
|
94
|
+
`"${params.reason.sourcePath}". Only absolute POSIX paths are supported for sandbox binds.`);
|
|
95
|
+
}
|
|
96
|
+
const verb = params.reason.kind === "covers" ? "covers" : "targets";
|
|
97
|
+
return new Error(`Sandbox security: bind mount "${params.bind}" ${verb} blocked path "${params.reason.blockedPath}". ` +
|
|
98
|
+
"Mounting system directories (or Docker socket paths) into sandbox containers is not allowed. " +
|
|
99
|
+
"Use project-specific paths instead (e.g. /home/user/myproject).");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate bind mounts — throws if any source path is dangerous.
|
|
103
|
+
* Includes a symlink/realpath pass when the source path exists.
|
|
104
|
+
*/
|
|
105
|
+
export function validateBindMounts(binds) {
|
|
106
|
+
if (!binds?.length) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
for (const rawBind of binds) {
|
|
110
|
+
const bind = rawBind.trim();
|
|
111
|
+
if (!bind) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
// Fast string-only check (covers .., //, ancestor/descendant logic).
|
|
115
|
+
const blocked = getBlockedBindReason(bind);
|
|
116
|
+
if (blocked) {
|
|
117
|
+
throw formatBindBlockedError({ bind, reason: blocked });
|
|
118
|
+
}
|
|
119
|
+
// Symlink escape hardening: resolve existing absolute paths and re-check.
|
|
120
|
+
const sourceRaw = parseBindSourcePath(bind);
|
|
121
|
+
const sourceNormalized = normalizeHostPath(sourceRaw);
|
|
122
|
+
const sourceReal = tryRealpathAbsolute(sourceNormalized);
|
|
123
|
+
if (sourceReal !== sourceNormalized) {
|
|
124
|
+
const reason = getBlockedReasonForSourcePath(sourceReal);
|
|
125
|
+
if (reason) {
|
|
126
|
+
throw formatBindBlockedError({ bind, reason });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export function validateNetworkMode(network) {
|
|
132
|
+
if (network && BLOCKED_NETWORK_MODES.has(network.trim().toLowerCase())) {
|
|
133
|
+
throw new Error(`Sandbox security: network mode "${network}" is blocked. ` +
|
|
134
|
+
'Network "host" mode bypasses container network isolation. ' +
|
|
135
|
+
'Use "bridge" or "none" instead.');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export function validateSeccompProfile(profile) {
|
|
139
|
+
if (profile && BLOCKED_SECCOMP_PROFILES.has(profile.trim().toLowerCase())) {
|
|
140
|
+
throw new Error(`Sandbox security: seccomp profile "${profile}" is blocked. ` +
|
|
141
|
+
"Disabling seccomp removes syscall filtering and weakens sandbox isolation. " +
|
|
142
|
+
"Use a custom seccomp profile file or omit this setting.");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export function validateApparmorProfile(profile) {
|
|
146
|
+
if (profile && BLOCKED_APPARMOR_PROFILES.has(profile.trim().toLowerCase())) {
|
|
147
|
+
throw new Error(`Sandbox security: apparmor profile "${profile}" is blocked. ` +
|
|
148
|
+
"Disabling AppArmor removes mandatory access controls and weakens sandbox isolation. " +
|
|
149
|
+
"Use a named AppArmor profile or omit this setting.");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export function validateSandboxSecurity(cfg) {
|
|
153
|
+
validateBindMounts(cfg.binds);
|
|
154
|
+
validateNetworkMode(cfg.network);
|
|
155
|
+
validateSeccompProfile(cfg.seccompProfile);
|
|
156
|
+
validateApparmorProfile(cfg.apparmorProfile);
|
|
157
|
+
}
|
|
@@ -24,6 +24,9 @@ function resolveToCwd(filePath, cwd) {
|
|
|
24
24
|
return expanded;
|
|
25
25
|
return path.resolve(cwd, expanded);
|
|
26
26
|
}
|
|
27
|
+
export function resolveSandboxInputPath(filePath, cwd) {
|
|
28
|
+
return resolveToCwd(filePath, cwd);
|
|
29
|
+
}
|
|
27
30
|
export function resolveSandboxPath(params) {
|
|
28
31
|
const resolved = resolveToCwd(params.filePath, params.cwd);
|
|
29
32
|
const rootResolved = path.resolve(params.root);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function unionAllow(base, extra) {
|
|
2
|
+
if (!Array.isArray(extra) || extra.length === 0) {
|
|
3
|
+
return base;
|
|
4
|
+
}
|
|
5
|
+
// If the user is using alsoAllow without an allowlist, treat it as additive on top of
|
|
6
|
+
// an implicit allow-all policy.
|
|
7
|
+
if (!Array.isArray(base) || base.length === 0) {
|
|
8
|
+
return Array.from(new Set(["*", ...extra]));
|
|
9
|
+
}
|
|
10
|
+
return Array.from(new Set([...base, ...extra]));
|
|
11
|
+
}
|
|
12
|
+
export function pickSandboxToolPolicy(config) {
|
|
13
|
+
if (!config) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const allow = Array.isArray(config.allow)
|
|
17
|
+
? unionAllow(config.allow, config.alsoAllow)
|
|
18
|
+
: Array.isArray(config.alsoAllow) && config.alsoAllow.length > 0
|
|
19
|
+
? unionAllow(undefined, config.alsoAllow)
|
|
20
|
+
: undefined;
|
|
21
|
+
const deny = Array.isArray(config.deny) ? config.deny : undefined;
|
|
22
|
+
if (!allow && !deny) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return { allow, deny };
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize untrusted strings before embedding them into an LLM prompt.
|
|
3
|
+
*
|
|
4
|
+
* Threat model (OC-19): attacker-controlled directory names (or other runtime strings)
|
|
5
|
+
* that contain newline/control characters can break prompt structure and inject
|
|
6
|
+
* arbitrary instructions.
|
|
7
|
+
*
|
|
8
|
+
* Strategy (Option 3 hardening):
|
|
9
|
+
* - Strip Unicode "control" (Cc) + "format" (Cf) characters (includes CR/LF/NUL, bidi marks, zero-width chars).
|
|
10
|
+
* - Strip explicit line/paragraph separators (Zl/Zp): U+2028/U+2029.
|
|
11
|
+
*
|
|
12
|
+
* Notes:
|
|
13
|
+
* - This is intentionally lossy; it trades edge-case path fidelity for prompt integrity.
|
|
14
|
+
* - If you need lossless representation, escape instead of stripping.
|
|
15
|
+
*/
|
|
16
|
+
export function sanitizeForPromptLiteral(value) {
|
|
17
|
+
return value.replace(/[\p{Cc}\p{Cf}\u2028\u2029]/gu, "");
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function resolveAgentSessionDirs(stateDir) {
|
|
4
|
+
const agentsDir = path.join(stateDir, "agents");
|
|
5
|
+
let entries = [];
|
|
6
|
+
try {
|
|
7
|
+
entries = await fs.readdir(agentsDir, { withFileTypes: true });
|
|
8
|
+
}
|
|
9
|
+
catch (err) {
|
|
10
|
+
const code = err.code;
|
|
11
|
+
if (code === "ENOENT") {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
throw err;
|
|
15
|
+
}
|
|
16
|
+
return entries
|
|
17
|
+
.filter((entry) => entry.isDirectory())
|
|
18
|
+
.map((entry) => path.join(agentsDir, entry.name, "sessions"))
|
|
19
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
20
|
+
}
|
|
@@ -1,19 +1,89 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
|
|
4
|
+
import { isPidAlive } from "../shared/pid-alive.js";
|
|
5
|
+
import { resolveProcessScopedMap } from "../shared/process-scoped-map.js";
|
|
6
|
+
const HELD_LOCKS_KEY = Symbol.for("poolbot.sessionWriteLockHeldLocks");
|
|
7
|
+
const WATCHDOG_STATE_KEY = Symbol.for("poolbot.sessionWriteLockWatchdogState");
|
|
5
8
|
const CLEANUP_SIGNALS = ["SIGINT", "SIGTERM", "SIGQUIT", "SIGABRT"];
|
|
9
|
+
const DEFAULT_STALE_MS = 30 * 60 * 1000;
|
|
10
|
+
const DEFAULT_MAX_HOLD_MS = 5 * 60 * 1000;
|
|
11
|
+
const DEFAULT_WATCHDOG_INTERVAL_MS = 60_000;
|
|
12
|
+
const DEFAULT_TIMEOUT_GRACE_MS = 2 * 60 * 1000;
|
|
13
|
+
const MAX_LOCK_HOLD_MS = 2_147_000_000;
|
|
6
14
|
const cleanupHandlers = new Map();
|
|
7
|
-
|
|
8
|
-
|
|
15
|
+
const HELD_LOCKS = resolveProcessScopedMap(HELD_LOCKS_KEY);
|
|
16
|
+
function resolveWatchdogState() {
|
|
17
|
+
const proc = process;
|
|
18
|
+
if (!proc[WATCHDOG_STATE_KEY]) {
|
|
19
|
+
proc[WATCHDOG_STATE_KEY] = {
|
|
20
|
+
started: false,
|
|
21
|
+
intervalMs: DEFAULT_WATCHDOG_INTERVAL_MS,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return proc[WATCHDOG_STATE_KEY];
|
|
25
|
+
}
|
|
26
|
+
function resolvePositiveMs(value, fallback, opts = {}) {
|
|
27
|
+
if (typeof value !== "number" || Number.isNaN(value) || value <= 0) {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
if (value === Number.POSITIVE_INFINITY) {
|
|
31
|
+
return opts.allowInfinity ? value : fallback;
|
|
32
|
+
}
|
|
33
|
+
if (!Number.isFinite(value)) {
|
|
34
|
+
return fallback;
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
export function resolveSessionLockMaxHoldFromTimeout(params) {
|
|
39
|
+
const minMs = resolvePositiveMs(params.minMs, DEFAULT_MAX_HOLD_MS);
|
|
40
|
+
const timeoutMs = resolvePositiveMs(params.timeoutMs, minMs, { allowInfinity: true });
|
|
41
|
+
if (timeoutMs === Number.POSITIVE_INFINITY) {
|
|
42
|
+
return MAX_LOCK_HOLD_MS;
|
|
43
|
+
}
|
|
44
|
+
const graceMs = resolvePositiveMs(params.graceMs, DEFAULT_TIMEOUT_GRACE_MS);
|
|
45
|
+
return Math.min(MAX_LOCK_HOLD_MS, Math.max(minMs, timeoutMs + graceMs));
|
|
46
|
+
}
|
|
47
|
+
async function releaseHeldLock(normalizedSessionFile, held, opts = {}) {
|
|
48
|
+
const current = HELD_LOCKS.get(normalizedSessionFile);
|
|
49
|
+
if (current !== held) {
|
|
9
50
|
return false;
|
|
10
|
-
|
|
11
|
-
|
|
51
|
+
}
|
|
52
|
+
if (opts.force) {
|
|
53
|
+
held.count = 0;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
held.count -= 1;
|
|
57
|
+
if (held.count > 0) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (held.releasePromise) {
|
|
62
|
+
await held.releasePromise.catch(() => undefined);
|
|
12
63
|
return true;
|
|
13
64
|
}
|
|
14
|
-
|
|
15
|
-
|
|
65
|
+
HELD_LOCKS.delete(normalizedSessionFile);
|
|
66
|
+
held.releasePromise = (async () => {
|
|
67
|
+
try {
|
|
68
|
+
await held.handle.close();
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Ignore errors during cleanup - best effort.
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await fs.rm(held.lockPath, { force: true });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Ignore errors during cleanup - best effort.
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
try {
|
|
81
|
+
await held.releasePromise;
|
|
16
82
|
}
|
|
83
|
+
finally {
|
|
84
|
+
held.releasePromise = undefined;
|
|
85
|
+
}
|
|
86
|
+
return true;
|
|
17
87
|
}
|
|
18
88
|
/**
|
|
19
89
|
* Synchronously release all held locks.
|
|
@@ -74,24 +144,127 @@ function registerCleanupHandlers() {
|
|
|
74
144
|
}
|
|
75
145
|
}
|
|
76
146
|
}
|
|
147
|
+
async function runLockWatchdogCheck(nowMs = Date.now()) {
|
|
148
|
+
let released = 0;
|
|
149
|
+
for (const [sessionFile, held] of HELD_LOCKS.entries()) {
|
|
150
|
+
const heldForMs = nowMs - held.acquiredAt;
|
|
151
|
+
if (heldForMs <= held.maxHoldMs) {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// eslint-disable-next-line no-console
|
|
155
|
+
console.warn(`[session-write-lock] releasing lock held for ${heldForMs}ms (max=${held.maxHoldMs}ms): ${held.lockPath}`);
|
|
156
|
+
const didRelease = await releaseHeldLock(sessionFile, held, { force: true });
|
|
157
|
+
if (didRelease) {
|
|
158
|
+
released += 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return released;
|
|
162
|
+
}
|
|
163
|
+
function ensureWatchdogStarted(intervalMs) {
|
|
164
|
+
const watchdogState = resolveWatchdogState();
|
|
165
|
+
if (watchdogState.started) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
watchdogState.started = true;
|
|
169
|
+
watchdogState.intervalMs = intervalMs;
|
|
170
|
+
watchdogState.timer = setInterval(() => {
|
|
171
|
+
void runLockWatchdogCheck().catch(() => {
|
|
172
|
+
// Ignore watchdog errors - best effort cleanup only.
|
|
173
|
+
});
|
|
174
|
+
}, intervalMs);
|
|
175
|
+
watchdogState.timer.unref?.();
|
|
176
|
+
}
|
|
77
177
|
async function readLockPayload(lockPath) {
|
|
78
178
|
try {
|
|
79
179
|
const raw = await fs.readFile(lockPath, "utf8");
|
|
80
180
|
const parsed = JSON.parse(raw);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
181
|
+
const payload = {};
|
|
182
|
+
if (typeof parsed.pid === "number") {
|
|
183
|
+
payload.pid = parsed.pid;
|
|
184
|
+
}
|
|
185
|
+
if (typeof parsed.createdAt === "string") {
|
|
186
|
+
payload.createdAt = parsed.createdAt;
|
|
187
|
+
}
|
|
188
|
+
return payload;
|
|
86
189
|
}
|
|
87
190
|
catch {
|
|
88
191
|
return null;
|
|
89
192
|
}
|
|
90
193
|
}
|
|
194
|
+
function inspectLockPayload(payload, staleMs, nowMs) {
|
|
195
|
+
const pid = typeof payload?.pid === "number" ? payload.pid : null;
|
|
196
|
+
const pidAlive = pid !== null ? isPidAlive(pid) : false;
|
|
197
|
+
const createdAt = typeof payload?.createdAt === "string" ? payload.createdAt : null;
|
|
198
|
+
const createdAtMs = createdAt ? Date.parse(createdAt) : Number.NaN;
|
|
199
|
+
const ageMs = Number.isFinite(createdAtMs) ? Math.max(0, nowMs - createdAtMs) : null;
|
|
200
|
+
const staleReasons = [];
|
|
201
|
+
if (pid === null) {
|
|
202
|
+
staleReasons.push("missing-pid");
|
|
203
|
+
}
|
|
204
|
+
else if (!pidAlive) {
|
|
205
|
+
staleReasons.push("dead-pid");
|
|
206
|
+
}
|
|
207
|
+
if (ageMs === null) {
|
|
208
|
+
staleReasons.push("invalid-createdAt");
|
|
209
|
+
}
|
|
210
|
+
else if (ageMs > staleMs) {
|
|
211
|
+
staleReasons.push("too-old");
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
pid,
|
|
215
|
+
pidAlive,
|
|
216
|
+
createdAt,
|
|
217
|
+
ageMs,
|
|
218
|
+
stale: staleReasons.length > 0,
|
|
219
|
+
staleReasons,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
export async function cleanStaleLockFiles(params) {
|
|
223
|
+
const sessionsDir = path.resolve(params.sessionsDir);
|
|
224
|
+
const staleMs = resolvePositiveMs(params.staleMs, DEFAULT_STALE_MS);
|
|
225
|
+
const removeStale = params.removeStale !== false;
|
|
226
|
+
const nowMs = params.nowMs ?? Date.now();
|
|
227
|
+
let entries = [];
|
|
228
|
+
try {
|
|
229
|
+
entries = await fs.readdir(sessionsDir, { withFileTypes: true });
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
const code = err.code;
|
|
233
|
+
if (code === "ENOENT") {
|
|
234
|
+
return { locks: [], cleaned: [] };
|
|
235
|
+
}
|
|
236
|
+
throw err;
|
|
237
|
+
}
|
|
238
|
+
const locks = [];
|
|
239
|
+
const cleaned = [];
|
|
240
|
+
const lockEntries = entries
|
|
241
|
+
.filter((entry) => entry.name.endsWith(".jsonl.lock"))
|
|
242
|
+
.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
243
|
+
for (const entry of lockEntries) {
|
|
244
|
+
const lockPath = path.join(sessionsDir, entry.name);
|
|
245
|
+
const payload = await readLockPayload(lockPath);
|
|
246
|
+
const inspected = inspectLockPayload(payload, staleMs, nowMs);
|
|
247
|
+
const lockInfo = {
|
|
248
|
+
lockPath,
|
|
249
|
+
...inspected,
|
|
250
|
+
removed: false,
|
|
251
|
+
};
|
|
252
|
+
if (lockInfo.stale && removeStale) {
|
|
253
|
+
await fs.rm(lockPath, { force: true });
|
|
254
|
+
lockInfo.removed = true;
|
|
255
|
+
cleaned.push(lockInfo);
|
|
256
|
+
params.log?.warn?.(`removed stale session lock: ${lockPath} (${lockInfo.staleReasons.join(", ") || "unknown"})`);
|
|
257
|
+
}
|
|
258
|
+
locks.push(lockInfo);
|
|
259
|
+
}
|
|
260
|
+
return { locks, cleaned };
|
|
261
|
+
}
|
|
91
262
|
export async function acquireSessionWriteLock(params) {
|
|
92
263
|
registerCleanupHandlers();
|
|
93
|
-
|
|
94
|
-
const
|
|
264
|
+
ensureWatchdogStarted(DEFAULT_WATCHDOG_INTERVAL_MS);
|
|
265
|
+
const timeoutMs = resolvePositiveMs(params.timeoutMs, 10_000, { allowInfinity: true });
|
|
266
|
+
const staleMs = resolvePositiveMs(params.staleMs, DEFAULT_STALE_MS);
|
|
267
|
+
const maxHoldMs = resolvePositiveMs(params.maxHoldMs, DEFAULT_MAX_HOLD_MS);
|
|
95
268
|
const sessionFile = path.resolve(params.sessionFile);
|
|
96
269
|
const sessionDir = path.dirname(sessionFile);
|
|
97
270
|
await fs.mkdir(sessionDir, { recursive: true });
|
|
@@ -109,15 +282,7 @@ export async function acquireSessionWriteLock(params) {
|
|
|
109
282
|
held.count += 1;
|
|
110
283
|
return {
|
|
111
284
|
release: async () => {
|
|
112
|
-
|
|
113
|
-
if (!current)
|
|
114
|
-
return;
|
|
115
|
-
current.count -= 1;
|
|
116
|
-
if (current.count > 0)
|
|
117
|
-
return;
|
|
118
|
-
HELD_LOCKS.delete(normalizedSessionFile);
|
|
119
|
-
await current.handle.close();
|
|
120
|
-
await fs.rm(current.lockPath, { force: true });
|
|
285
|
+
await releaseHeldLock(normalizedSessionFile, held);
|
|
121
286
|
},
|
|
122
287
|
};
|
|
123
288
|
}
|
|
@@ -127,19 +292,19 @@ export async function acquireSessionWriteLock(params) {
|
|
|
127
292
|
attempt += 1;
|
|
128
293
|
try {
|
|
129
294
|
const handle = await fs.open(lockPath, "wx");
|
|
130
|
-
|
|
131
|
-
|
|
295
|
+
const createdAt = new Date().toISOString();
|
|
296
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, createdAt }, null, 2), "utf8");
|
|
297
|
+
const createdHeld = {
|
|
298
|
+
count: 1,
|
|
299
|
+
handle,
|
|
300
|
+
lockPath,
|
|
301
|
+
acquiredAt: Date.now(),
|
|
302
|
+
maxHoldMs,
|
|
303
|
+
};
|
|
304
|
+
HELD_LOCKS.set(normalizedSessionFile, createdHeld);
|
|
132
305
|
return {
|
|
133
306
|
release: async () => {
|
|
134
|
-
|
|
135
|
-
if (!current)
|
|
136
|
-
return;
|
|
137
|
-
current.count -= 1;
|
|
138
|
-
if (current.count > 0)
|
|
139
|
-
return;
|
|
140
|
-
HELD_LOCKS.delete(normalizedSessionFile);
|
|
141
|
-
await current.handle.close();
|
|
142
|
-
await fs.rm(current.lockPath, { force: true });
|
|
307
|
+
await releaseHeldLock(normalizedSessionFile, createdHeld);
|
|
143
308
|
},
|
|
144
309
|
};
|
|
145
310
|
}
|
|
@@ -148,10 +313,8 @@ export async function acquireSessionWriteLock(params) {
|
|
|
148
313
|
if (code !== "EEXIST")
|
|
149
314
|
throw err;
|
|
150
315
|
const payload = await readLockPayload(lockPath);
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
const alive = payload?.pid ? isAlive(payload.pid) : false;
|
|
154
|
-
if (stale || !alive) {
|
|
316
|
+
const inspected = inspectLockPayload(payload, staleMs, Date.now());
|
|
317
|
+
if (inspected.stale) {
|
|
155
318
|
await fs.rm(lockPath, { force: true });
|
|
156
319
|
continue;
|
|
157
320
|
}
|
|
@@ -160,11 +323,12 @@ export async function acquireSessionWriteLock(params) {
|
|
|
160
323
|
}
|
|
161
324
|
}
|
|
162
325
|
const payload = await readLockPayload(lockPath);
|
|
163
|
-
const owner = payload?.pid ? `pid=${payload.pid}` : "unknown";
|
|
326
|
+
const owner = typeof payload?.pid === "number" ? `pid=${payload.pid}` : "unknown";
|
|
164
327
|
throw new Error(`session file locked (timeout ${timeoutMs}ms): ${owner} ${lockPath}`);
|
|
165
328
|
}
|
|
166
329
|
export const __testing = {
|
|
167
330
|
cleanupSignals: [...CLEANUP_SIGNALS],
|
|
168
331
|
handleTerminationSignal,
|
|
169
332
|
releaseAllLocksSync,
|
|
333
|
+
runLockWatchdogCheck,
|
|
170
334
|
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function normalizeSkillFilter(skillFilter) {
|
|
2
|
+
if (skillFilter === undefined) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
return skillFilter.map((entry) => String(entry).trim()).filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
export function normalizeSkillFilterForComparison(skillFilter) {
|
|
8
|
+
const normalized = normalizeSkillFilter(skillFilter);
|
|
9
|
+
if (normalized === undefined) {
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
return Array.from(new Set(normalized)).toSorted();
|
|
13
|
+
}
|
|
14
|
+
export function matchesSkillFilter(cached, next) {
|
|
15
|
+
const cachedNormalized = normalizeSkillFilterForComparison(cached);
|
|
16
|
+
const nextNormalized = normalizeSkillFilterForComparison(next);
|
|
17
|
+
if (cachedNormalized === undefined || nextNormalized === undefined) {
|
|
18
|
+
return cachedNormalized === nextNormalized;
|
|
19
|
+
}
|
|
20
|
+
if (cachedNormalized.length !== nextNormalized.length) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return cachedNormalized.every((entry, index) => entry === nextNormalized[index]);
|
|
24
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { safePathSegmentHashed } from "../../infra/install-safe-path.js";
|
|
3
|
+
import { resolveConfigDir } from "../../utils.js";
|
|
4
|
+
import { resolveSkillKey } from "./frontmatter.js";
|
|
5
|
+
export function resolveSkillToolsRootDir(entry) {
|
|
6
|
+
const key = resolveSkillKey(entry.skill, entry);
|
|
7
|
+
const safeKey = safePathSegmentHashed(key);
|
|
8
|
+
return path.join(resolveConfigDir(), "tools", safeKey);
|
|
9
|
+
}
|