@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tests for the default `user-prompt-submit
|
|
2
|
+
* Tests for the default `user-prompt-submit` hook (memory retrieval +
|
|
3
3
|
* runtime injection).
|
|
4
4
|
*
|
|
5
5
|
* Covers the retrieval behavior, the side effects the hook owns (injected-block
|
|
@@ -37,6 +37,7 @@ const applyRuntimeInjectionsMock = mock(async (messages: unknown) => ({
|
|
|
37
37
|
mock.module("../daemon/conversation-runtime-assembly.js", () => ({
|
|
38
38
|
applyRuntimeInjections: applyRuntimeInjectionsMock,
|
|
39
39
|
resolveTurnInboundActorContext: () => null,
|
|
40
|
+
resolveTurnModelProfileLabel: () => null,
|
|
40
41
|
}));
|
|
41
42
|
|
|
42
43
|
// The hook self-resolves the live conversation and its trust class; both are
|
|
@@ -56,14 +57,22 @@ mock.module("../config/loader.js", () => ({
|
|
|
56
57
|
getConfig: () => ({}) as AssistantConfig,
|
|
57
58
|
}));
|
|
58
59
|
|
|
60
|
+
// The hook publishes `memory_recalled` (and forwards the sink into
|
|
61
|
+
// `prepareMemory` for `memory_status`) through the shared `broadcastMessage`
|
|
62
|
+
// hub. Stub it so tests can assert what the hook emits.
|
|
63
|
+
const broadcastMessageMock = mock((_msg: unknown) => {});
|
|
64
|
+
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
65
|
+
broadcastMessage: broadcastMessageMock,
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
import type { UserPromptSubmitContext } from "@vellumai/plugin-api";
|
|
69
|
+
|
|
59
70
|
import type { AssistantConfig } from "../config/schema.js";
|
|
60
71
|
import type { Conversation } from "../daemon/conversation.js";
|
|
61
72
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
62
73
|
import type { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
63
74
|
import type { QdrantSparseVector } from "../memory/qdrant-client.js";
|
|
64
|
-
import userPromptSubmitMemoryRetrieval
|
|
65
|
-
type MemoryRetrievalHookContext,
|
|
66
|
-
} from "../plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.js";
|
|
75
|
+
import userPromptSubmitMemoryRetrieval from "../plugins/defaults/memory-retrieval/hooks/user-prompt-submit.js";
|
|
67
76
|
import type { Message } from "../providers/types.js";
|
|
68
77
|
|
|
69
78
|
/** Canonical metrics payload the graph retriever attaches to a real hit. */
|
|
@@ -136,19 +145,20 @@ function makeFakeGraphMemory(overrides?: {
|
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
function makeHookCtx(
|
|
139
|
-
overrides: Partial<
|
|
140
|
-
):
|
|
148
|
+
overrides: Partial<UserPromptSubmitContext> = {},
|
|
149
|
+
): UserPromptSubmitContext {
|
|
141
150
|
return {
|
|
142
|
-
onEvent: () => {},
|
|
143
151
|
conversationId: "conv-test",
|
|
144
152
|
userMessageId: "msg-test",
|
|
145
153
|
logger: {
|
|
146
154
|
warn: () => {},
|
|
147
|
-
} as unknown as
|
|
155
|
+
} as unknown as UserPromptSubmitContext["logger"],
|
|
148
156
|
latestMessages: [],
|
|
149
157
|
requestId: "req-test",
|
|
150
158
|
isNonInteractive: false,
|
|
151
|
-
|
|
159
|
+
modelProfileKey: null,
|
|
160
|
+
prompt: "",
|
|
161
|
+
originalMessages: [],
|
|
152
162
|
...overrides,
|
|
153
163
|
};
|
|
154
164
|
}
|
|
@@ -160,7 +170,10 @@ function makeHookCtx(
|
|
|
160
170
|
*/
|
|
161
171
|
function installConversation(
|
|
162
172
|
graphMemory: ConversationGraphMemory,
|
|
163
|
-
opts?: {
|
|
173
|
+
opts?: {
|
|
174
|
+
trusted?: boolean;
|
|
175
|
+
signal?: AbortSignal;
|
|
176
|
+
},
|
|
164
177
|
): void {
|
|
165
178
|
currentTrustClass = opts?.trusted === false ? "unknown" : "guardian";
|
|
166
179
|
currentConversation = {
|
|
@@ -175,11 +188,12 @@ beforeEach(() => {
|
|
|
175
188
|
recordMemoryRecallLogMock.mockReset();
|
|
176
189
|
applyRuntimeInjectionsMock.mockClear();
|
|
177
190
|
findConversationOrSubagentMock.mockClear();
|
|
191
|
+
broadcastMessageMock.mockReset();
|
|
178
192
|
currentConversation = undefined;
|
|
179
193
|
currentTrustClass = "guardian";
|
|
180
194
|
});
|
|
181
195
|
|
|
182
|
-
describe("user-prompt-submit
|
|
196
|
+
describe("user-prompt-submit hook (memory retrieval)", () => {
|
|
183
197
|
test("adopts the injected run messages when the actor is trusted", async () => {
|
|
184
198
|
const injected: Message[] = [
|
|
185
199
|
{ role: "user", content: [{ type: "text", text: "injected" }] },
|
|
@@ -299,14 +313,12 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
|
|
|
299
313
|
});
|
|
300
314
|
|
|
301
315
|
test("persists injected block, recall log, and emits memory_recalled", async () => {
|
|
302
|
-
const received: ServerMessage[] = [];
|
|
303
316
|
const { memory } = makeFakeGraphMemory({
|
|
304
317
|
injectedBlockText: "injected-block",
|
|
305
318
|
metrics: makeMetrics(),
|
|
306
319
|
});
|
|
307
320
|
installConversation(memory, { trusted: true });
|
|
308
321
|
const ctx = makeHookCtx({
|
|
309
|
-
onEvent: (msg) => received.push(msg),
|
|
310
322
|
userMessageId: "msg-42",
|
|
311
323
|
conversationId: "conv-42",
|
|
312
324
|
});
|
|
@@ -323,8 +335,98 @@ describe("user-prompt-submit-temp hook (memory retrieval)", () => {
|
|
|
323
335
|
};
|
|
324
336
|
expect(logEntry.conversationId).toBe("conv-42");
|
|
325
337
|
expect(logEntry.reason).toBe("graph:none");
|
|
326
|
-
|
|
327
|
-
expect(
|
|
338
|
+
// The `memory_recalled` summary publishes through the shared broadcast hub.
|
|
339
|
+
expect(broadcastMessageMock).toHaveBeenCalledTimes(1);
|
|
340
|
+
const emitted = broadcastMessageMock.mock.calls[0]?.[0] as ServerMessage;
|
|
341
|
+
expect(emitted.type).toBe("memory_recalled");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test("memory-v3 active → v2 persists immediately, then the combined update removes it and persists the v3 card block", async () => {
|
|
345
|
+
// The v2 block is persisted RIGHT AFTER retrieval (no loss window: a
|
|
346
|
+
// crash between retrieval and assembly must not leave v2's activation
|
|
347
|
+
// store claiming pages whose block never reached metadata). Assembly then
|
|
348
|
+
// reports memoryV3Active (v3 superseded/stripped v2's tail block), so the
|
|
349
|
+
// post-assembly combined update REMOVES the v2 key — persisting it would
|
|
350
|
+
// rehydrate a block that is not in the live history.
|
|
351
|
+
const { memory } = makeFakeGraphMemory({
|
|
352
|
+
injectedBlockText: "v2-block-superseded",
|
|
353
|
+
metrics: makeMetrics(),
|
|
354
|
+
});
|
|
355
|
+
installConversation(memory, { trusted: true });
|
|
356
|
+
applyRuntimeInjectionsMock.mockImplementationOnce(
|
|
357
|
+
async (messages: unknown) => ({
|
|
358
|
+
messages,
|
|
359
|
+
blocks: {
|
|
360
|
+
memoryV3Active: true,
|
|
361
|
+
memoryV3InjectedBlock: "header\n\n# memory/concepts/page-a.md\nhead",
|
|
362
|
+
},
|
|
363
|
+
}),
|
|
364
|
+
);
|
|
365
|
+
const ctx = makeHookCtx({ userMessageId: "msg-43" });
|
|
366
|
+
|
|
367
|
+
await userPromptSubmitMemoryRetrieval(ctx);
|
|
368
|
+
|
|
369
|
+
expect(updateMessageMetadataMock.mock.calls).toEqual([
|
|
370
|
+
["msg-43", { memoryInjectedBlock: "v2-block-superseded" }],
|
|
371
|
+
[
|
|
372
|
+
"msg-43",
|
|
373
|
+
{
|
|
374
|
+
// `undefined` deletes the key (JSON.stringify drops it) — the turn
|
|
375
|
+
// ends with the two memory layers mutually exclusive per row.
|
|
376
|
+
memoryInjectedBlock: undefined,
|
|
377
|
+
memoryV3InjectedBlock: "header\n\n# memory/concepts/page-a.md\nhead",
|
|
378
|
+
},
|
|
379
|
+
],
|
|
380
|
+
]);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test("memory-v3 active with EMPTY net-new → the immediate v2 persist is removed again", async () => {
|
|
384
|
+
// All-repeat turn: v2 was stripped (superseded) and v3 attached nothing
|
|
385
|
+
// new — the row must rehydrate with no memory block, matching live state.
|
|
386
|
+
const { memory } = makeFakeGraphMemory({
|
|
387
|
+
injectedBlockText: "v2-block-superseded",
|
|
388
|
+
metrics: makeMetrics(),
|
|
389
|
+
});
|
|
390
|
+
installConversation(memory, { trusted: true });
|
|
391
|
+
applyRuntimeInjectionsMock.mockImplementationOnce(
|
|
392
|
+
async (messages: unknown) => ({
|
|
393
|
+
messages,
|
|
394
|
+
blocks: { memoryV3Active: true },
|
|
395
|
+
}),
|
|
396
|
+
);
|
|
397
|
+
const ctx = makeHookCtx({ userMessageId: "msg-44" });
|
|
398
|
+
|
|
399
|
+
await userPromptSubmitMemoryRetrieval(ctx);
|
|
400
|
+
|
|
401
|
+
expect(updateMessageMetadataMock.mock.calls).toEqual([
|
|
402
|
+
["msg-44", { memoryInjectedBlock: "v2-block-superseded" }],
|
|
403
|
+
["msg-44", { memoryInjectedBlock: undefined }],
|
|
404
|
+
]);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test("v2 block is already persisted when runtime assembly throws (no loss window)", async () => {
|
|
408
|
+
// Regression guard for the v2 path: with the v3 shadow flag on, assembly
|
|
409
|
+
// includes a seconds-long selector LLM call. If the process dies or the
|
|
410
|
+
// turn aborts in that window, the v2 block must already be in metadata —
|
|
411
|
+
// v2's activation store claimed its pages during retrieval, and a
|
|
412
|
+
// claimed-but-never-persisted block is absent from history on reload AND
|
|
413
|
+
// suppressed until compaction.
|
|
414
|
+
const { memory } = makeFakeGraphMemory({
|
|
415
|
+
injectedBlockText: "v2-block",
|
|
416
|
+
metrics: makeMetrics(),
|
|
417
|
+
});
|
|
418
|
+
installConversation(memory, { trusted: true });
|
|
419
|
+
applyRuntimeInjectionsMock.mockImplementationOnce(async () => {
|
|
420
|
+
throw new Error("assembly failed");
|
|
421
|
+
});
|
|
422
|
+
const ctx = makeHookCtx({ userMessageId: "msg-45" });
|
|
423
|
+
|
|
424
|
+
await expect(userPromptSubmitMemoryRetrieval(ctx)).rejects.toThrow(
|
|
425
|
+
"assembly failed",
|
|
426
|
+
);
|
|
427
|
+
expect(updateMessageMetadataMock).toHaveBeenCalledWith("msg-45", {
|
|
428
|
+
memoryInjectedBlock: "v2-block",
|
|
429
|
+
});
|
|
328
430
|
});
|
|
329
431
|
|
|
330
432
|
test("skips metadata persist when no block text is injected", async () => {
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
|
|
10
10
|
import { describe, expect, test } from "bun:test";
|
|
11
11
|
|
|
12
|
-
import type { ConversationCandidateSet } from "../notifications/conversation-candidates.js";
|
|
13
12
|
import {
|
|
14
13
|
buildAccessRequestContractText,
|
|
15
14
|
buildAccessRequestIdentityLine,
|
|
16
|
-
composeFallbackCopy,
|
|
17
15
|
hasAccessRequestInstructions,
|
|
18
16
|
hasInviteFlowDirective,
|
|
19
17
|
normalizeForDirectiveMatching,
|
|
20
18
|
sanitizeIdentityField,
|
|
21
19
|
sanitizeMessagePreview,
|
|
22
|
-
} from "../notifications/copy
|
|
20
|
+
} from "../notifications/access-request-copy.js";
|
|
21
|
+
import type { ConversationCandidateSet } from "../notifications/conversation-candidates.js";
|
|
22
|
+
import { composeFallbackCopy } from "../notifications/copy-composer.js";
|
|
23
23
|
import {
|
|
24
24
|
enforceGuardianCallConversationAffinity,
|
|
25
25
|
validateConversationActions,
|
|
@@ -58,7 +58,6 @@ import {
|
|
|
58
58
|
getAppByProviderAndClientId,
|
|
59
59
|
getConnection,
|
|
60
60
|
getConnectionByProvider,
|
|
61
|
-
getConnectionByProviderAndAccount,
|
|
62
61
|
getProvider,
|
|
63
62
|
isProviderConnected,
|
|
64
63
|
listActiveConnectionsByProvider,
|
|
@@ -1689,90 +1688,6 @@ describe("connection operations", () => {
|
|
|
1689
1688
|
});
|
|
1690
1689
|
});
|
|
1691
1690
|
|
|
1692
|
-
describe("getConnectionByProviderAndAccount", () => {
|
|
1693
|
-
test("returns the connection matching the given account", async () => {
|
|
1694
|
-
const app = await createTestApp("github", "client-1");
|
|
1695
|
-
|
|
1696
|
-
const conn1 = createConnection({
|
|
1697
|
-
oauthAppId: app.id,
|
|
1698
|
-
provider: "github",
|
|
1699
|
-
accountInfo: "user1@example.com",
|
|
1700
|
-
grantedScopes: ["repo"],
|
|
1701
|
-
hasRefreshToken: false,
|
|
1702
|
-
createdAt: 1000,
|
|
1703
|
-
});
|
|
1704
|
-
|
|
1705
|
-
createConnection({
|
|
1706
|
-
oauthAppId: app.id,
|
|
1707
|
-
provider: "github",
|
|
1708
|
-
accountInfo: "user2@example.com",
|
|
1709
|
-
grantedScopes: ["repo"],
|
|
1710
|
-
hasRefreshToken: false,
|
|
1711
|
-
createdAt: 2000,
|
|
1712
|
-
});
|
|
1713
|
-
|
|
1714
|
-
const result = getConnectionByProviderAndAccount(
|
|
1715
|
-
"github",
|
|
1716
|
-
"user1@example.com",
|
|
1717
|
-
);
|
|
1718
|
-
expect(result).toBeDefined();
|
|
1719
|
-
expect(result!.id).toBe(conn1.id);
|
|
1720
|
-
});
|
|
1721
|
-
|
|
1722
|
-
test("falls back to getConnectionByProvider when accountInfo is undefined", async () => {
|
|
1723
|
-
const app = await createTestApp("github", "client-1");
|
|
1724
|
-
|
|
1725
|
-
const conn = createConnection({
|
|
1726
|
-
oauthAppId: app.id,
|
|
1727
|
-
provider: "github",
|
|
1728
|
-
accountInfo: "user@example.com",
|
|
1729
|
-
grantedScopes: ["repo"],
|
|
1730
|
-
hasRefreshToken: false,
|
|
1731
|
-
});
|
|
1732
|
-
|
|
1733
|
-
const result = getConnectionByProviderAndAccount("github", undefined);
|
|
1734
|
-
expect(result).toBeDefined();
|
|
1735
|
-
expect(result!.id).toBe(conn.id);
|
|
1736
|
-
});
|
|
1737
|
-
|
|
1738
|
-
test("returns undefined when no connection matches the account", async () => {
|
|
1739
|
-
const app = await createTestApp("github", "client-1");
|
|
1740
|
-
|
|
1741
|
-
createConnection({
|
|
1742
|
-
oauthAppId: app.id,
|
|
1743
|
-
provider: "github",
|
|
1744
|
-
accountInfo: "user@example.com",
|
|
1745
|
-
grantedScopes: ["repo"],
|
|
1746
|
-
hasRefreshToken: false,
|
|
1747
|
-
});
|
|
1748
|
-
|
|
1749
|
-
const result = getConnectionByProviderAndAccount(
|
|
1750
|
-
"github",
|
|
1751
|
-
"other@example.com",
|
|
1752
|
-
);
|
|
1753
|
-
expect(result).toBeUndefined();
|
|
1754
|
-
});
|
|
1755
|
-
|
|
1756
|
-
test("skips revoked connections", async () => {
|
|
1757
|
-
const app = await createTestApp("github", "client-1");
|
|
1758
|
-
|
|
1759
|
-
const conn = createConnection({
|
|
1760
|
-
oauthAppId: app.id,
|
|
1761
|
-
provider: "github",
|
|
1762
|
-
accountInfo: "user@example.com",
|
|
1763
|
-
grantedScopes: ["repo"],
|
|
1764
|
-
hasRefreshToken: false,
|
|
1765
|
-
});
|
|
1766
|
-
updateConnection(conn.id, { status: "revoked" });
|
|
1767
|
-
|
|
1768
|
-
const result = getConnectionByProviderAndAccount(
|
|
1769
|
-
"github",
|
|
1770
|
-
"user@example.com",
|
|
1771
|
-
);
|
|
1772
|
-
expect(result).toBeUndefined();
|
|
1773
|
-
});
|
|
1774
|
-
});
|
|
1775
|
-
|
|
1776
1691
|
describe("listActiveConnectionsByProvider", () => {
|
|
1777
1692
|
test("returns all active connections for a provider", async () => {
|
|
1778
1693
|
const app = await createTestApp("github", "client-1");
|
|
@@ -2,7 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import type { ContextOverflowRecoveryConfig } from "../config/schemas/inference.js";
|
|
4
4
|
import { ContextOverflowRecoveryConfigSchema } from "../config/schemas/inference.js";
|
|
5
|
-
import { resolveOverflowAction } from "../
|
|
5
|
+
import { resolveOverflowAction } from "../plugins/defaults/compaction/overflow-policy.js";
|
|
6
6
|
|
|
7
7
|
/** Parse an empty object to get all defaults. */
|
|
8
8
|
const DEFAULTS: ContextOverflowRecoveryConfig =
|
|
@@ -137,6 +137,7 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
137
137
|
});
|
|
138
138
|
const start = Date.now();
|
|
139
139
|
await loop.run({
|
|
140
|
+
requestId: "test-request",
|
|
140
141
|
messages: [userMessage],
|
|
141
142
|
onEvent: () => {},
|
|
142
143
|
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
@@ -185,6 +186,7 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
185
186
|
});
|
|
186
187
|
const start = Date.now();
|
|
187
188
|
await loop.run({
|
|
189
|
+
requestId: "test-request",
|
|
188
190
|
messages: [userMessage],
|
|
189
191
|
onEvent: () => {},
|
|
190
192
|
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
@@ -246,6 +248,7 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
246
248
|
const events: AgentEvent[] = [];
|
|
247
249
|
const start = Date.now();
|
|
248
250
|
await loop.run({
|
|
251
|
+
requestId: "test-request",
|
|
249
252
|
messages: [userMessage],
|
|
250
253
|
onEvent: collectEvents(events),
|
|
251
254
|
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
@@ -319,6 +322,7 @@ describe("Parallel tool execution benchmarks", () => {
|
|
|
319
322
|
});
|
|
320
323
|
const start = Date.now();
|
|
321
324
|
const { history } = await loop.run({
|
|
325
|
+
requestId: "test-request",
|
|
322
326
|
messages: [userMessage],
|
|
323
327
|
onEvent: () => {},
|
|
324
328
|
trust: { sourceChannel: "vellum", trustClass: "unknown" },
|
|
@@ -30,7 +30,6 @@ mock.module("../agent/image-optimize.js", () => ({
|
|
|
30
30
|
}),
|
|
31
31
|
}));
|
|
32
32
|
|
|
33
|
-
import { persistUnsendableImageDowngrades } from "../daemon/persist-unsendable-image.js";
|
|
34
33
|
import {
|
|
35
34
|
addMessage,
|
|
36
35
|
createConversation,
|
|
@@ -38,6 +37,7 @@ import {
|
|
|
38
37
|
} from "../memory/conversation-crud.js";
|
|
39
38
|
import { getDb } from "../memory/db-connection.js";
|
|
40
39
|
import { initializeDb } from "../memory/db-init.js";
|
|
40
|
+
import { persistUnsendableImageDowngrades } from "../plugins/defaults/image-recovery/recover.js";
|
|
41
41
|
import type { ContentBlock } from "../providers/types.js";
|
|
42
42
|
|
|
43
43
|
initializeDb();
|
|
@@ -58,9 +58,22 @@ function oversizedPngBase64(): string {
|
|
|
58
58
|
const height = 9000;
|
|
59
59
|
return Buffer.from(
|
|
60
60
|
Uint8Array.from([
|
|
61
|
-
0x89,
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
0x89,
|
|
62
|
+
0x50,
|
|
63
|
+
0x4e,
|
|
64
|
+
0x47,
|
|
65
|
+
0x0d,
|
|
66
|
+
0x0a,
|
|
67
|
+
0x1a,
|
|
68
|
+
0x0a, // PNG signature
|
|
69
|
+
0x00,
|
|
70
|
+
0x00,
|
|
71
|
+
0x00,
|
|
72
|
+
0x0d, // IHDR length (13)
|
|
73
|
+
0x49,
|
|
74
|
+
0x48,
|
|
75
|
+
0x44,
|
|
76
|
+
0x52, // "IHDR"
|
|
64
77
|
(width >>> 24) & 0xff,
|
|
65
78
|
(width >>> 16) & 0xff,
|
|
66
79
|
(width >>> 8) & 0xff,
|
|
@@ -69,7 +82,11 @@ function oversizedPngBase64(): string {
|
|
|
69
82
|
(height >>> 16) & 0xff,
|
|
70
83
|
(height >>> 8) & 0xff,
|
|
71
84
|
height & 0xff,
|
|
72
|
-
0x08,
|
|
85
|
+
0x08,
|
|
86
|
+
0x06,
|
|
87
|
+
0x00,
|
|
88
|
+
0x00,
|
|
89
|
+
0x00,
|
|
73
90
|
]),
|
|
74
91
|
).toString("base64");
|
|
75
92
|
}
|
|
@@ -80,7 +97,10 @@ function toolResultWithImage(data: string): ContentBlock {
|
|
|
80
97
|
tool_use_id: "toolu_123",
|
|
81
98
|
content: "Screenshot captured",
|
|
82
99
|
contentBlocks: [
|
|
83
|
-
{
|
|
100
|
+
{
|
|
101
|
+
type: "image",
|
|
102
|
+
source: { type: "base64", media_type: "image/png", data },
|
|
103
|
+
},
|
|
84
104
|
],
|
|
85
105
|
};
|
|
86
106
|
}
|
|
@@ -119,9 +139,9 @@ describe("persistUnsendableImageDowngrades (downscalable host)", () => {
|
|
|
119
139
|
};
|
|
120
140
|
const nested = toolResult.contentBlocks?.[0];
|
|
121
141
|
expect(nested?.type).toBe("image");
|
|
122
|
-
expect(
|
|
123
|
-
|
|
124
|
-
);
|
|
142
|
+
expect(
|
|
143
|
+
(nested as Extract<ContentBlock, { type: "image" }>).source.data,
|
|
144
|
+
).toBe(SHRUNK_DATA);
|
|
125
145
|
});
|
|
126
146
|
|
|
127
147
|
/** Re-running is a no-op: the downscaled payload is within limits. */
|
|
@@ -14,10 +14,6 @@
|
|
|
14
14
|
|
|
15
15
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
oversizedImageReplacement,
|
|
19
|
-
persistUnsendableImageDowngrades,
|
|
20
|
-
} from "../daemon/persist-unsendable-image.js";
|
|
21
17
|
import {
|
|
22
18
|
addMessage,
|
|
23
19
|
createConversation,
|
|
@@ -25,6 +21,10 @@ import {
|
|
|
25
21
|
} from "../memory/conversation-crud.js";
|
|
26
22
|
import { getDb } from "../memory/db-connection.js";
|
|
27
23
|
import { initializeDb } from "../memory/db-init.js";
|
|
24
|
+
import {
|
|
25
|
+
oversizedImageReplacement,
|
|
26
|
+
persistUnsendableImageDowngrades,
|
|
27
|
+
} from "../plugins/defaults/image-recovery/recover.js";
|
|
28
28
|
import type { ContentBlock } from "../providers/types.js";
|
|
29
29
|
|
|
30
30
|
initializeDb();
|
|
@@ -370,3 +370,81 @@ describe("persistence-layer secret redaction", () => {
|
|
|
370
370
|
expect(textBlock?.text).toBe(text);
|
|
371
371
|
});
|
|
372
372
|
});
|
|
373
|
+
|
|
374
|
+
describe("thinking timing persistence", () => {
|
|
375
|
+
let state: EventHandlerState;
|
|
376
|
+
|
|
377
|
+
beforeEach(() => {
|
|
378
|
+
addMessageCalls.length = 0;
|
|
379
|
+
state = createEventHandlerState();
|
|
380
|
+
state.turnStartedAt = 1_700_000_000_000;
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
afterEach(() => {
|
|
384
|
+
addMessageCalls.length = 0;
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
function makeThinkingCompleteEvent(): Extract<
|
|
388
|
+
AgentEvent,
|
|
389
|
+
{ type: "message_complete" }
|
|
390
|
+
> {
|
|
391
|
+
return {
|
|
392
|
+
type: "message_complete",
|
|
393
|
+
message: {
|
|
394
|
+
role: "assistant",
|
|
395
|
+
content: [
|
|
396
|
+
{ type: "thinking", thinking: "let me reason", signature: "sig" },
|
|
397
|
+
{ type: "text", text: "the answer" },
|
|
398
|
+
],
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
test("stamps per-block thinking timing onto the persisted thinking block", async () => {
|
|
404
|
+
// GIVEN a turn whose row was reserved at llm_call_started
|
|
405
|
+
await handleLlmCallStarted(state, makeDeps());
|
|
406
|
+
|
|
407
|
+
// AND streaming then captured timing for one thinking block (startedAt when
|
|
408
|
+
// it opened, completedAt at its last reasoning delta). The reserve resets
|
|
409
|
+
// the accumulator, so deltas — and thus the timing — land after it.
|
|
410
|
+
state.currentThinkingTimestamps = [{ startedAt: 1000, completedAt: 1750 }];
|
|
411
|
+
|
|
412
|
+
// WHEN the message completes and content is persisted
|
|
413
|
+
await handleMessageComplete(state, makeDeps(), makeThinkingCompleteEvent());
|
|
414
|
+
|
|
415
|
+
// THEN the persisted thinking block carries the internal `_`-prefixed
|
|
416
|
+
// timing so a history reload can surface the duration + "Started at" hover
|
|
417
|
+
const persisted = lastPersisted("assistant");
|
|
418
|
+
const blocks = JSON.parse(persisted.content) as Array<{
|
|
419
|
+
type: string;
|
|
420
|
+
_startedAt?: number;
|
|
421
|
+
_completedAt?: number;
|
|
422
|
+
}>;
|
|
423
|
+
const thinkingBlock = blocks.find((b) => b.type === "thinking");
|
|
424
|
+
expect(thinkingBlock?._startedAt).toBe(1000);
|
|
425
|
+
expect(thinkingBlock?._completedAt).toBe(1750);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
test("persists no thinking timing when none was captured this turn", async () => {
|
|
429
|
+
// GIVEN a turn that produced a thinking block but captured no timing
|
|
430
|
+
// (thinking streaming disabled, so the timing list stays empty)
|
|
431
|
+
expect(state.currentThinkingTimestamps).toEqual([]);
|
|
432
|
+
|
|
433
|
+
// WHEN the message completes and content is persisted
|
|
434
|
+
await handleLlmCallStarted(state, makeDeps());
|
|
435
|
+
await handleMessageComplete(state, makeDeps(), makeThinkingCompleteEvent());
|
|
436
|
+
|
|
437
|
+
// THEN the thinking block is persisted without timing, so the client hides
|
|
438
|
+
// the duration exactly as a tool call with no timing
|
|
439
|
+
const persisted = lastPersisted("assistant");
|
|
440
|
+
const blocks = JSON.parse(persisted.content) as Array<{
|
|
441
|
+
type: string;
|
|
442
|
+
_startedAt?: number;
|
|
443
|
+
_completedAt?: number;
|
|
444
|
+
}>;
|
|
445
|
+
const thinkingBlock = blocks.find((b) => b.type === "thinking");
|
|
446
|
+
expect(thinkingBlock).toBeDefined();
|
|
447
|
+
expect(thinkingBlock?._startedAt).toBeUndefined();
|
|
448
|
+
expect(thinkingBlock?._completedAt).toBeUndefined();
|
|
449
|
+
});
|
|
450
|
+
});
|