@vellumai/assistant 0.8.3 → 0.8.5
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 +2 -2
- package/docker-entrypoint.sh +0 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +1492 -100
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +302 -33
- package/src/__tests__/approval-cascade.test.ts +1 -1
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +2 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
- package/src/__tests__/conversation-agent-loop.test.ts +188 -129
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- 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-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +324 -0
- package/src/__tests__/conversation-lifecycle.test.ts +53 -12
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
- 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 +2 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- 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-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -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-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +7 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- 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__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -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-shell-tool.test.ts +1 -1
- 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__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-chain.test.ts +2 -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__/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 +77 -17
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +161 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +151 -0
- package/src/__tests__/openai-responses-provider.test.ts +118 -16
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -5
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- 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__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/server-history-render.test.ts +83 -4
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +161 -124
- package/src/__tests__/terminal-tools.test.ts +12 -2
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +89 -53
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +94 -10
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +65 -20
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference-providers.ts +147 -1
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +483 -0
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/program.ts +2 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +258 -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-open.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +24 -12
- package/src/config/call-site-defaults.ts +20 -0
- package/src/config/feature-flag-registry.json +115 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
- package/src/config/schemas/call-site-catalog.ts +35 -0
- package/src/config/schemas/llm.ts +14 -0
- package/src/config/schemas/memory-v2.ts +294 -1
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +60 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- 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 +155 -36
- package/src/daemon/conversation-agent-loop.ts +307 -88
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +149 -118
- 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 +145 -84
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +70 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +54 -32
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +2 -0
- package/src/daemon/handlers/conversations.ts +90 -3
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +5 -5
- package/src/daemon/host-file-proxy.ts +5 -5
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +11 -11
- package/src/daemon/lifecycle.ts +40 -23
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +14 -9
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +66 -7
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/daemon/trust-context.ts +6 -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/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +85 -0
- package/src/home/suggested-prompts.ts +168 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +191 -100
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +26 -0
- package/src/memory/db-maintenance.ts +30 -21
- 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/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +68 -15
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +11 -3
- package/src/memory/memory-retrospective-job.ts +413 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +41 -14
- 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/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/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +34 -0
- package/src/memory/migrations/registry.ts +58 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +22 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +158 -112
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +660 -4
- package/src/memory/v2/consolidation-job.ts +14 -0
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +42 -25
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +369 -62
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/messaging/providers/index.ts +7 -1
- 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__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +38 -50
- package/src/notifications/home-feed-side-effect.ts +60 -30
- 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/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +8 -5
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +6 -3
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +100 -20
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +69 -19
- package/src/prompts/system-prompt.ts +118 -216
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +10 -2
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +281 -9
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +159 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/fireworks/client.ts +20 -2
- package/src/providers/gemini/client.ts +49 -6
- 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/adapter-factory.ts +18 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +91 -1
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +63 -23
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +86 -23
- package/src/providers/openrouter/client.ts +5 -1
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/types.ts +25 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- 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 +152 -56
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +43 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -6
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +50 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -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/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/approval-routes.ts +4 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +147 -2
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +196 -31
- package/src/runtime/routes/conversation-routes.ts +472 -425
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- 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 +17 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- 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/acl-enforcement.ts +96 -3
- 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 +20 -4
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +427 -0
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/sanity-routes.ts +159 -0
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +188 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/conversation-serializer.ts +30 -4
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- 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/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
- package/src/tools/browser/browser-execution.ts +106 -0
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
- package/src/tools/browser/cdp-client/factory.ts +171 -4
- package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
- package/src/tools/browser/cdp-client/types.ts +101 -0
- package/src/tools/browser/pinned-tabs.ts +146 -0
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- 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 +187 -2
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -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 +216 -73
- package/src/tools/network/web-search.ts +216 -98
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +31 -98
- package/src/tools/ui-surface/definitions.ts +9 -23
- package/src/types/onboarding-context.ts +4 -0
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -7
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- 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/prompts/cache-boundary.ts +0 -8
- 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
|
@@ -82,6 +82,25 @@ mock.module("../../util/logger.js", () => ({
|
|
|
82
82
|
}),
|
|
83
83
|
}));
|
|
84
84
|
|
|
85
|
+
// Stub secure-keys so the `prepareAgentEnv` preflight finds a token without
|
|
86
|
+
// the test having to populate the real OS keyring. Driven via `secureKeyStore`
|
|
87
|
+
// per test in beforeEach; the default seeds a vault token so existing tests
|
|
88
|
+
// (which assume claude spawns succeed) keep passing.
|
|
89
|
+
//
|
|
90
|
+
// The real module's other exports are spread in so transitive importers
|
|
91
|
+
// (e.g. session-manager → pending-interactions → credential-routes, which
|
|
92
|
+
// imports `getSecureKeyResultAsync`) still resolve at parse time. Bun's
|
|
93
|
+
// `mock.module` is process-global and returns *exactly* the keys the factory
|
|
94
|
+
// returns — without the spread, any consumer pulling a non-`getSecureKeyAsync`
|
|
95
|
+
// export errors with "Export named '<X>' not found".
|
|
96
|
+
const secureKeyStore = new Map<string, string>();
|
|
97
|
+
const realSecureKeys = await import("../../security/secure-keys.js");
|
|
98
|
+
|
|
99
|
+
mock.module("../../security/secure-keys.js", () => ({
|
|
100
|
+
...realSecureKeys,
|
|
101
|
+
getSecureKeyAsync: async (key: string) => secureKeyStore.get(key),
|
|
102
|
+
}));
|
|
103
|
+
|
|
85
104
|
// Stub session manager so we don't actually spawn child processes.
|
|
86
105
|
const spawnMock = mock(
|
|
87
106
|
async (
|
|
@@ -131,6 +150,14 @@ beforeEach(() => {
|
|
|
131
150
|
_resetAdapterVersionCacheForTests();
|
|
132
151
|
config.setConfig({ agents: DEFAULT_TEST_AGENTS });
|
|
133
152
|
which.setWhich((cmd) => `/usr/local/bin/${cmd}`);
|
|
153
|
+
// Default: vault has a claude token so the preflight in `prepareAgentEnv`
|
|
154
|
+
// succeeds for tests that don't care about env injection. Env-injection
|
|
155
|
+
// tests below clear/override this explicitly.
|
|
156
|
+
secureKeyStore.clear();
|
|
157
|
+
secureKeyStore.set(
|
|
158
|
+
"credential/acp/claude_oauth_token",
|
|
159
|
+
"default-test-token",
|
|
160
|
+
);
|
|
134
161
|
});
|
|
135
162
|
|
|
136
163
|
// ---------------------------------------------------------------------------
|
|
@@ -376,3 +403,95 @@ describe("executeAcpSpawn — per-agent resume hint", () => {
|
|
|
376
403
|
expect(payload.message).not.toContain("To resume this session later");
|
|
377
404
|
});
|
|
378
405
|
});
|
|
406
|
+
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
// executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN env injection + preflight
|
|
409
|
+
//
|
|
410
|
+
// Mirrors the HTTP-route test block in
|
|
411
|
+
// `runtime/routes/acp-routes.test.ts` — the skill tool calls into the same
|
|
412
|
+
// `prepareAgentEnv` helper, and the contract must be identical so a
|
|
413
|
+
// missing token fails the spawn at the tool boundary (`isError: true`)
|
|
414
|
+
// instead of letting a token-less subprocess zombie out. PR-history
|
|
415
|
+
// context: the inline env-injection used to live in the route only; this
|
|
416
|
+
// tool path was bypassing it entirely, causing every skill-driven ACP
|
|
417
|
+
// spawn to die on first prompt with "Authentication required". Pin both
|
|
418
|
+
// the happy paths and the throw path here so future drift on either
|
|
419
|
+
// caller fails the suite loudly.
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
|
|
422
|
+
describe("executeAcpSpawn — CLAUDE_CODE_OAUTH_TOKEN injection", () => {
|
|
423
|
+
test("injects CLAUDE_CODE_OAUTH_TOKEN from the secure store for the claude agent", async () => {
|
|
424
|
+
secureKeyStore.clear();
|
|
425
|
+
secureKeyStore.set(
|
|
426
|
+
"credential/acp/claude_oauth_token",
|
|
427
|
+
"tool-vault-token-abc",
|
|
428
|
+
);
|
|
429
|
+
execScripts.set("npm ls", { error: new Error("npm not installed") });
|
|
430
|
+
execScripts.set("npm view", { error: new Error("npm not installed") });
|
|
431
|
+
|
|
432
|
+
const result = await executeAcpSpawn(
|
|
433
|
+
{ agent: "claude", task: "do something" },
|
|
434
|
+
makeContext(),
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
expect(result.isError).toBe(false);
|
|
438
|
+
expect(spawnMock).toHaveBeenCalledTimes(1);
|
|
439
|
+
const agentConfigArg = spawnMock.mock.calls[0][1] as {
|
|
440
|
+
env?: Record<string, string>;
|
|
441
|
+
};
|
|
442
|
+
expect(agentConfigArg.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
|
|
443
|
+
"tool-vault-token-abc",
|
|
444
|
+
);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test("accepts CLAUDE_CODE_OAUTH_TOKEN from config.json agent.env without a vault entry", async () => {
|
|
448
|
+
// Mirrors the route-level precedence test: a config.json env override
|
|
449
|
+
// is the first-priority provisioning route. The resolver surfaces it
|
|
450
|
+
// on `resolved.agent.env`, which the helper preserves.
|
|
451
|
+
secureKeyStore.clear();
|
|
452
|
+
config.setConfig({
|
|
453
|
+
agents: {
|
|
454
|
+
claude: {
|
|
455
|
+
command: "claude-agent-acp",
|
|
456
|
+
args: [],
|
|
457
|
+
env: { CLAUDE_CODE_OAUTH_TOKEN: "tool-config-token-xyz" },
|
|
458
|
+
},
|
|
459
|
+
codex: { command: "codex-acp", args: [] },
|
|
460
|
+
"unknown-agent": { command: "some-other-binary", args: [] },
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
execScripts.set("npm ls", { error: new Error("npm not installed") });
|
|
464
|
+
execScripts.set("npm view", { error: new Error("npm not installed") });
|
|
465
|
+
|
|
466
|
+
const result = await executeAcpSpawn(
|
|
467
|
+
{ agent: "claude", task: "do something" },
|
|
468
|
+
makeContext(),
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
expect(result.isError).toBe(false);
|
|
472
|
+
const agentConfigArg = spawnMock.mock.calls[0][1] as {
|
|
473
|
+
env?: Record<string, string>;
|
|
474
|
+
};
|
|
475
|
+
expect(agentConfigArg.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe(
|
|
476
|
+
"tool-config-token-xyz",
|
|
477
|
+
);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test("returns isError when no token is available from either route (preflight throw mapped to tool result)", async () => {
|
|
481
|
+
// Both routes empty. The helper throws `FailedDependencyError`; the
|
|
482
|
+
// tool catches it and returns `{ isError: true, content: <msg> }`
|
|
483
|
+
// rather than letting it propagate (the tool boundary is a sync
|
|
484
|
+
// ToolExecutionResult, not an HTTP response).
|
|
485
|
+
secureKeyStore.clear();
|
|
486
|
+
|
|
487
|
+
const result = await executeAcpSpawn(
|
|
488
|
+
{ agent: "claude", task: "do something" },
|
|
489
|
+
makeContext(),
|
|
490
|
+
);
|
|
491
|
+
|
|
492
|
+
expect(result.isError).toBe(true);
|
|
493
|
+
expect(result.content).toContain("CLAUDE_CODE_OAUTH_TOKEN");
|
|
494
|
+
// Spawn was NEVER called — preflight fired before the subprocess started.
|
|
495
|
+
expect(spawnMock).not.toHaveBeenCalled();
|
|
496
|
+
});
|
|
497
|
+
});
|
package/src/tools/acp/spawn.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
|
|
3
3
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
4
|
+
import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
|
|
4
5
|
import { resolveAcpAgent } from "../../acp/resolve-agent.js";
|
|
5
6
|
import { DEFAULT_AGENT_NPM_PACKAGES } from "../../config/acp-defaults.js";
|
|
6
7
|
import { getLogger } from "../../util/logger.js";
|
|
@@ -159,8 +160,6 @@ export async function executeAcpSpawn(
|
|
|
159
160
|
}
|
|
160
161
|
}
|
|
161
162
|
}
|
|
162
|
-
const agentConfig = resolved.agent;
|
|
163
|
-
|
|
164
163
|
const sendToClient = context.sendToClient as
|
|
165
164
|
| ((msg: { type: string; [key: string]: unknown }) => void)
|
|
166
165
|
| undefined;
|
|
@@ -171,6 +170,20 @@ export async function executeAcpSpawn(
|
|
|
171
170
|
};
|
|
172
171
|
}
|
|
173
172
|
|
|
173
|
+
// Inject required env vars and preflight via the shared helper. Mirrors
|
|
174
|
+
// the HTTP route at `runtime/routes/acp-routes.ts:spawnSession` — both
|
|
175
|
+
// call sites MUST go through `prepareAgentEnv` before `manager.spawn`,
|
|
176
|
+
// otherwise the spawned subprocess starts with no auth and dies as a
|
|
177
|
+
// zombie after the first prompt. See `acp/prepare-agent-env.ts` for
|
|
178
|
+
// the full rationale.
|
|
179
|
+
let agentConfig;
|
|
180
|
+
try {
|
|
181
|
+
agentConfig = await prepareAgentEnv(resolved.agent);
|
|
182
|
+
} catch (err) {
|
|
183
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
184
|
+
return { content: msg, isError: true };
|
|
185
|
+
}
|
|
186
|
+
|
|
174
187
|
// Best-effort version check — never blocks the spawn. If outdated, we
|
|
175
188
|
// append a non-blocking warning to the success payload.
|
|
176
189
|
const versionInfo = await checkAdapterVersion(agentConfig.command);
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { RiskLevel } from "../../permissions/types.js";
|
|
12
|
-
import type { ToolDefinition } from "../../providers/types.js";
|
|
13
12
|
import type { Tool, ToolExecutionResult } from "../types.js";
|
|
14
13
|
|
|
15
14
|
// ---------------------------------------------------------------------------
|
|
@@ -33,12 +32,9 @@ const appOpenTool: Tool = {
|
|
|
33
32
|
category: "apps",
|
|
34
33
|
defaultRiskLevel: RiskLevel.Low,
|
|
35
34
|
executionMode: "proxy",
|
|
35
|
+
executionTarget: "host",
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
name: this.name,
|
|
40
|
-
description: this.description,
|
|
41
|
-
input_schema: {
|
|
37
|
+
input_schema: {
|
|
42
38
|
type: "object",
|
|
43
39
|
properties: {
|
|
44
40
|
app_id: {
|
|
@@ -54,8 +50,6 @@ const appOpenTool: Tool = {
|
|
|
54
50
|
},
|
|
55
51
|
required: ["app_id"],
|
|
56
52
|
},
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
53
|
|
|
60
54
|
execute: proxyExecute,
|
|
61
55
|
};
|
|
@@ -51,7 +51,7 @@ const singleQ = {
|
|
|
51
51
|
|
|
52
52
|
describe("AskQuestionTool definition", () => {
|
|
53
53
|
test("exposes the expected schema shape and description language", () => {
|
|
54
|
-
const def = new AskQuestionTool()
|
|
54
|
+
const def = new AskQuestionTool();
|
|
55
55
|
expect(def.name).toBe("ask_question");
|
|
56
56
|
expect(def.description).toContain("free-text fallback is always added");
|
|
57
57
|
expect(def.description).toContain("do not");
|
|
@@ -463,8 +463,8 @@ describe("AskQuestionTool batched input", () => {
|
|
|
463
463
|
|
|
464
464
|
describe("AskQuestionTool definition (batched schema)", () => {
|
|
465
465
|
test("exposes `questions[]` shape, keeps legacy fields, omits per-question id", () => {
|
|
466
|
-
const def = new AskQuestionTool()
|
|
467
|
-
const schema = def.input_schema as {
|
|
466
|
+
const def = new AskQuestionTool();
|
|
467
|
+
const schema = def.input_schema as unknown as {
|
|
468
468
|
properties: Record<
|
|
469
469
|
string,
|
|
470
470
|
{
|
|
@@ -2,13 +2,12 @@ import { z } from "zod";
|
|
|
2
2
|
|
|
3
3
|
import { QuestionPrompter } from "../../permissions/question-prompter.js";
|
|
4
4
|
import { RiskLevel } from "../../permissions/types.js";
|
|
5
|
-
import type { ToolDefinition } from "../../providers/types.js";
|
|
6
5
|
import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
|
|
7
6
|
import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
|
|
8
7
|
|
|
9
8
|
// ── Input schema ────────────────────────────────────────────────────
|
|
10
9
|
// Runtime validation lives in Zod; the wire-level definition surfaced
|
|
11
|
-
// to the LLM is the hand-written JSON Schema in
|
|
10
|
+
// to the LLM is the hand-written JSON Schema in `input_schema` below.
|
|
12
11
|
// (The codebase does not currently use zod-to-json-schema for tool defs,
|
|
13
12
|
// so the two are kept in sync manually.)
|
|
14
13
|
|
|
@@ -109,53 +108,37 @@ const DESCRIPTION = [
|
|
|
109
108
|
"context shown beneath the label.",
|
|
110
109
|
].join("\n");
|
|
111
110
|
|
|
111
|
+
// Shared option-schema fragment used by both the batched `questions[]`
|
|
112
|
+
// shape and the legacy flat `options` field.
|
|
113
|
+
const OPTION_ITEMS_SCHEMA = {
|
|
114
|
+
type: "object",
|
|
115
|
+
properties: {
|
|
116
|
+
id: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description:
|
|
119
|
+
"Stable identifier for this option (returned verbatim in the response).",
|
|
120
|
+
},
|
|
121
|
+
label: {
|
|
122
|
+
type: "string",
|
|
123
|
+
description: "Short human-readable label.",
|
|
124
|
+
},
|
|
125
|
+
description: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Optional one-line context shown beneath the label.",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
required: ["id", "label"],
|
|
131
|
+
} as const;
|
|
132
|
+
|
|
112
133
|
// ── Tool ────────────────────────────────────────────────────────────
|
|
113
134
|
|
|
114
135
|
export class AskQuestionTool implements Tool {
|
|
115
136
|
name = "ask_question";
|
|
116
137
|
description = DESCRIPTION;
|
|
117
138
|
category = "interaction";
|
|
139
|
+
executionTarget = "sandbox" as const;
|
|
118
140
|
defaultRiskLevel = RiskLevel.Low;
|
|
119
|
-
|
|
120
|
-
// Override hook for tests: lets a test replace the prompter factory
|
|
121
|
-
// without monkey-patching the module. Default factory wires the real
|
|
122
|
-
// broadcastMessage so the question reaches every connected client.
|
|
123
|
-
private prompterFactory: () => Pick<QuestionPrompter, "prompt">;
|
|
124
|
-
|
|
125
|
-
constructor(
|
|
126
|
-
prompterFactory: () => Pick<QuestionPrompter, "prompt"> = () =>
|
|
127
|
-
new QuestionPrompter({ broadcastMessage }),
|
|
128
|
-
) {
|
|
129
|
-
this.prompterFactory = prompterFactory;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
getDefinition(): ToolDefinition {
|
|
133
|
-
// Shared option-schema fragment used by both the batched `questions[]`
|
|
134
|
-
// shape and the legacy flat `options` field.
|
|
135
|
-
const optionItemsSchema = {
|
|
136
|
-
type: "object",
|
|
137
|
-
properties: {
|
|
138
|
-
id: {
|
|
139
|
-
type: "string",
|
|
140
|
-
description:
|
|
141
|
-
"Stable identifier for this option (returned verbatim in the response).",
|
|
142
|
-
},
|
|
143
|
-
label: {
|
|
144
|
-
type: "string",
|
|
145
|
-
description: "Short human-readable label.",
|
|
146
|
-
},
|
|
147
|
-
description: {
|
|
148
|
-
type: "string",
|
|
149
|
-
description: "Optional one-line context shown beneath the label.",
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
required: ["id", "label"],
|
|
153
|
-
} as const;
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
name: this.name,
|
|
157
|
-
description: this.description,
|
|
158
|
-
input_schema: {
|
|
141
|
+
input_schema = {
|
|
159
142
|
type: "object",
|
|
160
143
|
properties: {
|
|
161
144
|
// ── Recommended shape ─────────────────────────────────────
|
|
@@ -182,7 +165,7 @@ export class AskQuestionTool implements Tool {
|
|
|
182
165
|
maxItems: 4,
|
|
183
166
|
description:
|
|
184
167
|
"2–4 structured options. The UI always appends a free-text fallback slot, so do not include a 'something else' option here.",
|
|
185
|
-
items:
|
|
168
|
+
items: OPTION_ITEMS_SCHEMA,
|
|
186
169
|
},
|
|
187
170
|
freeTextPlaceholder: {
|
|
188
171
|
type: "string",
|
|
@@ -212,7 +195,7 @@ export class AskQuestionTool implements Tool {
|
|
|
212
195
|
maxItems: 4,
|
|
213
196
|
description:
|
|
214
197
|
"Legacy: 2–4 structured options. Prefer `questions[].options`. The UI always appends a free-text fallback slot, so do not include a 'something else' option here.",
|
|
215
|
-
items:
|
|
198
|
+
items: OPTION_ITEMS_SCHEMA,
|
|
216
199
|
},
|
|
217
200
|
freeTextPlaceholder: {
|
|
218
201
|
type: "string",
|
|
@@ -222,8 +205,18 @@ export class AskQuestionTool implements Tool {
|
|
|
222
205
|
},
|
|
223
206
|
// No top-level `required` — caller must supply either `questions`
|
|
224
207
|
// or the legacy flat trio (`question` + `options`). Enforced in Zod.
|
|
225
|
-
},
|
|
226
208
|
};
|
|
209
|
+
|
|
210
|
+
// Override hook for tests: lets a test replace the prompter factory
|
|
211
|
+
// without monkey-patching the module. Default factory wires the real
|
|
212
|
+
// broadcastMessage so the question reaches every connected client.
|
|
213
|
+
private prompterFactory: () => Pick<QuestionPrompter, "prompt">;
|
|
214
|
+
|
|
215
|
+
constructor(
|
|
216
|
+
prompterFactory: () => Pick<QuestionPrompter, "prompt"> = () =>
|
|
217
|
+
new QuestionPrompter({ broadcastMessage }),
|
|
218
|
+
) {
|
|
219
|
+
this.prompterFactory = prompterFactory;
|
|
227
220
|
}
|
|
228
221
|
|
|
229
222
|
async execute(
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
__resetPinnedTabsForTests,
|
|
5
|
+
clearPinnedTab,
|
|
6
|
+
clearPinnedTabByTabId,
|
|
7
|
+
getPinnedTab,
|
|
8
|
+
setPinnedTab,
|
|
9
|
+
} from "../pinned-tabs.js";
|
|
10
|
+
|
|
11
|
+
describe("pinned-tabs", () => {
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
__resetPinnedTabsForTests();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("setPinnedTab / getPinnedTab round-trips", () => {
|
|
17
|
+
setPinnedTab("conv-a", "42");
|
|
18
|
+
expect(getPinnedTab("conv-a")).toBe("42");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("getPinnedTab returns undefined for unset conversation", () => {
|
|
22
|
+
expect(getPinnedTab("unset-conv")).toBeUndefined();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("conversations have independent pins", () => {
|
|
26
|
+
setPinnedTab("conv-a", "42");
|
|
27
|
+
setPinnedTab("conv-b", "99");
|
|
28
|
+
expect(getPinnedTab("conv-a")).toBe("42");
|
|
29
|
+
expect(getPinnedTab("conv-b")).toBe("99");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("setPinnedTab overwrites a prior pin for the same conversation", () => {
|
|
33
|
+
setPinnedTab("conv-a", "42");
|
|
34
|
+
setPinnedTab("conv-a", "100");
|
|
35
|
+
expect(getPinnedTab("conv-a")).toBe("100");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("clearPinnedTab removes the pin", () => {
|
|
39
|
+
setPinnedTab("conv-a", "42");
|
|
40
|
+
clearPinnedTab("conv-a");
|
|
41
|
+
expect(getPinnedTab("conv-a")).toBeUndefined();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("clearPinnedTab on a non-existent conversation is a no-op", () => {
|
|
45
|
+
// Should not throw or affect other conversations.
|
|
46
|
+
setPinnedTab("conv-a", "42");
|
|
47
|
+
clearPinnedTab("nonexistent");
|
|
48
|
+
expect(getPinnedTab("conv-a")).toBe("42");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("clearPinnedTabByTabId clears every matching conversation", () => {
|
|
52
|
+
setPinnedTab("conv-a", "42");
|
|
53
|
+
setPinnedTab("conv-b", "42");
|
|
54
|
+
setPinnedTab("conv-c", "99");
|
|
55
|
+
|
|
56
|
+
const cleared = clearPinnedTabByTabId("42");
|
|
57
|
+
|
|
58
|
+
expect(cleared).toBe(2);
|
|
59
|
+
expect(getPinnedTab("conv-a")).toBeUndefined();
|
|
60
|
+
expect(getPinnedTab("conv-b")).toBeUndefined();
|
|
61
|
+
expect(getPinnedTab("conv-c")).toBe("99"); // unchanged
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("clearPinnedTabByTabId on a non-matching tab is a no-op returning 0", () => {
|
|
65
|
+
setPinnedTab("conv-a", "42");
|
|
66
|
+
const cleared = clearPinnedTabByTabId("999");
|
|
67
|
+
expect(cleared).toBe(0);
|
|
68
|
+
expect(getPinnedTab("conv-a")).toBe("42");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("empty conversationId is rejected on set", () => {
|
|
72
|
+
setPinnedTab("", "42");
|
|
73
|
+
expect(getPinnedTab("")).toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("empty tabId is rejected on set", () => {
|
|
77
|
+
setPinnedTab("conv-a", "");
|
|
78
|
+
expect(getPinnedTab("conv-a")).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("cross-client isolation (#31361)", () => {
|
|
82
|
+
test("same tabId on different clients are stored independently", () => {
|
|
83
|
+
setPinnedTab("conv-a", "99", "clientA");
|
|
84
|
+
setPinnedTab("conv-a", "99", "clientB");
|
|
85
|
+
expect(getPinnedTab("conv-a", "clientA")).toBe("99");
|
|
86
|
+
expect(getPinnedTab("conv-a", "clientB")).toBe("99");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("clearPinnedTabByTabId with clientId only clears that client", () => {
|
|
90
|
+
setPinnedTab("conv-a", "99", "clientA");
|
|
91
|
+
setPinnedTab("conv-b", "99", "clientB");
|
|
92
|
+
|
|
93
|
+
const cleared = clearPinnedTabByTabId("99", "clientA");
|
|
94
|
+
|
|
95
|
+
expect(cleared).toBe(1);
|
|
96
|
+
expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
|
|
97
|
+
expect(getPinnedTab("conv-b", "clientB")).toBe("99"); // clientB's pin survives
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("getPinnedTab with matching clientId returns correct tabId", () => {
|
|
101
|
+
setPinnedTab("conv-x", "42", "clientA");
|
|
102
|
+
setPinnedTab("conv-x", "77", "clientB");
|
|
103
|
+
|
|
104
|
+
expect(getPinnedTab("conv-x", "clientA")).toBe("42");
|
|
105
|
+
expect(getPinnedTab("conv-x", "clientB")).toBe("77");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("clearPinnedTabByTabId without clientId clears all matching entries (backward compat)", () => {
|
|
109
|
+
setPinnedTab("conv-a", "99", "clientA");
|
|
110
|
+
setPinnedTab("conv-b", "99", "clientB");
|
|
111
|
+
|
|
112
|
+
const cleared = clearPinnedTabByTabId("99");
|
|
113
|
+
|
|
114
|
+
expect(cleared).toBe(2);
|
|
115
|
+
expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
|
|
116
|
+
expect(getPinnedTab("conv-b", "clientB")).toBeUndefined();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("clearPinnedTab with clientId only removes that client slot", () => {
|
|
120
|
+
setPinnedTab("conv-a", "42", "clientA");
|
|
121
|
+
setPinnedTab("conv-a", "77", "clientB");
|
|
122
|
+
|
|
123
|
+
clearPinnedTab("conv-a", "clientA");
|
|
124
|
+
|
|
125
|
+
expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
|
|
126
|
+
expect(getPinnedTab("conv-a", "clientB")).toBe("77");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("clearPinnedTab without clientId removes all slots", () => {
|
|
130
|
+
setPinnedTab("conv-a", "42", "clientA");
|
|
131
|
+
setPinnedTab("conv-a", "77", "clientB");
|
|
132
|
+
|
|
133
|
+
clearPinnedTab("conv-a");
|
|
134
|
+
|
|
135
|
+
expect(getPinnedTab("conv-a", "clientA")).toBeUndefined();
|
|
136
|
+
expect(getPinnedTab("conv-a", "clientB")).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("getPinnedTab with unknown clientId falls back to __default__ slot", () => {
|
|
140
|
+
setPinnedTab("conv-a", "42"); // stored in __default__ slot
|
|
141
|
+
expect(getPinnedTab("conv-a", "clientA")).toBe("42");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("getPinnedTab without clientId returns first entry", () => {
|
|
145
|
+
setPinnedTab("conv-a", "42", "clientA");
|
|
146
|
+
const result = getPinnedTab("conv-a");
|
|
147
|
+
expect(result).toBe("42");
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -70,6 +70,7 @@ import type {
|
|
|
70
70
|
CdpClient,
|
|
71
71
|
CdpClientKind,
|
|
72
72
|
} from "./cdp-client/types.js";
|
|
73
|
+
import { clearPinnedTab, setPinnedTab } from "./pinned-tabs.js";
|
|
73
74
|
import { checkBrowserRuntime } from "./runtime-check.js";
|
|
74
75
|
|
|
75
76
|
const log = getLogger("headless-browser");
|
|
@@ -471,6 +472,24 @@ function wrapWithKindMemo(
|
|
|
471
472
|
dispose(): void {
|
|
472
473
|
inner.dispose();
|
|
473
474
|
},
|
|
475
|
+
// Proxy the optional setCdpSessionId through to the underlying
|
|
476
|
+
// client so the navigate executor's --new-tab path can pin the
|
|
477
|
+
// freshly-created tab onto the extension CDP client. We define
|
|
478
|
+
// the method unconditionally here (rather than only when the
|
|
479
|
+
// inner client implements it) so callers can use a simple optional
|
|
480
|
+
// chain on this wrapper without re-walking the inner reference.
|
|
481
|
+
setCdpSessionId(cdpSessionId: string): void {
|
|
482
|
+
inner.setCdpSessionId?.(cdpSessionId);
|
|
483
|
+
},
|
|
484
|
+
listTabs() {
|
|
485
|
+
return inner.listTabs();
|
|
486
|
+
},
|
|
487
|
+
selectTab(tabId: number) {
|
|
488
|
+
return inner.selectTab(tabId);
|
|
489
|
+
},
|
|
490
|
+
closeTab(tabId: number) {
|
|
491
|
+
return inner.closeTab(tabId);
|
|
492
|
+
},
|
|
474
493
|
};
|
|
475
494
|
}
|
|
476
495
|
|
|
@@ -624,6 +643,93 @@ export async function executeBrowserNavigate(
|
|
|
624
643
|
if (acquired.errorResult) return acquired.errorResult;
|
|
625
644
|
const { cdp, browserMode } = acquired;
|
|
626
645
|
|
|
646
|
+
// --new-tab: open a fresh tab via the extension's Vellum.createTab
|
|
647
|
+
// pseudo-CDP method, then pin this client (and the conversation) to
|
|
648
|
+
// the returned tabId so this Page.navigate and every subsequent
|
|
649
|
+
// command on the same conversation routes to the new tab instead of
|
|
650
|
+
// the user's currently-active tab. Extension backend only; the local
|
|
651
|
+
// (Playwright) backend manages its own isolated browser and the
|
|
652
|
+
// cdp-inspect backend connects to a single tab by URL pattern.
|
|
653
|
+
const newTab = input.new_tab === true;
|
|
654
|
+
if (newTab && cdp.kind === "extension") {
|
|
655
|
+
try {
|
|
656
|
+
const result = await cdp.send<{ tabId?: number | string; clientId?: string }>(
|
|
657
|
+
"Vellum.createTab",
|
|
658
|
+
{},
|
|
659
|
+
context.signal,
|
|
660
|
+
);
|
|
661
|
+
const tabId =
|
|
662
|
+
typeof result?.tabId === "number"
|
|
663
|
+
? String(result.tabId)
|
|
664
|
+
: typeof result?.tabId === "string"
|
|
665
|
+
? result.tabId
|
|
666
|
+
: undefined;
|
|
667
|
+
const clientId =
|
|
668
|
+
typeof result?.clientId === "string" && result.clientId.length > 0
|
|
669
|
+
? result.clientId
|
|
670
|
+
: undefined;
|
|
671
|
+
if (!tabId) {
|
|
672
|
+
// Malformed createTab response (no tabId). We're nominally falling
|
|
673
|
+
// back to active-tab routing — but the live `cdp` instance was
|
|
674
|
+
// already constructed with whatever pin was in scope for this
|
|
675
|
+
// conversation, AND the pin store still holds it for future
|
|
676
|
+
// client construction. Clear both: the pin store (so the next
|
|
677
|
+
// executeBrowserNavigate builds a clean client) AND the current
|
|
678
|
+
// cdp instance's session (so the Page.navigate that runs in a
|
|
679
|
+
// few lines targets the active tab rather than the stale pin).
|
|
680
|
+
// Without the setCdpSessionId(undefined) call, the warn message
|
|
681
|
+
// is a lie: navigation would still route to the dead tab via the
|
|
682
|
+
// already-injected cdpSessionId and likely fail with
|
|
683
|
+
// cdp_session_not_found.
|
|
684
|
+
clearPinnedTab(context.conversationId);
|
|
685
|
+
cdp.setCdpSessionId?.(undefined);
|
|
686
|
+
log.warn(
|
|
687
|
+
{ conversationId: context.conversationId, result },
|
|
688
|
+
"Vellum.createTab returned no tabId; cleared stale pin and live session, falling back to active-tab routing",
|
|
689
|
+
);
|
|
690
|
+
} else {
|
|
691
|
+
cdp.setCdpSessionId?.(tabId);
|
|
692
|
+
setPinnedTab(context.conversationId, tabId, clientId);
|
|
693
|
+
log.debug(
|
|
694
|
+
{ conversationId: context.conversationId, tabId, clientId },
|
|
695
|
+
"Opened new tab via --new-tab; pinned subsequent ops to it",
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
} catch (err) {
|
|
699
|
+
// Surface the failure rather than silently clobbering the active
|
|
700
|
+
// tab — that's exactly the behavior --new-tab is supposed to
|
|
701
|
+
// avoid. Clear any stale pin so subsequent ops don't route to a
|
|
702
|
+
// dead tab. Note: an old extension build without Vellum.createTab
|
|
703
|
+
// support will land here (CDP returns an "unknown method" error).
|
|
704
|
+
// We're early-returning before the main try/finally block below,
|
|
705
|
+
// so we must dispose the cdp client manually to avoid leaking it.
|
|
706
|
+
clearPinnedTab(context.conversationId);
|
|
707
|
+
const message =
|
|
708
|
+
err instanceof Error ? err.message : String(err);
|
|
709
|
+
log.warn(
|
|
710
|
+
{ conversationId: context.conversationId, err },
|
|
711
|
+
"Vellum.createTab failed; aborting --new-tab navigate",
|
|
712
|
+
);
|
|
713
|
+
try {
|
|
714
|
+
cdp.dispose();
|
|
715
|
+
} catch (disposeErr) {
|
|
716
|
+
log.warn(
|
|
717
|
+
{ conversationId: context.conversationId, err: disposeErr },
|
|
718
|
+
"Failed to dispose CDP client after Vellum.createTab failure",
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
content: `Error: Failed to open a new tab for navigation: ${message}. The Chrome extension may need an update to support --new-tab.`,
|
|
723
|
+
isError: true,
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
} else if (newTab && cdp.kind !== "extension") {
|
|
727
|
+
log.debug(
|
|
728
|
+
{ conversationId: context.conversationId, backendKind: cdp.kind },
|
|
729
|
+
"--new-tab requested but backend does not support it; ignoring",
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
|
|
627
733
|
// Screencast + handoff are Playwright-backed and only meaningful
|
|
628
734
|
// for the local sacrificial-profile path. On the extension path the
|
|
629
735
|
// user already has their own Chrome window, so both are no-ops.
|