@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- 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__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The key store attributes a channel conversation when it materializes it.
|
|
3
|
+
*
|
|
4
|
+
* This is the principal inbound seam: a new Slack, Telegram, or phone
|
|
5
|
+
* conversation gets its row here, before the daemon's own creation path is
|
|
6
|
+
* ever consulted. Stamping only in `createConversation` therefore misses the
|
|
7
|
+
* case that matters most, which is why these cases exist separately.
|
|
8
|
+
*
|
|
9
|
+
* The trust assertion at the end is the one worth protecting. `origin_channel`
|
|
10
|
+
* is what `recoverRestingTrustContext` reads, and the native channel recovers
|
|
11
|
+
* INTERNAL_GUARDIAN_TRUST_CONTEXT on every later wake and boot-resume. A
|
|
12
|
+
* remote conversation that reached this seam unattributed, or attributed as
|
|
13
|
+
* native, would resume as the guardian's own.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
import { recoverRestingTrustContext } from "../daemon/conversation-resting-trust.js";
|
|
19
|
+
import { setConversationOriginChannelIfUnset } from "../persistence/conversation-crud.js";
|
|
20
|
+
import { getOrCreateConversation } from "../persistence/conversation-key-store.js";
|
|
21
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
22
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
23
|
+
import { conversations } from "../persistence/schema/index.js";
|
|
24
|
+
|
|
25
|
+
await initializeDb();
|
|
26
|
+
|
|
27
|
+
function rawOriginChannel(id: string): string | null {
|
|
28
|
+
const row = getDb()
|
|
29
|
+
.all(`SELECT origin_channel FROM conversations WHERE id = '${id}'`)
|
|
30
|
+
.at(0) as { origin_channel: string | null } | undefined;
|
|
31
|
+
return row?.origin_channel ?? null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
getDb().delete(conversations).run();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("materializing a conversation from a channel key", () => {
|
|
39
|
+
test("stamps the channel at creation, before any message", () => {
|
|
40
|
+
const { conversationId, created } = getOrCreateConversation(
|
|
41
|
+
"default:slack:slack",
|
|
42
|
+
{ origin: "slack" },
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(created).toBe(true);
|
|
46
|
+
expect(rawOriginChannel(conversationId)).toBe("slack");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("the first message does not change what creation established", () => {
|
|
50
|
+
// Attribution is guarded on `IS NULL`, so a row attributed at creation is
|
|
51
|
+
// never re-claimed. Before and after the first message agree.
|
|
52
|
+
const { conversationId } = getOrCreateConversation("default:slack:slack", {
|
|
53
|
+
origin: "slack",
|
|
54
|
+
});
|
|
55
|
+
const atCreation = rawOriginChannel(conversationId);
|
|
56
|
+
|
|
57
|
+
setConversationOriginChannelIfUnset(conversationId, "telegram");
|
|
58
|
+
|
|
59
|
+
expect(rawOriginChannel(conversationId)).toBe(atCreation);
|
|
60
|
+
expect(rawOriginChannel(conversationId)).toBe("slack");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("reusing an existing key does not re-attribute it", () => {
|
|
64
|
+
const first = getOrCreateConversation("default:slack:slack", {
|
|
65
|
+
origin: "slack",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const second = getOrCreateConversation("default:slack:slack", {
|
|
69
|
+
origin: "vellum",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(second.created).toBe(false);
|
|
73
|
+
expect(second.conversationId).toBe(first.conversationId);
|
|
74
|
+
expect(rawOriginChannel(first.conversationId)).toBe("slack");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("a caller that states no origin leaves the column unset", () => {
|
|
78
|
+
const { conversationId } = getOrCreateConversation("some:other:key");
|
|
79
|
+
|
|
80
|
+
expect(rawOriginChannel(conversationId)).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("what the attribution protects", () => {
|
|
85
|
+
test("a channel conversation recovers no resting trust", () => {
|
|
86
|
+
// The property the whole change exists to hold. Driven through the
|
|
87
|
+
// creation seam rather than a raw column write, so it fails if creation
|
|
88
|
+
// ever stops attributing the row.
|
|
89
|
+
const { conversationId } = getOrCreateConversation("default:slack:slack", {
|
|
90
|
+
origin: "slack",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(recoverRestingTrustContext(conversationId)).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("a native conversation still recovers guardian trust", () => {
|
|
97
|
+
// The other half: attributing at creation must not withdraw trust from
|
|
98
|
+
// the guardian's own conversations, or every local wake regresses.
|
|
99
|
+
const { conversationId } = getOrCreateConversation("native-key", {
|
|
100
|
+
origin: "vellum",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(recoverRestingTrustContext(conversationId)).not.toBeNull();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { createConversation } from "../persistence/conversation-crud.js";
|
|
5
|
+
import { ensureGroupMigration } from "../persistence/conversation-group-migration.js";
|
|
6
|
+
import {
|
|
7
|
+
listConversations,
|
|
8
|
+
listPinnedConversations,
|
|
9
|
+
} from "../persistence/conversation-queries.js";
|
|
10
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
11
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
12
|
+
import { createGroup } from "../persistence/group-crud.js";
|
|
13
|
+
|
|
14
|
+
await initializeDb();
|
|
15
|
+
ensureGroupMigration();
|
|
16
|
+
|
|
17
|
+
function getRawDb(): Database {
|
|
18
|
+
return (getDb() as unknown as { $client: Database }).$client;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Every conversation list orders by recency, pinned and custom groups
|
|
23
|
+
* included.
|
|
24
|
+
*
|
|
25
|
+
* `display_order` is a live column and some rows carry a value in it, so
|
|
26
|
+
* "nothing writes it" is not enough to make the order consistent: a read that
|
|
27
|
+
* consulted it would serve those rows in an arrangement no user can change,
|
|
28
|
+
* while every other surface shows recency.
|
|
29
|
+
*
|
|
30
|
+
* Each test sets `display_order` to contradict recency, so a read that
|
|
31
|
+
* consults it fails here rather than passing by coincidence.
|
|
32
|
+
*/
|
|
33
|
+
describe("conversation list ordering ignores display_order", () => {
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
const raw = getRawDb();
|
|
36
|
+
raw.run("DELETE FROM conversations");
|
|
37
|
+
raw.run("DELETE FROM conversation_groups WHERE is_system_group = 0");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/** Give a row a `display_order` and a recency stamp that disagree. */
|
|
41
|
+
function arrange(id: string, displayOrder: number, lastMessageAt: number) {
|
|
42
|
+
getRawDb().run(
|
|
43
|
+
"UPDATE conversations SET display_order = ?, last_message_at = ? WHERE id = ?",
|
|
44
|
+
[displayOrder, lastMessageAt, id],
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test("a custom group orders by recency, ignoring display_order", () => {
|
|
49
|
+
const groupId = createGroup("Briefs").id;
|
|
50
|
+
createConversation({ id: "conv-old", source: "user", groupId });
|
|
51
|
+
createConversation({ id: "conv-new", source: "user", groupId });
|
|
52
|
+
// `display_order` says old-then-new; recency says the opposite.
|
|
53
|
+
arrange("conv-old", 0, 1_000);
|
|
54
|
+
arrange("conv-new", 1, 9_000);
|
|
55
|
+
|
|
56
|
+
expect(listConversations({ groupId }).map((c) => c.id)).toEqual([
|
|
57
|
+
"conv-new",
|
|
58
|
+
"conv-old",
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("the pinned group orders by recency, ignoring display_order", () => {
|
|
63
|
+
createConversation({
|
|
64
|
+
id: "pin-old",
|
|
65
|
+
source: "user",
|
|
66
|
+
groupId: "system:pinned",
|
|
67
|
+
});
|
|
68
|
+
createConversation({
|
|
69
|
+
id: "pin-new",
|
|
70
|
+
source: "user",
|
|
71
|
+
groupId: "system:pinned",
|
|
72
|
+
});
|
|
73
|
+
arrange("pin-old", 0, 1_000);
|
|
74
|
+
arrange("pin-new", 1, 9_000);
|
|
75
|
+
|
|
76
|
+
expect(
|
|
77
|
+
listConversations({ groupId: "system:pinned" }).map((c) => c.id),
|
|
78
|
+
).toEqual(["pin-new", "pin-old"]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/* The page-one pinned injection reads through its own query, so it needs
|
|
82
|
+
the same guarantee: one section must not have two orderings depending on
|
|
83
|
+
which read served it. */
|
|
84
|
+
test("listPinnedConversations orders by recency too", () => {
|
|
85
|
+
createConversation({
|
|
86
|
+
id: "pin-old",
|
|
87
|
+
source: "user",
|
|
88
|
+
groupId: "system:pinned",
|
|
89
|
+
});
|
|
90
|
+
createConversation({
|
|
91
|
+
id: "pin-new",
|
|
92
|
+
source: "user",
|
|
93
|
+
groupId: "system:pinned",
|
|
94
|
+
});
|
|
95
|
+
getRawDb().run("UPDATE conversations SET is_pinned = 1");
|
|
96
|
+
arrange("pin-old", 0, 1_000);
|
|
97
|
+
arrange("pin-new", 1, 9_000);
|
|
98
|
+
|
|
99
|
+
expect(listPinnedConversations().map((c) => c.id)).toEqual([
|
|
100
|
+
"pin-new",
|
|
101
|
+
"pin-old",
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation provenance is stated at creation.
|
|
3
|
+
*
|
|
4
|
+
* `createConversation` accepts the origin so the row is attributed from the
|
|
5
|
+
* moment it exists, rather than being patched afterward by
|
|
6
|
+
* `setConversationOriginChannelIfUnset` on the first inbound message. See
|
|
7
|
+
* JARVIS-1466 for why the post-hoc path leaves the column in a third
|
|
8
|
+
* "not yet attributed" state that different readers resolve in opposite
|
|
9
|
+
* directions.
|
|
10
|
+
*
|
|
11
|
+
* The omitted-origin case is pinned deliberately: while call sites are being
|
|
12
|
+
* migrated, omitting the argument has to behave exactly as it did before the
|
|
13
|
+
* parameter existed, or migrating them stops being safe to do piecemeal.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
createConversation,
|
|
20
|
+
getConversationOriginChannel,
|
|
21
|
+
setConversationOriginChannelIfUnset,
|
|
22
|
+
} from "../persistence/conversation-crud.js";
|
|
23
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
24
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
25
|
+
import { conversations } from "../persistence/schema/index.js";
|
|
26
|
+
|
|
27
|
+
await initializeDb();
|
|
28
|
+
|
|
29
|
+
/** The raw column, not the parsed accessor, so NULL stays distinguishable. */
|
|
30
|
+
function rawOriginChannel(id: string): string | null {
|
|
31
|
+
const row = getDb()
|
|
32
|
+
.all(`SELECT origin_channel FROM conversations WHERE id = '${id}'`)
|
|
33
|
+
.at(0) as { origin_channel: string | null } | undefined;
|
|
34
|
+
return row?.origin_channel ?? null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
getDb().delete(conversations).run();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("stating the origin at creation", () => {
|
|
42
|
+
test("stamps a native conversation", () => {
|
|
43
|
+
const conv = createConversation({ title: "in-app", origin: "vellum" });
|
|
44
|
+
|
|
45
|
+
expect(rawOriginChannel(conv.id)).toBe("vellum");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("stamps an external channel", () => {
|
|
49
|
+
const conv = createConversation({ title: "from-slack", origin: "slack" });
|
|
50
|
+
|
|
51
|
+
expect(rawOriginChannel(conv.id)).toBe("slack");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("a stamped row is attributed before any message arrives", () => {
|
|
55
|
+
// The point of the change: the row is already correct, so nothing has to
|
|
56
|
+
// run afterward to make it so.
|
|
57
|
+
const conv = createConversation({ title: "from-slack", origin: "slack" });
|
|
58
|
+
|
|
59
|
+
expect(getConversationOriginChannel(conv.id)).toBe("slack");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("inheriting the origin from a parent", () => {
|
|
64
|
+
test("a fork lands on its parent's channel", () => {
|
|
65
|
+
const parent = createConversation({ title: "parent", origin: "telegram" });
|
|
66
|
+
|
|
67
|
+
const fork = createConversation({
|
|
68
|
+
title: "fork",
|
|
69
|
+
origin: { inheritFrom: parent.id },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(rawOriginChannel(fork.id)).toBe("telegram");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("inheriting from a native parent stays native", () => {
|
|
76
|
+
const parent = createConversation({ title: "parent", origin: "vellum" });
|
|
77
|
+
|
|
78
|
+
const fork = createConversation({
|
|
79
|
+
title: "fork",
|
|
80
|
+
origin: { inheritFrom: parent.id },
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(rawOriginChannel(fork.id)).toBe("vellum");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("a parent whose own origin is unset yields an unset origin", () => {
|
|
87
|
+
// Faithful inheritance, not an error. Most rows sit unattributed until a
|
|
88
|
+
// message claims them or startup sweeps them, so treating this as a
|
|
89
|
+
// failure would break the majority of forks and subagent spawns.
|
|
90
|
+
const parent = createConversation({ title: "unattributed-parent" });
|
|
91
|
+
|
|
92
|
+
const fork = createConversation({
|
|
93
|
+
title: "fork",
|
|
94
|
+
origin: { inheritFrom: parent.id },
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(rawOriginChannel(fork.id)).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("a parent that does not exist fails the insert rather than defaulting", () => {
|
|
101
|
+
// Defaulting here would be a trust grant, not a cosmetic guess:
|
|
102
|
+
// `recoverRestingTrustContext` recovers INTERNAL_GUARDIAN_TRUST_CONTEXT
|
|
103
|
+
// for the native channel, so a fork of a remote conversation whose parent
|
|
104
|
+
// we failed to read would come back as the guardian's own on every later
|
|
105
|
+
// wake and boot-resume.
|
|
106
|
+
expect(() =>
|
|
107
|
+
createConversation({
|
|
108
|
+
title: "orphan",
|
|
109
|
+
origin: { inheritFrom: "no-such-conversation" },
|
|
110
|
+
}),
|
|
111
|
+
).toThrow(/Cannot inherit conversation origin/);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("a remote parent's fork does not become native", () => {
|
|
115
|
+
// The property that matters for trust, stated directly.
|
|
116
|
+
const parent = createConversation({ title: "parent", origin: "slack" });
|
|
117
|
+
|
|
118
|
+
const fork = createConversation({
|
|
119
|
+
title: "fork",
|
|
120
|
+
origin: { inheritFrom: parent.id },
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(rawOriginChannel(fork.id)).not.toBe("vellum");
|
|
124
|
+
expect(rawOriginChannel(fork.id)).toBe("slack");
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("callers that have not been migrated yet", () => {
|
|
129
|
+
test("omitting the origin leaves the column unset", () => {
|
|
130
|
+
const conv = createConversation({ title: "unmigrated" });
|
|
131
|
+
|
|
132
|
+
expect(rawOriginChannel(conv.id)).toBeNull();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("attribution still claims an unstamped row", () => {
|
|
136
|
+
const conv = createConversation({ title: "unmigrated" });
|
|
137
|
+
|
|
138
|
+
setConversationOriginChannelIfUnset(conv.id, "slack");
|
|
139
|
+
|
|
140
|
+
expect(rawOriginChannel(conv.id)).toBe("slack");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("attribution does not overwrite an origin stated at creation", () => {
|
|
144
|
+
// The `isNull` guard is what makes stamping and attribution able to
|
|
145
|
+
// coexist during the migration: a row that already knows its origin is
|
|
146
|
+
// never re-claimed by a later message.
|
|
147
|
+
const conv = createConversation({ title: "from-slack", origin: "slack" });
|
|
148
|
+
|
|
149
|
+
setConversationOriginChannelIfUnset(conv.id, "telegram");
|
|
150
|
+
|
|
151
|
+
expect(rawOriginChannel(conv.id)).toBe("slack");
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { createConversation } from "../persistence/conversation-crud.js";
|
|
5
|
+
import { ensureGroupMigration } from "../persistence/conversation-group-migration.js";
|
|
6
|
+
import { listConversations } from "../persistence/conversation-queries.js";
|
|
7
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
8
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
9
|
+
|
|
10
|
+
await initializeDb();
|
|
11
|
+
ensureGroupMigration();
|
|
12
|
+
|
|
13
|
+
function getRawDb(): Database {
|
|
14
|
+
return (getDb() as unknown as { $client: Database }).$client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* `originChannel` is what lets each sidebar channel card fetch its own rows,
|
|
19
|
+
* and what lets the Chats card ask for the ones no channel claimed.
|
|
20
|
+
*
|
|
21
|
+
* The asymmetry under test: `origin_channel` is deliberately NULL at insert so
|
|
22
|
+
* an inbound message can claim the conversation for its channel, and migration
|
|
23
|
+
* 288 settles the unclaimed ones to 'vellum' at daemon startup. So NULL means
|
|
24
|
+
* "not yet attributed", and between one boot and the next it is what most rows
|
|
25
|
+
* carry. Reading `vellum` strictly would leave every one of them in no section
|
|
26
|
+
* at all: not native, not any channel.
|
|
27
|
+
*/
|
|
28
|
+
describe("listConversations originChannel filter", () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
const raw = getRawDb();
|
|
31
|
+
raw.run("DELETE FROM conversations");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** Set `origin_channel` directly; `createConversation` leaves it unset. */
|
|
35
|
+
function stamp(id: string, originChannel: string | null): void {
|
|
36
|
+
getRawDb().run("UPDATE conversations SET origin_channel = ? WHERE id = ?", [
|
|
37
|
+
originChannel,
|
|
38
|
+
id,
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function listedIds(originChannel: string): string[] {
|
|
43
|
+
return listConversations({ originChannel } as Parameters<
|
|
44
|
+
typeof listConversations
|
|
45
|
+
>[0]).map((c) => c.id);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test("vellum matches rows that were never attributed", () => {
|
|
49
|
+
createConversation({ id: "conv-unattributed", source: "user" });
|
|
50
|
+
stamp("conv-unattributed", null);
|
|
51
|
+
|
|
52
|
+
expect(listedIds("vellum")).toEqual(["conv-unattributed"]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("vellum matches rows explicitly stamped vellum", () => {
|
|
56
|
+
createConversation({ id: "conv-native", source: "user" });
|
|
57
|
+
stamp("conv-native", "vellum");
|
|
58
|
+
|
|
59
|
+
expect(listedIds("vellum")).toEqual(["conv-native"]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/* The whole point: an unattributed row and a stamped native row are the same
|
|
63
|
+
section. Asserting both are returned together, rather than each alone,
|
|
64
|
+
is what fails if the predicate goes back to a strict equality. */
|
|
65
|
+
test("vellum returns unattributed and stamped rows together", () => {
|
|
66
|
+
createConversation({ id: "conv-native", source: "user" });
|
|
67
|
+
stamp("conv-native", "vellum");
|
|
68
|
+
createConversation({ id: "conv-unattributed", source: "user" });
|
|
69
|
+
stamp("conv-unattributed", null);
|
|
70
|
+
|
|
71
|
+
expect(listedIds("vellum").sort()).toEqual([
|
|
72
|
+
"conv-native",
|
|
73
|
+
"conv-unattributed",
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("vellum does not claim a row belonging to a channel", () => {
|
|
78
|
+
createConversation({ id: "conv-slack", source: "user" });
|
|
79
|
+
stamp("conv-slack", "slack");
|
|
80
|
+
|
|
81
|
+
expect(listedIds("vellum")).toEqual([]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
/* Only `vellum` is tolerant. A row is claimed for a channel by that channel
|
|
85
|
+
alone, so there is no ambiguity for the others to absorb - and a tolerant
|
|
86
|
+
`slack` would pull every unattributed row into the Slack card. */
|
|
87
|
+
test("a channel matches exactly and never absorbs unattributed rows", () => {
|
|
88
|
+
createConversation({ id: "conv-slack", source: "user" });
|
|
89
|
+
stamp("conv-slack", "slack");
|
|
90
|
+
createConversation({ id: "conv-unattributed", source: "user" });
|
|
91
|
+
stamp("conv-unattributed", null);
|
|
92
|
+
|
|
93
|
+
expect(listedIds("slack")).toEqual(["conv-slack"]);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filing a background or scheduled conversation into a section the user reads
|
|
3
|
+
* promotes it durably.
|
|
4
|
+
*
|
|
5
|
+
* Placement into Pinned or a custom group stamps `surfaced_at`, so the
|
|
6
|
+
* promotion outlives the placement that caused it: taking the conversation
|
|
7
|
+
* back out returns it to Recents rather than hiding it. Demotion is the
|
|
8
|
+
* mirror image, and filing into `system:background` / `system:scheduled`
|
|
9
|
+
* clears the stamp.
|
|
10
|
+
*
|
|
11
|
+
* The cases below pin both edges. Under-promoting hides a conversation the
|
|
12
|
+
* user deliberately filed somewhere; over-promoting drags hidden automation
|
|
13
|
+
* into the sidebar, so the "what promotion must not do" block is as
|
|
14
|
+
* load-bearing as the rest.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
batchSetConversationPlacement,
|
|
21
|
+
createConversation,
|
|
22
|
+
} from "../persistence/conversation-crud.js";
|
|
23
|
+
import { listConversations } from "../persistence/conversation-queries.js";
|
|
24
|
+
import { getDb } from "../persistence/db-connection.js";
|
|
25
|
+
import { initializeDb } from "../persistence/db-init.js";
|
|
26
|
+
import { createGroup } from "../persistence/group-crud.js";
|
|
27
|
+
import { rawRun } from "../persistence/raw-query.js";
|
|
28
|
+
import { conversations } from "../persistence/schema/index.js";
|
|
29
|
+
|
|
30
|
+
await initializeDb();
|
|
31
|
+
|
|
32
|
+
function seedRoutedBackground(title: string, type: "background" | "scheduled") {
|
|
33
|
+
const conv = createConversation({ title, conversationType: type });
|
|
34
|
+
rawRun(
|
|
35
|
+
"test:routeToSystemBucket",
|
|
36
|
+
"UPDATE conversations SET group_id = ? WHERE id = ?",
|
|
37
|
+
type === "scheduled" ? "system:scheduled" : "system:background",
|
|
38
|
+
conv.id,
|
|
39
|
+
);
|
|
40
|
+
return conv;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Titles the sidebar's standard listing would render. */
|
|
44
|
+
function visibleTitles(): string[] {
|
|
45
|
+
return listConversations({ limit: 100 }).map((c) => c.title ?? "");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function titlesInGroup(groupId: string): string[] {
|
|
49
|
+
return listConversations({ limit: 100, groupId }).map((c) => c.title ?? "");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
getDb().delete(conversations).run();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("pinning a background conversation", () => {
|
|
57
|
+
test("shows it in Pinned", () => {
|
|
58
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
59
|
+
expect(visibleTitles()).toEqual([]);
|
|
60
|
+
|
|
61
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:pinned" }]);
|
|
62
|
+
|
|
63
|
+
expect(titlesInGroup("system:pinned")).toEqual(["bg-job"]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("unpinning lands it in Recents rather than hiding it again", () => {
|
|
67
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
68
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:pinned" }]);
|
|
69
|
+
|
|
70
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:all" }]);
|
|
71
|
+
|
|
72
|
+
expect(titlesInGroup("system:all")).toEqual(["bg-job"]);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("filing a background conversation into a custom group", () => {
|
|
77
|
+
test("shows it in that group", () => {
|
|
78
|
+
const group = createGroup("Car Chat");
|
|
79
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
80
|
+
|
|
81
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: group.id }]);
|
|
82
|
+
|
|
83
|
+
expect(titlesInGroup(group.id)).toEqual(["bg-job"]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("removing it from the group lands it in Recents rather than losing it", () => {
|
|
87
|
+
const group = createGroup("Car Chat");
|
|
88
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
89
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: group.id }]);
|
|
90
|
+
|
|
91
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:all" }]);
|
|
92
|
+
|
|
93
|
+
expect(titlesInGroup("system:all")).toEqual(["bg-job"]);
|
|
94
|
+
expect(visibleTitles()).toEqual(["bg-job"]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("applies to scheduled conversations too", () => {
|
|
98
|
+
const group = createGroup("Morning Briefs");
|
|
99
|
+
const conv = seedRoutedBackground("sched-job", "scheduled");
|
|
100
|
+
|
|
101
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: group.id }]);
|
|
102
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:all" }]);
|
|
103
|
+
|
|
104
|
+
expect(titlesInGroup("system:all")).toEqual(["sched-job"]);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("what promotion must not do", () => {
|
|
109
|
+
test("filing into system:background still demotes", () => {
|
|
110
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
111
|
+
// Promote first, so the demotion has something to undo.
|
|
112
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:pinned" }]);
|
|
113
|
+
expect(titlesInGroup("system:pinned")).toEqual(["bg-job"]);
|
|
114
|
+
|
|
115
|
+
batchSetConversationPlacement([
|
|
116
|
+
{ id: conv.id, groupId: "system:background" },
|
|
117
|
+
]);
|
|
118
|
+
|
|
119
|
+
expect(visibleTitles()).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("a background conversation nobody placed anywhere stays hidden", () => {
|
|
123
|
+
seedRoutedBackground("untouched", "background");
|
|
124
|
+
|
|
125
|
+
expect(visibleTitles()).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("moving to system:all does not by itself promote", () => {
|
|
129
|
+
// Removal is not a promotion: a row that was never filed into a section
|
|
130
|
+
// the user reads must not become visible just by being moved out of its
|
|
131
|
+
// system bucket.
|
|
132
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
133
|
+
|
|
134
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:all" }]);
|
|
135
|
+
|
|
136
|
+
expect(visibleTitles()).toEqual([]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("unpinning through the legacy isPinned path still lands in Recents", () => {
|
|
140
|
+
// Old clients send `isPinned: false` with no `groupId`, and that branch
|
|
141
|
+
// restores the row's original system bucket to keep its provenance. It
|
|
142
|
+
// deliberately does not clear the promotion: unpinning is not a demotion,
|
|
143
|
+
// so the conversation belongs in Recents afterwards, the same as the
|
|
144
|
+
// modern path that sends `groupId: "system:all"`.
|
|
145
|
+
//
|
|
146
|
+
// The row therefore sits in `system:background` while remaining visible.
|
|
147
|
+
// That is the intended split: the bucket records where the conversation
|
|
148
|
+
// came from, `surfaced_at` records that the user promoted it, and only
|
|
149
|
+
// an explicit demotion clears the latter.
|
|
150
|
+
const conv = seedRoutedBackground("bg-job", "background");
|
|
151
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: "system:pinned" }]);
|
|
152
|
+
|
|
153
|
+
batchSetConversationPlacement([{ id: conv.id, isPinned: false }]);
|
|
154
|
+
|
|
155
|
+
expect(visibleTitles()).toEqual(["bg-job"]);
|
|
156
|
+
expect(titlesInGroup("system:all")).toEqual(["bg-job"]);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("standard conversations are not stamped as surfaced", () => {
|
|
160
|
+
// They are already visible, so stamping them would leave `surfaced_at`
|
|
161
|
+
// meaning nothing.
|
|
162
|
+
const group = createGroup("Car Chat");
|
|
163
|
+
const conv = createConversation("plain-chat");
|
|
164
|
+
|
|
165
|
+
batchSetConversationPlacement([{ id: conv.id, groupId: group.id }]);
|
|
166
|
+
|
|
167
|
+
const row = getDb()
|
|
168
|
+
.all(`SELECT surfaced_at FROM conversations WHERE id = '${conv.id}'`)
|
|
169
|
+
.at(0) as { surfaced_at: number | null };
|
|
170
|
+
expect(row.surfaced_at).toBeNull();
|
|
171
|
+
});
|
|
172
|
+
});
|