@vellumai/assistant 0.9.1-staging.1 → 0.10.0-staging.2
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/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -5,11 +5,22 @@ import {
|
|
|
5
5
|
} from "node:https";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import { getConfig } from "../../config/loader.js";
|
|
9
|
+
import type {
|
|
10
|
+
WebFetchMetadata,
|
|
11
|
+
WebFetchProviderId,
|
|
12
|
+
} from "../../daemon/message-types/web-activity.js";
|
|
9
13
|
import { RiskLevel } from "../../permissions/types.js";
|
|
14
|
+
import { getProviderKeyAsync } from "../../security/secure-keys.js";
|
|
10
15
|
import { wrapUntrustedContent } from "../../security/untrusted-content.js";
|
|
11
16
|
import { faviconUrlForDomain } from "../../util/favicon.js";
|
|
12
17
|
import { getLogger } from "../../util/logger.js";
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_BASE_DELAY_MS,
|
|
20
|
+
DEFAULT_MAX_RETRIES,
|
|
21
|
+
getHttpRetryDelay,
|
|
22
|
+
sleep,
|
|
23
|
+
} from "../../util/retry.js";
|
|
13
24
|
import { safeStringSlice } from "../../util/unicode.js";
|
|
14
25
|
import { registerTool } from "../registry.js";
|
|
15
26
|
import type {
|
|
@@ -35,6 +46,8 @@ import {
|
|
|
35
46
|
|
|
36
47
|
const log = getLogger("web-fetch");
|
|
37
48
|
|
|
49
|
+
const FIRECRAWL_SCRAPE_API_URL = "https://api.firecrawl.dev/v2/scrape";
|
|
50
|
+
|
|
38
51
|
const DEFAULT_TIMEOUT_SECONDS = 20;
|
|
39
52
|
const MAX_TIMEOUT_SECONDS = 60;
|
|
40
53
|
const DEFAULT_MAX_CHARS = 12_000;
|
|
@@ -614,6 +627,7 @@ export async function executeWebFetch(
|
|
|
614
627
|
webFetch: {
|
|
615
628
|
url: safeUrl,
|
|
616
629
|
finalUrl: safeFinalUrl,
|
|
630
|
+
provider: "default",
|
|
617
631
|
status: meta.status ?? 0,
|
|
618
632
|
contentType: meta.contentType,
|
|
619
633
|
byteCount: 0,
|
|
@@ -972,6 +986,7 @@ export async function executeWebFetch(
|
|
|
972
986
|
const meta: WebFetchMetadata = {
|
|
973
987
|
url: safeRequestedUrl,
|
|
974
988
|
finalUrl: sanitizeUrlForOutput(currentUrl),
|
|
989
|
+
provider: "default",
|
|
975
990
|
status: getUpstreamStatus(response),
|
|
976
991
|
contentType: contentType || undefined,
|
|
977
992
|
byteCount: body.bytesRead,
|
|
@@ -1029,6 +1044,349 @@ export async function executeWebFetch(
|
|
|
1029
1044
|
}
|
|
1030
1045
|
}
|
|
1031
1046
|
|
|
1047
|
+
// ----------------------------------------------------------------------------
|
|
1048
|
+
// Provider abstraction
|
|
1049
|
+
//
|
|
1050
|
+
// `web_fetch` defaults to the built-in fetcher above (`executeWebFetch`). When
|
|
1051
|
+
// `services.web-fetch.provider` selects a BYOK provider (e.g. `firecrawl`),
|
|
1052
|
+
// the tool routes through that provider's hosted API instead — which can read
|
|
1053
|
+
// JavaScript-rendered pages the static fetcher can't. The provider's stored
|
|
1054
|
+
// key is shared with its web-search counterpart (one `firecrawl` key powers
|
|
1055
|
+
// both tools). The dispatcher falls back to the built-in fetcher when the
|
|
1056
|
+
// provider has no key, or when the target is a private/local host the hosted
|
|
1057
|
+
// scraper can't reach.
|
|
1058
|
+
// ----------------------------------------------------------------------------
|
|
1059
|
+
|
|
1060
|
+
interface FirecrawlScrapeMetadata {
|
|
1061
|
+
title?: string;
|
|
1062
|
+
description?: string;
|
|
1063
|
+
sourceURL?: string;
|
|
1064
|
+
url?: string;
|
|
1065
|
+
statusCode?: number;
|
|
1066
|
+
contentType?: string;
|
|
1067
|
+
error?: string;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
interface FirecrawlScrapeResponse {
|
|
1071
|
+
success?: boolean;
|
|
1072
|
+
data?: {
|
|
1073
|
+
markdown?: string;
|
|
1074
|
+
metadata?: FirecrawlScrapeMetadata;
|
|
1075
|
+
warning?: string | null;
|
|
1076
|
+
};
|
|
1077
|
+
warning?: string | null;
|
|
1078
|
+
error?: string;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function getWebFetchProvider(): WebFetchProviderId {
|
|
1082
|
+
const configured = getConfig().services["web-fetch"]?.provider ?? "default";
|
|
1083
|
+
return configured === "firecrawl" ? "firecrawl" : "default";
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Decide whether a request may be routed to the hosted Firecrawl provider.
|
|
1088
|
+
*
|
|
1089
|
+
* Posting a URL to Firecrawl sends its path + query (which can hold secrets) to
|
|
1090
|
+
* a third party, so we apply the SAME safety gate as the built-in fetcher
|
|
1091
|
+
* BEFORE dispatching — not just the lexical host check:
|
|
1092
|
+
* - only http(s) URLs (Firecrawl can't do other schemes anyway),
|
|
1093
|
+
* - never `allow_private_network` requests (those are intentionally local;
|
|
1094
|
+
* Firecrawl can't reach them and the built-in path owns that mode),
|
|
1095
|
+
* - and a DNS resolution check so a public hostname that resolves to a
|
|
1096
|
+
* private/blocked address (e.g. `internal.example` → 10.x.x.x) is NOT
|
|
1097
|
+
* leaked to Firecrawl.
|
|
1098
|
+
* Anything that fails falls back to the built-in fetcher, which enforces its
|
|
1099
|
+
* own SSRF rules and returns the appropriate error.
|
|
1100
|
+
*/
|
|
1101
|
+
async function canRouteToFirecrawl(
|
|
1102
|
+
input: Record<string, unknown>,
|
|
1103
|
+
): Promise<boolean> {
|
|
1104
|
+
if (input.allow_private_network === true) return false;
|
|
1105
|
+
const parsed = parseUrl(input.url);
|
|
1106
|
+
if (!parsed) return false;
|
|
1107
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
|
|
1108
|
+
if (isPrivateOrLocalHost(parsed.hostname)) return false;
|
|
1109
|
+
try {
|
|
1110
|
+
const resolution = await resolveRequestAddress(
|
|
1111
|
+
parsed.hostname,
|
|
1112
|
+
resolveHostAddresses,
|
|
1113
|
+
false,
|
|
1114
|
+
);
|
|
1115
|
+
if (resolution.blockedAddress || resolution.addresses.length === 0) {
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1118
|
+
} catch {
|
|
1119
|
+
return false;
|
|
1120
|
+
}
|
|
1121
|
+
return true;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
function firecrawlErrorResult(
|
|
1125
|
+
requestedUrl: string,
|
|
1126
|
+
startedAt: number,
|
|
1127
|
+
errorMessage: string,
|
|
1128
|
+
status = 0,
|
|
1129
|
+
): ToolExecutionResult {
|
|
1130
|
+
const domain = extractDomain(requestedUrl);
|
|
1131
|
+
return {
|
|
1132
|
+
content: `Error: ${errorMessage}`,
|
|
1133
|
+
isError: true,
|
|
1134
|
+
activityMetadata: {
|
|
1135
|
+
webFetch: {
|
|
1136
|
+
url: requestedUrl,
|
|
1137
|
+
finalUrl: requestedUrl,
|
|
1138
|
+
provider: "firecrawl",
|
|
1139
|
+
status,
|
|
1140
|
+
byteCount: 0,
|
|
1141
|
+
charCount: 0,
|
|
1142
|
+
truncated: false,
|
|
1143
|
+
domain,
|
|
1144
|
+
faviconUrl: faviconUrlForDomain(domain),
|
|
1145
|
+
redirectCount: 0,
|
|
1146
|
+
durationMs: Date.now() - startedAt,
|
|
1147
|
+
errorMessage,
|
|
1148
|
+
},
|
|
1149
|
+
},
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
/**
|
|
1154
|
+
* Fetch a page via Firecrawl's hosted `/v2/scrape` endpoint, returning clean
|
|
1155
|
+
* markdown. Mirrors the built-in fetcher's output shape (same
|
|
1156
|
+
* {@link formatWebFetchOutput} and {@link WebFetchMetadata}) so the model and
|
|
1157
|
+
* client UIs can't tell which backend served the page apart from
|
|
1158
|
+
* `metadata.provider`.
|
|
1159
|
+
*
|
|
1160
|
+
* Exported for direct unit testing; the registered tool dispatches here via
|
|
1161
|
+
* {@link getWebFetchProvider}.
|
|
1162
|
+
*/
|
|
1163
|
+
export async function executeFirecrawlScrape(
|
|
1164
|
+
input: Record<string, unknown>,
|
|
1165
|
+
options: { apiKey: string; signal?: AbortSignal },
|
|
1166
|
+
): Promise<ToolExecutionResult> {
|
|
1167
|
+
const startedAt = Date.now();
|
|
1168
|
+
const parsedUrl = parseUrl(input.url);
|
|
1169
|
+
const targetUrl =
|
|
1170
|
+
parsedUrl?.href ?? (typeof input.url === "string" ? input.url : "");
|
|
1171
|
+
const safeRequestedUrl = parsedUrl
|
|
1172
|
+
? sanitizeUrlForOutput(parsedUrl)
|
|
1173
|
+
: sanitizeUrlStringForOutput(targetUrl);
|
|
1174
|
+
|
|
1175
|
+
if (!targetUrl) {
|
|
1176
|
+
return firecrawlErrorResult(
|
|
1177
|
+
safeRequestedUrl,
|
|
1178
|
+
startedAt,
|
|
1179
|
+
"url is required and must be a valid HTTP(S) URL",
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
// Never forward URL-embedded credentials (user:password@host) to the hosted
|
|
1184
|
+
// Firecrawl API. The built-in fetcher turns them into a Basic-auth header to
|
|
1185
|
+
// the target; Firecrawl can't, so reject rather than leak them to a third
|
|
1186
|
+
// party.
|
|
1187
|
+
if (parsedUrl?.username || parsedUrl?.password) {
|
|
1188
|
+
return firecrawlErrorResult(
|
|
1189
|
+
safeRequestedUrl,
|
|
1190
|
+
startedAt,
|
|
1191
|
+
"URLs with embedded credentials are not supported by the Firecrawl provider. Remove the user:password@ portion of the URL.",
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
const maxChars = clampInteger(
|
|
1196
|
+
input.max_chars,
|
|
1197
|
+
DEFAULT_MAX_CHARS,
|
|
1198
|
+
1,
|
|
1199
|
+
MAX_MAX_CHARS,
|
|
1200
|
+
);
|
|
1201
|
+
const startIndex = clampInteger(input.start_index, 0, 0, 10_000_000);
|
|
1202
|
+
const timeoutSeconds = clampInteger(
|
|
1203
|
+
input.timeout_seconds,
|
|
1204
|
+
DEFAULT_TIMEOUT_SECONDS,
|
|
1205
|
+
1,
|
|
1206
|
+
MAX_TIMEOUT_SECONDS,
|
|
1207
|
+
);
|
|
1208
|
+
|
|
1209
|
+
const requestBody = {
|
|
1210
|
+
url: targetUrl,
|
|
1211
|
+
formats: ["markdown"],
|
|
1212
|
+
onlyMainContent: true,
|
|
1213
|
+
timeout: timeoutSeconds * 1000,
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1216
|
+
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
1217
|
+
let response: Response;
|
|
1218
|
+
try {
|
|
1219
|
+
response = await fetch(FIRECRAWL_SCRAPE_API_URL, {
|
|
1220
|
+
method: "POST",
|
|
1221
|
+
headers: {
|
|
1222
|
+
"Content-Type": "application/json",
|
|
1223
|
+
Authorization: `Bearer ${options.apiKey}`,
|
|
1224
|
+
"X-Client-Source": "vellum-assistant",
|
|
1225
|
+
},
|
|
1226
|
+
body: JSON.stringify(requestBody),
|
|
1227
|
+
signal: options.signal,
|
|
1228
|
+
});
|
|
1229
|
+
} catch (err) {
|
|
1230
|
+
if (
|
|
1231
|
+
options.signal?.aborted ||
|
|
1232
|
+
(err instanceof Error && err.name === "AbortError")
|
|
1233
|
+
) {
|
|
1234
|
+
return firecrawlErrorResult(
|
|
1235
|
+
safeRequestedUrl,
|
|
1236
|
+
startedAt,
|
|
1237
|
+
"web fetch was cancelled",
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1241
|
+
return firecrawlErrorResult(
|
|
1242
|
+
safeRequestedUrl,
|
|
1243
|
+
startedAt,
|
|
1244
|
+
`Firecrawl scrape failed: ${msg}`,
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
if (response.ok) {
|
|
1249
|
+
let json: FirecrawlScrapeResponse;
|
|
1250
|
+
try {
|
|
1251
|
+
json = (await response.json()) as FirecrawlScrapeResponse;
|
|
1252
|
+
} catch {
|
|
1253
|
+
return firecrawlErrorResult(
|
|
1254
|
+
safeRequestedUrl,
|
|
1255
|
+
startedAt,
|
|
1256
|
+
"Firecrawl scrape returned an invalid JSON payload.",
|
|
1257
|
+
response.status,
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
const data = json.data ?? {};
|
|
1261
|
+
const fcMeta = data.metadata ?? {};
|
|
1262
|
+
// A 200 can still carry a payload-level failure (success:false, a
|
|
1263
|
+
// top-level error, or a per-page error in data.metadata). Surface it
|
|
1264
|
+
// instead of treating an empty body as a successful "no content" scrape.
|
|
1265
|
+
const payloadError = json.error ?? fcMeta.error;
|
|
1266
|
+
if (json.success === false || payloadError) {
|
|
1267
|
+
return firecrawlErrorResult(
|
|
1268
|
+
safeRequestedUrl,
|
|
1269
|
+
startedAt,
|
|
1270
|
+
payloadError ?? "Firecrawl scrape failed.",
|
|
1271
|
+
response.status,
|
|
1272
|
+
);
|
|
1273
|
+
}
|
|
1274
|
+
const processed = normalizeMarkdown(
|
|
1275
|
+
(data.markdown ?? "").replace(/\0/g, ""),
|
|
1276
|
+
);
|
|
1277
|
+
|
|
1278
|
+
const safeStart = Math.min(startIndex, processed.length);
|
|
1279
|
+
const safeEnd = Math.min(processed.length, safeStart + maxChars);
|
|
1280
|
+
const sliced = processed.slice(safeStart, safeEnd);
|
|
1281
|
+
const bytesRead = Buffer.byteLength(processed, "utf8");
|
|
1282
|
+
|
|
1283
|
+
const finalUrlRaw = fcMeta.url ?? fcMeta.sourceURL ?? targetUrl;
|
|
1284
|
+
const finalUrl = sanitizeUrlStringForOutput(finalUrlRaw);
|
|
1285
|
+
const status = fcMeta.statusCode ?? 200;
|
|
1286
|
+
const contentType = fcMeta.contentType ?? "text/markdown";
|
|
1287
|
+
|
|
1288
|
+
const notices: string[] = [];
|
|
1289
|
+
const warning = data.warning ?? json.warning;
|
|
1290
|
+
if (warning) notices.push(`Firecrawl: ${warning}`);
|
|
1291
|
+
if (safeEnd < processed.length) {
|
|
1292
|
+
notices.push(`Output truncated by max_chars=${maxChars}.`);
|
|
1293
|
+
}
|
|
1294
|
+
if (startIndex > processed.length) {
|
|
1295
|
+
notices.push(
|
|
1296
|
+
`start_index (${startIndex}) exceeded available content length (${processed.length}).`,
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
const content = formatWebFetchOutput({
|
|
1301
|
+
requestedUrl: safeRequestedUrl,
|
|
1302
|
+
finalUrl,
|
|
1303
|
+
status,
|
|
1304
|
+
statusText: "",
|
|
1305
|
+
contentType,
|
|
1306
|
+
bytesRead,
|
|
1307
|
+
totalChars: processed.length,
|
|
1308
|
+
startIndex: safeStart,
|
|
1309
|
+
endIndex: safeEnd,
|
|
1310
|
+
content: sliced,
|
|
1311
|
+
title: fcMeta.title,
|
|
1312
|
+
description: fcMeta.description,
|
|
1313
|
+
notices,
|
|
1314
|
+
raw: false,
|
|
1315
|
+
markdown: true,
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
const finalDomain = extractDomain(finalUrl);
|
|
1319
|
+
const meta: WebFetchMetadata = {
|
|
1320
|
+
url: safeRequestedUrl,
|
|
1321
|
+
finalUrl,
|
|
1322
|
+
provider: "firecrawl",
|
|
1323
|
+
status,
|
|
1324
|
+
contentType,
|
|
1325
|
+
byteCount: bytesRead,
|
|
1326
|
+
charCount: sliced.length,
|
|
1327
|
+
truncated: safeEnd < processed.length,
|
|
1328
|
+
title: fcMeta.title,
|
|
1329
|
+
domain: finalDomain,
|
|
1330
|
+
faviconUrl: faviconUrlForDomain(finalDomain),
|
|
1331
|
+
redirectCount: 0,
|
|
1332
|
+
durationMs: Date.now() - startedAt,
|
|
1333
|
+
};
|
|
1334
|
+
|
|
1335
|
+
return {
|
|
1336
|
+
content,
|
|
1337
|
+
isError: false,
|
|
1338
|
+
status: notices.length > 0 ? notices.join("\n") : undefined,
|
|
1339
|
+
activityMetadata: { webFetch: meta },
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
const bodyText = await response.text();
|
|
1344
|
+
|
|
1345
|
+
if (response.status === 401 || response.status === 403) {
|
|
1346
|
+
return firecrawlErrorResult(
|
|
1347
|
+
safeRequestedUrl,
|
|
1348
|
+
startedAt,
|
|
1349
|
+
"Invalid or expired Firecrawl API key",
|
|
1350
|
+
response.status,
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
if (response.status === 429 && attempt < DEFAULT_MAX_RETRIES) {
|
|
1355
|
+
const delayMs = getHttpRetryDelay(response, attempt, DEFAULT_BASE_DELAY_MS);
|
|
1356
|
+
log.warn(
|
|
1357
|
+
{ attempt: attempt + 1, delayMs },
|
|
1358
|
+
"Firecrawl scrape rate limited, retrying",
|
|
1359
|
+
);
|
|
1360
|
+
await sleep(delayMs);
|
|
1361
|
+
continue;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
log.warn(
|
|
1365
|
+
{ status: response.status, body: safeStringSlice(bodyText, 0, 200) },
|
|
1366
|
+
"Firecrawl scrape API error",
|
|
1367
|
+
);
|
|
1368
|
+
const errorMessage =
|
|
1369
|
+
response.status === 429
|
|
1370
|
+
? "Firecrawl scrape rate limit exceeded after retries. Try again shortly."
|
|
1371
|
+
: response.status === 402
|
|
1372
|
+
? "Firecrawl scrape failed: account balance/credits exhausted."
|
|
1373
|
+
: `Firecrawl scrape API returned status ${response.status}`;
|
|
1374
|
+
return firecrawlErrorResult(
|
|
1375
|
+
safeRequestedUrl,
|
|
1376
|
+
startedAt,
|
|
1377
|
+
errorMessage,
|
|
1378
|
+
response.status,
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
return firecrawlErrorResult(
|
|
1383
|
+
safeRequestedUrl,
|
|
1384
|
+
startedAt,
|
|
1385
|
+
"Firecrawl scrape rate limit exceeded after retries. Try again shortly.",
|
|
1386
|
+
429,
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1032
1390
|
export const webFetchTool = {
|
|
1033
1391
|
name: "web_fetch",
|
|
1034
1392
|
description:
|
|
@@ -1076,6 +1434,19 @@ export const webFetchTool = {
|
|
|
1076
1434
|
input: Record<string, unknown>,
|
|
1077
1435
|
context: ToolContext,
|
|
1078
1436
|
): Promise<ToolExecutionResult> {
|
|
1437
|
+
if (getWebFetchProvider() === "firecrawl" && (await canRouteToFirecrawl(input))) {
|
|
1438
|
+
const apiKey = await getProviderKeyAsync("firecrawl");
|
|
1439
|
+
if (apiKey) {
|
|
1440
|
+
return executeFirecrawlScrape(input, {
|
|
1441
|
+
apiKey,
|
|
1442
|
+
signal: context.signal,
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
log.info(
|
|
1446
|
+
"web_fetch provider is firecrawl but no API key is configured; falling back to the built-in fetcher",
|
|
1447
|
+
);
|
|
1448
|
+
// Fall through to the built-in fetcher.
|
|
1449
|
+
}
|
|
1079
1450
|
return executeWebFetch(input, { signal: context.signal });
|
|
1080
1451
|
},
|
|
1081
1452
|
} satisfies ToolDefinition;
|