@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +610 -16
- 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 +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__/config-watcher.test.ts +1 -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-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 +25 -7
- 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-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 +264 -81
- 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__/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__/first-greeting.test.ts +23 -2
- package/src/__tests__/headless-browser-navigate.test.ts +172 -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-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-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 +17 -16
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- package/src/__tests__/llm-resolver.test.ts +85 -1
- package/src/__tests__/log-export-routes.test.ts +99 -2
- package/src/__tests__/message-queue-steer.test.ts +114 -0
- package/src/__tests__/openai-provider.test.ts +105 -0
- package/src/__tests__/openai-responses-provider.test.ts +4 -4
- 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.test.ts +0 -3
- package/src/__tests__/plugin-source-watcher.test.ts +302 -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__/server-history-render.test.ts +83 -4
- package/src/__tests__/steer-tool-repair.test.ts +249 -0
- package/src/__tests__/system-prompt.test.ts +51 -28
- 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-088-deprecate-background-conversation-override.test.ts +158 -0
- package/src/agent/attachments.ts +1 -0
- package/src/agent/loop.ts +57 -20
- 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/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
- 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 +24 -2
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- 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/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +22 -12
- package/src/config/call-site-defaults.ts +19 -0
- package/src/config/feature-flag-registry.json +99 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
- package/src/config/schemas/call-site-catalog.ts +21 -0
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-v2.ts +48 -1
- package/src/context/compactor.ts +8 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- 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 +153 -23
- package/src/daemon/conversation-agent-loop.ts +223 -54
- 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 +135 -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 -5
- 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 +1 -0
- package/src/daemon/handlers/conversations.ts +79 -0
- package/src/daemon/handlers/shared.ts +92 -29
- 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-transfer-proxy.ts +1 -1
- package/src/daemon/lifecycle.ts +18 -4
- 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/surfaces.ts +3 -1
- 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/query-complexity-router.ts +75 -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/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
- 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 +94 -0
- package/src/home/suggested-prompts.ts +177 -9
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
- package/src/memory/conversation-crud.ts +133 -43
- package/src/memory/db-init.ts +16 -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/jobs-worker.ts +21 -1
- package/src/memory/memory-retrospective-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +3 -2
- package/src/memory/memory-retrospective-job.ts +408 -18
- 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/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 +17 -0
- package/src/memory/migrations/registry.ts +25 -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 +1 -0
- package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +31 -14
- 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/consolidation-job.ts +14 -0
- package/src/memory/v2/injection-events.ts +101 -0
- package/src/memory/v2/injection.ts +21 -10
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/router.ts +209 -55
- 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/conversation-seed-composer.ts +14 -2
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +9 -1
- 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 +5 -2
- package/src/permissions/secret-prompter.ts +4 -1
- package/src/plugins/defaults/injectors.ts +82 -9
- package/src/prompts/__tests__/system-prompt.test.ts +46 -2
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/sections.ts +32 -14
- package/src/prompts/system-prompt.ts +105 -68
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +8 -0
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +53 -3
- package/src/providers/anthropic/client.ts +132 -5
- package/src/providers/fireworks/client.ts +20 -2
- 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 +15 -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/model-catalog.ts +48 -1
- package/src/providers/openai/chat-completions-provider.ts +57 -20
- package/src/providers/openai/responses-provider.ts +9 -3
- package/src/providers/openrouter/client.ts +5 -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 +151 -56
- package/src/runtime/auth/route-policy.ts +7 -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-types.ts +7 -4
- package/src/runtime/pending-interactions.ts +51 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
- 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/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 +146 -1
- package/src/runtime/routes/conversation-query-routes.ts +60 -1
- package/src/runtime/routes/conversation-routes.ts +281 -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 +12 -4
- package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -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/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/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/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/terminal/safe-env.ts +3 -2
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/types.ts +4 -0
- 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/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/registry.ts +2 -0
- 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/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
|
@@ -27,6 +27,7 @@ let cdpSendHandler: (
|
|
|
27
27
|
params?: Record<string, unknown>,
|
|
28
28
|
) => unknown = () => ({});
|
|
29
29
|
let cdpDisposed = false;
|
|
30
|
+
let cdpSetSessionIdCalls: Array<string | undefined> = [];
|
|
30
31
|
|
|
31
32
|
function makeFakeCdp(kind: "local" | "extension", conversationId: string) {
|
|
32
33
|
return {
|
|
@@ -43,6 +44,12 @@ function makeFakeCdp(kind: "local" | "extension", conversationId: string) {
|
|
|
43
44
|
dispose() {
|
|
44
45
|
cdpDisposed = true;
|
|
45
46
|
},
|
|
47
|
+
// Mirrors the optional method on the real CdpClient interface so
|
|
48
|
+
// the --new-tab path can be exercised end-to-end. Recorded for
|
|
49
|
+
// per-test assertions.
|
|
50
|
+
setCdpSessionId(cdpSessionId: string | undefined) {
|
|
51
|
+
cdpSetSessionIdCalls.push(cdpSessionId);
|
|
52
|
+
},
|
|
46
53
|
};
|
|
47
54
|
}
|
|
48
55
|
|
|
@@ -135,6 +142,7 @@ mock.module("../tools/network/url-safety.js", () => ({
|
|
|
135
142
|
}));
|
|
136
143
|
|
|
137
144
|
import { executeBrowserNavigate } from "../tools/browser/browser-execution.js";
|
|
145
|
+
import { __resetPinnedTabsForTests } from "../tools/browser/pinned-tabs.js";
|
|
138
146
|
import type { ToolContext } from "../tools/types.js";
|
|
139
147
|
|
|
140
148
|
const ctx: ToolContext = {
|
|
@@ -212,6 +220,7 @@ function resetCdp() {
|
|
|
212
220
|
cdpSendCalls = [];
|
|
213
221
|
cdpDisposed = false;
|
|
214
222
|
cdpSendHandler = defaultCdpHandler;
|
|
223
|
+
cdpSetSessionIdCalls = [];
|
|
215
224
|
}
|
|
216
225
|
|
|
217
226
|
describe("executeBrowserNavigate", () => {
|
|
@@ -223,6 +232,7 @@ describe("executeBrowserNavigate", () => {
|
|
|
223
232
|
resolveResult = {};
|
|
224
233
|
resetMockPage();
|
|
225
234
|
resetCdp();
|
|
235
|
+
__resetPinnedTabsForTests();
|
|
226
236
|
});
|
|
227
237
|
|
|
228
238
|
// ── Input validation ───────────────────────────────────────────
|
|
@@ -664,6 +674,168 @@ describe("executeBrowserNavigate", () => {
|
|
|
664
674
|
expect(cdpDisposed).toBe(true);
|
|
665
675
|
});
|
|
666
676
|
|
|
677
|
+
// ── --new-tab flag (extension path only) ──────────────────────
|
|
678
|
+
|
|
679
|
+
test("extension path with new_tab: true opens a fresh tab and pins it before Page.navigate", async () => {
|
|
680
|
+
parseUrlResult = new URL("https://example.com/page");
|
|
681
|
+
mockExtensionAvailable = true;
|
|
682
|
+
|
|
683
|
+
cdpSendHandler = (method, params) => {
|
|
684
|
+
if (method === "Vellum.createTab") return { tabId: "999" };
|
|
685
|
+
return defaultCdpHandler(method, params);
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
const result = await executeBrowserNavigate(
|
|
689
|
+
{ url: "https://example.com/page", new_tab: true },
|
|
690
|
+
{ ...ctx },
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
expect(result.isError).toBe(false);
|
|
694
|
+
|
|
695
|
+
// Vellum.createTab fired and fired BEFORE Page.navigate.
|
|
696
|
+
const createIdx = cdpSendCalls.findIndex(
|
|
697
|
+
(c) => c.method === "Vellum.createTab",
|
|
698
|
+
);
|
|
699
|
+
const navIdx = cdpSendCalls.findIndex((c) => c.method === "Page.navigate");
|
|
700
|
+
expect(createIdx).toBeGreaterThanOrEqual(0);
|
|
701
|
+
expect(navIdx).toBeGreaterThanOrEqual(0);
|
|
702
|
+
expect(createIdx).toBeLessThan(navIdx);
|
|
703
|
+
|
|
704
|
+
// setCdpSessionId was invoked with the returned tabId so the
|
|
705
|
+
// follow-on commands on this client route to the new tab.
|
|
706
|
+
expect(cdpSetSessionIdCalls).toContain("999");
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
test("new_tab: true on extension path with createTab failure aborts with a clear error", async () => {
|
|
710
|
+
parseUrlResult = new URL("https://example.com/page");
|
|
711
|
+
mockExtensionAvailable = true;
|
|
712
|
+
|
|
713
|
+
cdpSendHandler = (method, params) => {
|
|
714
|
+
if (method === "Vellum.createTab") {
|
|
715
|
+
throw new Error("createTab returned no tabId");
|
|
716
|
+
}
|
|
717
|
+
return defaultCdpHandler(method, params);
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const result = await executeBrowserNavigate(
|
|
721
|
+
{ url: "https://example.com/page", new_tab: true },
|
|
722
|
+
{ ...ctx },
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
expect(result.isError).toBe(true);
|
|
726
|
+
expect(result.content).toContain("Failed to open a new tab");
|
|
727
|
+
// Page.navigate must NOT fire — that's exactly what --new-tab is
|
|
728
|
+
// supposed to prevent in the failure case (silent fallback to
|
|
729
|
+
// active-tab clobbering would defeat the purpose of the flag).
|
|
730
|
+
expect(cdpSendCalls.some((c) => c.method === "Page.navigate")).toBe(false);
|
|
731
|
+
// The createTab-failure path early-returns BEFORE the main
|
|
732
|
+
// try/finally that wraps the navigate flow, so the executor has to
|
|
733
|
+
// dispose the CDP client manually. Verifying here so future edits
|
|
734
|
+
// to that early-return don't silently regress the cleanup.
|
|
735
|
+
expect(cdpDisposed).toBe(true);
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
test("new_tab: true on extension path with no tabId in response clears live session and still continues", async () => {
|
|
739
|
+
// Defensive: dispatcher returns success but no tabId. The
|
|
740
|
+
// executor logs a warn, resets the live cdp session to undefined
|
|
741
|
+
// (so the follow-on Page.navigate routes to the active tab
|
|
742
|
+
// instead of any stale pin the cdp instance was constructed
|
|
743
|
+
// with), and proceeds. The navigate still runs (degraded
|
|
744
|
+
// behaviour but not a hard failure).
|
|
745
|
+
parseUrlResult = new URL("https://example.com/page");
|
|
746
|
+
mockExtensionAvailable = true;
|
|
747
|
+
|
|
748
|
+
cdpSendHandler = (method, params) => {
|
|
749
|
+
if (method === "Vellum.createTab") return {}; // no tabId
|
|
750
|
+
return defaultCdpHandler(method, params);
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
const result = await executeBrowserNavigate(
|
|
754
|
+
{ url: "https://example.com/page", new_tab: true },
|
|
755
|
+
{ ...ctx },
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
expect(result.isError).toBe(false);
|
|
759
|
+
// No new pin was set, BUT the live session was reset to undefined
|
|
760
|
+
// so the follow-on Page.navigate falls back to active-tab routing
|
|
761
|
+
// instead of any stale pin the cdp instance held at construction.
|
|
762
|
+
expect(cdpSetSessionIdCalls).toEqual([undefined]);
|
|
763
|
+
// Page.navigate still ran.
|
|
764
|
+
expect(cdpSendCalls.some((c) => c.method === "Page.navigate")).toBe(true);
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
test("new_tab: true with no tabId in response clears a pre-existing stale pin AND live session (regression)", async () => {
|
|
768
|
+
// Regression for the Codex round-2 findings (P2 + round-3 P1):
|
|
769
|
+
// when Vellum.createTab returns a malformed response with no
|
|
770
|
+
// tabId, the executor falls back to active-tab routing — but
|
|
771
|
+
// (a) the pin store still held the stale pin (round-2 P2 fix
|
|
772
|
+
// added clearPinnedTab), and (b) the LIVE cdp instance was
|
|
773
|
+
// already constructed with that stale cdpSessionId, so the
|
|
774
|
+
// follow-on Page.navigate would still route to the dead tab
|
|
775
|
+
// unless we reset the session on the cdp instance too (round-3
|
|
776
|
+
// P1 fix added cdp.setCdpSessionId(undefined)).
|
|
777
|
+
const { setPinnedTab, getPinnedTab } = await import(
|
|
778
|
+
"../tools/browser/pinned-tabs.js"
|
|
779
|
+
);
|
|
780
|
+
setPinnedTab(ctx.conversationId, "stale-pinned-tab-id");
|
|
781
|
+
expect(getPinnedTab(ctx.conversationId)).toBe("stale-pinned-tab-id");
|
|
782
|
+
|
|
783
|
+
parseUrlResult = new URL("https://example.com/page");
|
|
784
|
+
mockExtensionAvailable = true;
|
|
785
|
+
|
|
786
|
+
cdpSendHandler = (method, params) => {
|
|
787
|
+
if (method === "Vellum.createTab") return {}; // no tabId
|
|
788
|
+
return defaultCdpHandler(method, params);
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
const result = await executeBrowserNavigate(
|
|
792
|
+
{ url: "https://example.com/page", new_tab: true },
|
|
793
|
+
{ ...ctx },
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
expect(result.isError).toBe(false);
|
|
797
|
+
// (a) Pin store cleared.
|
|
798
|
+
expect(getPinnedTab(ctx.conversationId)).toBeUndefined();
|
|
799
|
+
// (b) Live cdp session reset (the fake records every
|
|
800
|
+
// setCdpSessionId arg; expect exactly one call with undefined).
|
|
801
|
+
expect(cdpSetSessionIdCalls).toEqual([undefined]);
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
test("new_tab: true on LOCAL path is a no-op (Playwright manages its own isolated browser)", async () => {
|
|
805
|
+
parseUrlResult = new URL("https://example.com/page");
|
|
806
|
+
mockExtensionAvailable = false; // local path
|
|
807
|
+
|
|
808
|
+
const result = await executeBrowserNavigate(
|
|
809
|
+
{ url: "https://example.com/page", new_tab: true },
|
|
810
|
+
{ ...ctx },
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
expect(result.isError).toBe(false);
|
|
814
|
+
// No Vellum.createTab was issued on the local path — the flag is
|
|
815
|
+
// silently ignored because Playwright opens its own browser and
|
|
816
|
+
// there's no user-tab to disturb.
|
|
817
|
+
expect(cdpSendCalls.some((c) => c.method === "Vellum.createTab")).toBe(
|
|
818
|
+
false,
|
|
819
|
+
);
|
|
820
|
+
expect(cdpSetSessionIdCalls).toEqual([]);
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
test("absence of new_tab leaves extension path untouched (no Vellum.createTab)", async () => {
|
|
824
|
+
parseUrlResult = new URL("https://example.com/page");
|
|
825
|
+
mockExtensionAvailable = true;
|
|
826
|
+
|
|
827
|
+
const result = await executeBrowserNavigate(
|
|
828
|
+
{ url: "https://example.com/page" }, // no new_tab key at all
|
|
829
|
+
{ ...ctx },
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
expect(result.isError).toBe(false);
|
|
833
|
+
expect(cdpSendCalls.some((c) => c.method === "Vellum.createTab")).toBe(
|
|
834
|
+
false,
|
|
835
|
+
);
|
|
836
|
+
expect(cdpSetSessionIdCalls).toEqual([]);
|
|
837
|
+
});
|
|
838
|
+
|
|
667
839
|
// ── Defense-in-depth: post-navigation final URL check ─────────
|
|
668
840
|
|
|
669
841
|
test("post-nav check blocks when final URL resolves to private target", async () => {
|
|
@@ -31,6 +31,12 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
31
31
|
_conversationId?: string,
|
|
32
32
|
options?: unknown,
|
|
33
33
|
) => {
|
|
34
|
+
// `interaction_resolved` envelopes are emitted by the pending-interactions
|
|
35
|
+
// tracker for every resolution. They are orthogonal to the host-proxy
|
|
36
|
+
// wire messages these tests assert on, so swallow them here.
|
|
37
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
34
40
|
sentMessages.push(msg);
|
|
35
41
|
sentMessageOptions.push(options);
|
|
36
42
|
},
|
|
@@ -31,6 +31,13 @@ let mockClients: MockClient[] = [];
|
|
|
31
31
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
32
32
|
assistantEventHub: {
|
|
33
33
|
publish: async (event: unknown, _options?: unknown) => {
|
|
34
|
+
// `interaction_resolved` envelopes are emitted by the
|
|
35
|
+
// pending-interactions tracker for every resolution. They are
|
|
36
|
+
// orthogonal to the host-browser wire messages these tests assert
|
|
37
|
+
// on, so swallow them here.
|
|
38
|
+
if ((event as { type?: string } | null)?.type === "interaction_resolved") {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
34
41
|
publishedEvents.push(event);
|
|
35
42
|
},
|
|
36
43
|
getMostRecentClientByCapability: (cap: string) =>
|
|
@@ -41,6 +48,9 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
|
41
48
|
mockClients.find((c) => c.clientId === clientId)?.actorPrincipalId,
|
|
42
49
|
},
|
|
43
50
|
broadcastMessage: (msg: unknown) => {
|
|
51
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
44
54
|
publishedEvents.push(msg);
|
|
45
55
|
},
|
|
46
56
|
}));
|
|
@@ -10,7 +10,14 @@ type MockClient = {
|
|
|
10
10
|
let mockClients: MockClient[] = [];
|
|
11
11
|
|
|
12
12
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
13
|
-
broadcastMessage: (msg: unknown) =>
|
|
13
|
+
broadcastMessage: (msg: unknown) => {
|
|
14
|
+
// Skip `interaction_resolved` envelopes — pending-interactions emits one
|
|
15
|
+
// on every resolve and these tests assert on host-proxy wire messages.
|
|
16
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
sentMessages.push(msg);
|
|
20
|
+
},
|
|
14
21
|
assistantEventHub: {
|
|
15
22
|
getMostRecentClientByCapability: (cap: string) =>
|
|
16
23
|
cap === "host_cu" && mockHasClient ? { id: "mock-client" } : null,
|
|
@@ -12,7 +12,14 @@ interface MockClient {
|
|
|
12
12
|
let mockClients: MockClient[] = [];
|
|
13
13
|
|
|
14
14
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
15
|
-
broadcastMessage: (msg: unknown) =>
|
|
15
|
+
broadcastMessage: (msg: unknown) => {
|
|
16
|
+
// Skip `interaction_resolved` envelopes — pending-interactions emits one
|
|
17
|
+
// on every resolve and these tests assert on host-proxy wire messages.
|
|
18
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
sentMessages.push(msg);
|
|
22
|
+
},
|
|
16
23
|
assistantEventHub: {
|
|
17
24
|
getMostRecentClientByCapability: (cap: string) => {
|
|
18
25
|
if (mockClients.length > 0) {
|
|
@@ -8,7 +8,14 @@ const sentMessages: unknown[] = [];
|
|
|
8
8
|
let mockHasClient = false;
|
|
9
9
|
|
|
10
10
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
11
|
-
broadcastMessage: (msg: unknown) =>
|
|
11
|
+
broadcastMessage: (msg: unknown) => {
|
|
12
|
+
// Skip `interaction_resolved` envelopes — pending-interactions emits one
|
|
13
|
+
// on every resolve and these tests assert on host-proxy wire messages.
|
|
14
|
+
if ((msg as { type?: string } | null)?.type === "interaction_resolved") {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
sentMessages.push(msg);
|
|
18
|
+
},
|
|
12
19
|
assistantEventHub: {
|
|
13
20
|
getMostRecentClientByCapability: (cap: string) =>
|
|
14
21
|
cap === "host_file" && mockHasClient ? { id: "mock-client" } : null,
|
|
@@ -24,8 +24,12 @@ mock.module("../util/logger.js", () => ({
|
|
|
24
24
|
|
|
25
25
|
import {
|
|
26
26
|
handleDetailedHealth,
|
|
27
|
+
handleReadyz,
|
|
27
28
|
ROUTES,
|
|
28
29
|
} from "../runtime/routes/identity-routes.js";
|
|
30
|
+
import {
|
|
31
|
+
setCesClient,
|
|
32
|
+
} from "../security/secure-keys.js";
|
|
29
33
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
30
34
|
import {
|
|
31
35
|
getHatchedSidecarPath,
|
|
@@ -177,6 +181,59 @@ describe("identity routes — health endpoint", () => {
|
|
|
177
181
|
expect(body.timestamp).toBeDefined();
|
|
178
182
|
expect(body.migrations).toBeDefined();
|
|
179
183
|
});
|
|
184
|
+
|
|
185
|
+
test("includes ces.connected=false when no CES client is registered", async () => {
|
|
186
|
+
const res = handleDetailedHealth();
|
|
187
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
188
|
+
|
|
189
|
+
expect(body.ces).toBeDefined();
|
|
190
|
+
expect((body.ces as Record<string, unknown>).connected).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("CES readiness", () => {
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
setCesClient(undefined);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("readyz returns 200 and logs warning when CES is unavailable", () => {
|
|
200
|
+
const res = handleReadyz();
|
|
201
|
+
expect(res.status).toBe(200);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("readyz returns 200 when CES is connected and ready", () => {
|
|
205
|
+
const mockClient = { isReady: () => true, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
206
|
+
setCesClient(mockClient);
|
|
207
|
+
const res = handleReadyz();
|
|
208
|
+
expect(res.status).toBe(200);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("readyz returns 200 when CES client exists but is not ready", () => {
|
|
212
|
+
const mockClient = { isReady: () => false, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
213
|
+
setCesClient(mockClient);
|
|
214
|
+
const res = handleReadyz();
|
|
215
|
+
expect(res.status).toBe(200);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("/v1/health reports ces.connected=true when CES is ready", async () => {
|
|
219
|
+
const mockClient = { isReady: () => true, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
220
|
+
setCesClient(mockClient);
|
|
221
|
+
const res = handleDetailedHealth();
|
|
222
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
223
|
+
|
|
224
|
+
expect(body.ces).toBeDefined();
|
|
225
|
+
expect((body.ces as Record<string, unknown>).connected).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("/v1/health reports ces.connected=false when CES is not ready", async () => {
|
|
229
|
+
const mockClient = { isReady: () => false, close: () => {} } as unknown as import("../credential-execution/client.js").CesClient;
|
|
230
|
+
setCesClient(mockClient);
|
|
231
|
+
const res = handleDetailedHealth();
|
|
232
|
+
const body = (await res.json()) as Record<string, unknown>;
|
|
233
|
+
|
|
234
|
+
expect(body.ces).toBeDefined();
|
|
235
|
+
expect((body.ces as Record<string, unknown>).connected).toBe(false);
|
|
236
|
+
});
|
|
180
237
|
});
|
|
181
238
|
|
|
182
239
|
describe("profiler payload (profiler enabled)", () => {
|
|
@@ -148,6 +148,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
|
|
|
148
148
|
{
|
|
149
149
|
slackInbound: {
|
|
150
150
|
channelId: "C0123CHANNEL",
|
|
151
|
+
channelName: "engineering",
|
|
151
152
|
channelTs: "1700000001.111111",
|
|
152
153
|
threadTs: "1700000000.000001",
|
|
153
154
|
displayName: "Alice",
|
|
@@ -162,6 +163,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
|
|
|
162
163
|
expect(slackMeta!.source).toBe("slack");
|
|
163
164
|
expect(slackMeta!.eventKind).toBe("message");
|
|
164
165
|
expect(slackMeta!.channelId).toBe("C0123CHANNEL");
|
|
166
|
+
expect(slackMeta!.channelName).toBe("engineering");
|
|
165
167
|
expect(slackMeta!.channelTs).toBe("1700000001.111111");
|
|
166
168
|
expect(slackMeta!.threadTs).toBe("1700000000.000001");
|
|
167
169
|
expect(slackMeta!.displayName).toBe("Alice");
|
|
@@ -194,6 +196,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
|
|
|
194
196
|
expect(slackMeta!.channelTs).toBe("1700000010.222222");
|
|
195
197
|
expect(slackMeta!.threadTs).toBeUndefined();
|
|
196
198
|
expect(slackMeta!.displayName).toBe("Bob");
|
|
199
|
+
expect(slackMeta!.channelName).toBeUndefined();
|
|
197
200
|
});
|
|
198
201
|
|
|
199
202
|
test("Slack normalized content is persisted with raw channelTs in slackMeta", async () => {
|
|
@@ -102,6 +102,7 @@ describe("injector chain", () => {
|
|
|
102
102
|
"memory-v2-static",
|
|
103
103
|
"now-md",
|
|
104
104
|
"active-documents",
|
|
105
|
+
"document-comments",
|
|
105
106
|
"subagent-status",
|
|
106
107
|
"slack-messages",
|
|
107
108
|
"thread-focus",
|
|
@@ -166,6 +167,7 @@ describe("injector chain", () => {
|
|
|
166
167
|
"memory-v2-static", // 38
|
|
167
168
|
"now-md", // 40
|
|
168
169
|
"active-documents", // 45
|
|
170
|
+
"document-comments", // 46
|
|
169
171
|
"subagent-status", // 50
|
|
170
172
|
"slack-messages", // 60
|
|
171
173
|
"thread-focus", // 70
|