@vellumai/assistant 0.8.10 → 0.8.11-staging.1
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/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -23,8 +23,11 @@ import {
|
|
|
23
23
|
import { queryUnreportedLifecycleEvents } from "../memory/lifecycle-events-store.js";
|
|
24
24
|
import { queryUnreportedUsageEvents } from "../memory/llm-usage-store.js";
|
|
25
25
|
import { queryUnreportedOnboardingEvents } from "../memory/onboarding-events-store.js";
|
|
26
|
+
import { queryUnreportedSkillLoadedEvents } from "../memory/skill-loaded-events-store.js";
|
|
27
|
+
import { queryUnreportedToolExecutedEvents } from "../memory/tool-executed-events-store.js";
|
|
26
28
|
import { queryUnreportedTurnEvents } from "../memory/turn-events-store.js";
|
|
27
29
|
import { VellumPlatformClient } from "../platform/client.js";
|
|
30
|
+
import type { UsageAttributionProfileSource } from "../usage/types.js";
|
|
28
31
|
import { getDeviceId } from "../util/device-id.js";
|
|
29
32
|
import { getLogger } from "../util/logger.js";
|
|
30
33
|
import { APP_VERSION } from "../version.js";
|
|
@@ -56,6 +59,39 @@ const CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK =
|
|
|
56
59
|
"telemetry:auth_fallback:last_reported_at";
|
|
57
60
|
const CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK_ID =
|
|
58
61
|
"telemetry:auth_fallback:last_reported_id";
|
|
62
|
+
const CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK =
|
|
63
|
+
"telemetry:tool_executed:last_reported_at";
|
|
64
|
+
const CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK_ID =
|
|
65
|
+
"telemetry:tool_executed:last_reported_id";
|
|
66
|
+
const CHECKPOINT_KEY_SKILL_LOADED_WATERMARK =
|
|
67
|
+
"telemetry:skill_loaded:last_reported_at";
|
|
68
|
+
const CHECKPOINT_KEY_SKILL_LOADED_WATERMARK_ID =
|
|
69
|
+
"telemetry:skill_loaded:last_reported_id";
|
|
70
|
+
// Written into the `*_id` watermark checkpoints by the opt-out flush branch.
|
|
71
|
+
// Sorts lexicographically above every real row ID (all event stores generate
|
|
72
|
+
// lowercase v4 UUIDs), so the compound cursor's same-millisecond arm
|
|
73
|
+
// (`createdAt == watermark AND id > afterId`) can never match an opt-out row.
|
|
74
|
+
const OPT_OUT_WATERMARK_ID_SENTINEL = "ffffffff-ffff-ffff-ffff-ffffffffffff";
|
|
75
|
+
// (timestamp, id) checkpoint-key pairs for every event type's compound
|
|
76
|
+
// cursor — keep in sync when adding an event type.
|
|
77
|
+
const WATERMARK_KEY_PAIRS: ReadonlyArray<readonly [string, string]> = [
|
|
78
|
+
[CHECKPOINT_KEY_WATERMARK, CHECKPOINT_KEY_WATERMARK_ID],
|
|
79
|
+
[CHECKPOINT_KEY_TURN_WATERMARK, CHECKPOINT_KEY_TURN_WATERMARK_ID],
|
|
80
|
+
[CHECKPOINT_KEY_LIFECYCLE_WATERMARK, CHECKPOINT_KEY_LIFECYCLE_WATERMARK_ID],
|
|
81
|
+
[CHECKPOINT_KEY_ONBOARDING_WATERMARK, CHECKPOINT_KEY_ONBOARDING_WATERMARK_ID],
|
|
82
|
+
[
|
|
83
|
+
CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK,
|
|
84
|
+
CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK_ID,
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK,
|
|
88
|
+
CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK_ID,
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
CHECKPOINT_KEY_SKILL_LOADED_WATERMARK,
|
|
92
|
+
CHECKPOINT_KEY_SKILL_LOADED_WATERMARK_ID,
|
|
93
|
+
],
|
|
94
|
+
];
|
|
59
95
|
const REPORT_INTERVAL_MS = 5 * 60 * 1000;
|
|
60
96
|
const INITIAL_FLUSH_DELAY_MS = 30_000; // Delay first flush to let CES handshake complete
|
|
61
97
|
const BATCH_SIZE = 500;
|
|
@@ -87,6 +123,41 @@ export class UsageTelemetryReporter {
|
|
|
87
123
|
private timer: ReturnType<typeof setInterval> | null = null;
|
|
88
124
|
private activeFlush: Promise<void> | null = null;
|
|
89
125
|
|
|
126
|
+
constructor() {
|
|
127
|
+
// `tool_invocations` is an always-on audit table that predates this
|
|
128
|
+
// reporter shipping its rows: an absent watermark means no flush (opted
|
|
129
|
+
// in or out) has ever advanced it, so rows recorded before this build —
|
|
130
|
+
// including any opted-out period under older builds that gated the
|
|
131
|
+
// reporter on collectUsageData — would otherwise ship retroactively.
|
|
132
|
+
// Initialize an absent watermark to "now" at construction. Construction
|
|
133
|
+
// happens during daemon startup before any tool runs, so no legitimate
|
|
134
|
+
// row falls behind the watermark — initializing at first FLUSH instead
|
|
135
|
+
// would drop tools used during the 30s+ flush delay. The checkpoint is
|
|
136
|
+
// persisted immediately so a crash before the first flush can't leave it
|
|
137
|
+
// absent and re-initialize later. An EXISTING watermark is never touched:
|
|
138
|
+
// opted-out sessions keep it advancing via the opt-out flush branch, and
|
|
139
|
+
// overwriting it here would drop a legitimate unshipped backlog.
|
|
140
|
+
// `skill_loaded` needs no init: recording is gated on collectUsageData,
|
|
141
|
+
// so opt-out rows never exist and its standard 0 default is safe.
|
|
142
|
+
//
|
|
143
|
+
// Best-effort: DB init failures are tolerated at daemon startup (degraded
|
|
144
|
+
// mode), so this must never throw out of the constructor — matching
|
|
145
|
+
// flush(), which treats DB errors as non-fatal.
|
|
146
|
+
try {
|
|
147
|
+
if (getMemoryCheckpoint(CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK) == null) {
|
|
148
|
+
setMemoryCheckpoint(
|
|
149
|
+
CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK,
|
|
150
|
+
String(Date.now()),
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
} catch (err) {
|
|
154
|
+
log.warn(
|
|
155
|
+
{ err },
|
|
156
|
+
"tool_executed watermark init failed at construction — non-fatal; a later construction with a working DB re-runs the absent-checkpoint init",
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
90
161
|
start(): void {
|
|
91
162
|
// Delay the first flush to allow the credential infrastructure (CES
|
|
92
163
|
// handshake) to complete. Without this delay, VellumPlatformClient.create()
|
|
@@ -134,21 +205,35 @@ export class UsageTelemetryReporter {
|
|
|
134
205
|
try {
|
|
135
206
|
if (batchCount >= MAX_CONSECUTIVE_BATCHES) return;
|
|
136
207
|
|
|
137
|
-
// Respect
|
|
138
|
-
//
|
|
139
|
-
// opt-out window are never sent retroactively.
|
|
208
|
+
// Respect opt-out: if the user has disabled usage data collection, skip
|
|
209
|
+
// the flush and advance watermarks so events recorded during the
|
|
210
|
+
// opt-out window are never sent retroactively. The daemon runs the
|
|
211
|
+
// reporter even when opted out specifically so this branch keeps
|
|
212
|
+
// executing — every cycle plus the final flush in stop() — which is
|
|
213
|
+
// what lets a later opt-in (runtime or via restart) resume from a
|
|
214
|
+
// watermark that already covers the opt-out window. One caveat: a
|
|
215
|
+
// RUNTIME false→true flip can still ship up to one flush interval
|
|
216
|
+
// (≤5 min) of pre-toggle rows recorded since the last opted-out flush;
|
|
217
|
+
// the restart path is fully covered by the final flush in stop(). The
|
|
218
|
+
// caveat applies to the always-on tables without a write-time opt-out
|
|
219
|
+
// gate (llm_usage, turn events) and to tool_invocations rows recorded
|
|
220
|
+
// under builds predating the audit listener's write-time gate — new
|
|
221
|
+
// opted-out tool_invocations rows persist NULL telemetry columns and
|
|
222
|
+
// are unreportable by construction regardless of watermark timing.
|
|
140
223
|
if (!getConfig().collectUsageData) {
|
|
141
|
-
// Advance
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
// timestamp-only `gt(createdAt, watermark)`
|
|
145
|
-
//
|
|
224
|
+
// Advance the timestamp watermarks and pin the ID watermarks to a
|
|
225
|
+
// sentinel that sorts above any real UUID. The sentinel (rather than
|
|
226
|
+
// "") keeps the compound-cursor branch active — a falsy ID would
|
|
227
|
+
// downgrade the query to a timestamp-only `gt(createdAt, watermark)`
|
|
228
|
+
// — while making its same-millisecond arm unsatisfiable, so a row
|
|
229
|
+
// written in the same millisecond as this flush's Date.now() can
|
|
230
|
+
// never ship after a later opt-in. The next opted-in flush that
|
|
231
|
+
// ships events overwrites the sentinel with a real row ID.
|
|
146
232
|
const now = String(Date.now());
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
setMemoryCheckpoint(CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK, now);
|
|
233
|
+
for (const [timestampKey, idKey] of WATERMARK_KEY_PAIRS) {
|
|
234
|
+
setMemoryCheckpoint(timestampKey, now);
|
|
235
|
+
setMemoryCheckpoint(idKey, OPT_OUT_WATERMARK_ID_SENTINEL);
|
|
236
|
+
}
|
|
152
237
|
return;
|
|
153
238
|
}
|
|
154
239
|
|
|
@@ -189,6 +274,27 @@ export class UsageTelemetryReporter {
|
|
|
189
274
|
getMemoryCheckpoint(CHECKPOINT_KEY_AUTH_FALLBACK_WATERMARK_ID) ??
|
|
190
275
|
undefined;
|
|
191
276
|
|
|
277
|
+
// Read tool-executed watermark (compound cursor: createdAt + id).
|
|
278
|
+
// An absent checkpoint was initialized to construction time (see the
|
|
279
|
+
// constructor), guarding opt-out windows; the 0 fallback here is a
|
|
280
|
+
// defensive default matching the other event types. Legacy rows are
|
|
281
|
+
// excluded at the query level (see queryUnreportedToolExecutedEvents).
|
|
282
|
+
const toolExecutedWatermark = Number(
|
|
283
|
+
getMemoryCheckpoint(CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK) ?? "0",
|
|
284
|
+
);
|
|
285
|
+
const toolExecutedWatermarkId =
|
|
286
|
+
getMemoryCheckpoint(CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK_ID) ??
|
|
287
|
+
undefined;
|
|
288
|
+
|
|
289
|
+
// Read skill-loaded watermark (compound cursor: createdAt + id).
|
|
290
|
+
// Brand-new table, so the standard 0 default is safe.
|
|
291
|
+
const skillLoadedWatermark = Number(
|
|
292
|
+
getMemoryCheckpoint(CHECKPOINT_KEY_SKILL_LOADED_WATERMARK) ?? "0",
|
|
293
|
+
);
|
|
294
|
+
const skillLoadedWatermarkId =
|
|
295
|
+
getMemoryCheckpoint(CHECKPOINT_KEY_SKILL_LOADED_WATERMARK_ID) ??
|
|
296
|
+
undefined;
|
|
297
|
+
|
|
192
298
|
// Query unreported events
|
|
193
299
|
const events = queryUnreportedUsageEvents(
|
|
194
300
|
watermark,
|
|
@@ -215,13 +321,25 @@ export class UsageTelemetryReporter {
|
|
|
215
321
|
authFallbackWatermarkId,
|
|
216
322
|
BATCH_SIZE,
|
|
217
323
|
);
|
|
324
|
+
const toolExecutedEvents = queryUnreportedToolExecutedEvents(
|
|
325
|
+
toolExecutedWatermark,
|
|
326
|
+
toolExecutedWatermarkId,
|
|
327
|
+
BATCH_SIZE,
|
|
328
|
+
);
|
|
329
|
+
const skillLoadedEvents = queryUnreportedSkillLoadedEvents(
|
|
330
|
+
skillLoadedWatermark,
|
|
331
|
+
skillLoadedWatermarkId,
|
|
332
|
+
BATCH_SIZE,
|
|
333
|
+
);
|
|
218
334
|
|
|
219
335
|
if (
|
|
220
336
|
events.length === 0 &&
|
|
221
337
|
turnEvents.length === 0 &&
|
|
222
338
|
lifecycleEvents.length === 0 &&
|
|
223
339
|
onboardingEvents.length === 0 &&
|
|
224
|
-
authFallbackEvents.length === 0
|
|
340
|
+
authFallbackEvents.length === 0 &&
|
|
341
|
+
toolExecutedEvents.length === 0 &&
|
|
342
|
+
skillLoadedEvents.length === 0
|
|
225
343
|
)
|
|
226
344
|
return;
|
|
227
345
|
|
|
@@ -236,6 +354,8 @@ export class UsageTelemetryReporter {
|
|
|
236
354
|
lifecycleCount: lifecycleEvents.length,
|
|
237
355
|
onboardingCount: onboardingEvents.length,
|
|
238
356
|
authFallbackCount: authFallbackEvents.length,
|
|
357
|
+
toolExecutedCount: toolExecutedEvents.length,
|
|
358
|
+
skillLoadedCount: skillLoadedEvents.length,
|
|
239
359
|
},
|
|
240
360
|
"Telemetry flush: resolved auth context",
|
|
241
361
|
);
|
|
@@ -375,11 +495,11 @@ export class UsageTelemetryReporter {
|
|
|
375
495
|
...(e.stepIndex != null ? { step_index: e.stepIndex } : {}),
|
|
376
496
|
...(e.completedAt ? { completed_at: e.completedAt } : {}),
|
|
377
497
|
...(e.funnelVersion ? { funnel_version: e.funnelVersion } : {}),
|
|
378
|
-
// Onboarding events fall back to the envelope `assistant_version
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
498
|
+
// Onboarding events fall back to the envelope `assistant_version`,
|
|
499
|
+
// so events recorded under an older build are attributed to the
|
|
500
|
+
// version running at upload time. Adding a record-time column to
|
|
501
|
+
// `onboarding_events` (mirroring `llm_usage_events`) is a known
|
|
502
|
+
// follow-up.
|
|
383
503
|
assistant_version: APP_VERSION,
|
|
384
504
|
}),
|
|
385
505
|
),
|
|
@@ -402,6 +522,50 @@ export class UsageTelemetryReporter {
|
|
|
402
522
|
assistant_version: APP_VERSION,
|
|
403
523
|
}),
|
|
404
524
|
),
|
|
525
|
+
...toolExecutedEvents.map(
|
|
526
|
+
(e): TelemetryEvent => ({
|
|
527
|
+
type: "tool_executed",
|
|
528
|
+
daemon_event_id: e.id,
|
|
529
|
+
recorded_at: e.createdAt,
|
|
530
|
+
tool_name: e.toolName,
|
|
531
|
+
// The store filters out permission-denied rows, so the only
|
|
532
|
+
// non-success decision that reaches here is "error".
|
|
533
|
+
status: e.decision === "error" ? "errored" : "fulfilled",
|
|
534
|
+
duration_ms: e.durationMs,
|
|
535
|
+
arg_bytes: e.argBytes,
|
|
536
|
+
result_bytes: e.resultBytes,
|
|
537
|
+
conversation_id: e.conversationId,
|
|
538
|
+
provider: e.provider,
|
|
539
|
+
model: e.model,
|
|
540
|
+
inference_profile: e.inferenceProfile,
|
|
541
|
+
inference_profile_source:
|
|
542
|
+
e.inferenceProfileSource as UsageAttributionProfileSource | null,
|
|
543
|
+
// `tool_invocations` has no record-time version column — stamp
|
|
544
|
+
// the running binary's `APP_VERSION` so the wire value is
|
|
545
|
+
// concrete rather than an explicit null that would override the
|
|
546
|
+
// envelope under the platform's per-event-wins contract.
|
|
547
|
+
assistant_version: APP_VERSION,
|
|
548
|
+
}),
|
|
549
|
+
),
|
|
550
|
+
...skillLoadedEvents.map(
|
|
551
|
+
(e): TelemetryEvent => ({
|
|
552
|
+
type: "skill_loaded",
|
|
553
|
+
daemon_event_id: e.id,
|
|
554
|
+
recorded_at: e.createdAt,
|
|
555
|
+
skill_name: e.skillName,
|
|
556
|
+
skill_updated_at: e.skillUpdatedAt,
|
|
557
|
+
conversation_id: e.conversationId,
|
|
558
|
+
provider: e.provider,
|
|
559
|
+
model: e.model,
|
|
560
|
+
inference_profile: e.inferenceProfile,
|
|
561
|
+
inference_profile_source:
|
|
562
|
+
e.inferenceProfileSource as UsageAttributionProfileSource | null,
|
|
563
|
+
// `skill_loaded_events` has no record-time version column — same
|
|
564
|
+
// upload-time APP_VERSION stamping as the other non-llm_usage
|
|
565
|
+
// event types.
|
|
566
|
+
assistant_version: APP_VERSION,
|
|
567
|
+
}),
|
|
568
|
+
),
|
|
405
569
|
];
|
|
406
570
|
|
|
407
571
|
const organizationId = getPlatformOrganizationId() || undefined;
|
|
@@ -501,13 +665,42 @@ export class UsageTelemetryReporter {
|
|
|
501
665
|
);
|
|
502
666
|
}
|
|
503
667
|
|
|
668
|
+
// Advance tool-executed watermark (compound cursor)
|
|
669
|
+
if (toolExecutedEvents.length > 0) {
|
|
670
|
+
const lastToolExecuted =
|
|
671
|
+
toolExecutedEvents[toolExecutedEvents.length - 1];
|
|
672
|
+
setMemoryCheckpoint(
|
|
673
|
+
CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK,
|
|
674
|
+
String(lastToolExecuted.createdAt),
|
|
675
|
+
);
|
|
676
|
+
setMemoryCheckpoint(
|
|
677
|
+
CHECKPOINT_KEY_TOOL_EXECUTED_WATERMARK_ID,
|
|
678
|
+
lastToolExecuted.id,
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Advance skill-loaded watermark (compound cursor)
|
|
683
|
+
if (skillLoadedEvents.length > 0) {
|
|
684
|
+
const lastSkillLoaded = skillLoadedEvents[skillLoadedEvents.length - 1];
|
|
685
|
+
setMemoryCheckpoint(
|
|
686
|
+
CHECKPOINT_KEY_SKILL_LOADED_WATERMARK,
|
|
687
|
+
String(lastSkillLoaded.createdAt),
|
|
688
|
+
);
|
|
689
|
+
setMemoryCheckpoint(
|
|
690
|
+
CHECKPOINT_KEY_SKILL_LOADED_WATERMARK_ID,
|
|
691
|
+
lastSkillLoaded.id,
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
|
|
504
695
|
// If we got a full batch of any type, there may be more — recurse
|
|
505
696
|
if (
|
|
506
697
|
events.length === BATCH_SIZE ||
|
|
507
698
|
turnEvents.length === BATCH_SIZE ||
|
|
508
699
|
lifecycleEvents.length === BATCH_SIZE ||
|
|
509
700
|
onboardingEvents.length === BATCH_SIZE ||
|
|
510
|
-
authFallbackEvents.length === BATCH_SIZE
|
|
701
|
+
authFallbackEvents.length === BATCH_SIZE ||
|
|
702
|
+
toolExecutedEvents.length === BATCH_SIZE ||
|
|
703
|
+
skillLoadedEvents.length === BATCH_SIZE
|
|
511
704
|
) {
|
|
512
705
|
await this._doFlush(batchCount + 1);
|
|
513
706
|
}
|
|
@@ -11,14 +11,15 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
11
11
|
|
|
12
12
|
import { createMockLoggerModule } from "../../../__tests__/helpers/mock-logger.js";
|
|
13
13
|
import type { ToolContext } from "../../types.js";
|
|
14
|
-
import
|
|
14
|
+
import { CdpError } from "../cdp-client/errors.js";
|
|
15
|
+
import type { CdpClientKind, InternalBrowserMode } from "../cdp-client/types.js";
|
|
15
16
|
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Captured call state
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
|
|
20
21
|
interface CdpClientCallOpts {
|
|
21
|
-
mode?:
|
|
22
|
+
mode?: InternalBrowserMode;
|
|
22
23
|
targetClientId?: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -197,4 +198,50 @@ describe("acquireCdpClientWithMode: target_client_id overrides sticky backend mo
|
|
|
197
198
|
expect(getCdpClientCalls[0].mode).toBe("extension");
|
|
198
199
|
expect(getCdpClientCalls[0].targetClientId).toBe("host-client-fresh");
|
|
199
200
|
});
|
|
201
|
+
|
|
202
|
+
test("sticky host-bridge + no target_client_id → getCdpClient receives mode:host-bridge", async () => {
|
|
203
|
+
// A prior turn succeeded on the desktop SSE bridge; the memo holds the
|
|
204
|
+
// internal-only "host-bridge" kind and must round-trip through the
|
|
205
|
+
// factory's pinned path.
|
|
206
|
+
stickyKind = "host-bridge";
|
|
207
|
+
|
|
208
|
+
await executeBrowserAttach({}, makeContext("sticky-bridge-honored"));
|
|
209
|
+
|
|
210
|
+
expect(getCdpClientMock).toHaveBeenCalledTimes(1);
|
|
211
|
+
expect(getCdpClientCalls[0].mode).toBe("host-bridge");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("sticky host-bridge + target_client_id → getCdpClient receives mode:extension", async () => {
|
|
215
|
+
stickyKind = "host-bridge";
|
|
216
|
+
|
|
217
|
+
await executeBrowserAttach(
|
|
218
|
+
{ target_client_id: "host-client-bridge" },
|
|
219
|
+
makeContext("sticky-bridge-override"),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
expect(getCdpClientMock).toHaveBeenCalledTimes(1);
|
|
223
|
+
expect(getCdpClientCalls[0].mode).toBe("extension");
|
|
224
|
+
expect(getCdpClientCalls[0].targetClientId).toBe("host-client-bridge");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("sticky host-bridge gone → memo cleared and auto retry runs", async () => {
|
|
228
|
+
// The pinned host-bridge build throws (bridge disconnected or an
|
|
229
|
+
// extension connected since the memo was written); the acquire layer
|
|
230
|
+
// must clear the stale memo and retry with fresh auto selection.
|
|
231
|
+
stickyKind = "host-bridge";
|
|
232
|
+
getCdpClientMock.mockImplementationOnce(() => {
|
|
233
|
+
getCdpClientCalls.push({ mode: "host-bridge" });
|
|
234
|
+
throw new CdpError(
|
|
235
|
+
"transport_error",
|
|
236
|
+
'Pinned mode "host-bridge" unavailable: a Chrome Extension is now connected',
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await executeBrowserAttach({}, makeContext("sticky-bridge-fallback"));
|
|
241
|
+
|
|
242
|
+
expect(getCdpClientMock).toHaveBeenCalledTimes(2);
|
|
243
|
+
expect(getCdpClientCalls[0].mode).toBe("host-bridge");
|
|
244
|
+
expect(getCdpClientCalls[1].mode).toBe("auto");
|
|
245
|
+
expect(clearPreferredBackendKindMock).toHaveBeenCalledTimes(1);
|
|
246
|
+
});
|
|
200
247
|
});
|
|
@@ -20,11 +20,13 @@ const probeErrors: Record<string, CdpError | null> = {
|
|
|
20
20
|
[BROWSER_STATUS_MODE.LOCAL]: null,
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
const buildCandidateListMock = mock(
|
|
24
|
-
{ kind:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const buildCandidateListMock = mock(
|
|
24
|
+
(_context: ToolContext): Array<{ kind: string; reason: string }> => [
|
|
25
|
+
{ kind: BROWSER_STATUS_MODE.EXTENSION, reason: "mock" },
|
|
26
|
+
{ kind: BROWSER_STATUS_MODE.CDP_INSPECT, reason: "mock" },
|
|
27
|
+
{ kind: BROWSER_STATUS_MODE.LOCAL, reason: "mock" },
|
|
28
|
+
],
|
|
29
|
+
);
|
|
28
30
|
|
|
29
31
|
const getCdpClientMock = mock(
|
|
30
32
|
(_context: ToolContext, options?: { mode?: string }) => {
|
|
@@ -254,6 +256,28 @@ describe("executeBrowserStatus", () => {
|
|
|
254
256
|
expect(payload.recommendedMode).toBe(BROWSER_STATUS_MODE.CDP_INSPECT);
|
|
255
257
|
});
|
|
256
258
|
|
|
259
|
+
test("host-bridge auto candidate is reported but never recommended as a status mode", async () => {
|
|
260
|
+
// Bridge-only scenario: the auto candidate list leads with host-bridge,
|
|
261
|
+
// which is not a pinnable status mode — recommendation must fall to the
|
|
262
|
+
// first candidate that IS a checkable mode.
|
|
263
|
+
mockSingletonProxy = {
|
|
264
|
+
isAvailable: () => true,
|
|
265
|
+
hasExtensionClient: () => false,
|
|
266
|
+
request: () => {},
|
|
267
|
+
};
|
|
268
|
+
buildCandidateListMock.mockImplementationOnce((_context: ToolContext) => [
|
|
269
|
+
{ kind: "host-bridge", reason: "mock bridge" },
|
|
270
|
+
{ kind: BROWSER_STATUS_MODE.CDP_INSPECT, reason: "mock" },
|
|
271
|
+
{ kind: BROWSER_STATUS_MODE.LOCAL, reason: "mock" },
|
|
272
|
+
]);
|
|
273
|
+
|
|
274
|
+
const result = await executeBrowserStatus({}, makeContext());
|
|
275
|
+
expect(result.isError).toBe(false);
|
|
276
|
+
const payload = JSON.parse(result.content);
|
|
277
|
+
expect(payload.autoCandidateOrder[0]).toBe("host-bridge");
|
|
278
|
+
expect(payload.recommendedMode).toBe(BROWSER_STATUS_MODE.CDP_INSPECT);
|
|
279
|
+
});
|
|
280
|
+
|
|
257
281
|
test("restricted chrome:// page probe includes transport details", async () => {
|
|
258
282
|
mockSingletonProxy = { isAvailable: () => true, hasExtensionClient: () => true, request: () => {} };
|
|
259
283
|
probeOutcomes[BROWSER_STATUS_MODE.EXTENSION] = "fail";
|
|
@@ -70,6 +70,7 @@ import type {
|
|
|
70
70
|
AttemptDiagnostic,
|
|
71
71
|
CdpClient,
|
|
72
72
|
CdpClientKind,
|
|
73
|
+
InternalBrowserMode,
|
|
73
74
|
} from "./cdp-client/types.js";
|
|
74
75
|
import { clearPinnedTab, setPinnedTab } from "./pinned-tabs.js";
|
|
75
76
|
import { checkBrowserRuntime } from "./runtime-check.js";
|
|
@@ -227,6 +228,18 @@ const REMEDIATION_HINTS: Record<string, string[]> = {
|
|
|
227
228
|
"Verify the configured host:port matches Chrome's DevTools listener.",
|
|
228
229
|
"Consider using browser_mode: 'extension' or 'local' as an alternative.",
|
|
229
230
|
],
|
|
231
|
+
// Host-bridge backend (desktop SSE bridge → user's Chrome debug port)
|
|
232
|
+
"host-bridge:unreachable": [
|
|
233
|
+
"Ensure Chrome on the user's machine is on version 146 or higher (chrome://settings/help).",
|
|
234
|
+
'Ensure "Allow remote debugging for this browser instance" is toggled on at chrome://inspect/#remote-debugging.',
|
|
235
|
+
"Verify the desktop client is running and has an active SSE connection to the assistant.",
|
|
236
|
+
"Installing the Vellum Chrome extension is the preferred path and avoids the debug-port requirement.",
|
|
237
|
+
],
|
|
238
|
+
"host-bridge:transport_error": [
|
|
239
|
+
"The desktop client could not reach Chrome's remote-debugging endpoint on the user's machine.",
|
|
240
|
+
"Ensure Chrome is running with remote debugging enabled, or install the Vellum Chrome extension (preferred).",
|
|
241
|
+
"Verify the desktop client is running and connected.",
|
|
242
|
+
],
|
|
230
243
|
// Local/Playwright backend
|
|
231
244
|
"local:transport_error": [
|
|
232
245
|
"The local Playwright-managed browser failed to start or connect.",
|
|
@@ -390,7 +403,9 @@ function acquireCdpClientWithMode(
|
|
|
390
403
|
);
|
|
391
404
|
// target_client_id requires the extension proxy path — bypass any sticky
|
|
392
405
|
// backend remembered from prior turns so the explicit target always wins.
|
|
393
|
-
|
|
406
|
+
// InternalBrowserMode because the memo may hold "host-bridge", which is
|
|
407
|
+
// pinnable by the factory but never user-requestable.
|
|
408
|
+
const effectiveMode: InternalBrowserMode =
|
|
394
409
|
targetClientId != null
|
|
395
410
|
? "extension"
|
|
396
411
|
: browserMode === "auto" && rememberedKind !== null
|
|
@@ -1383,14 +1398,17 @@ export async function executeBrowserClose(
|
|
|
1383
1398
|
};
|
|
1384
1399
|
}
|
|
1385
1400
|
|
|
1386
|
-
//
|
|
1387
|
-
// close it.
|
|
1388
|
-
// clears promptly)
|
|
1401
|
+
// Non-local path: the user owns their Chrome tab — we must not
|
|
1402
|
+
// close it. On the extension backend, detach the debugger (so the
|
|
1403
|
+
// Chrome debugging banner clears promptly); other backends have no
|
|
1404
|
+
// Vellum.detach. Either way drop the cached snapshot state so stale
|
|
1389
1405
|
// eids from prior snapshots cannot be resolved by later tool calls.
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1406
|
+
if (cdp.kind === "extension") {
|
|
1407
|
+
try {
|
|
1408
|
+
await cdp.send("Vellum.detach", {}, context.signal);
|
|
1409
|
+
} catch {
|
|
1410
|
+
// Tolerate detach failures (already detached, tab closed, etc.)
|
|
1411
|
+
}
|
|
1394
1412
|
}
|
|
1395
1413
|
browserManager.clearSnapshotBackendNodeMap(context.conversationId);
|
|
1396
1414
|
browserManager.clearPreferredBackendKind(context.conversationId);
|
|
@@ -2382,7 +2400,7 @@ async function checkExtensionModeStatus(
|
|
|
2382
2400
|
): Promise<BrowserStatusModeResult> {
|
|
2383
2401
|
const proxy = HostBrowserProxy.instance;
|
|
2384
2402
|
|
|
2385
|
-
if (!proxy.hasExtensionClient()) {
|
|
2403
|
+
if (!proxy.hasExtensionClient(context.sourceActorPrincipalId)) {
|
|
2386
2404
|
return {
|
|
2387
2405
|
mode: BROWSER_STATUS_MODE.EXTENSION,
|
|
2388
2406
|
available: false,
|