@vellumai/assistant 0.8.2 → 0.8.4
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/ARCHITECTURE.md +11 -12
- package/docker-entrypoint.sh +13 -2
- package/docker-init-apt-root.sh +79 -6
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +945 -36
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +272 -0
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/compaction-events.test.ts +1 -1
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/context-token-estimator.test.ts +112 -57
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
- package/src/__tests__/conversation-agent-loop.test.ts +77 -3
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +161 -0
- package/src/__tests__/conversation-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +6 -0
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/external-plugin-loader.test.ts +91 -19
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/host-app-control-proxy.test.ts +241 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +7 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +3 -0
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
- package/src/__tests__/llm-resolver.test.ts +340 -3
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/managed-profile-guard.test.ts +10 -0
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/openai-provider.test.ts +323 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
- package/src/__tests__/platform-proxy-context.test.ts +6 -1
- package/src/__tests__/platform.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
- package/src/__tests__/plugin-types.test.ts +2 -2
- package/src/__tests__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +57 -101
- package/src/__tests__/terminal-tools.test.ts +11 -1
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-executor.test.ts +90 -1
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +88 -5
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +208 -22
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/browser/operations.ts +15 -0
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +14 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +469 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/memory-v2.ts +308 -0
- package/src/cli/commands/notifications.ts +89 -37
- package/src/cli/commands/plugins.ts +67 -0
- package/src/cli/commands/schedules.ts +297 -5
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/install-from-github.ts +8 -9
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/program.ts +14 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
- package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +124 -0
- package/src/config/feature-flag-registry.json +111 -23
- package/src/config/llm-resolver.ts +66 -1
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/channels.ts +9 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +14 -0
- package/src/config/schemas/llm.ts +4 -3
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +51 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/context/compactor.ts +80 -13
- package/src/context/token-estimator.ts +72 -31
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
- package/src/daemon/conversation-agent-loop.ts +252 -56
- package/src/daemon/conversation-lifecycle.ts +142 -116
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +144 -75
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +7 -0
- package/src/daemon/conversation.ts +42 -12
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +449 -0
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +80 -0
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-app-control-proxy.ts +69 -18
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +1 -1
- package/src/daemon/host-file-proxy.ts +1 -1
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/host-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +67 -65
- package/src/daemon/memory-v2-startup.ts +49 -13
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/messages.ts +68 -1
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/pkb-reminder-builder.test.ts +10 -53
- package/src/daemon/pkb-reminder-builder.ts +4 -19
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +72 -12
- package/src/daemon/query-complexity-router.ts +75 -0
- package/src/daemon/skill-memory-refresh.ts +5 -1
- package/src/daemon/trust-context.ts +6 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
- package/src/heartbeat/heartbeat-service.ts +35 -191
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +20 -3
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/ipc/cli-client.ts +147 -45
- package/src/memory/__tests__/conversation-queries.test.ts +220 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/conversation-queries.ts +87 -1
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +22 -0
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
- package/src/memory/graph/conversation-graph-memory.ts +18 -6
- package/src/memory/graph/tools.ts +6 -37
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs-worker.ts +21 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +4 -22
- package/src/memory/memory-retrospective-job.ts +438 -21
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +26 -8
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- package/src/memory/migrations/index.ts +20 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +2 -0
- package/src/memory/schema/infrastructure.ts +2 -0
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +221 -17
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/router.test.ts +489 -1
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +69 -29
- package/src/memory/v2/page-index.ts +246 -19
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +18 -3
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +111 -44
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +10 -1
- package/src/notifications/home-feed-side-effect.ts +136 -27
- package/src/notifications/signal.ts +0 -4
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +13 -4
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +118 -26
- package/src/plugins/external-plugin-loader.ts +82 -10
- package/src/plugins/types.ts +16 -7
- package/src/prompts/__tests__/system-prompt.test.ts +44 -45
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -76
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +13 -5
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +51 -10
- package/src/providers/__tests__/inference.test.ts +2 -0
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +63 -13
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +24 -21
- package/src/providers/inference/auth.ts +15 -3
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/connections.ts +85 -5
- package/src/providers/inference/resolve-auth.ts +50 -5
- package/src/providers/model-catalog.ts +244 -242
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +215 -25
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +46 -4
- package/src/providers/platform-proxy/constants.ts +3 -4
- package/src/providers/provider-catalog-visibility.ts +3 -1
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +30 -1
- package/src/providers/types.ts +25 -0
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +212 -57
- package/src/runtime/auth/route-policy.ts +20 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +7 -51
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/channel-availability-routes.ts +5 -0
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +130 -12
- package/src/runtime/routes/conversation-routes.ts +288 -76
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +8 -1
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +14 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
- package/src/runtime/routes/integrations/a2a.ts +294 -0
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-v2-routes.ts +217 -0
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/slack-channel-routes.ts +187 -0
- package/src/runtime/routes/subagents-routes.ts +41 -0
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- package/src/subagent/manager.ts +2 -0
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
- package/src/tools/browser/browser-execution.ts +93 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
- package/src/tools/browser/cdp-client/factory.ts +87 -3
- package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
- package/src/tools/browser/cdp-client/types.ts +36 -0
- package/src/tools/browser/pinned-tabs.ts +90 -0
- package/src/tools/document/document-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +128 -2
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +213 -64
- package/src/tools/network/web-search.ts +191 -66
- package/src/tools/registry.ts +2 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +41 -2
- package/src/tools/ui-surface/definitions.ts +3 -1
- package/src/types/onboarding-context.ts +4 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/platform.ts +0 -5
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -5,13 +5,16 @@ import {
|
|
|
5
5
|
} from "node:https";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
7
7
|
|
|
8
|
+
import type { WebFetchMetadata } from "../../daemon/message-types/web-activity.js";
|
|
8
9
|
import { RiskLevel } from "../../permissions/types.js";
|
|
9
10
|
import type { ToolDefinition } from "../../providers/types.js";
|
|
10
11
|
import { wrapUntrustedContent } from "../../security/untrusted-content.js";
|
|
12
|
+
import { faviconUrlForDomain } from "../../util/favicon.js";
|
|
11
13
|
import { getLogger } from "../../util/logger.js";
|
|
12
14
|
import { safeStringSlice } from "../../util/unicode.js";
|
|
13
15
|
import { registerTool } from "../registry.js";
|
|
14
16
|
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
17
|
+
import { extractDomain } from "./domain-normalize.js";
|
|
15
18
|
import {
|
|
16
19
|
buildHostHeader,
|
|
17
20
|
isIPv4,
|
|
@@ -351,6 +354,26 @@ function extractFirstMatch(
|
|
|
351
354
|
return value || undefined;
|
|
352
355
|
}
|
|
353
356
|
|
|
357
|
+
const MAX_TITLE_CHARS = 200;
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Parse the first HTML `<title>` element from a response body.
|
|
361
|
+
*
|
|
362
|
+
* Used to populate {@link WebFetchMetadata.title}. Returns `undefined`
|
|
363
|
+
* when no `<title>` is present. The result is HTML-entity-decoded and
|
|
364
|
+
* capped at {@link MAX_TITLE_CHARS} characters so client UIs never have
|
|
365
|
+
* to truncate.
|
|
366
|
+
*/
|
|
367
|
+
function parseHtmlTitle(html: string): string | undefined {
|
|
368
|
+
// Bound the search to the first 200KB to avoid scanning huge bodies.
|
|
369
|
+
const searchRegion = safeStringSlice(html, 0, 200_000);
|
|
370
|
+
const match = /<title[^>]*>([^<]+)<\/title>/i.exec(searchRegion);
|
|
371
|
+
if (!match) return undefined;
|
|
372
|
+
const decoded = decodeHtmlEntities(match[1]).trim();
|
|
373
|
+
if (!decoded) return undefined;
|
|
374
|
+
return safeStringSlice(decoded, 0, MAX_TITLE_CHARS);
|
|
375
|
+
}
|
|
376
|
+
|
|
354
377
|
function extractHtmlMetadata(html: string): {
|
|
355
378
|
title?: string;
|
|
356
379
|
description?: string;
|
|
@@ -518,15 +541,62 @@ export async function executeWebFetch(
|
|
|
518
541
|
input: Record<string, unknown>,
|
|
519
542
|
options?: ExecuteWebFetchOptions,
|
|
520
543
|
): Promise<ToolExecutionResult> {
|
|
521
|
-
const
|
|
522
|
-
|
|
544
|
+
const startedAt = Date.now();
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Build a {@link ToolExecutionResult} for an early-exit error path (bad
|
|
548
|
+
* input, blocked target, timeout, bad content-type, HTTP error, ...).
|
|
549
|
+
* Always attaches structured {@link WebFetchMetadata} so client UIs can
|
|
550
|
+
* still render failed visits.
|
|
551
|
+
*/
|
|
552
|
+
const buildErrorResult = (
|
|
553
|
+
errorMessage: string,
|
|
554
|
+
meta: {
|
|
555
|
+
url: string;
|
|
556
|
+
finalUrl?: string;
|
|
557
|
+
status?: number;
|
|
558
|
+
contentType?: string;
|
|
559
|
+
redirectCount?: number;
|
|
560
|
+
},
|
|
561
|
+
): ToolExecutionResult => {
|
|
562
|
+
const safeUrl = sanitizeUrlStringForOutput(meta.url);
|
|
563
|
+
const safeFinalUrl = meta.finalUrl
|
|
564
|
+
? sanitizeUrlStringForOutput(meta.finalUrl)
|
|
565
|
+
: safeUrl;
|
|
566
|
+
const domain = extractDomain(safeFinalUrl);
|
|
523
567
|
return {
|
|
524
|
-
content:
|
|
568
|
+
content: errorMessage,
|
|
525
569
|
isError: true,
|
|
570
|
+
activityMetadata: {
|
|
571
|
+
webFetch: {
|
|
572
|
+
url: safeUrl,
|
|
573
|
+
finalUrl: safeFinalUrl,
|
|
574
|
+
status: meta.status ?? 0,
|
|
575
|
+
contentType: meta.contentType,
|
|
576
|
+
byteCount: 0,
|
|
577
|
+
charCount: 0,
|
|
578
|
+
truncated: false,
|
|
579
|
+
domain,
|
|
580
|
+
faviconUrl: faviconUrlForDomain(domain),
|
|
581
|
+
redirectCount: meta.redirectCount ?? 0,
|
|
582
|
+
durationMs: Date.now() - startedAt,
|
|
583
|
+
errorMessage,
|
|
584
|
+
},
|
|
585
|
+
},
|
|
526
586
|
};
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
const parsedUrl = parseUrl(input.url);
|
|
590
|
+
if (!parsedUrl) {
|
|
591
|
+
return buildErrorResult(
|
|
592
|
+
"Error: url is required and must be a valid HTTP(S) URL",
|
|
593
|
+
{ url: typeof input.url === "string" ? input.url : "" },
|
|
594
|
+
);
|
|
527
595
|
}
|
|
528
596
|
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
529
|
-
return
|
|
597
|
+
return buildErrorResult("Error: url must use http or https", {
|
|
598
|
+
url: parsedUrl.href,
|
|
599
|
+
});
|
|
530
600
|
}
|
|
531
601
|
|
|
532
602
|
const allowPrivateNetwork = input.allow_private_network === true;
|
|
@@ -534,10 +604,10 @@ export async function executeWebFetch(
|
|
|
534
604
|
const requestExecutor = options?.requestExecutor ?? defaultRequestExecutor;
|
|
535
605
|
|
|
536
606
|
if (!allowPrivateNetwork && isPrivateOrLocalHost(parsedUrl.hostname)) {
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
607
|
+
return buildErrorResult(
|
|
608
|
+
`Error: Refusing to fetch local/private network target (${parsedUrl.hostname}). Set allow_private_network=true if you explicitly need it.`,
|
|
609
|
+
{ url: parsedUrl.href },
|
|
610
|
+
);
|
|
541
611
|
}
|
|
542
612
|
const timeoutSeconds = clampInteger(
|
|
543
613
|
input.timeout_seconds,
|
|
@@ -576,6 +646,9 @@ export async function executeWebFetch(
|
|
|
576
646
|
}
|
|
577
647
|
}
|
|
578
648
|
|
|
649
|
+
let currentUrl = new URL(requestedUrl);
|
|
650
|
+
let redirectCount = 0;
|
|
651
|
+
|
|
579
652
|
try {
|
|
580
653
|
log.debug(
|
|
581
654
|
{ url: safeRequestedUrl, timeoutSeconds, maxChars, startIndex, rawMode },
|
|
@@ -591,8 +664,6 @@ export async function executeWebFetch(
|
|
|
591
664
|
"VellumAssistant/1.0 (+https://vellum.ai)",
|
|
592
665
|
};
|
|
593
666
|
|
|
594
|
-
let currentUrl = new URL(requestedUrl);
|
|
595
|
-
let redirectCount = 0;
|
|
596
667
|
let response: Response | null = null;
|
|
597
668
|
let currentResolvedAddresses: string[] | undefined;
|
|
598
669
|
|
|
@@ -606,16 +677,16 @@ export async function executeWebFetch(
|
|
|
606
677
|
controller.signal,
|
|
607
678
|
);
|
|
608
679
|
if (resolution.blockedAddress) {
|
|
609
|
-
return
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
680
|
+
return buildErrorResult(
|
|
681
|
+
`Error: Refusing to fetch target (${currentUrl.hostname}) because it resolves to local/private network address ${resolution.blockedAddress}. Set allow_private_network=true if you explicitly need it.`,
|
|
682
|
+
{ url: requestedUrl, finalUrl: currentUrl.href, redirectCount },
|
|
683
|
+
);
|
|
613
684
|
}
|
|
614
685
|
if (resolution.addresses.length === 0) {
|
|
615
|
-
return
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
686
|
+
return buildErrorResult(
|
|
687
|
+
`Error: Unable to resolve host "${currentUrl.hostname}" while fetching ${safeRequestedUrl}`,
|
|
688
|
+
{ url: requestedUrl, finalUrl: currentUrl.href, redirectCount },
|
|
689
|
+
);
|
|
619
690
|
}
|
|
620
691
|
currentResolvedAddresses = resolution.addresses;
|
|
621
692
|
}
|
|
@@ -650,10 +721,10 @@ export async function executeWebFetch(
|
|
|
650
721
|
currentResolvedAddresses = undefined;
|
|
651
722
|
|
|
652
723
|
if (!response) {
|
|
653
|
-
return
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
724
|
+
return buildErrorResult(
|
|
725
|
+
"Error: Web fetch failed: no response returned",
|
|
726
|
+
{ url: requestedUrl, finalUrl: currentUrl.href, redirectCount },
|
|
727
|
+
);
|
|
657
728
|
}
|
|
658
729
|
|
|
659
730
|
const location = response.headers.get("location");
|
|
@@ -662,10 +733,15 @@ export async function executeWebFetch(
|
|
|
662
733
|
if (!isRedirect) break;
|
|
663
734
|
|
|
664
735
|
if (redirectCount >= MAX_REDIRECTS) {
|
|
665
|
-
return
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
736
|
+
return buildErrorResult(
|
|
737
|
+
`Error: Too many redirects (>${MAX_REDIRECTS}) while fetching ${safeRequestedUrl}`,
|
|
738
|
+
{
|
|
739
|
+
url: requestedUrl,
|
|
740
|
+
finalUrl: currentUrl.href,
|
|
741
|
+
status: response.status,
|
|
742
|
+
redirectCount,
|
|
743
|
+
},
|
|
744
|
+
);
|
|
669
745
|
}
|
|
670
746
|
|
|
671
747
|
let nextUrl: URL;
|
|
@@ -677,24 +753,39 @@ export async function executeWebFetch(
|
|
|
677
753
|
currentUrl,
|
|
678
754
|
);
|
|
679
755
|
const safeCurrentUrl = sanitizeUrlForOutput(currentUrl);
|
|
680
|
-
return
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
756
|
+
return buildErrorResult(
|
|
757
|
+
`Error: Invalid redirect location "${safeLocation}" received from ${safeCurrentUrl}`,
|
|
758
|
+
{
|
|
759
|
+
url: requestedUrl,
|
|
760
|
+
finalUrl: currentUrl.href,
|
|
761
|
+
status: response.status,
|
|
762
|
+
redirectCount,
|
|
763
|
+
},
|
|
764
|
+
);
|
|
684
765
|
}
|
|
685
766
|
|
|
686
767
|
if (nextUrl.protocol !== "http:" && nextUrl.protocol !== "https:") {
|
|
687
|
-
return
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
768
|
+
return buildErrorResult(
|
|
769
|
+
`Error: Refusing redirect to unsupported protocol "${nextUrl.protocol}"`,
|
|
770
|
+
{
|
|
771
|
+
url: requestedUrl,
|
|
772
|
+
finalUrl: currentUrl.href,
|
|
773
|
+
status: response.status,
|
|
774
|
+
redirectCount,
|
|
775
|
+
},
|
|
776
|
+
);
|
|
691
777
|
}
|
|
692
778
|
|
|
693
779
|
if (!allowPrivateNetwork && isPrivateOrLocalHost(nextUrl.hostname)) {
|
|
694
|
-
return
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
780
|
+
return buildErrorResult(
|
|
781
|
+
`Error: Refusing redirect to local/private network target (${nextUrl.hostname}). Set allow_private_network=true if you explicitly need it.`,
|
|
782
|
+
{
|
|
783
|
+
url: requestedUrl,
|
|
784
|
+
finalUrl: currentUrl.href,
|
|
785
|
+
status: response.status,
|
|
786
|
+
redirectCount,
|
|
787
|
+
},
|
|
788
|
+
);
|
|
698
789
|
}
|
|
699
790
|
if (!allowPrivateNetwork) {
|
|
700
791
|
const resolution = await withAbortSignal(
|
|
@@ -706,17 +797,27 @@ export async function executeWebFetch(
|
|
|
706
797
|
controller.signal,
|
|
707
798
|
);
|
|
708
799
|
if (resolution.blockedAddress) {
|
|
709
|
-
return
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
800
|
+
return buildErrorResult(
|
|
801
|
+
`Error: Refusing redirect to target (${nextUrl.hostname}) because it resolves to local/private network address ${resolution.blockedAddress}. Set allow_private_network=true if you explicitly need it.`,
|
|
802
|
+
{
|
|
803
|
+
url: requestedUrl,
|
|
804
|
+
finalUrl: currentUrl.href,
|
|
805
|
+
status: response.status,
|
|
806
|
+
redirectCount,
|
|
807
|
+
},
|
|
808
|
+
);
|
|
713
809
|
}
|
|
714
810
|
if (resolution.addresses.length === 0) {
|
|
715
811
|
const safeCurrentUrl = sanitizeUrlForOutput(currentUrl);
|
|
716
|
-
return
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
812
|
+
return buildErrorResult(
|
|
813
|
+
`Error: Unable to resolve redirect host "${nextUrl.hostname}" from ${safeCurrentUrl}`,
|
|
814
|
+
{
|
|
815
|
+
url: requestedUrl,
|
|
816
|
+
finalUrl: currentUrl.href,
|
|
817
|
+
status: response.status,
|
|
818
|
+
redirectCount,
|
|
819
|
+
},
|
|
820
|
+
);
|
|
720
821
|
}
|
|
721
822
|
currentResolvedAddresses = resolution.addresses;
|
|
722
823
|
}
|
|
@@ -726,18 +827,25 @@ export async function executeWebFetch(
|
|
|
726
827
|
}
|
|
727
828
|
|
|
728
829
|
if (!response) {
|
|
729
|
-
return {
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
830
|
+
return buildErrorResult("Error: Web fetch failed: no response returned", {
|
|
831
|
+
url: requestedUrl,
|
|
832
|
+
finalUrl: currentUrl.href,
|
|
833
|
+
redirectCount,
|
|
834
|
+
});
|
|
733
835
|
}
|
|
734
836
|
|
|
735
837
|
const contentType = response.headers.get("content-type") ?? "";
|
|
736
838
|
if (!isTextLikeContentType(contentType)) {
|
|
737
|
-
return
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
839
|
+
return buildErrorResult(
|
|
840
|
+
`Error: Unsupported content type "${contentType || "unknown"}". web_fetch only supports text-like responses.`,
|
|
841
|
+
{
|
|
842
|
+
url: requestedUrl,
|
|
843
|
+
finalUrl: currentUrl.href,
|
|
844
|
+
status: response.status,
|
|
845
|
+
contentType,
|
|
846
|
+
redirectCount,
|
|
847
|
+
},
|
|
848
|
+
);
|
|
741
849
|
}
|
|
742
850
|
|
|
743
851
|
const body = await readResponseText(response, MAX_DOWNLOAD_BYTES);
|
|
@@ -778,9 +886,20 @@ export async function executeWebFetch(
|
|
|
778
886
|
`start_index (${startIndex}) exceeded available content length (${processed.length}).`,
|
|
779
887
|
);
|
|
780
888
|
}
|
|
781
|
-
|
|
889
|
+
// Detect likely JS-rendered SPAs: text is absolutely tiny, or a non-trivial
|
|
890
|
+
// HTML payload compresses to almost nothing (a shell page whose meaningful
|
|
891
|
+
// content is painted after fetch + document.body rewrite).
|
|
892
|
+
const lowAbsolute = processed.length < 200;
|
|
893
|
+
const lowRatio =
|
|
894
|
+
body.bytesRead >= 10_000 && processed.length / body.bytesRead < 0.05;
|
|
895
|
+
const mayRequireJavaScript = html && !rawMode && (lowAbsolute || lowRatio);
|
|
896
|
+
if (mayRequireJavaScript) {
|
|
897
|
+
const pct =
|
|
898
|
+
body.bytesRead > 0
|
|
899
|
+
? ((processed.length / body.bytesRead) * 100).toFixed(1)
|
|
900
|
+
: "0";
|
|
782
901
|
notices.push(
|
|
783
|
-
`Extracted text
|
|
902
|
+
`Extracted only ${processed.length} chars of text from ${body.bytesRead} bytes of HTML (${pct}%). Content may be JavaScript-rendered — the static fetch likely missed dynamically injected content.`,
|
|
784
903
|
);
|
|
785
904
|
}
|
|
786
905
|
|
|
@@ -803,11 +922,32 @@ export async function executeWebFetch(
|
|
|
803
922
|
markdownTokens,
|
|
804
923
|
});
|
|
805
924
|
|
|
925
|
+
const truncated = body.truncated || safeEnd < processed.length;
|
|
926
|
+
const parsedTitle = html ? parseHtmlTitle(body.text) : undefined;
|
|
927
|
+
const finalDomain = extractDomain(currentUrl.href);
|
|
928
|
+
const meta: WebFetchMetadata = {
|
|
929
|
+
url: safeRequestedUrl,
|
|
930
|
+
finalUrl: sanitizeUrlForOutput(currentUrl),
|
|
931
|
+
status: response.status,
|
|
932
|
+
contentType: contentType || undefined,
|
|
933
|
+
byteCount: body.bytesRead,
|
|
934
|
+
charCount: sliced.length,
|
|
935
|
+
truncated,
|
|
936
|
+
title: parsedTitle,
|
|
937
|
+
domain: finalDomain,
|
|
938
|
+
faviconUrl: faviconUrlForDomain(finalDomain),
|
|
939
|
+
redirectCount,
|
|
940
|
+
durationMs: Date.now() - startedAt,
|
|
941
|
+
mayRequireJavaScript: mayRequireJavaScript || undefined,
|
|
942
|
+
};
|
|
943
|
+
|
|
806
944
|
if (!response.ok) {
|
|
945
|
+
const errorMessage = `Error: HTTP ${response.status}`;
|
|
807
946
|
return {
|
|
808
|
-
content:
|
|
947
|
+
content: `${errorMessage}\n\n${content}`,
|
|
809
948
|
isError: true,
|
|
810
949
|
status: notices.length > 0 ? notices.join("\n") : undefined,
|
|
950
|
+
activityMetadata: { webFetch: { ...meta, errorMessage } },
|
|
811
951
|
};
|
|
812
952
|
}
|
|
813
953
|
|
|
@@ -815,21 +955,30 @@ export async function executeWebFetch(
|
|
|
815
955
|
content,
|
|
816
956
|
isError: false,
|
|
817
957
|
status: notices.length > 0 ? notices.join("\n") : undefined,
|
|
958
|
+
activityMetadata: { webFetch: meta },
|
|
818
959
|
};
|
|
819
960
|
} catch (err) {
|
|
820
961
|
if (err instanceof Error && err.name === "AbortError") {
|
|
821
962
|
if (externalSignal?.aborted) {
|
|
822
|
-
return
|
|
963
|
+
return buildErrorResult("Error: web fetch was cancelled", {
|
|
964
|
+
url: requestedUrl,
|
|
965
|
+
finalUrl: currentUrl.href,
|
|
966
|
+
redirectCount,
|
|
967
|
+
});
|
|
823
968
|
}
|
|
824
|
-
return
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
969
|
+
return buildErrorResult(
|
|
970
|
+
`Error: web fetch timed out after ${timeoutSeconds}s`,
|
|
971
|
+
{ url: requestedUrl, finalUrl: currentUrl.href, redirectCount },
|
|
972
|
+
);
|
|
828
973
|
}
|
|
829
974
|
|
|
830
975
|
const msg = err instanceof Error ? err.message : String(err);
|
|
831
976
|
log.error({ err, url: safeRequestedUrl }, "Web fetch failed");
|
|
832
|
-
return
|
|
977
|
+
return buildErrorResult(`Error: Web fetch failed: ${msg}`, {
|
|
978
|
+
url: requestedUrl,
|
|
979
|
+
finalUrl: currentUrl.href,
|
|
980
|
+
redirectCount,
|
|
981
|
+
});
|
|
833
982
|
} finally {
|
|
834
983
|
clearTimeout(timeoutHandle);
|
|
835
984
|
externalSignal?.removeEventListener("abort", onExternalAbort);
|
|
@@ -839,7 +988,7 @@ export async function executeWebFetch(
|
|
|
839
988
|
class WebFetchTool implements Tool {
|
|
840
989
|
name = "web_fetch";
|
|
841
990
|
description =
|
|
842
|
-
"Fetch a webpage and return LLM-friendly extracted text with metadata. Use this after web_search when you need to read a specific result.";
|
|
991
|
+
"Fetch a webpage and return LLM-friendly extracted text with metadata. Use this after web_search when you need to read a specific result. To find pages on a site without guessing slugs, fetch /sitemap.xml first — it has ground-truth paths and works even when pages are JS-rendered.";
|
|
843
992
|
category = "network";
|
|
844
993
|
defaultRiskLevel = RiskLevel.Low;
|
|
845
994
|
|