@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
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that listTabs, selectTab, and closeTab are properly forwarded through
|
|
3
|
+
* the real buildChainedClient wrapper.
|
|
4
|
+
*
|
|
5
|
+
* Per the test-flat-mocks-trap rule, these tests use the REAL buildChainedClient
|
|
6
|
+
* function with a mock candidate — NOT a flat fake that has the methods defined
|
|
7
|
+
* directly on the scoped client.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import type { BrowserBackend, CdpCommand, CdpResult } from "../../../../browser-session/types.js";
|
|
13
|
+
import { CdpError } from "../errors.js";
|
|
14
|
+
import type { BackendCandidate, CdpClient, TabInfo } from "../types.js";
|
|
15
|
+
|
|
16
|
+
// Import buildChainedClient directly. Since this test file does not mock
|
|
17
|
+
// any of factory.ts's imports, we import directly.
|
|
18
|
+
const { buildChainedClient } = await import("../factory.js");
|
|
19
|
+
|
|
20
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a BrowserBackend that delegates CDP sends through a CdpClient.
|
|
24
|
+
* This is the real wiring that factory.ts uses — the backend wraps the
|
|
25
|
+
* client's send method via dispatchThroughClient.
|
|
26
|
+
*/
|
|
27
|
+
function makeBackendFromClient(client: CdpClient, kind: "extension" | "local" | "cdp-inspect"): BrowserBackend {
|
|
28
|
+
return {
|
|
29
|
+
kind,
|
|
30
|
+
isAvailable: () => true,
|
|
31
|
+
send: async (command: CdpCommand, signal?: AbortSignal): Promise<CdpResult> => {
|
|
32
|
+
try {
|
|
33
|
+
const result = await client.send(command.method, command.params, signal);
|
|
34
|
+
return { result };
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (err instanceof CdpError) {
|
|
37
|
+
return {
|
|
38
|
+
error: { code: -1, message: err.message, data: err },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
dispose: () => client.dispose(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface FakeClientWithTabMethods extends CdpClient {
|
|
49
|
+
kind: "extension";
|
|
50
|
+
conversationId: string;
|
|
51
|
+
listTabsMock: ReturnType<typeof mock>;
|
|
52
|
+
selectTabMock: ReturnType<typeof mock>;
|
|
53
|
+
closeTabMock: ReturnType<typeof mock>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function makeFakeExtensionClientWithTabMethods(
|
|
57
|
+
conversationId: string,
|
|
58
|
+
): FakeClientWithTabMethods {
|
|
59
|
+
const listTabsMock = mock(async (): Promise<TabInfo[]> => [
|
|
60
|
+
{
|
|
61
|
+
tabId: 1,
|
|
62
|
+
windowId: 10,
|
|
63
|
+
url: "https://example.com",
|
|
64
|
+
title: "Example",
|
|
65
|
+
active: true,
|
|
66
|
+
pinned: false,
|
|
67
|
+
},
|
|
68
|
+
]);
|
|
69
|
+
const selectTabMock = mock(async (_tabId: number) => ({
|
|
70
|
+
tabId: 42,
|
|
71
|
+
windowId: 10,
|
|
72
|
+
url: "https://example.com",
|
|
73
|
+
title: "Example",
|
|
74
|
+
clientId: "clientA",
|
|
75
|
+
}));
|
|
76
|
+
const closeTabMock = mock(async (_tabId: number) => ({
|
|
77
|
+
closed: true as const,
|
|
78
|
+
tabId: 99,
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
kind: "extension",
|
|
83
|
+
conversationId,
|
|
84
|
+
listTabsMock,
|
|
85
|
+
selectTabMock,
|
|
86
|
+
closeTabMock,
|
|
87
|
+
send: mock(async () => ({ ok: true })) as unknown as CdpClient["send"],
|
|
88
|
+
dispose: mock(() => {}),
|
|
89
|
+
listTabs: listTabsMock,
|
|
90
|
+
selectTab: selectTabMock,
|
|
91
|
+
closeTab: closeTabMock,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Build a BackendCandidate that wraps a fake extension client with tab methods.
|
|
97
|
+
*/
|
|
98
|
+
function makeCandidateFromClient(
|
|
99
|
+
conversationId: string,
|
|
100
|
+
fakeClient: CdpClient,
|
|
101
|
+
kind: "extension" | "local" | "cdp-inspect",
|
|
102
|
+
): BackendCandidate {
|
|
103
|
+
return {
|
|
104
|
+
kind,
|
|
105
|
+
reason: `test ${kind} client`,
|
|
106
|
+
create() {
|
|
107
|
+
return {
|
|
108
|
+
client: fakeClient,
|
|
109
|
+
backend: makeBackendFromClient(fakeClient, kind),
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Helpers for fresh-client (pre-sticky) tests ───────────────────────
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* A fake extension client whose `send` method returns Vellum.* pseudo-responses
|
|
119
|
+
* so that fresh-client tab calls can succeed without a prior `send("Runtime.*")`.
|
|
120
|
+
* This simulates how the real ExtensionCdpClient handles Vellum.listTabs etc.
|
|
121
|
+
* through the dispatcher before chrome.debugger.sendCommand.
|
|
122
|
+
*/
|
|
123
|
+
function makeFakeExtensionClientForFreshPath(conversationId: string) {
|
|
124
|
+
return {
|
|
125
|
+
kind: "extension" as const,
|
|
126
|
+
conversationId,
|
|
127
|
+
send: mock(
|
|
128
|
+
async (method: string, params?: Record<string, unknown>): Promise<unknown> => {
|
|
129
|
+
if (method === "Vellum.listTabs") {
|
|
130
|
+
return {
|
|
131
|
+
tabs: [
|
|
132
|
+
{
|
|
133
|
+
tabId: 1,
|
|
134
|
+
windowId: 10,
|
|
135
|
+
url: "https://example.com",
|
|
136
|
+
title: "Example",
|
|
137
|
+
active: true,
|
|
138
|
+
pinned: false,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (method === "Vellum.selectTab") {
|
|
144
|
+
return {
|
|
145
|
+
tabId: params?.tabId,
|
|
146
|
+
windowId: 10,
|
|
147
|
+
url: "https://example.com",
|
|
148
|
+
title: "Example",
|
|
149
|
+
clientId: "clientA",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (method === "Vellum.closeTab") {
|
|
153
|
+
return { closed: true, tabId: params?.tabId };
|
|
154
|
+
}
|
|
155
|
+
return {};
|
|
156
|
+
},
|
|
157
|
+
) as unknown as CdpClient["send"],
|
|
158
|
+
dispose: mock(() => {}),
|
|
159
|
+
// These must be present so active?.client.listTabs etc. check passes
|
|
160
|
+
listTabs: mock(async (): Promise<TabInfo[]> => []),
|
|
161
|
+
selectTab: mock(async (_tabId: number) => ({
|
|
162
|
+
tabId: _tabId,
|
|
163
|
+
windowId: 10,
|
|
164
|
+
url: "https://example.com",
|
|
165
|
+
title: "Example",
|
|
166
|
+
clientId: "clientA",
|
|
167
|
+
})),
|
|
168
|
+
closeTab: mock(async (_tabId: number) => ({
|
|
169
|
+
closed: true as const,
|
|
170
|
+
tabId: _tabId,
|
|
171
|
+
})),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ── Tests ─────────────────────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
describe("buildChainedClient — tab management methods", () => {
|
|
178
|
+
test("listTabs is forwarded to the underlying client after backend becomes sticky", async () => {
|
|
179
|
+
const conversationId = "conv-tabs-list";
|
|
180
|
+
const fakeClient = makeFakeExtensionClientWithTabMethods(conversationId);
|
|
181
|
+
const candidate = makeCandidateFromClient(conversationId, fakeClient, "extension");
|
|
182
|
+
|
|
183
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
184
|
+
|
|
185
|
+
// Establish sticky by sending a command first
|
|
186
|
+
await scoped.send("Runtime.evaluate", {});
|
|
187
|
+
|
|
188
|
+
const tabs = await scoped.listTabs();
|
|
189
|
+
expect(tabs).toHaveLength(1);
|
|
190
|
+
expect(tabs[0].tabId).toBe(1);
|
|
191
|
+
expect(tabs[0].active).toBe(true);
|
|
192
|
+
expect(fakeClient.listTabsMock).toHaveBeenCalledTimes(1);
|
|
193
|
+
|
|
194
|
+
scoped.dispose();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("selectTab is forwarded to the underlying client", async () => {
|
|
198
|
+
const conversationId = "conv-tabs-select";
|
|
199
|
+
const fakeClient = makeFakeExtensionClientWithTabMethods(conversationId);
|
|
200
|
+
const candidate = makeCandidateFromClient(conversationId, fakeClient, "extension");
|
|
201
|
+
|
|
202
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
203
|
+
|
|
204
|
+
// Establish sticky
|
|
205
|
+
await scoped.send("Runtime.evaluate", {});
|
|
206
|
+
|
|
207
|
+
const result = await scoped.selectTab(42);
|
|
208
|
+
expect(result.tabId).toBe(42);
|
|
209
|
+
expect(result.clientId).toBe("clientA");
|
|
210
|
+
expect(fakeClient.selectTabMock).toHaveBeenCalledWith(42);
|
|
211
|
+
|
|
212
|
+
scoped.dispose();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("closeTab is forwarded to the underlying client", async () => {
|
|
216
|
+
const conversationId = "conv-tabs-close";
|
|
217
|
+
const fakeClient = makeFakeExtensionClientWithTabMethods(conversationId);
|
|
218
|
+
const candidate = makeCandidateFromClient(conversationId, fakeClient, "extension");
|
|
219
|
+
|
|
220
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
221
|
+
|
|
222
|
+
// Establish sticky
|
|
223
|
+
await scoped.send("Runtime.evaluate", {});
|
|
224
|
+
|
|
225
|
+
const result = await scoped.closeTab(99);
|
|
226
|
+
expect(result.closed).toBe(true);
|
|
227
|
+
expect(result.tabId).toBe(99);
|
|
228
|
+
expect(fakeClient.closeTabMock).toHaveBeenCalledWith(99);
|
|
229
|
+
|
|
230
|
+
scoped.dispose();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("listTabs throws transport_error when backend does not support it", async () => {
|
|
234
|
+
const conversationId = "conv-tabs-no-list";
|
|
235
|
+
// A local client without listTabs/selectTab/closeTab
|
|
236
|
+
const noTabsClient: CdpClient & {
|
|
237
|
+
kind: "local";
|
|
238
|
+
conversationId: string;
|
|
239
|
+
} = {
|
|
240
|
+
kind: "local",
|
|
241
|
+
conversationId,
|
|
242
|
+
send: mock(async () => ({})) as unknown as CdpClient["send"],
|
|
243
|
+
dispose: mock(() => {}),
|
|
244
|
+
};
|
|
245
|
+
const candidate = makeCandidateFromClient(
|
|
246
|
+
conversationId,
|
|
247
|
+
noTabsClient,
|
|
248
|
+
"local",
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
252
|
+
|
|
253
|
+
// Establish sticky
|
|
254
|
+
await scoped.send("Runtime.evaluate", {});
|
|
255
|
+
|
|
256
|
+
let caught: unknown;
|
|
257
|
+
try {
|
|
258
|
+
await scoped.listTabs();
|
|
259
|
+
} catch (err) {
|
|
260
|
+
caught = err;
|
|
261
|
+
}
|
|
262
|
+
expect(caught).toBeInstanceOf(CdpError);
|
|
263
|
+
expect((caught as CdpError).code).toBe("transport_error");
|
|
264
|
+
|
|
265
|
+
scoped.dispose();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("selectTab throws transport_error when backend does not support it", async () => {
|
|
269
|
+
const conversationId = "conv-tabs-no-select";
|
|
270
|
+
const noTabsClient: CdpClient & {
|
|
271
|
+
kind: "local";
|
|
272
|
+
conversationId: string;
|
|
273
|
+
} = {
|
|
274
|
+
kind: "local",
|
|
275
|
+
conversationId,
|
|
276
|
+
send: mock(async () => ({})) as unknown as CdpClient["send"],
|
|
277
|
+
dispose: mock(() => {}),
|
|
278
|
+
};
|
|
279
|
+
const candidate = makeCandidateFromClient(
|
|
280
|
+
conversationId,
|
|
281
|
+
noTabsClient,
|
|
282
|
+
"local",
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
286
|
+
|
|
287
|
+
// Establish sticky
|
|
288
|
+
await scoped.send("Runtime.evaluate", {});
|
|
289
|
+
|
|
290
|
+
let caught: unknown;
|
|
291
|
+
try {
|
|
292
|
+
await scoped.selectTab(42);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
caught = err;
|
|
295
|
+
}
|
|
296
|
+
expect(caught).toBeInstanceOf(CdpError);
|
|
297
|
+
expect((caught as CdpError).code).toBe("transport_error");
|
|
298
|
+
|
|
299
|
+
scoped.dispose();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("closeTab throws transport_error when backend does not support it", async () => {
|
|
303
|
+
const conversationId = "conv-tabs-no-close";
|
|
304
|
+
const noTabsClient: CdpClient & {
|
|
305
|
+
kind: "local";
|
|
306
|
+
conversationId: string;
|
|
307
|
+
} = {
|
|
308
|
+
kind: "local",
|
|
309
|
+
conversationId,
|
|
310
|
+
send: mock(async () => ({})) as unknown as CdpClient["send"],
|
|
311
|
+
dispose: mock(() => {}),
|
|
312
|
+
};
|
|
313
|
+
const candidate = makeCandidateFromClient(
|
|
314
|
+
conversationId,
|
|
315
|
+
noTabsClient,
|
|
316
|
+
"local",
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
320
|
+
|
|
321
|
+
// Establish sticky
|
|
322
|
+
await scoped.send("Runtime.evaluate", {});
|
|
323
|
+
|
|
324
|
+
let caught: unknown;
|
|
325
|
+
try {
|
|
326
|
+
await scoped.closeTab(77);
|
|
327
|
+
} catch (err) {
|
|
328
|
+
caught = err;
|
|
329
|
+
}
|
|
330
|
+
expect(caught).toBeInstanceOf(CdpError);
|
|
331
|
+
expect((caught as CdpError).code).toBe("transport_error");
|
|
332
|
+
|
|
333
|
+
scoped.dispose();
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe("buildChainedClient — fresh-client tab calls (no prior send)", () => {
|
|
338
|
+
test("listTabs on a fresh client triggers failover walk and returns tabs", async () => {
|
|
339
|
+
const conversationId = "conv-fresh-list";
|
|
340
|
+
const fakeClient = makeFakeExtensionClientForFreshPath(conversationId);
|
|
341
|
+
const candidate = makeCandidateFromClient(conversationId, fakeClient, "extension");
|
|
342
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
343
|
+
|
|
344
|
+
// Call listTabs WITHOUT establishing sticky — this is the production scenario
|
|
345
|
+
const tabs = await scoped.listTabs();
|
|
346
|
+
expect(tabs).toHaveLength(1);
|
|
347
|
+
expect(tabs[0].tabId).toBe(1);
|
|
348
|
+
expect(tabs[0].active).toBe(true);
|
|
349
|
+
|
|
350
|
+
scoped.dispose();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("selectTab on a fresh client triggers failover walk and returns tab info", async () => {
|
|
354
|
+
const conversationId = "conv-fresh-select";
|
|
355
|
+
const fakeClient = makeFakeExtensionClientForFreshPath(conversationId);
|
|
356
|
+
const candidate = makeCandidateFromClient(conversationId, fakeClient, "extension");
|
|
357
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
358
|
+
|
|
359
|
+
const result = await scoped.selectTab(42);
|
|
360
|
+
expect(result.tabId).toBe(42);
|
|
361
|
+
expect(result.clientId).toBe("clientA");
|
|
362
|
+
|
|
363
|
+
scoped.dispose();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test("closeTab on a fresh client triggers failover walk and returns closed status", async () => {
|
|
367
|
+
const conversationId = "conv-fresh-close";
|
|
368
|
+
const fakeClient = makeFakeExtensionClientForFreshPath(conversationId);
|
|
369
|
+
const candidate = makeCandidateFromClient(conversationId, fakeClient, "extension");
|
|
370
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
371
|
+
|
|
372
|
+
const result = await scoped.closeTab(99);
|
|
373
|
+
expect(result.closed).toBe(true);
|
|
374
|
+
expect(result.tabId).toBe(99);
|
|
375
|
+
|
|
376
|
+
scoped.dispose();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("listTabs on fresh non-extension client throws transport_error after sticky established", async () => {
|
|
380
|
+
const conversationId = "conv-fresh-no-list";
|
|
381
|
+
const noTabsClient: CdpClient & { kind: "local"; conversationId: string } = {
|
|
382
|
+
kind: "local",
|
|
383
|
+
conversationId,
|
|
384
|
+
send: mock(async () => ({})) as unknown as CdpClient["send"],
|
|
385
|
+
dispose: mock(() => {}),
|
|
386
|
+
};
|
|
387
|
+
const candidate = makeCandidateFromClient(conversationId, noTabsClient, "local");
|
|
388
|
+
const scoped = buildChainedClient(conversationId, [candidate]);
|
|
389
|
+
|
|
390
|
+
// No prior send — fresh client, non-extension backend
|
|
391
|
+
let caught: unknown;
|
|
392
|
+
try {
|
|
393
|
+
await scoped.listTabs();
|
|
394
|
+
} catch (err) {
|
|
395
|
+
caught = err;
|
|
396
|
+
}
|
|
397
|
+
expect(caught).toBeInstanceOf(CdpError);
|
|
398
|
+
expect((caught as CdpError).code).toBe("transport_error");
|
|
399
|
+
|
|
400
|
+
scoped.dispose();
|
|
401
|
+
});
|
|
402
|
+
});
|
|
@@ -2382,4 +2382,32 @@ describe("macOS host-browser proxy without extension registry", () => {
|
|
|
2382
2382
|
);
|
|
2383
2383
|
expect(result).toEqual({ ok: true, via: "extension" });
|
|
2384
2384
|
});
|
|
2385
|
+
test("setCdpSessionId is forwarded to the underlying client through buildChainedClient", async () => {
|
|
2386
|
+
// This test exercises the real chained-client path that integrates
|
|
2387
|
+
// the extension CdpClient. Without it, the test suite would only ever
|
|
2388
|
+
// instantiate getCdpClient(), which returns the scopedClient directly.
|
|
2389
|
+
// buildChainedClient (used by the navigator executor) wraps that
|
|
2390
|
+
// client and must forward setCdpSessionId calls to the underlying
|
|
2391
|
+
// extension client — otherwise --new-tab would silently fail to
|
|
2392
|
+
// re-target CDP commands.
|
|
2393
|
+
const fakeProxy = makeAvailableProxy();
|
|
2394
|
+
mockSingletonProxy = fakeProxy;
|
|
2395
|
+
const ctx = makeContext({ conversationId: "set-session-id-test" });
|
|
2396
|
+
const client = getCdpClient(ctx);
|
|
2397
|
+
// The scopedClient should expose setCdpSessionId (which it didn't
|
|
2398
|
+
// before the fix).
|
|
2399
|
+
expect(typeof client.setCdpSessionId).toBe("function");
|
|
2400
|
+
// Call it; it should reach the underlying extension client without error.
|
|
2401
|
+
// (The fake client's setCdpSessionId is a no-op in this test, but in
|
|
2402
|
+
// production ExtensionCdpClient.setCdpSessionId updates cdpSessionId to
|
|
2403
|
+
// re-target follow-on commands at a specific tab.)
|
|
2404
|
+
const newTabId = "12345";
|
|
2405
|
+
client.setCdpSessionId(newTabId);
|
|
2406
|
+
// Verify the client is still functional after the session ID call.
|
|
2407
|
+
const result = await client.send<{ ok: boolean; via: string }>(
|
|
2408
|
+
"Page.navigate",
|
|
2409
|
+
{ url: "https://example.com" },
|
|
2410
|
+
);
|
|
2411
|
+
expect(result).toEqual({ ok: true, via: "extension" });
|
|
2412
|
+
});
|
|
2385
2413
|
});
|
|
@@ -88,6 +88,10 @@ describe("cdp-client re-exports", () => {
|
|
|
88
88
|
throw new CdpError("disposed", "stub");
|
|
89
89
|
},
|
|
90
90
|
dispose: () => {},
|
|
91
|
+
setCdpSessionId: () => {},
|
|
92
|
+
listTabs: async () => [],
|
|
93
|
+
selectTab: async (_tabId: number) => ({ tabId: _tabId }),
|
|
94
|
+
closeTab: async (_tabId: number) => ({ closed: true as const, tabId: _tabId }),
|
|
91
95
|
};
|
|
92
96
|
expect(scoped.kind).toBe(kind);
|
|
93
97
|
expect(scoped.conversationId).toBe("conv-123");
|
|
@@ -700,6 +700,28 @@ export class CdpInspectClient implements ScopedCdpClient {
|
|
|
700
700
|
}
|
|
701
701
|
}
|
|
702
702
|
|
|
703
|
+
/**
|
|
704
|
+
* CdpInspectClient doesn't support re-targeting to a specific tab/session.
|
|
705
|
+
* It connects directly to a remote CDP endpoint, which manages targets
|
|
706
|
+
* globally. This method is a no-op to satisfy the {@link ScopedCdpClient}
|
|
707
|
+
* interface.
|
|
708
|
+
*/
|
|
709
|
+
setCdpSessionId(): void {
|
|
710
|
+
// no-op
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async listTabs(): Promise<never> {
|
|
714
|
+
throw new CdpError("transport_error", "listTabs is not supported by the cdp-inspect backend (extension backend required)");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
async selectTab(_tabId: number): Promise<never> {
|
|
718
|
+
throw new CdpError("transport_error", "selectTab is not supported by the cdp-inspect backend (extension backend required)");
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
async closeTab(_tabId: number): Promise<never> {
|
|
722
|
+
throw new CdpError("transport_error", "closeTab is not supported by the cdp-inspect backend (extension backend required)");
|
|
723
|
+
}
|
|
724
|
+
|
|
703
725
|
dispose(): void {
|
|
704
726
|
if (this.disposed) return;
|
|
705
727
|
this.disposed = true;
|
|
@@ -2,7 +2,7 @@ import type { HostBrowserProxy } from "../../../daemon/host-browser-proxy.js";
|
|
|
2
2
|
import { getLogger } from "../../../util/logger.js";
|
|
3
3
|
import type { CdpErrorCode } from "./errors.js";
|
|
4
4
|
import { CdpError } from "./errors.js";
|
|
5
|
-
import type { CdpClientKind, ScopedCdpClient } from "./types.js";
|
|
5
|
+
import type { CdpClientKind, ScopedCdpClient, TabInfo } from "./types.js";
|
|
6
6
|
|
|
7
7
|
const log = getLogger("extension-cdp-client");
|
|
8
8
|
|
|
@@ -43,7 +43,7 @@ export class ExtensionCdpClient implements ScopedCdpClient {
|
|
|
43
43
|
constructor(
|
|
44
44
|
private readonly proxy: HostBrowserProxy,
|
|
45
45
|
public readonly conversationId: string,
|
|
46
|
-
private
|
|
46
|
+
private cdpSessionId?: string,
|
|
47
47
|
/**
|
|
48
48
|
* Caller's actor principal id. When provided, the proxy will refuse to
|
|
49
49
|
* dispatch this CDP command to a host_browser-capable client owned by a
|
|
@@ -182,6 +182,46 @@ export class ExtensionCdpClient implements ScopedCdpClient {
|
|
|
182
182
|
// it here. In-flight requests will be cancelled by the AbortSignal
|
|
183
183
|
// the tool passes in, or by conversation teardown.
|
|
184
184
|
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Update the `cdpSessionId` used on subsequent {@link send} calls.
|
|
188
|
+
*
|
|
189
|
+
* Used by the navigate executor after opening a new tab via the
|
|
190
|
+
* `Vellum.createTab` pseudo-CDP method: the returned tabId becomes
|
|
191
|
+
* this client's pinned target so the follow-on `Page.navigate`
|
|
192
|
+
* (and every command on this conversation thereafter) routes to
|
|
193
|
+
* the freshly-created tab instead of the user's currently-active
|
|
194
|
+
* tab.
|
|
195
|
+
*/
|
|
196
|
+
setCdpSessionId(cdpSessionId: string | undefined): void {
|
|
197
|
+
this.cdpSessionId = cdpSessionId;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** List all open browser tabs via `Vellum.listTabs`. */
|
|
201
|
+
async listTabs(): Promise<TabInfo[]> {
|
|
202
|
+
const result = await this.send<{ tabs: TabInfo[] }>("Vellum.listTabs", {});
|
|
203
|
+
return Array.isArray(result?.tabs) ? result.tabs : [];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Select (activate) an existing tab via `Vellum.selectTab`. */
|
|
207
|
+
async selectTab(tabId: number): Promise<{
|
|
208
|
+
tabId?: number;
|
|
209
|
+
windowId?: number;
|
|
210
|
+
url?: string;
|
|
211
|
+
title?: string;
|
|
212
|
+
clientId?: string;
|
|
213
|
+
}> {
|
|
214
|
+
return this.send("Vellum.selectTab", { tabId });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Close a browser tab via `Vellum.closeTab`. */
|
|
218
|
+
async closeTab(tabId: number): Promise<{
|
|
219
|
+
closed: boolean;
|
|
220
|
+
tabId: number;
|
|
221
|
+
clientId?: string;
|
|
222
|
+
}> {
|
|
223
|
+
return this.send("Vellum.closeTab", { tabId });
|
|
224
|
+
}
|
|
185
225
|
}
|
|
186
226
|
|
|
187
227
|
/**
|