@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
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 +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -472,6 +472,8 @@ mock.module("../memory/archive-store.js", () => ({
|
|
|
472
472
|
import { AgentLoop } from "../agent/loop.js";
|
|
473
473
|
import type { Conversation } from "../daemon/conversation.js";
|
|
474
474
|
import { runAgentLoopImpl } from "../daemon/conversation-agent-loop.js";
|
|
475
|
+
import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
|
|
476
|
+
import { asConversation } from "./helpers/mock-conversation.js";
|
|
475
477
|
import {
|
|
476
478
|
createMockProvider,
|
|
477
479
|
type ScriptedResponse,
|
|
@@ -519,7 +521,7 @@ function makeCtx(
|
|
|
519
521
|
toolExecutor,
|
|
520
522
|
});
|
|
521
523
|
|
|
522
|
-
const ctx = {
|
|
524
|
+
const ctx = asConversation({
|
|
523
525
|
conversationId: "test-conv",
|
|
524
526
|
messages: [
|
|
525
527
|
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
@@ -572,12 +574,7 @@ function makeCtx(
|
|
|
572
574
|
skillProjectionCache:
|
|
573
575
|
new Map() as unknown as Conversation["skillProjectionCache"],
|
|
574
576
|
|
|
575
|
-
usageStats: {
|
|
576
|
-
totalInputTokens: 0,
|
|
577
|
-
totalOutputTokens: 0,
|
|
578
|
-
totalEstimatedCost: 0,
|
|
579
|
-
model: "",
|
|
580
|
-
},
|
|
577
|
+
usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
|
|
581
578
|
turnCount: 0,
|
|
582
579
|
|
|
583
580
|
lastAssistantAttachments: [],
|
|
@@ -595,12 +592,12 @@ function makeCtx(
|
|
|
595
592
|
getQueueDepth: () => 0,
|
|
596
593
|
hasQueuedMessages: () => false,
|
|
597
594
|
canHandoffAtCheckpoint: () => false,
|
|
598
|
-
drainQueue: (_reason?:
|
|
595
|
+
drainQueue: async (_reason?: QueueDrainReason) => {},
|
|
599
596
|
// Forwards to drainQueue so tests that spy the drain observe the agent
|
|
600
597
|
// loop's post-turn kick through the guarded entry point.
|
|
601
598
|
kickDrainQueue(
|
|
602
|
-
this: { drainQueue: (reason?:
|
|
603
|
-
reason:
|
|
599
|
+
this: { drainQueue: (reason?: QueueDrainReason) => Promise<void> },
|
|
600
|
+
reason: QueueDrainReason = "loop_complete",
|
|
604
601
|
_origin?: string,
|
|
605
602
|
) {
|
|
606
603
|
return this.drainQueue(reason);
|
|
@@ -632,7 +629,7 @@ function makeCtx(
|
|
|
632
629
|
} as unknown as Conversation["graphMemory"],
|
|
633
630
|
|
|
634
631
|
...ctxOverrides,
|
|
635
|
-
}
|
|
632
|
+
});
|
|
636
633
|
// The convergence driver resolves the turn-scoped overflow ladder off the
|
|
637
634
|
// manager; give every fake manager the ladder methods unless a test supplied
|
|
638
635
|
// its own.
|
|
@@ -268,7 +268,18 @@ const setConversationHistoryStrippedAtMock = mock(
|
|
|
268
268
|
const updateConversationSlackContextWatermarkMock = mock(
|
|
269
269
|
(_conversationId: string, _watermarkTs: string, _compactedAt?: number) => {},
|
|
270
270
|
);
|
|
271
|
-
|
|
271
|
+
interface MockConversationRow {
|
|
272
|
+
conversationType?: string;
|
|
273
|
+
source?: string;
|
|
274
|
+
contextSummary?: string | null;
|
|
275
|
+
contextCompactedMessageCount?: number;
|
|
276
|
+
contextCompactedAt?: number | null;
|
|
277
|
+
slackContextCompactionWatermarkTs?: string | null;
|
|
278
|
+
lastNotifiedInferenceProfile?: string | null;
|
|
279
|
+
processingStartedAt?: string | null;
|
|
280
|
+
[key: string]: unknown;
|
|
281
|
+
}
|
|
282
|
+
let mockConversationRow: MockConversationRow = {
|
|
272
283
|
id: "conv-1",
|
|
273
284
|
createdAt: 1_700_000_000_000,
|
|
274
285
|
contextSummary: null,
|
|
@@ -675,7 +686,9 @@ import {
|
|
|
675
686
|
applyCompactionResult,
|
|
676
687
|
runAgentLoopImpl,
|
|
677
688
|
} from "../daemon/conversation-agent-loop.js";
|
|
689
|
+
import type { QueueDrainReason } from "../daemon/conversation-queue-manager.js";
|
|
678
690
|
import { settleTurnTail } from "../daemon/turn-tail-chain.js";
|
|
691
|
+
import { asConversation } from "./helpers/mock-conversation.js";
|
|
679
692
|
import {
|
|
680
693
|
createMockProvider,
|
|
681
694
|
type ScriptedResponse,
|
|
@@ -731,7 +744,7 @@ function makeCtx(
|
|
|
731
744
|
toolExecutor,
|
|
732
745
|
});
|
|
733
746
|
|
|
734
|
-
const ctx = {
|
|
747
|
+
const ctx = asConversation({
|
|
735
748
|
conversationId: "test-conv",
|
|
736
749
|
messages: [
|
|
737
750
|
{ role: "user", content: [{ type: "text", text: "Hello" }] },
|
|
@@ -791,12 +804,7 @@ function makeCtx(
|
|
|
791
804
|
skillProjectionCache:
|
|
792
805
|
new Map() as unknown as Conversation["skillProjectionCache"],
|
|
793
806
|
|
|
794
|
-
usageStats: {
|
|
795
|
-
totalInputTokens: 0,
|
|
796
|
-
totalOutputTokens: 0,
|
|
797
|
-
totalEstimatedCost: 0,
|
|
798
|
-
model: "",
|
|
799
|
-
},
|
|
807
|
+
usageStats: { inputTokens: 0, outputTokens: 0, estimatedCost: 0 },
|
|
800
808
|
turnCount: 0,
|
|
801
809
|
|
|
802
810
|
lastAssistantAttachments: [],
|
|
@@ -814,12 +822,12 @@ function makeCtx(
|
|
|
814
822
|
getQueueDepth: () => 0,
|
|
815
823
|
hasQueuedMessages: () => false,
|
|
816
824
|
canHandoffAtCheckpoint: () => false,
|
|
817
|
-
drainQueue: (_reason?:
|
|
825
|
+
drainQueue: async (_reason?: QueueDrainReason) => {},
|
|
818
826
|
// Forwards to drainQueue so tests that spy the drain observe the agent
|
|
819
827
|
// loop's post-turn kick through the guarded entry point.
|
|
820
828
|
kickDrainQueue(
|
|
821
|
-
this: { drainQueue: (reason?:
|
|
822
|
-
reason:
|
|
829
|
+
this: { drainQueue: (reason?: QueueDrainReason) => Promise<void> },
|
|
830
|
+
reason: QueueDrainReason = "loop_complete",
|
|
823
831
|
_origin?: string,
|
|
824
832
|
) {
|
|
825
833
|
return this.drainQueue(reason);
|
|
@@ -851,7 +859,7 @@ function makeCtx(
|
|
|
851
859
|
} as unknown as Conversation["graphMemory"],
|
|
852
860
|
|
|
853
861
|
...ctxOverrides,
|
|
854
|
-
}
|
|
862
|
+
});
|
|
855
863
|
// Reactive overflow recovery resolves the turn-scoped reduction ladder off
|
|
856
864
|
// the manager; give every fake manager the ladder methods unless a test
|
|
857
865
|
// supplied its own.
|
|
@@ -1337,7 +1345,10 @@ describe("session-agent-loop", () => {
|
|
|
1337
1345
|
});
|
|
1338
1346
|
const cleanupFlagDuringInjection = captureCleanupFlagDuringInjection(ctx);
|
|
1339
1347
|
|
|
1340
|
-
|
|
1348
|
+
// Presence is declared per turn; the policy input must carry it.
|
|
1349
|
+
await runAgentLoopImpl(ctx, "free up space", "msg-1", () => {}, {
|
|
1350
|
+
isInteractive: true,
|
|
1351
|
+
});
|
|
1341
1352
|
|
|
1342
1353
|
expect(classifyDiskPressureTurnPolicyMock).toHaveBeenCalledWith(
|
|
1343
1354
|
mockDiskPressureStatus,
|
|
@@ -1795,7 +1806,7 @@ describe("session-agent-loop", () => {
|
|
|
1795
1806
|
],
|
|
1796
1807
|
toolExecutor: async () => ({ content: "file content", isError: false }),
|
|
1797
1808
|
canHandoffAtCheckpoint: () => true,
|
|
1798
|
-
}
|
|
1809
|
+
});
|
|
1799
1810
|
|
|
1800
1811
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1801
1812
|
|
|
@@ -1826,7 +1837,7 @@ describe("session-agent-loop", () => {
|
|
|
1826
1837
|
],
|
|
1827
1838
|
toolExecutor: async () => ({ content: "content", isError: false }),
|
|
1828
1839
|
canHandoffAtCheckpoint: () => false,
|
|
1829
|
-
}
|
|
1840
|
+
});
|
|
1830
1841
|
|
|
1831
1842
|
await runAgentLoopImpl(ctx, "hello", "msg-1", (msg) => events.push(msg));
|
|
1832
1843
|
|
|
@@ -1975,13 +1986,13 @@ describe("session-agent-loop", () => {
|
|
|
1975
1986
|
|
|
1976
1987
|
test("drains queue after completion", async () => {
|
|
1977
1988
|
// GIVEN a real loop that answers in a single text turn
|
|
1978
|
-
let drainReason:
|
|
1989
|
+
let drainReason: QueueDrainReason | undefined;
|
|
1979
1990
|
const ctx = makeCtx({
|
|
1980
1991
|
providerResponses: [textResponse("ok")],
|
|
1981
|
-
drainQueue: (reason
|
|
1992
|
+
drainQueue: async (reason?: QueueDrainReason) => {
|
|
1982
1993
|
drainReason = reason;
|
|
1983
1994
|
},
|
|
1984
|
-
}
|
|
1995
|
+
});
|
|
1985
1996
|
|
|
1986
1997
|
// WHEN the orchestrator runs the turn to completion
|
|
1987
1998
|
await runAgentLoopImpl(ctx, "hi", "msg-1", () => {});
|
|
@@ -2018,10 +2029,10 @@ describe("session-agent-loop", () => {
|
|
|
2018
2029
|
abortController,
|
|
2019
2030
|
// Fire the watchdog quickly instead of the ~45s production default.
|
|
2020
2031
|
abortWatchdogMs: 30,
|
|
2021
|
-
drainQueue: (reason
|
|
2032
|
+
drainQueue: async (reason?: QueueDrainReason) => {
|
|
2022
2033
|
drainReason = reason;
|
|
2023
2034
|
},
|
|
2024
|
-
}
|
|
2035
|
+
});
|
|
2025
2036
|
|
|
2026
2037
|
try {
|
|
2027
2038
|
// WHEN the orchestrator runs the turn
|
|
@@ -2520,7 +2531,7 @@ describe("session-agent-loop", () => {
|
|
|
2520
2531
|
},
|
|
2521
2532
|
],
|
|
2522
2533
|
toolExecutor: async () => ({ content: "file content", isError: false }),
|
|
2523
|
-
}
|
|
2534
|
+
});
|
|
2524
2535
|
|
|
2525
2536
|
await runAgentLoopImpl(ctx, "hello", "msg-user-daily", () => {});
|
|
2526
2537
|
|
|
@@ -15,7 +15,6 @@ mock.module("../permissions/checker.js", () => ({
|
|
|
15
15
|
isDynamicSkillLoadInvocation: () => false,
|
|
16
16
|
check: checkSpy,
|
|
17
17
|
classifyRisk: () => Promise.resolve({ level: "low" }),
|
|
18
|
-
generateAllowlistOptions: () => Promise.resolve([]),
|
|
19
18
|
generateScopeOptions: () => [],
|
|
20
19
|
}));
|
|
21
20
|
|
|
@@ -832,3 +832,66 @@ describe("conversation-attention-store", () => {
|
|
|
832
832
|
});
|
|
833
833
|
});
|
|
834
834
|
});
|
|
835
|
+
|
|
836
|
+
describe("attention watermarks and streaming rows", () => {
|
|
837
|
+
test("state init from an upgraded database anchors on the latest finalized reply", () => {
|
|
838
|
+
clearTables();
|
|
839
|
+
ensureConversation("conv-1");
|
|
840
|
+
insertAssistantMessage("conv-1", "msg-done", 1_000);
|
|
841
|
+
getDb()
|
|
842
|
+
.insert(messages)
|
|
843
|
+
.values({
|
|
844
|
+
id: "msg-streaming",
|
|
845
|
+
conversationId: "conv-1",
|
|
846
|
+
role: "assistant",
|
|
847
|
+
content: "still being written",
|
|
848
|
+
createdAt: 2_000,
|
|
849
|
+
metadata: null,
|
|
850
|
+
finalized: 0,
|
|
851
|
+
})
|
|
852
|
+
.run();
|
|
853
|
+
|
|
854
|
+
// No attention row exists, so the seen signal initializes state from the
|
|
855
|
+
// messages table. The streaming row is newest but is not a reply yet;
|
|
856
|
+
// anchoring the watermark on it would classify the finished reply as
|
|
857
|
+
// already seen the moment it completes.
|
|
858
|
+
recordConversationSeenSignal({
|
|
859
|
+
conversationId: "conv-1",
|
|
860
|
+
sourceChannel: "vellum",
|
|
861
|
+
signalType: "macos_conversation_opened",
|
|
862
|
+
confidence: "explicit",
|
|
863
|
+
source: "desktop-client",
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const state = getAttentionStateByConversationIds(["conv-1"]).get("conv-1")!;
|
|
867
|
+
expect(state.latestAssistantMessageId).toBe("msg-done");
|
|
868
|
+
expect(state.lastSeenAssistantMessageId).toBe("msg-done");
|
|
869
|
+
});
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
describe("markConversationUnread with only a streaming reply", () => {
|
|
873
|
+
test("reports no assistant message rather than rewinding onto the streaming row", () => {
|
|
874
|
+
clearTables();
|
|
875
|
+
ensureConversation("conv-1");
|
|
876
|
+
getDb()
|
|
877
|
+
.insert(messages)
|
|
878
|
+
.values({
|
|
879
|
+
id: "msg-streaming-only",
|
|
880
|
+
conversationId: "conv-1",
|
|
881
|
+
role: "assistant",
|
|
882
|
+
content: "still being written",
|
|
883
|
+
createdAt: 1_000,
|
|
884
|
+
metadata: null,
|
|
885
|
+
finalized: 0,
|
|
886
|
+
})
|
|
887
|
+
.run();
|
|
888
|
+
|
|
889
|
+
// The write-path projection anchors at the finalize seam
|
|
890
|
+
// (`reserveMessage` deliberately skips it), so attention state for this
|
|
891
|
+
// conversation says "no reply yet". The rewind fallback must agree with
|
|
892
|
+
// the projection rather than anchoring on the streaming row.
|
|
893
|
+
expect(() => markConversationUnread("conv-1")).toThrow(
|
|
894
|
+
/no assistant message/i,
|
|
895
|
+
);
|
|
896
|
+
});
|
|
897
|
+
});
|
|
@@ -431,3 +431,115 @@ describe("sendToClient receives state signals", () => {
|
|
|
431
431
|
});
|
|
432
432
|
});
|
|
433
433
|
});
|
|
434
|
+
|
|
435
|
+
describe("the sink is fixed for the conversation's life", () => {
|
|
436
|
+
test("a cold instance delivers activity state with no turn ever having run", () => {
|
|
437
|
+
// The summarize-up-to route resolves its conversation outside the send
|
|
438
|
+
// path and emits a thinking indicator before any turn: it must reach the
|
|
439
|
+
// sink the conversation was built with, not depend on a send having wired
|
|
440
|
+
// one (LUM-2987).
|
|
441
|
+
const clientMsgs: AssistantEvent[] = [];
|
|
442
|
+
const conversation = makeConversation((msg) => clientMsgs.push(msg));
|
|
443
|
+
|
|
444
|
+
conversation.emitActivityState("thinking", "context_compacting", {
|
|
445
|
+
statusText: "Summarizing conversation",
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const activity = clientMsgs.filter(
|
|
449
|
+
(m) => m.type === "assistant_activity_state",
|
|
450
|
+
);
|
|
451
|
+
expect(activity).toHaveLength(1);
|
|
452
|
+
expect((activity[0] as { statusText?: string }).statusText).toBe(
|
|
453
|
+
"Summarizing conversation",
|
|
454
|
+
);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
test("presence is a property of the turn, not of delivery", () => {
|
|
458
|
+
// Nothing is in flight, so no human is asserted present, even though the
|
|
459
|
+
// sink is live and delivering.
|
|
460
|
+
const clientMsgs: AssistantEvent[] = [];
|
|
461
|
+
const conversation = makeConversation((msg) => clientMsgs.push(msg));
|
|
462
|
+
expect(conversation.hasNoClient).toBe(true);
|
|
463
|
+
conversation.emitActivityState("thinking", "message_dequeued");
|
|
464
|
+
expect(clientMsgs).toHaveLength(1);
|
|
465
|
+
expect(conversation.hasNoClient).toBe(true);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
describe("event observers", () => {
|
|
470
|
+
test("an observer sees each event after the sink delivered it, and detaches", () => {
|
|
471
|
+
const order: string[] = [];
|
|
472
|
+
const conversation = makeConversation((msg) => {
|
|
473
|
+
order.push(`sink:${msg.type}`);
|
|
474
|
+
});
|
|
475
|
+
const detach = conversation.addEventObserver((msg) => {
|
|
476
|
+
order.push(`observer:${msg.type}`);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
conversation.emitActivityState("thinking", "message_dequeued");
|
|
480
|
+
expect(order).toEqual([
|
|
481
|
+
"sink:assistant_activity_state",
|
|
482
|
+
"observer:assistant_activity_state",
|
|
483
|
+
]);
|
|
484
|
+
|
|
485
|
+
detach();
|
|
486
|
+
conversation.emitActivityState("idle", "message_complete");
|
|
487
|
+
expect(order).toEqual([
|
|
488
|
+
"sink:assistant_activity_state",
|
|
489
|
+
"observer:assistant_activity_state",
|
|
490
|
+
"sink:assistant_activity_state",
|
|
491
|
+
]);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("a throwing observer neither blocks the sink nor its siblings", () => {
|
|
495
|
+
const seen: string[] = [];
|
|
496
|
+
const conversation = makeConversation((msg) => {
|
|
497
|
+
seen.push(`sink:${msg.type}`);
|
|
498
|
+
});
|
|
499
|
+
conversation.addEventObserver(() => {
|
|
500
|
+
throw new Error("observer boom");
|
|
501
|
+
});
|
|
502
|
+
conversation.addEventObserver((msg) => {
|
|
503
|
+
seen.push(`second:${msg.type}`);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
conversation.emitActivityState("thinking", "message_dequeued");
|
|
507
|
+
expect(seen).toEqual([
|
|
508
|
+
"sink:assistant_activity_state",
|
|
509
|
+
"second:assistant_activity_state",
|
|
510
|
+
]);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test("the prompter's confirmation_request reaches observers after the sink", () => {
|
|
514
|
+
// Voice auto-resolves approval prompts it has no UI for; it must observe
|
|
515
|
+
// the request only once the sink has put it on the wire, so its resolution
|
|
516
|
+
// lands after the request it answers.
|
|
517
|
+
const order: string[] = [];
|
|
518
|
+
const conversation = makeConversation((msg) => {
|
|
519
|
+
order.push(`sink:${msg.type}`);
|
|
520
|
+
});
|
|
521
|
+
conversation.addEventObserver((msg) => {
|
|
522
|
+
if (msg.type === "confirmation_request") {
|
|
523
|
+
order.push("observer:confirmation_request");
|
|
524
|
+
conversation.handleConfirmationResponse(msg.requestId, "deny", {
|
|
525
|
+
emissionContext: { source: "system" },
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
void conversation.prompter.prompt(
|
|
531
|
+
"bash",
|
|
532
|
+
{ command: "rm -rf /tmp/x" },
|
|
533
|
+
"high",
|
|
534
|
+
[],
|
|
535
|
+
[],
|
|
536
|
+
undefined,
|
|
537
|
+
conversation.conversationId,
|
|
538
|
+
"sandbox",
|
|
539
|
+
false,
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
expect(order[0]).toBe("sink:confirmation_request");
|
|
543
|
+
expect(order[1]).toBe("observer:confirmation_request");
|
|
544
|
+
});
|
|
545
|
+
});
|
|
@@ -10,10 +10,6 @@ mock.module("../config/env.js", () => ({
|
|
|
10
10
|
hasUngatedHttpAuthDisabled: () => false,
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
|
-
mock.module("../daemon/conversation-store.js", () => ({
|
|
14
|
-
destroyActiveConversation: () => {},
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
13
|
mock.module("../daemon/handlers/conversations.js", () => ({
|
|
18
14
|
cancelGeneration: () => true,
|
|
19
15
|
clearAllConversations: async () => 0,
|
|
@@ -1794,3 +1794,72 @@ describe("forkConversation + memory_retrospective_state", () => {
|
|
|
1794
1794
|
expect(fork.slackContextCompactionWatermarkAt).toBe(compactedAt);
|
|
1795
1795
|
});
|
|
1796
1796
|
});
|
|
1797
|
+
|
|
1798
|
+
describe("forkConversation with unfinalized rows", () => {
|
|
1799
|
+
beforeEach(() => {
|
|
1800
|
+
resetTables();
|
|
1801
|
+
});
|
|
1802
|
+
|
|
1803
|
+
test("skips unfinalized rows and anchors the fork at the last finalized message", async () => {
|
|
1804
|
+
const source = createConversation("Mid-turn fork");
|
|
1805
|
+
await addMessage(source.id, "user", "question", { skipIndexing: true });
|
|
1806
|
+
const lastFinalized = await addMessage(
|
|
1807
|
+
source.id,
|
|
1808
|
+
"assistant",
|
|
1809
|
+
"completed reply",
|
|
1810
|
+
{ skipIndexing: true },
|
|
1811
|
+
);
|
|
1812
|
+
const streaming = await addMessage(
|
|
1813
|
+
source.id,
|
|
1814
|
+
"assistant",
|
|
1815
|
+
"partial reply still being written",
|
|
1816
|
+
{ skipIndexing: true },
|
|
1817
|
+
);
|
|
1818
|
+
rawRun(
|
|
1819
|
+
"test:markUnfinalized",
|
|
1820
|
+
"UPDATE messages SET finalized = 0 WHERE id = ?",
|
|
1821
|
+
streaming.id,
|
|
1822
|
+
);
|
|
1823
|
+
|
|
1824
|
+
const fork = forkConversation({ conversationId: source.id });
|
|
1825
|
+
const forkMessages = getMessages(fork.id);
|
|
1826
|
+
|
|
1827
|
+
// The invariant, not mere presence: exactly the finalized rows were
|
|
1828
|
+
// copied, every copy reads as finalized, no copy descends from the
|
|
1829
|
+
// unfinalized source row, and the lineage anchor is a row the fork
|
|
1830
|
+
// actually holds.
|
|
1831
|
+
expect(forkMessages).toHaveLength(2);
|
|
1832
|
+
expect(forkMessages.every((row) => row.finalized === 1)).toBe(true);
|
|
1833
|
+
const forkedFromIds = forkMessages.map(
|
|
1834
|
+
(row) =>
|
|
1835
|
+
(parseMetadata(row.metadata) as { forkSourceMessageId?: string })
|
|
1836
|
+
?.forkSourceMessageId,
|
|
1837
|
+
);
|
|
1838
|
+
expect(forkedFromIds).not.toContain(streaming.id);
|
|
1839
|
+
expect(fork.forkParentMessageId).toBe(lastFinalized.id);
|
|
1840
|
+
// The source still owns its in-flight row untouched.
|
|
1841
|
+
expect(getMessages(source.id)).toHaveLength(3);
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
test("a stale mid-history unfinalized row is skipped without shifting the anchor", async () => {
|
|
1845
|
+
const source = createConversation("Stale row fork");
|
|
1846
|
+
await addMessage(source.id, "user", "first", { skipIndexing: true });
|
|
1847
|
+
const stale = await addMessage(source.id, "assistant", "orphaned partial", {
|
|
1848
|
+
skipIndexing: true,
|
|
1849
|
+
});
|
|
1850
|
+
const tail = await addMessage(source.id, "user", "latest", {
|
|
1851
|
+
skipIndexing: true,
|
|
1852
|
+
});
|
|
1853
|
+
rawRun(
|
|
1854
|
+
"test:markUnfinalized",
|
|
1855
|
+
"UPDATE messages SET finalized = 0 WHERE id = ?",
|
|
1856
|
+
stale.id,
|
|
1857
|
+
);
|
|
1858
|
+
|
|
1859
|
+
const fork = forkConversation({ conversationId: source.id });
|
|
1860
|
+
const forkMessages = getMessages(fork.id);
|
|
1861
|
+
|
|
1862
|
+
expect(forkMessages).toHaveLength(2);
|
|
1863
|
+
expect(fork.forkParentMessageId).toBe(tail.id);
|
|
1864
|
+
});
|
|
1865
|
+
});
|
|
@@ -350,3 +350,70 @@ describe("referential forking", () => {
|
|
|
350
350
|
expect(textOf(getMessages(source.id))).toHaveLength(4);
|
|
351
351
|
});
|
|
352
352
|
});
|
|
353
|
+
|
|
354
|
+
describe("referential forking with unfinalized rows", () => {
|
|
355
|
+
beforeEach(() => {
|
|
356
|
+
resetTables();
|
|
357
|
+
clearConfig();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("ancestor rows are hidden while unfinalized and appear once finalized", async () => {
|
|
361
|
+
const source = await seedSource("Launch");
|
|
362
|
+
const midRow = getMessages(source.id)[1]!;
|
|
363
|
+
getDb()
|
|
364
|
+
.update(messages)
|
|
365
|
+
.set({ finalized: 0 })
|
|
366
|
+
.where(eq(messages.id, midRow.id))
|
|
367
|
+
.run();
|
|
368
|
+
|
|
369
|
+
setForkStrategy("reference");
|
|
370
|
+
const fork = await forkConversationForRetrospective({
|
|
371
|
+
conversationId: source.id,
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// The fork's lineage read excludes the ancestor's in-flight row; the
|
|
375
|
+
// source's own read keeps it.
|
|
376
|
+
expect(textOf(getMessages(fork.id))).toEqual([
|
|
377
|
+
"draft a launch plan",
|
|
378
|
+
"tweak the timeline",
|
|
379
|
+
"updated",
|
|
380
|
+
]);
|
|
381
|
+
expect(getMessages(source.id)).toHaveLength(4);
|
|
382
|
+
|
|
383
|
+
getDb()
|
|
384
|
+
.update(messages)
|
|
385
|
+
.set({ finalized: 1 })
|
|
386
|
+
.where(eq(messages.id, midRow.id))
|
|
387
|
+
.run();
|
|
388
|
+
|
|
389
|
+
// Once the row finalizes it enters the inherited window on its own.
|
|
390
|
+
expect(textOf(getMessages(fork.id))).toEqual([
|
|
391
|
+
"draft a launch plan",
|
|
392
|
+
"here is a first pass",
|
|
393
|
+
"tweak the timeline",
|
|
394
|
+
"updated",
|
|
395
|
+
]);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("an unfinalized tail is not the fork anchor", async () => {
|
|
399
|
+
const source = await seedSource("Launch");
|
|
400
|
+
const tail = getMessages(source.id).at(-1)!;
|
|
401
|
+
getDb()
|
|
402
|
+
.update(messages)
|
|
403
|
+
.set({ finalized: 0 })
|
|
404
|
+
.where(eq(messages.id, tail.id))
|
|
405
|
+
.run();
|
|
406
|
+
|
|
407
|
+
setForkStrategy("reference");
|
|
408
|
+
const fork = await forkConversationForRetrospective({
|
|
409
|
+
conversationId: source.id,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
expect(fork.forkParentMessageId).not.toBe(tail.id);
|
|
413
|
+
expect(textOf(getMessages(fork.id))).toEqual([
|
|
414
|
+
"draft a launch plan",
|
|
415
|
+
"here is a first pass",
|
|
416
|
+
"tweak the timeline",
|
|
417
|
+
]);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
@@ -723,3 +723,27 @@ describe("forkConversationForRetrospective — compacted source", () => {
|
|
|
723
723
|
});
|
|
724
724
|
});
|
|
725
725
|
});
|
|
726
|
+
|
|
727
|
+
describe("forkConversationForRetrospective with unfinalized rows", () => {
|
|
728
|
+
beforeEach(() => {
|
|
729
|
+
resetTables();
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
test("skips the unfinalized tail and anchors at the last finalized message", async () => {
|
|
733
|
+
const source = await seedSource("Mid-turn retro");
|
|
734
|
+
const tail = getMessages(source.id).at(-1)!;
|
|
735
|
+
const db = getDb();
|
|
736
|
+
db.run(`UPDATE messages SET finalized = 0 WHERE id = '${tail.id}'`);
|
|
737
|
+
|
|
738
|
+
const fork = await forkConversationForRetrospective({
|
|
739
|
+
conversationId: source.id,
|
|
740
|
+
});
|
|
741
|
+
const forkMessages = getMessages(fork.id);
|
|
742
|
+
|
|
743
|
+
// Invariant: only finalized rows are copied, and the lineage anchor is a
|
|
744
|
+
// row the fork actually holds.
|
|
745
|
+
expect(forkMessages).toHaveLength(3);
|
|
746
|
+
expect(forkMessages.every((row) => row.finalized === 1)).toBe(true);
|
|
747
|
+
expect(fork.forkParentMessageId).not.toBe(tail.id);
|
|
748
|
+
});
|
|
749
|
+
});
|