@vellumai/assistant 0.8.5 → 0.8.6
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/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -135,6 +135,8 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
135
135
|
updateConversationTitle: () => {},
|
|
136
136
|
getMessageById: () => null,
|
|
137
137
|
getLastUserTimestampBefore: () => 0,
|
|
138
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
139
|
+
updateMessageContent: mock(() => {}),
|
|
138
140
|
}));
|
|
139
141
|
|
|
140
142
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -204,7 +206,7 @@ mock.module("../config/skill-state.js", () => ({
|
|
|
204
206
|
interface PendingRun {
|
|
205
207
|
resolve: (history: Message[]) => void;
|
|
206
208
|
messages: Message[];
|
|
207
|
-
onEvent: (event: AgentEvent) => void
|
|
209
|
+
onEvent: (event: AgentEvent) => void | Promise<void>;
|
|
208
210
|
}
|
|
209
211
|
|
|
210
212
|
let pendingRuns: PendingRun[] = [];
|
|
@@ -223,7 +225,7 @@ mock.module("../agent/loop.js", () => ({
|
|
|
223
225
|
}
|
|
224
226
|
async run(
|
|
225
227
|
messages: Message[],
|
|
226
|
-
onEvent: (event: AgentEvent) => void
|
|
228
|
+
onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
227
229
|
_signal?: AbortSignal,
|
|
228
230
|
_requestId?: string,
|
|
229
231
|
_onCheckpoint?: (
|
|
@@ -333,21 +335,24 @@ async function waitForPendingRun(
|
|
|
333
335
|
}
|
|
334
336
|
}
|
|
335
337
|
|
|
336
|
-
function resolveRun(index: number) {
|
|
338
|
+
async function resolveRun(index: number) {
|
|
337
339
|
const run = pendingRuns[index];
|
|
338
340
|
if (!run) throw new Error(`No pending run at index ${index}`);
|
|
339
341
|
const assistantMsg: Message = {
|
|
340
342
|
role: "assistant",
|
|
341
343
|
content: [{ type: "text", text: `reply-${index}` }],
|
|
342
344
|
};
|
|
343
|
-
|
|
345
|
+
// Prime the assistant row anchor — production code emits this from
|
|
346
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
347
|
+
await run.onEvent({ type: "llm_call_started" });
|
|
348
|
+
await run.onEvent({
|
|
344
349
|
type: "usage",
|
|
345
350
|
inputTokens: 10,
|
|
346
351
|
outputTokens: 5,
|
|
347
352
|
model: "mock",
|
|
348
353
|
providerDurationMs: 100,
|
|
349
354
|
});
|
|
350
|
-
run.onEvent({ type: "message_complete", message: assistantMsg });
|
|
355
|
+
await run.onEvent({ type: "message_complete", message: assistantMsg });
|
|
351
356
|
run.resolve([...run.messages, assistantMsg]);
|
|
352
357
|
}
|
|
353
358
|
|
|
@@ -398,17 +403,20 @@ describe("Conversation queue — slash-like messages pass through to agent loop"
|
|
|
398
403
|
|
|
399
404
|
// Enqueue a slash-like passthrough and a normal passthrough after it.
|
|
400
405
|
// Both resolve to passthrough, so the batch builder groups them into one run.
|
|
401
|
-
conversation.enqueueMessage(
|
|
402
|
-
"/not-a-skill",
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
406
|
+
conversation.enqueueMessage({
|
|
407
|
+
content: "/not-a-skill",
|
|
408
|
+
onEvent: (e) => eventsSlash.push(e),
|
|
409
|
+
requestId: "req-slash",
|
|
410
|
+
});
|
|
411
|
+
conversation.enqueueMessage({
|
|
412
|
+
content: "msg-3",
|
|
413
|
+
onEvent: (e) => events3.push(e),
|
|
414
|
+
requestId: "req-3",
|
|
415
|
+
});
|
|
408
416
|
expect(conversation.getQueueDepth()).toBe(2);
|
|
409
417
|
|
|
410
418
|
// Complete first run — drain pulls both queued messages into one batched run.
|
|
411
|
-
resolveRun(0);
|
|
419
|
+
await resolveRun(0);
|
|
412
420
|
await p1;
|
|
413
421
|
await waitForPendingRun(2);
|
|
414
422
|
|
|
@@ -419,8 +427,46 @@ describe("Conversation queue — slash-like messages pass through to agent loop"
|
|
|
419
427
|
// Exactly 2 runs total: msg-1 + batched [/not-a-skill, msg-3].
|
|
420
428
|
expect(pendingRuns.length).toBe(2);
|
|
421
429
|
|
|
430
|
+
await resolveRun(1);
|
|
431
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
test("batched queued messages with the same event sink do not duplicate assistant stream events", async () => {
|
|
435
|
+
const conversation = makeConversation();
|
|
436
|
+
await conversation.loadFromDb();
|
|
437
|
+
|
|
438
|
+
const sharedEvents: ServerMessage[] = [];
|
|
439
|
+
const sharedOnEvent = (event: ServerMessage) => sharedEvents.push(event);
|
|
440
|
+
|
|
441
|
+
const p1 = conversation.processMessage("msg-1", [], () => {}, "req-1");
|
|
442
|
+
await waitForPendingRun(1);
|
|
443
|
+
|
|
444
|
+
conversation.enqueueMessage({
|
|
445
|
+
content: "msg-2",
|
|
446
|
+
onEvent: sharedOnEvent,
|
|
447
|
+
requestId: "req-2",
|
|
448
|
+
});
|
|
449
|
+
conversation.enqueueMessage({
|
|
450
|
+
content: "msg-3",
|
|
451
|
+
onEvent: sharedOnEvent,
|
|
452
|
+
requestId: "req-3",
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
resolveRun(0);
|
|
456
|
+
await p1;
|
|
457
|
+
await waitForPendingRun(2);
|
|
458
|
+
|
|
459
|
+
expect(
|
|
460
|
+
sharedEvents.filter((event) => event.type === "message_dequeued"),
|
|
461
|
+
).toHaveLength(2);
|
|
462
|
+
|
|
463
|
+
sharedEvents.length = 0;
|
|
422
464
|
resolveRun(1);
|
|
423
465
|
await new Promise((r) => setTimeout(r, 50));
|
|
466
|
+
|
|
467
|
+
expect(
|
|
468
|
+
sharedEvents.filter((event) => event.type === "message_complete"),
|
|
469
|
+
).toHaveLength(1);
|
|
424
470
|
});
|
|
425
471
|
|
|
426
472
|
test("queued skill-name slash passes through as-is", async () => {
|
|
@@ -440,15 +486,14 @@ describe("Conversation queue — slash-like messages pass through to agent loop"
|
|
|
440
486
|
await waitForPendingRun(1);
|
|
441
487
|
|
|
442
488
|
// Enqueue a slash command that matches a skill name — still passes through
|
|
443
|
-
conversation.enqueueMessage(
|
|
444
|
-
"/start-the-day",
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
);
|
|
489
|
+
conversation.enqueueMessage({
|
|
490
|
+
content: "/start-the-day",
|
|
491
|
+
onEvent: (e) => eventsSlash.push(e),
|
|
492
|
+
requestId: "req-slash",
|
|
493
|
+
});
|
|
449
494
|
|
|
450
495
|
// Complete first run — triggers drain
|
|
451
|
-
resolveRun(0);
|
|
496
|
+
await resolveRun(0);
|
|
452
497
|
await p1;
|
|
453
498
|
await waitForPendingRun(2);
|
|
454
499
|
|
|
@@ -463,7 +508,7 @@ describe("Conversation queue — slash-like messages pass through to agent loop"
|
|
|
463
508
|
// Content passes through as-is — no rewriting
|
|
464
509
|
expect(text).toContain("/start-the-day");
|
|
465
510
|
|
|
466
|
-
resolveRun(1);
|
|
511
|
+
await resolveRun(1);
|
|
467
512
|
await new Promise((r) => setTimeout(r, 50));
|
|
468
513
|
});
|
|
469
514
|
|
|
@@ -483,18 +528,25 @@ describe("Conversation queue — slash-like messages pass through to agent loop"
|
|
|
483
528
|
// batch builder stops at "hi" (length-1 batch → drainSingleMessage). Then
|
|
484
529
|
// /compact takes its short-circuit path (no new runAgentLoop), and "bye"
|
|
485
530
|
// drains as its own run.
|
|
486
|
-
conversation.enqueueMessage(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
531
|
+
conversation.enqueueMessage({
|
|
532
|
+
content: "hi",
|
|
533
|
+
onEvent: (e) => eventsHi.push(e),
|
|
534
|
+
requestId: "req-hi",
|
|
535
|
+
});
|
|
536
|
+
conversation.enqueueMessage({
|
|
537
|
+
content: "/compact",
|
|
538
|
+
onEvent: (e) => eventsCompact.push(e),
|
|
539
|
+
requestId: "req-compact",
|
|
540
|
+
});
|
|
541
|
+
conversation.enqueueMessage({
|
|
542
|
+
content: "bye",
|
|
543
|
+
onEvent: (e) => eventsBye.push(e),
|
|
544
|
+
requestId: "req-bye",
|
|
545
|
+
});
|
|
494
546
|
expect(conversation.getQueueDepth()).toBe(3);
|
|
495
547
|
|
|
496
548
|
// Resolve msg-1 → drain pulls only "hi" (batch builder stops at /compact).
|
|
497
|
-
resolveRun(0);
|
|
549
|
+
await resolveRun(0);
|
|
498
550
|
await p1;
|
|
499
551
|
await waitForPendingRun(2);
|
|
500
552
|
|
|
@@ -503,14 +555,14 @@ describe("Conversation queue — slash-like messages pass through to agent loop"
|
|
|
503
555
|
|
|
504
556
|
// Resolve "hi" → /compact short-circuits without a new runAgentLoop, then
|
|
505
557
|
// drains "bye" as its own run.
|
|
506
|
-
resolveRun(1);
|
|
558
|
+
await resolveRun(1);
|
|
507
559
|
await waitForPendingRun(3);
|
|
508
560
|
|
|
509
561
|
expect(eventsCompact.some((e) => e.type === "message_complete")).toBe(true);
|
|
510
562
|
expect(eventsBye.some((e) => e.type === "message_dequeued")).toBe(true);
|
|
511
563
|
expect(pendingRuns.length).toBe(3);
|
|
512
564
|
|
|
513
|
-
resolveRun(2);
|
|
565
|
+
await resolveRun(2);
|
|
514
566
|
await new Promise((r) => setTimeout(r, 50));
|
|
515
567
|
});
|
|
516
568
|
|
|
@@ -136,6 +136,8 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
136
136
|
updateConversationTitle: () => {},
|
|
137
137
|
getMessageById: () => null,
|
|
138
138
|
getLastUserTimestampBefore: () => 0,
|
|
139
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
140
|
+
updateMessageContent: mock(() => {}),
|
|
139
141
|
}));
|
|
140
142
|
|
|
141
143
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -254,6 +256,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
254
256
|
checkpoint: CheckpointInfo,
|
|
255
257
|
) => CheckpointDecision | Promise<CheckpointDecision>,
|
|
256
258
|
): Promise<Message[]> {
|
|
259
|
+
// Prime the assistant row anchor — production code emits this from
|
|
260
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
261
|
+
await onEvent({ type: "llm_call_started" });
|
|
257
262
|
agentLoopRunCalled = true;
|
|
258
263
|
const assistantMsg: Message = {
|
|
259
264
|
role: "assistant",
|
|
@@ -150,6 +150,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
150
150
|
addMessage: () => ({ id: `msg-${Date.now()}` }),
|
|
151
151
|
updateConversationUsage: () => {},
|
|
152
152
|
updateConversationTitle: () => {},
|
|
153
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
153
154
|
}));
|
|
154
155
|
|
|
155
156
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -182,6 +182,52 @@ describe("surface action delivery to assistant", () => {
|
|
|
182
182
|
);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
|
+
test("history-restored relay action uses stored prompt data and can complete the surface", async () => {
|
|
186
|
+
const ctx = makeContext();
|
|
187
|
+
const surfaceId = "max-token-surface";
|
|
188
|
+
ctx.surfaceState.set(surfaceId, {
|
|
189
|
+
surfaceType: "card",
|
|
190
|
+
data: {
|
|
191
|
+
title: "Response limit reached",
|
|
192
|
+
body: "Continue from where the assistant stopped.",
|
|
193
|
+
},
|
|
194
|
+
actions: [
|
|
195
|
+
{
|
|
196
|
+
id: "relay_prompt",
|
|
197
|
+
label: "Continue",
|
|
198
|
+
style: "primary",
|
|
199
|
+
data: {
|
|
200
|
+
prompt: "Continue from where you stopped.",
|
|
201
|
+
_completeSurface: true,
|
|
202
|
+
_completionSummary: "Continue",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await handleSurfaceAction(ctx, surfaceId, "relay_prompt");
|
|
209
|
+
|
|
210
|
+
expect(ctx.processMessageCalls).toHaveLength(1);
|
|
211
|
+
expect(ctx.processMessageCalls[0]!.content).toBe(
|
|
212
|
+
"Continue from where you stopped.",
|
|
213
|
+
);
|
|
214
|
+
expect(
|
|
215
|
+
broadcastedMessages.some(
|
|
216
|
+
(msg) =>
|
|
217
|
+
msg.type === "ui_surface_complete" &&
|
|
218
|
+
msg.surfaceId === surfaceId &&
|
|
219
|
+
msg.summary === "Continue",
|
|
220
|
+
),
|
|
221
|
+
).toBe(true);
|
|
222
|
+
expect(
|
|
223
|
+
broadcastedMessages.some(
|
|
224
|
+
(msg) =>
|
|
225
|
+
msg.type === "user_message_echo" &&
|
|
226
|
+
msg.text === "Continue from where you stopped.",
|
|
227
|
+
),
|
|
228
|
+
).toBe(true);
|
|
229
|
+
});
|
|
230
|
+
|
|
185
231
|
test("action on history-restored surface (no pending) still processes", async () => {
|
|
186
232
|
const sent: ServerMessage[] = [];
|
|
187
233
|
const ctx = makeContext(sent);
|
|
@@ -29,6 +29,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
29
29
|
getMessages: (conversationId: string) => getMessagesImpl(conversationId),
|
|
30
30
|
updateMessageContent: (id: string, content: string) =>
|
|
31
31
|
updateMessageContentSpy(id, content),
|
|
32
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
32
33
|
}));
|
|
33
34
|
|
|
34
35
|
// Imports must come AFTER mock.module so the surface module picks up
|
|
@@ -72,9 +72,12 @@ function createMockContext(
|
|
|
72
72
|
hostCuProxy: undefined,
|
|
73
73
|
hasNoClient: overrides?.hasNoClient ?? false,
|
|
74
74
|
isProcessing: () => false,
|
|
75
|
-
enqueueMessage: (
|
|
76
|
-
const resolvedId = requestId ?? "mock-request-id";
|
|
77
|
-
enqueuedMessages.push({
|
|
75
|
+
enqueueMessage: (options) => {
|
|
76
|
+
const resolvedId = options.requestId ?? "mock-request-id";
|
|
77
|
+
enqueuedMessages.push({
|
|
78
|
+
content: options.content,
|
|
79
|
+
requestId: resolvedId,
|
|
80
|
+
});
|
|
78
81
|
return { queued: false, requestId: resolvedId };
|
|
79
82
|
},
|
|
80
83
|
getQueueDepth: () => 0,
|
|
@@ -63,9 +63,12 @@ function createMockContext(
|
|
|
63
63
|
hostCuProxy: undefined,
|
|
64
64
|
hasNoClient: overrides?.hasNoClient ?? false,
|
|
65
65
|
isProcessing: () => false,
|
|
66
|
-
enqueueMessage: (
|
|
67
|
-
const resolvedId = requestId ?? "mock-request-id";
|
|
68
|
-
enqueuedMessages.push({
|
|
66
|
+
enqueueMessage: (options) => {
|
|
67
|
+
const resolvedId = options.requestId ?? "mock-request-id";
|
|
68
|
+
enqueuedMessages.push({
|
|
69
|
+
content: options.content,
|
|
70
|
+
requestId: resolvedId,
|
|
71
|
+
});
|
|
69
72
|
return { queued: false, requestId: resolvedId };
|
|
70
73
|
},
|
|
71
74
|
getQueueDepth: () => 0,
|
|
@@ -45,9 +45,9 @@ function makeContext(opts?: {
|
|
|
45
45
|
surfaceActionRequestIds: new Set<string>(),
|
|
46
46
|
currentTurnSurfaces: [],
|
|
47
47
|
isProcessing: () => false,
|
|
48
|
-
enqueueMessage: (
|
|
49
|
-
const resolvedId = requestId ?? "mock-request-id";
|
|
50
|
-
enqueueCalls.push({ content, requestId: resolvedId });
|
|
48
|
+
enqueueMessage: (options) => {
|
|
49
|
+
const resolvedId = options.requestId ?? "mock-request-id";
|
|
50
|
+
enqueueCalls.push({ content: options.content, requestId: resolvedId });
|
|
51
51
|
return { queued: false, requestId: resolvedId };
|
|
52
52
|
},
|
|
53
53
|
getQueueDepth: () => 0,
|
|
@@ -64,25 +64,15 @@ function makeContext(): SurfaceConversationContext & {
|
|
|
64
64
|
surfaceActionRequestIds: new Set<string>(),
|
|
65
65
|
currentTurnSurfaces: [],
|
|
66
66
|
isProcessing: () => false,
|
|
67
|
-
enqueueMessage: (
|
|
68
|
-
content,
|
|
69
|
-
attachments,
|
|
70
|
-
_onEvent,
|
|
71
|
-
requestId,
|
|
72
|
-
surfaceId,
|
|
73
|
-
_currentPage,
|
|
74
|
-
_metadata,
|
|
75
|
-
_options,
|
|
76
|
-
displayContent,
|
|
77
|
-
) => {
|
|
67
|
+
enqueueMessage: (options) => {
|
|
78
68
|
enqueueCalls.push({
|
|
79
|
-
content,
|
|
80
|
-
requestId: requestId ?? "enq-req",
|
|
81
|
-
attachments,
|
|
82
|
-
surfaceId,
|
|
83
|
-
displayContent,
|
|
69
|
+
content: options.content,
|
|
70
|
+
requestId: options.requestId ?? "enq-req",
|
|
71
|
+
attachments: options.attachments ?? [],
|
|
72
|
+
surfaceId: options.activeSurfaceId,
|
|
73
|
+
displayContent: options.displayContent,
|
|
84
74
|
});
|
|
85
|
-
return { queued: false, requestId: requestId ?? "enq-req" };
|
|
75
|
+
return { queued: false, requestId: options.requestId ?? "enq-req" };
|
|
86
76
|
},
|
|
87
77
|
getQueueDepth: () => 0,
|
|
88
78
|
processMessage: async (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
1
3
|
import { afterAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
4
|
|
|
3
5
|
import {
|
|
@@ -8,14 +10,15 @@ import {
|
|
|
8
10
|
projectAssistantMessage,
|
|
9
11
|
recordConversationSeenSignal,
|
|
10
12
|
} from "../memory/conversation-attention-store.js";
|
|
11
|
-
import { createConversation } from "../memory/conversation-crud.js";
|
|
12
|
-
import { getDb
|
|
13
|
+
import { addMessage, createConversation } from "../memory/conversation-crud.js";
|
|
14
|
+
import { getDb } from "../memory/db-connection.js";
|
|
13
15
|
import { initializeDb } from "../memory/db-init.js";
|
|
14
16
|
import type { AssistantEvent } from "../runtime/assistant-event.js";
|
|
15
17
|
import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
16
18
|
import { ROUTES as CONVERSATION_LIST_ROUTES } from "../runtime/routes/conversation-list-routes.js";
|
|
17
19
|
import { ROUTES as CONVERSATION_MANAGEMENT_ROUTES } from "../runtime/routes/conversation-management-routes.js";
|
|
18
20
|
import type { RouteDefinition } from "../runtime/routes/types.js";
|
|
21
|
+
import { resetDbForTesting } from "./db-test-helpers.js";
|
|
19
22
|
import { waitFor } from "./helpers/wait-for.js";
|
|
20
23
|
|
|
21
24
|
initializeDb();
|
|
@@ -70,7 +73,7 @@ describe("conversation sync tags", () => {
|
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
afterAll(() => {
|
|
73
|
-
|
|
76
|
+
resetDbForTesting();
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
test("rename emits the typed title event and a metadata-only sync tag (no list umbrella)", async () => {
|
|
@@ -107,9 +110,9 @@ describe("conversation sync tags", () => {
|
|
|
107
110
|
// Defense-in-depth: the umbrella `conversationsList` tag would force
|
|
108
111
|
// web to redrain the full paginated list — we deliberately omit it
|
|
109
112
|
// for content-only reasons.
|
|
110
|
-
expect(
|
|
111
|
-
|
|
112
|
-
)
|
|
113
|
+
expect((received[1]!.message as { tags: string[] }).tags).not.toContain(
|
|
114
|
+
SYNC_TAGS.conversationsList,
|
|
115
|
+
);
|
|
113
116
|
// The legacy invalidation broadcast is macOS-scoped and must not
|
|
114
117
|
// reach this process-type subscriber.
|
|
115
118
|
expect(
|
|
@@ -119,6 +122,22 @@ describe("conversation sync tags", () => {
|
|
|
119
122
|
).toBe(false);
|
|
120
123
|
});
|
|
121
124
|
|
|
125
|
+
test("agent-loop title updates emit metadata-only sync tags (no list umbrella)", () => {
|
|
126
|
+
const source = readFileSync(
|
|
127
|
+
join(import.meta.dir, "..", "daemon", "conversation-agent-loop.ts"),
|
|
128
|
+
"utf-8",
|
|
129
|
+
);
|
|
130
|
+
const titleUpdateBlocks =
|
|
131
|
+
source.match(
|
|
132
|
+
/type: "conversation_title_updated"[\s\S]{0,500}?type: "sync_changed"[\s\S]{0,250}?tags: \[[\s\S]*?\]/g,
|
|
133
|
+
) ?? [];
|
|
134
|
+
|
|
135
|
+
expect(titleUpdateBlocks.length).toBeGreaterThanOrEqual(2);
|
|
136
|
+
for (const block of titleUpdateBlocks) {
|
|
137
|
+
expect(block).not.toContain("SYNC_TAGS.conversationsList");
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
122
141
|
test("create emits a sync_changed with the conversationsList umbrella tag", async () => {
|
|
123
142
|
// Create is shape-changing — a row is added to the paginated list,
|
|
124
143
|
// so web must redrain the list (cannot patch a row it has never
|
|
@@ -246,9 +265,9 @@ describe("conversation sync tags", () => {
|
|
|
246
265
|
// Defense-in-depth: the umbrella `conversationsList` tag would
|
|
247
266
|
// force the very paginated-list drain this redesign exists to
|
|
248
267
|
// avoid. It must not appear here.
|
|
249
|
-
expect(
|
|
250
|
-
|
|
251
|
-
)
|
|
268
|
+
expect((received[0]!.message as { tags: string[] }).tags).not.toContain(
|
|
269
|
+
SYNC_TAGS.conversationsList,
|
|
270
|
+
);
|
|
252
271
|
expect(
|
|
253
272
|
received.some(
|
|
254
273
|
(event) => event.message.type === "conversation_list_invalidated",
|
|
@@ -290,13 +309,110 @@ describe("conversation sync tags", () => {
|
|
|
290
309
|
type: "sync_changed",
|
|
291
310
|
tags: [conversationMetadataSyncTag(conversation.id)],
|
|
292
311
|
});
|
|
293
|
-
expect(
|
|
294
|
-
|
|
295
|
-
)
|
|
312
|
+
expect((received[0]!.message as { tags: string[] }).tags).not.toContain(
|
|
313
|
+
SYNC_TAGS.conversationsList,
|
|
314
|
+
);
|
|
296
315
|
expect(
|
|
297
316
|
received.some(
|
|
298
317
|
(event) => event.message.type === "conversation_list_invalidated",
|
|
299
318
|
),
|
|
300
319
|
).toBe(false);
|
|
301
320
|
});
|
|
321
|
+
|
|
322
|
+
test("addMessage('assistant') emits a metadata sync tag when attention state transitions", async () => {
|
|
323
|
+
// When an assistant message transitions a conversation from seen to
|
|
324
|
+
// unseen, `addMessage` emits `conversation:<id>:metadata` so the web
|
|
325
|
+
// sidebar picks up the attention state change without a full list
|
|
326
|
+
// refetch. This is the fix for LUM-1907: background processes that
|
|
327
|
+
// add assistant messages (notification delivery, proactive artifacts)
|
|
328
|
+
// now automatically notify clients.
|
|
329
|
+
const conversation = createConversation("Attention sync");
|
|
330
|
+
|
|
331
|
+
const received = await captureEvents(async () => {
|
|
332
|
+
await addMessage(
|
|
333
|
+
conversation.id,
|
|
334
|
+
"assistant",
|
|
335
|
+
JSON.stringify([{ type: "text", text: "hello" }]),
|
|
336
|
+
);
|
|
337
|
+
}, 1);
|
|
338
|
+
|
|
339
|
+
expect(received.map((event) => event.message.type)).toEqual([
|
|
340
|
+
"sync_changed",
|
|
341
|
+
]);
|
|
342
|
+
expect(received[0]!.message).toEqual({
|
|
343
|
+
type: "sync_changed",
|
|
344
|
+
tags: [conversationMetadataSyncTag(conversation.id)],
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
test("addMessage('assistant') does not emit a metadata sync tag when already unseen", async () => {
|
|
349
|
+
// When the conversation is already unseen (attention cursor was
|
|
350
|
+
// already past the seen cursor), a subsequent assistant message
|
|
351
|
+
// should NOT emit a metadata tag — no state transition occurred.
|
|
352
|
+
const conversation = createConversation("Already unseen");
|
|
353
|
+
await addMessage(
|
|
354
|
+
conversation.id,
|
|
355
|
+
"assistant",
|
|
356
|
+
JSON.stringify([{ type: "text", text: "first" }]),
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
const received: AssistantEvent[] = [];
|
|
360
|
+
const subscription = assistantEventHub.subscribe({
|
|
361
|
+
type: "process",
|
|
362
|
+
callback: (event) => {
|
|
363
|
+
received.push(event);
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
try {
|
|
367
|
+
await addMessage(
|
|
368
|
+
conversation.id,
|
|
369
|
+
"assistant",
|
|
370
|
+
JSON.stringify([{ type: "text", text: "second" }]),
|
|
371
|
+
);
|
|
372
|
+
// Brief wait to ensure no event is emitted
|
|
373
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
374
|
+
const metadataEvents = received.filter(
|
|
375
|
+
(event) =>
|
|
376
|
+
event.message.type === "sync_changed" &&
|
|
377
|
+
(event.message as { tags: string[] }).tags.some((tag: string) =>
|
|
378
|
+
tag.includes(":metadata"),
|
|
379
|
+
),
|
|
380
|
+
);
|
|
381
|
+
expect(metadataEvents).toHaveLength(0);
|
|
382
|
+
} finally {
|
|
383
|
+
subscription.dispose();
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test("addMessage('user') does not emit a metadata sync tag", async () => {
|
|
388
|
+
// User messages never affect attention state — only assistant
|
|
389
|
+
// messages advance the attention cursor via projectAssistantMessage.
|
|
390
|
+
const conversation = createConversation("User message");
|
|
391
|
+
|
|
392
|
+
const received: AssistantEvent[] = [];
|
|
393
|
+
const subscription = assistantEventHub.subscribe({
|
|
394
|
+
type: "process",
|
|
395
|
+
callback: (event) => {
|
|
396
|
+
received.push(event);
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
try {
|
|
400
|
+
await addMessage(
|
|
401
|
+
conversation.id,
|
|
402
|
+
"user",
|
|
403
|
+
JSON.stringify([{ type: "text", text: "hello" }]),
|
|
404
|
+
);
|
|
405
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
406
|
+
const metadataEvents = received.filter(
|
|
407
|
+
(event) =>
|
|
408
|
+
event.message.type === "sync_changed" &&
|
|
409
|
+
(event.message as { tags: string[] }).tags.some((tag: string) =>
|
|
410
|
+
tag.includes(":metadata"),
|
|
411
|
+
),
|
|
412
|
+
);
|
|
413
|
+
expect(metadataEvents).toHaveLength(0);
|
|
414
|
+
} finally {
|
|
415
|
+
subscription.dispose();
|
|
416
|
+
}
|
|
417
|
+
});
|
|
302
418
|
});
|
|
@@ -37,6 +37,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
37
37
|
getConversation: mockGetConversation,
|
|
38
38
|
getMessages: mockGetMessages,
|
|
39
39
|
updateConversationTitle: mockUpdateConversationTitle,
|
|
40
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
40
41
|
}));
|
|
41
42
|
|
|
42
43
|
mock.module("../providers/provider-send-message.js", () => ({
|
|
@@ -295,6 +295,36 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
295
295
|
});
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
+
test("canonicalizes legacy computer_use_press_key skill_execute alias before dispatch", async () => {
|
|
299
|
+
const ctx = makeCtx({ allowedToolNames: new Set(["computer_use_key"]) });
|
|
300
|
+
const executor = makeFakeExecutor({ content: "ok", isError: false });
|
|
301
|
+
|
|
302
|
+
const toolFn = createToolExecutor(
|
|
303
|
+
executor as unknown as ToolExecutor,
|
|
304
|
+
noopPrompter,
|
|
305
|
+
noopSecretPrompter,
|
|
306
|
+
ctx,
|
|
307
|
+
noopLifecycleHandler,
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
await toolFn("skill_execute", {
|
|
311
|
+
tool: "computer_use_press_key",
|
|
312
|
+
input: {
|
|
313
|
+
key: "Space",
|
|
314
|
+
modifiers: ["Command"],
|
|
315
|
+
reasoning: "Open Spotlight",
|
|
316
|
+
},
|
|
317
|
+
activity: "Opening Spotlight",
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const calls = executor.execute.mock.calls as unknown[][];
|
|
321
|
+
expect(calls[0][0]).toBe("computer_use_key");
|
|
322
|
+
expect(calls[0][1]).toEqual({
|
|
323
|
+
key: "cmd+space",
|
|
324
|
+
reasoning: "Open Spotlight",
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
298
328
|
test("preserves exact active create_app skill tool when app_create is also active", async () => {
|
|
299
329
|
const ctx = makeCtx({
|
|
300
330
|
allowedToolNames: new Set(["create_app", "app_create"]),
|
|
@@ -111,6 +111,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
111
111
|
updateConversationContextWindow: () => {},
|
|
112
112
|
deleteMessageById: () => ({ segmentIds: [], deletedSummaryIds: [] }),
|
|
113
113
|
deleteLastExchange: () => 0,
|
|
114
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
114
115
|
}));
|
|
115
116
|
|
|
116
117
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -145,6 +145,8 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
145
145
|
setLastNotifiedInferenceProfile: () => {},
|
|
146
146
|
getConversationOverrideProfileFromRow: () => undefined,
|
|
147
147
|
updateMessageMetadata: () => {},
|
|
148
|
+
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
149
|
+
updateMessageContent: mock(() => {}),
|
|
148
150
|
}));
|
|
149
151
|
|
|
150
152
|
mock.module("../memory/conversation-queries.js", () => ({
|
|
@@ -244,6 +246,9 @@ mock.module("../agent/loop.js", () => ({
|
|
|
244
246
|
messages: Message[],
|
|
245
247
|
onEvent: (event: AgentEvent) => void,
|
|
246
248
|
): Promise<Message[]> {
|
|
249
|
+
// Prime the assistant row anchor — production code emits this from
|
|
250
|
+
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
251
|
+
await onEvent({ type: "llm_call_started" });
|
|
247
252
|
runCalls.push(messages);
|
|
248
253
|
agentLoopScript(onEvent);
|
|
249
254
|
onEvent({
|