@vellumai/assistant 0.12.1 → 0.12.2-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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +13 -8
- package/node_modules/@vellumai/slack-text/src/label-resolution-entities.test.ts +95 -0
- package/openapi.yaml +360 -51
- package/package.json +3 -1
- package/scripts/smoke-desktop-window-theme.ts +186 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +18 -10
- package/src/__tests__/anthropic-provider.test.ts +0 -63
- package/src/__tests__/app-compiler.test.ts +6 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +5 -2
- package/src/__tests__/app-dir-path-guard.test.ts +1 -3
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +6 -7
- package/src/__tests__/assistant-event-hub.test.ts +44 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +1 -1
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +5 -2
- package/src/__tests__/computer-use-tools.test.ts +19 -7
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +28 -29
- package/src/__tests__/config-watcher.test.ts +23 -8
- package/src/__tests__/connection-model-compat.test.ts +2 -2
- package/src/__tests__/conversation-key-store-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +26 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -10
- package/src/__tests__/conversation-queue.test.ts +48 -207
- package/src/__tests__/conversation-retry-discard.test.ts +54 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +28 -0
- package/src/__tests__/conversation-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- package/src/__tests__/conversation-surfaces-point-at-budget.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-point-at-capability.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
- package/src/__tests__/conversation-title-service.test.ts +14 -10
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +3 -5
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +51 -0
- package/src/__tests__/credential-record-retire.test.ts +171 -1
- package/src/__tests__/credential-routes.test.ts +250 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/cu-unified-flow.test.ts +14 -4
- package/src/__tests__/document-sync-tags.test.ts +25 -0
- package/src/__tests__/drain-kick-guard.test.ts +0 -2
- package/src/__tests__/drain-requeue-on-contention.test.ts +0 -6
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/git-porcelain.test.ts +47 -0
- package/src/__tests__/heartbeat-service.test.ts +56 -40
- package/src/__tests__/helpers/set-workspace-mcp.ts +11 -0
- package/src/__tests__/host-bash-proxy.test.ts +26 -45
- package/src/__tests__/host-browser-proxy.test.ts +6 -17
- package/src/__tests__/host-cu-proxy.test.ts +596 -0
- package/src/__tests__/host-file-proxy-targeted.test.ts +23 -0
- package/src/__tests__/host-file-proxy.test.ts +11 -23
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +30 -9
- package/src/__tests__/list-messages-camera-frame.test.ts +121 -0
- package/src/__tests__/llm-catalog-parity.test.ts +25 -0
- package/src/__tests__/mcp-auth-routes.test.ts +6 -9
- package/src/__tests__/mcp-config-secret-boundary.test.ts +78 -136
- package/src/__tests__/mcp-health-check.test.ts +8 -11
- package/src/__tests__/mcp-list-plugin-servers.test.ts +8 -10
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +119 -0
- package/src/__tests__/messages-after-tiebreaker.test.ts +91 -0
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +355 -548
- package/src/__tests__/model-intents.test.ts +3 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +85 -2
- package/src/__tests__/oauth-commands-routes.test.ts +168 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +22 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-provider.test.ts +1 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-callback-registration.test.ts +8 -16
- package/src/__tests__/platform-client-verify-credential.test.ts +2 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +14 -0
- package/src/__tests__/qdrant-manager.test.ts +132 -0
- package/src/__tests__/resolve-app.test.ts +135 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +229 -6
- package/src/__tests__/script-proxy-certs.test.ts +35 -0
- package/src/__tests__/secret-routes-acp-guard.test.ts +59 -1
- package/src/__tests__/service-account-token.test.ts +247 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/source-fingerprint.test.ts +184 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +70 -3
- package/src/__tests__/subagent-tools.test.ts +3 -2
- package/src/__tests__/telephony-tts-guard.test.ts +38 -3
- package/src/__tests__/test-support/computer-use-skill-harness.ts +3 -2
- package/src/__tests__/ui-channel-variants.test.ts +1 -56
- package/src/__tests__/unicode.test.ts +13 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +4 -0
- package/src/__tests__/watch-retro-tool-availability.test.ts +8 -17
- package/src/__tests__/workspace-git-service.test.ts +26 -0
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +336 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +257 -6
- package/src/acp/__tests__/acp-credentials.test.ts +13 -0
- package/src/acp/__tests__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -0
- package/src/acp/__tests__/claude-token-refresh.test.ts +257 -0
- package/src/acp/__tests__/client-handler.test.ts +90 -2
- package/src/acp/__tests__/helpers/acp-model-option.ts +51 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +60 -1
- package/src/acp/__tests__/session-manager-persistence.test.ts +5 -1
- package/src/acp/__tests__/session-manager-resume.test.ts +270 -6
- package/src/acp/__tests__/session-manager-usage.test.ts +4 -2
- package/src/acp/__tests__/session-manager.test.ts +895 -4
- package/src/acp/acp-claude-oauth.ts +328 -14
- package/src/acp/acp-credentials.ts +19 -0
- package/src/acp/agent-process.test.ts +4 -1
- package/src/acp/agent-process.ts +96 -10
- package/src/acp/auth-required.ts +50 -1
- package/src/acp/auto-install.test.ts +9 -7
- package/src/acp/claude-token-refresh.ts +150 -0
- package/src/acp/client-handler.ts +16 -3
- package/src/acp/model-config.test.ts +166 -0
- package/src/acp/model-config.ts +138 -0
- package/src/acp/prepare-agent-env.ts +21 -6
- package/src/acp/resolve-agent.test.ts +220 -6
- package/src/acp/resolve-agent.ts +88 -23
- package/src/acp/session-manager.test.ts +196 -3
- package/src/acp/session-manager.ts +441 -18
- package/src/acp/types.ts +50 -5
- package/src/agent/loop.ts +1 -0
- package/src/api/events/acp-session-model-update.test.ts +88 -0
- package/src/api/events/acp-session-model-update.ts +46 -0
- package/src/api/events/app-files-changed.ts +4 -3
- package/src/api/events/notification-intent.ts +2 -0
- package/src/api/events/user-message-echo.ts +7 -1
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +1 -1
- package/src/apps/resolve-app.ts +81 -0
- package/src/apps/source-fingerprint.ts +266 -0
- package/src/avatar/identity-avatar.ts +7 -2
- package/src/bundler/app-compiler.ts +15 -0
- package/src/calls/__tests__/barge-in-guard.test.ts +81 -0
- package/src/{__tests__ → calls/__tests__}/call-controller.test.ts +503 -82
- package/src/{__tests__ → calls/__tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-output.test.ts +37 -8
- package/src/{__tests__ → calls/__tests__}/media-stream-parser.test.ts +1 -1
- package/src/{__tests__ → calls/__tests__}/media-stream-server-integration.test.ts +150 -57
- package/src/{__tests__ → calls/__tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{__tests__ → calls/__tests__}/media-turn-detector.test.ts +1 -1
- package/src/{live-voice → calls}/__tests__/progress-narration.test.ts +4 -4
- package/src/{live-voice → calls}/__tests__/progress-phrases.test.ts +1 -1
- package/src/calls/__tests__/voice-control-protocol.test.ts +120 -25
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +229 -0
- package/src/calls/__tests__/voice-progress-cadence.test.ts +274 -0
- package/src/{__tests__/voice-session-bridge.test.ts → calls/__tests__/voice-session-bridge-turn-flow.test.ts} +20 -12
- package/src/calls/__tests__/voice-session-bridge.test.ts +56 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +105 -0
- package/src/calls/barge-in-guard.ts +118 -0
- package/src/calls/call-constants.ts +24 -4
- package/src/calls/call-controller.ts +499 -199
- package/src/calls/call-transport.ts +16 -0
- package/src/calls/media-stream-output.ts +32 -0
- package/src/calls/media-stream-server.ts +82 -30
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/{live-voice → calls}/progress-narration.ts +2 -2
- package/src/calls/resolve-call-tts-provider.ts +33 -2
- package/src/calls/tts-call-strategy.ts +9 -5
- package/src/calls/types.ts +2 -0
- package/src/calls/voice-control-protocol.ts +155 -21
- package/src/calls/voice-leg-coordinator.ts +189 -0
- package/src/calls/voice-progress-cadence.ts +423 -0
- package/src/calls/voice-session-bridge.ts +92 -72
- package/src/calls/voice-triage-escalate.ts +178 -59
- package/src/cli/commands/__tests__/apps.test.ts +245 -0
- package/src/cli/commands/__tests__/bash.test.ts +53 -0
- package/src/cli/commands/__tests__/roadmap.help.test.ts +19 -0
- package/src/cli/commands/apps.help.ts +67 -13
- package/src/cli/commands/apps.ts +183 -0
- package/src/cli/commands/bash.help.ts +8 -13
- package/src/cli/commands/bash.ts +7 -25
- package/src/cli/commands/channels/__tests__/send.test.ts +218 -0
- package/src/cli/commands/channels/index.help.ts +70 -2
- package/src/cli/commands/channels/index.ts +16 -3
- package/src/cli/commands/channels/send.ts +128 -0
- package/src/cli/commands/credentials.help.ts +6 -2
- package/src/cli/commands/credentials.ts +2 -2
- package/src/cli/commands/mcp.help.ts +8 -8
- package/src/cli/commands/mcp.ts +1 -1
- package/src/cli/commands/oauth/status.ts +5 -0
- package/src/cli/commands/roadmap.help.ts +5 -1
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +103 -90
- package/src/config/__tests__/default-profile-catalog.test.ts +22 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +15 -1
- package/src/config/__tests__/platform-identity.test.ts +255 -0
- package/src/config/__tests__/platform-rehydration.test.ts +99 -9
- package/src/config/acp-agent-write.ts +43 -0
- package/src/config/acp-defaults.test.ts +13 -3
- package/src/config/acp-defaults.ts +23 -6
- package/src/config/acp-schema.test.ts +81 -0
- package/src/config/acp-schema.ts +30 -2
- package/src/config/bundled-skills/acp/SKILL.md +22 -14
- package/src/config/bundled-skills/acp/TOOLS.json +5 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +12 -9
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +29 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +99 -0
- package/src/config/bundled-skills/computer-use/tools/computer-use-sequence.ts +12 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -1
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -1
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -1
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +67 -139
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +2 -1
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +2 -1
- package/src/config/bundled-skills/schedule/SKILL.md +3 -4
- package/src/config/bundled-skills/screen-annotation/SKILL.md +8 -1
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +4 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +6 -2
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +5 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +33 -13
- package/src/config/loader.ts +2 -0
- package/src/config/memory-v3-gate.test.ts +31 -0
- package/src/config/memory-v3-gate.ts +20 -16
- package/src/config/platform-identity.ts +201 -0
- package/src/config/platform-rehydration.ts +11 -27
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +8 -158
- package/src/config/schemas/__tests__/voice.test.ts +148 -0
- package/src/config/schemas/calls.ts +1 -28
- package/src/config/schemas/live-voice.ts +1 -173
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +10 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/schemas/skills.ts +0 -14
- package/src/config/schemas/voice.ts +150 -0
- package/src/credential-execution/process-manager.test.ts +18 -19
- package/src/credential-health/credential-health-service.ts +11 -17
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -121
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +4 -1
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +133 -0
- package/src/daemon/app-change-notify.ts +45 -0
- package/src/daemon/config-watcher.ts +13 -194
- package/src/daemon/conversation-error.ts +3 -2
- package/src/daemon/conversation-media-retry.ts +2 -1
- package/src/daemon/conversation-process.ts +35 -64
- package/src/daemon/conversation-runtime-assembly.ts +20 -7
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +21 -10
- package/src/daemon/conversation-tool-setup.ts +45 -59
- package/src/daemon/conversation.ts +0 -3
- package/src/daemon/dictation-profile-store.ts +2 -1
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +6 -3
- package/src/daemon/host-bash-proxy.ts +8 -12
- package/src/daemon/host-browser-proxy.ts +15 -1
- package/src/daemon/host-cu-proxy.ts +224 -19
- package/src/daemon/host-file-proxy.ts +8 -10
- package/src/daemon/host-proxy-preactivation.ts +2 -1
- package/src/daemon/host-transfer-proxy.ts +14 -18
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/orphan-reaper.ts +1 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/reaction-history-render.ts +2 -1
- package/src/daemon/shutdown-handlers.ts +1 -9
- package/src/daemon/skill-memory-refresh.ts +64 -0
- package/src/daemon/tool-side-effects.ts +7 -18
- package/src/desktop/__tests__/fake-desktop.ts +122 -0
- package/src/desktop/desktop-chrome-policy.test.ts +79 -0
- package/src/desktop/desktop-chrome-policy.ts +47 -0
- package/src/desktop/desktop-chrome-session.ts +37 -0
- package/src/desktop/desktop-dependencies.test.ts +102 -0
- package/src/desktop/desktop-dependencies.ts +308 -0
- package/src/desktop/desktop-feature.test.ts +18 -0
- package/src/desktop/desktop-feature.ts +19 -0
- package/src/desktop/desktop-panel-config.test.ts +135 -0
- package/src/desktop/desktop-panel-config.ts +149 -0
- package/src/desktop/desktop-session-manager.test.ts +730 -0
- package/src/desktop/desktop-session-manager.ts +850 -0
- package/src/desktop/desktop-stream-bridge.test.ts +395 -0
- package/src/desktop/desktop-stream-bridge.ts +214 -0
- package/src/desktop/desktop-wallpaper.test.ts +191 -0
- package/src/desktop/desktop-wallpaper.ts +113 -0
- package/src/desktop/desktop-window-drag.ts +177 -0
- package/src/desktop/desktop-window-manager-config.test.ts +230 -0
- package/src/desktop/desktop-window-manager-config.ts +238 -0
- package/src/desktop/desktop-window-theme.ts +170 -0
- package/src/heartbeat/heartbeat-service.ts +21 -3
- package/src/home/__tests__/feed-writer.test.ts +22 -0
- package/src/home/feed-writer.ts +11 -2
- package/src/hooks/types.ts +33 -0
- package/src/i18n/AGENTS.md +22 -0
- package/src/i18n/__tests__/i18n.test.ts +108 -0
- package/src/i18n/index.ts +16 -0
- package/src/i18n/locales.ts +93 -0
- package/src/i18n/messages.ts +61 -0
- package/src/i18n/title.ts +25 -0
- package/src/inbound/platform-callback-registration.ts +4 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +121 -0
- package/src/live-voice/__tests__/live-voice-events.test.ts +8 -7
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +94 -15
- package/src/live-voice/__tests__/live-voice-sight-frame-echo.test.ts +238 -0
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/live-voice/__tests__/protocol.test.ts +39 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/__tests__/session-controls.test.ts +79 -0
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +297 -699
- package/src/live-voice/protocol.ts +60 -2
- package/src/live-voice/session-controls.ts +111 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +135 -0
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +29 -45
- package/src/mcp/spec-schema.ts +111 -0
- package/src/mcp/startup.ts +8 -4
- package/src/mcp/workspace-mcp-config.ts +188 -0
- package/src/messaging/provider.ts +10 -3
- package/src/messaging/providers/__tests__/proactive-address.test.ts +95 -0
- package/src/messaging/providers/callback-routing.ts +18 -0
- package/src/messaging/providers/channel-transport.ts +56 -0
- package/src/messaging/providers/discord/transport.ts +17 -0
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +6 -17
- package/src/messaging/providers/slack/adapter.ts +7 -44
- package/src/messaging/providers/slack/transport.ts +16 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +1 -48
- package/src/messaging/providers/telegram-bot/client.ts +0 -26
- package/src/messaging/providers/telegram-bot/transport.ts +20 -0
- package/src/messaging/providers/whatsapp/adapter.ts +1 -48
- package/src/messaging/providers/whatsapp/transport.ts +18 -0
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +8 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +55 -0
- package/src/notifications/__tests__/guardian-feed-projection.test.ts +158 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +153 -3
- package/src/notifications/__tests__/schedule-result-producer.test.ts +99 -3
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/assistant-reply-producer.ts +8 -3
- package/src/notifications/conversation-pairing.ts +36 -16
- package/src/notifications/delivered-post-record.ts +11 -1
- package/src/notifications/guardian-feed-projection.ts +125 -5
- package/src/notifications/home-feed-side-effect.ts +55 -7
- package/src/notifications/schedule-result-producer.ts +66 -22
- package/src/oauth/AGENTS.md +2 -0
- package/src/oauth/__tests__/seed-providers-managed.test.ts +149 -0
- package/src/oauth/byo-connection.test.ts +105 -0
- package/src/oauth/byo-connection.ts +36 -6
- package/src/oauth/connection-resolver.test.ts +100 -0
- package/src/oauth/connection-resolver.ts +120 -3
- package/src/oauth/provider-serializer.ts +9 -0
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +314 -27
- package/src/outbound-proxy/certs.ts +3 -5
- package/src/permissions/checker.test.ts +11 -6
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +71 -63
- package/src/persistence/conversation-lineage.ts +12 -4
- package/src/persistence/conversation-queries.ts +2 -2
- package/src/persistence/conversation-title-placeholders.ts +23 -0
- package/src/persistence/conversation-title-service.ts +9 -26
- package/src/persistence/embeddings/qdrant-manager.ts +163 -18
- package/src/persistence/llm-request-log-sink-clickhouse.ts +3 -3
- package/src/persistence/llm-request-log-source-clickhouse.ts +3 -3
- package/src/persistence/message-cursor.ts +78 -0
- package/src/persistence/message-reads.ts +30 -0
- package/src/persistence/migrations/157-invite-contact-id.ts +18 -6
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +149 -0
- package/src/persistence/schema/memory-core.ts +5 -0
- package/src/platform/client.test.ts +4 -2
- package/src/platform/client.ts +2 -10
- package/src/plugin-api/constants.ts +2 -0
- package/src/plugin-api/index.ts +1 -0
- package/src/plugin-api/types.ts +2 -0
- package/src/plugins/defaults/index.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +46 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +118 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +83 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +9 -5
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +20 -10
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +5 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +219 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +31 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +63 -0
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +4 -0
- package/src/plugins/defaults/memory/context-search/agent-runner.ts +3 -2
- package/src/plugins/defaults/memory/context-search/format.ts +3 -2
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +6 -3
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +3 -2
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -7
- package/src/plugins/defaults/memory/hooks/init.ts +10 -0
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +21 -0
- package/src/plugins/defaults/memory/hooks/shutdown.ts +2 -0
- package/src/plugins/defaults/memory/host-utils.ts +10 -0
- package/src/plugins/defaults/memory/jobs-worker.ts +13 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +34 -60
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +126 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +79 -0
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +28 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +3 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +12 -3
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +312 -71
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +2 -1
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +5 -3
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +9 -0
- package/src/plugins/defaults/memory/startup.ts +28 -2
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +7 -9
- package/src/plugins/defaults/memory/substrate/page-index.ts +6 -1
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v1/graph/consolidation.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +4 -2
- package/src/plugins/defaults/memory/v1/graph/retriever.ts +4 -1
- package/src/plugins/defaults/memory/v2/reranker.ts +6 -1
- package/src/plugins/defaults/memory/v3/__tests__/card.test.ts +24 -0
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +125 -0
- package/src/plugins/defaults/memory/v3/__tests__/sections.test.ts +26 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +30 -0
- package/src/plugins/defaults/memory/v3/card.ts +3 -2
- package/src/plugins/defaults/memory/v3/pool-select.ts +5 -3
- package/src/plugins/defaults/memory/v3/sections.ts +9 -3
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +5 -2
- package/src/plugins/defaults/memory/worker.ts +6 -0
- package/src/plugins/mcp-servers.ts +15 -75
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/__tests__/retry-callsite.test.ts +41 -0
- package/src/providers/__tests__/retry-outbound-sanitize.test.ts +176 -0
- package/src/providers/anthropic/client.ts +0 -47
- package/src/providers/gemini/client.ts +9 -23
- package/src/providers/gemini-thought-signature.test.ts +63 -0
- package/src/providers/gemini-thought-signature.ts +44 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +42 -3
- package/src/providers/inference/auth.ts +2 -3
- package/src/providers/inference/connection-availability.ts +27 -8
- package/src/providers/inference/endpoint-probe.ts +10 -1
- package/src/providers/inference/resolve-auth.ts +22 -4
- package/src/providers/inference/service-account-token.ts +248 -0
- package/src/providers/model-catalog.ts +34 -0
- package/src/providers/model-intents.ts +10 -5
- package/src/providers/openai/__tests__/chat-completions-output-token-limit.test.ts +141 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +146 -18
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +613 -0
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +235 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +83 -1
- package/src/providers/openai/chat-completions-provider.ts +212 -79
- package/src/providers/openai/codex-models.ts +0 -4
- package/src/providers/openai/google-thought-signature.ts +320 -0
- package/src/providers/opencode/client.test.ts +73 -4
- package/src/providers/opencode/client.ts +60 -3
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/outbound-request-sanitize.ts +96 -0
- package/src/providers/retry.ts +30 -19
- package/src/providers/unparseable-tool-args.ts +5 -1
- package/src/runtime/AGENTS.md +3 -1
- package/src/runtime/__tests__/channel-send.test.ts +443 -0
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +89 -0
- package/src/runtime/__tests__/host-observe.test.ts +40 -1
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +58 -0
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +72 -0
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +59 -0
- package/src/runtime/agent-wake.ts +2 -1
- package/src/runtime/assistant-event-hub.ts +21 -3
- package/src/runtime/auth/same-actor.ts +38 -3
- package/src/runtime/channel-send.ts +316 -0
- package/src/runtime/host-observe.ts +11 -1
- package/src/runtime/http-server.ts +160 -151
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +16 -5
- package/src/runtime/routes/__tests__/acp-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +275 -0
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +127 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +108 -1
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +2 -2
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +107 -0
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +16 -12
- package/src/runtime/routes/acp-claude-auth-routes.ts +11 -3
- package/src/runtime/routes/acp-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +90 -2
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/channel-send-routes.ts +85 -0
- package/src/runtime/routes/conversation-list-routes.ts +14 -3
- package/src/runtime/routes/conversation-query-routes.ts +40 -0
- package/src/runtime/routes/conversation-routes.ts +16 -3
- package/src/runtime/routes/credential-routes.ts +20 -14
- package/src/runtime/routes/desktop-setup-routes.test.ts +74 -0
- package/src/runtime/routes/desktop-setup-routes.ts +35 -0
- package/src/runtime/routes/diagnostics-routes.ts +61 -4
- package/src/runtime/routes/events-routes.ts +4 -0
- package/src/runtime/routes/host-cu-routes.ts +6 -0
- package/src/runtime/routes/index.ts +4 -2
- package/src/runtime/routes/log-export-routes.ts +2 -0
- package/src/runtime/routes/mcp-auth-routes.ts +39 -47
- package/src/runtime/routes/migration-routes.ts +6 -20
- package/src/runtime/routes/oauth-commands-routes.ts +50 -0
- package/src/runtime/routes/oauth-providers.ts +7 -0
- package/src/runtime/routes/platform-routes.ts +18 -15
- package/src/runtime/routes/workspace-routes.test.ts +55 -26
- package/src/runtime/routes/workspace-routes.ts +46 -63
- package/src/runtime/routes/workspace-tree-walk.test.ts +244 -0
- package/src/runtime/routes/workspace-tree-walk.ts +240 -0
- package/src/runtime/routes/workspace-utils.ts +17 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +16 -3
- package/src/runtime/sync/resource-sync-events.ts +2 -1
- package/src/schedule/run-script.ts +2 -1
- package/src/schedule/schedule-store.ts +9 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/__tests__/ces-http-record-backend.test.ts +137 -0
- package/src/security/ces-http-record-backend.ts +120 -0
- package/src/security/ces-rpc-record-backend.ts +7 -0
- package/src/security/untrusted-content.ts +3 -1
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +60 -0
- package/src/skills/workspace-skill-md-signature.ts +50 -0
- package/src/subagent/consult-context.ts +12 -25
- package/src/tools/__tests__/side-effect-abort-guard.test.ts +1 -0
- package/src/tools/acp/list-agents.test.ts +3 -1
- package/src/tools/acp/spawn.test.ts +146 -11
- package/src/tools/acp/spawn.ts +21 -3
- package/src/tools/acp/status.test.ts +94 -0
- package/src/tools/acp/status.ts +40 -3
- package/src/tools/app-control/skill-proxy-bridge.ts +2 -1
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +9 -3
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +2 -1
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +3 -2
- package/src/tools/browser/network-recorder.ts +2 -1
- package/src/tools/capability-offer.test.ts +22 -2
- package/src/tools/capability-offer.ts +16 -1
- package/src/tools/computer-use/definitions.ts +121 -2
- package/src/tools/computer-use/skill-proxy-bridge.ts +2 -1
- package/src/tools/credentials/metadata-store.ts +100 -0
- package/src/tools/filesystem/search.ts +2 -1
- package/src/tools/host-filesystem/edit.test.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +2 -1
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +2 -1
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +16 -0
- package/src/tools/host-terminal/host-shell.ts +4 -20
- package/src/tools/network/web-search.ts +6 -1
- package/src/tools/policy-context.ts +6 -7
- package/src/tools/skills/scaffold-managed.ts +61 -3
- package/src/tools/subagent/spawn.ts +5 -8
- package/src/tools/terminal/__tests__/sanitized-bash.test.ts +57 -0
- package/src/tools/terminal/sanitized-bash.ts +105 -0
- package/src/tools/ui-surface/channel-variants.ts +10 -59
- package/src/tools/watch/watch-retro-report.ts +0 -8
- package/src/types/host-capabilities.ts +5 -3
- package/src/util/__tests__/language-subtag.test.ts +23 -1
- package/src/util/ca-bundle.ts +13 -0
- package/src/util/language-subtag.ts +16 -0
- package/src/util/platform.ts +8 -0
- package/src/util/unicode.ts +15 -1
- package/src/watch/watch-retro.ts +6 -8
- package/src/watch/watch-timeline.ts +2 -2
- package/src/workflows/run-manager.ts +2 -1
- package/src/workspace/git-porcelain.ts +66 -0
- package/src/workspace/git-service.ts +6 -55
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +258 -0
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +78 -0
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +214 -0
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +102 -0
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +199 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/app-source-watcher.test.ts +0 -218
- package/src/__tests__/config-watcher-skill-reseed.test.ts +0 -359
- package/src/__tests__/speaker-identification.test.ts +0 -56
- package/src/calls/speaker-identification.ts +0 -233
- package/src/daemon/app-source-watcher.ts +0 -218
- package/src/messaging/providers/whatsapp/client.ts +0 -29
- package/src/runtime/routes/debug-bash-routes.ts +0 -173
- package/src/util/fs-watcher-error.ts +0 -36
- /package/src/{live-voice → calls}/progress-phrases.ts +0 -0
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
* can never abort a turn.
|
|
31
31
|
*/
|
|
32
32
|
import { getConfigReadOnly } from "../config/loader.js";
|
|
33
|
+
import { resolvePlatformAssistantIdOrNull } from "../config/platform-identity.js";
|
|
33
34
|
import type { LlmRequestLogsClickHouseConfig } from "../config/schemas/llm-request-logs.js";
|
|
34
35
|
import { credentialKey } from "../security/credential-key.js";
|
|
35
36
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
@@ -71,7 +72,7 @@ export interface ClickHouseLlmRequestLogSinkDeps {
|
|
|
71
72
|
resolveUrl?: () => Promise<string | null>;
|
|
72
73
|
/** Override the credential read for `clickhouse:password`. */
|
|
73
74
|
resolvePassword?: () => Promise<string | null>;
|
|
74
|
-
/** Override the
|
|
75
|
+
/** Override the in-memory platform assistant id. */
|
|
75
76
|
resolveAssistantId?: () => Promise<string | null>;
|
|
76
77
|
/** Override fetch for testing. */
|
|
77
78
|
fetchImpl?: ClickHouseSinkFetch;
|
|
@@ -98,8 +99,7 @@ export class ClickHouseLlmRequestLogSink implements LlmRequestLogWriter {
|
|
|
98
99
|
deps.resolvePassword ??
|
|
99
100
|
(() => readCredentialOrNull("clickhouse", "password"));
|
|
100
101
|
this.resolveAssistantId =
|
|
101
|
-
deps.resolveAssistantId ??
|
|
102
|
-
(() => readCredentialOrNull("vellum", "platform_assistant_id"));
|
|
102
|
+
deps.resolveAssistantId ?? resolvePlatformAssistantIdOrNull;
|
|
103
103
|
this.fetchImpl = deps.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* than the local source. Acceptable for the "internal use while we
|
|
16
16
|
* finetune prompts" use case; revisit when mirror updates are added.
|
|
17
17
|
*/
|
|
18
|
+
import { resolvePlatformAssistantIdOrNull } from "../config/platform-identity.js";
|
|
18
19
|
import type { LlmRequestLogsClickHouseConfig } from "../config/schemas/llm-request-logs.js";
|
|
19
20
|
import { credentialKey } from "../security/credential-key.js";
|
|
20
21
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
@@ -104,7 +105,7 @@ export interface ClickHouseLlmRequestLogSourceDeps {
|
|
|
104
105
|
resolveUrl?: () => Promise<string | null>;
|
|
105
106
|
/** Override the credential read for `clickhouse:password`. */
|
|
106
107
|
resolvePassword?: () => Promise<string | null>;
|
|
107
|
-
/** Override the
|
|
108
|
+
/** Override the in-memory platform assistant id. */
|
|
108
109
|
resolveAssistantId?: () => Promise<string | null>;
|
|
109
110
|
/** Override the turn-id resolver (default: `getAssistantMessageIdsInTurn`). */
|
|
110
111
|
resolveTurnMessageIds?: (messageId: string) => string[];
|
|
@@ -138,8 +139,7 @@ export class ClickHouseLlmRequestLogSource implements LlmRequestLogSource {
|
|
|
138
139
|
deps.resolvePassword ??
|
|
139
140
|
(() => readCredentialOrNull("clickhouse", "password"));
|
|
140
141
|
this.resolveAssistantId =
|
|
141
|
-
deps.resolveAssistantId ??
|
|
142
|
-
(() => readCredentialOrNull("vellum", "platform_assistant_id"));
|
|
142
|
+
deps.resolveAssistantId ?? resolvePlatformAssistantIdOrNull;
|
|
143
143
|
this.resolveTurnMessageIds =
|
|
144
144
|
deps.resolveTurnMessageIds ?? getAssistantMessageIdsInTurn;
|
|
145
145
|
this.resolveMessage = deps.resolveMessage ?? getMessageById;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Message cursors: the "(createdAt, id) after which" reference behind the
|
|
3
|
+
// messages-after reads, resilient to the referenced row being deleted.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
//
|
|
6
|
+
// A bare message id bounds a read only while its row exists. A regenerated
|
|
7
|
+
// reply (the conversation retry route discards the latest assistant turn) or a
|
|
8
|
+
// retried turn deletes the row, and a caller holding just the id can no longer
|
|
9
|
+
// say where it sat. A `MessageCursor` carries the row's `createdAt` alongside
|
|
10
|
+
// its id, so the `(createdAt, id)` bound is reconstructible from the cursor
|
|
11
|
+
// alone. The live row stays authoritative whenever it exists.
|
|
12
|
+
|
|
13
|
+
import { eq } from "drizzle-orm";
|
|
14
|
+
|
|
15
|
+
import type { LineageBound } from "./conversation-lineage.js";
|
|
16
|
+
import { getDb } from "./db-connection.js";
|
|
17
|
+
import { messages } from "./schema/index.js";
|
|
18
|
+
|
|
19
|
+
export { messagesAfterBoundFilter } from "./conversation-lineage.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A `(createdAt, id)` message reference. `createdAt` is null when the holder
|
|
23
|
+
* never recorded it; such a cursor bounds a read only while its row exists.
|
|
24
|
+
*/
|
|
25
|
+
export interface MessageCursor {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly createdAt: number | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Accepted "after" references: a message id, a {@link MessageCursor}, or
|
|
32
|
+
* null. `null`, `""`, and a cursor whose id is `""` all mean "no lower bound".
|
|
33
|
+
*/
|
|
34
|
+
export type MessagesAfterRef = string | MessageCursor | null;
|
|
35
|
+
|
|
36
|
+
/** Where a messages-after read starts. */
|
|
37
|
+
export type MessagesAfterBound =
|
|
38
|
+
/** No lower bound: every row the conversation owns. */
|
|
39
|
+
| { kind: "all" }
|
|
40
|
+
/** Rows strictly after `bound` in `(createdAt, id)` order. */
|
|
41
|
+
| { kind: "after"; bound: LineageBound }
|
|
42
|
+
/**
|
|
43
|
+
* The referenced row is gone and the reference carried no timestamp, so
|
|
44
|
+
* the read has no defensible starting point. Readers return nothing.
|
|
45
|
+
*/
|
|
46
|
+
| { kind: "vanished" };
|
|
47
|
+
|
|
48
|
+
/** The `(createdAt, id)` bound of a single message, or null when it is gone. */
|
|
49
|
+
export function loadMessageBound(messageId: string): LineageBound | null {
|
|
50
|
+
const row = getDb()
|
|
51
|
+
.select({ createdAt: messages.createdAt, id: messages.id })
|
|
52
|
+
.from(messages)
|
|
53
|
+
.where(eq(messages.id, messageId))
|
|
54
|
+
.get();
|
|
55
|
+
return row ?? null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Resolve an "after" reference to the bound a read filters on. A live row
|
|
60
|
+
* wins; a cursor whose row is gone falls back to the `createdAt` it carries,
|
|
61
|
+
* paired with its id so same-millisecond neighbours keep their order.
|
|
62
|
+
*/
|
|
63
|
+
export function resolveMessagesAfterBound(
|
|
64
|
+
after: MessagesAfterRef,
|
|
65
|
+
): MessagesAfterBound {
|
|
66
|
+
const id = typeof after === "string" ? after : (after?.id ?? "");
|
|
67
|
+
if (id === "") {
|
|
68
|
+
return { kind: "all" };
|
|
69
|
+
}
|
|
70
|
+
const live = loadMessageBound(id);
|
|
71
|
+
if (live) {
|
|
72
|
+
return { kind: "after", bound: live };
|
|
73
|
+
}
|
|
74
|
+
if (typeof after === "object" && after !== null && after.createdAt !== null) {
|
|
75
|
+
return { kind: "after", bound: { createdAt: after.createdAt, id } };
|
|
76
|
+
}
|
|
77
|
+
return { kind: "vanished" };
|
|
78
|
+
}
|
|
@@ -190,6 +190,36 @@ export function existingMessageIds(
|
|
|
190
190
|
);
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
/**
|
|
194
|
+
* `createdAt` of each of `messageIds` that exists as a row, keyed by id, in
|
|
195
|
+
* any state.
|
|
196
|
+
*
|
|
197
|
+
* Any-state deliberately: a row's `createdAt` is fixed at insert, so its
|
|
198
|
+
* completeness does not change the answer, and the callers (retrospective
|
|
199
|
+
* cursor bookkeeping) hold ids taken from rows they have already reviewed.
|
|
200
|
+
* An empty input returns an empty map without querying.
|
|
201
|
+
*/
|
|
202
|
+
export function messageCreatedAtByIds(
|
|
203
|
+
messageIds: readonly string[],
|
|
204
|
+
opts?: { db?: MessageReadHandle },
|
|
205
|
+
): Map<string, number> {
|
|
206
|
+
const db = opts?.db ?? getDb();
|
|
207
|
+
const result = new Map<string, number>();
|
|
208
|
+
// Chunked so a whole-conversation id list stays under SQLite's bound
|
|
209
|
+
// parameter limit.
|
|
210
|
+
for (let offset = 0; offset < messageIds.length; offset += 500) {
|
|
211
|
+
const rows = db
|
|
212
|
+
.select({ id: messages.id, createdAt: messages.createdAt })
|
|
213
|
+
.from(messages)
|
|
214
|
+
.where(inArray(messages.id, messageIds.slice(offset, offset + 500)))
|
|
215
|
+
.all();
|
|
216
|
+
for (const row of rows) {
|
|
217
|
+
result.set(row.id, row.createdAt);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return result;
|
|
221
|
+
}
|
|
222
|
+
|
|
193
223
|
/**
|
|
194
224
|
* The conversation that owns `messageId`, in any state, or null when the
|
|
195
225
|
* message does not exist.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
2
|
+
import { tableExists } from "./schema-introspection.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Add a required contact_id column to assistant_ingress_invites.
|
|
@@ -10,8 +11,16 @@ import { type DrizzleDb, getSqliteFrom } from "../db-connection.js";
|
|
|
10
11
|
* 1. Add the column as nullable.
|
|
11
12
|
* 2. Delete legacy rows that have no contact binding (NULL).
|
|
12
13
|
* 3. Rebuild the table to enforce NOT NULL (SQLite cannot ALTER COLUMN).
|
|
14
|
+
*
|
|
15
|
+
* The rebuild copies whichever session-id column is live:
|
|
16
|
+
* `source_conversation_id` (after migrateRenameCreatedBySessionIdColumns)
|
|
17
|
+
* or `created_by_session_id`.
|
|
13
18
|
*/
|
|
14
19
|
export function migrateInviteContactId(database: DrizzleDb): void {
|
|
20
|
+
if (!tableExists(database, "assistant_ingress_invites")) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
const raw = getSqliteFrom(database);
|
|
16
25
|
|
|
17
26
|
const cols = (
|
|
@@ -20,25 +29,28 @@ export function migrateInviteContactId(database: DrizzleDb): void {
|
|
|
20
29
|
notnull: number;
|
|
21
30
|
}>
|
|
22
31
|
).map((c) => ({ name: c.name, notnull: c.notnull }));
|
|
32
|
+
const colNames = new Set(cols.map((c) => c.name));
|
|
23
33
|
|
|
24
34
|
const col = cols.find((c) => c.name === "contact_id");
|
|
25
35
|
|
|
26
|
-
// Step 1: Add the column as nullable
|
|
27
36
|
if (!col) {
|
|
28
37
|
raw.exec(
|
|
29
38
|
/*sql*/ `ALTER TABLE assistant_ingress_invites ADD COLUMN contact_id TEXT`,
|
|
30
39
|
);
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
// Step 2: Delete legacy rows with no contact binding
|
|
34
42
|
raw.exec(
|
|
35
43
|
/*sql*/ `DELETE FROM assistant_ingress_invites WHERE contact_id IS NULL OR contact_id = ''`,
|
|
36
44
|
);
|
|
37
45
|
|
|
38
|
-
// Step 3: Rebuild the table with NOT NULL on contact_id
|
|
39
46
|
if (col && col.notnull === 1) {
|
|
40
47
|
return;
|
|
41
|
-
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const sessionCol = colNames.has("source_conversation_id")
|
|
51
|
+
? "source_conversation_id"
|
|
52
|
+
: "created_by_session_id";
|
|
53
|
+
const sessionSelect = colNames.has(sessionCol) ? sessionCol : "NULL";
|
|
42
54
|
|
|
43
55
|
raw.exec("PRAGMA foreign_keys = OFF");
|
|
44
56
|
try {
|
|
@@ -49,7 +61,7 @@ export function migrateInviteContactId(database: DrizzleDb): void {
|
|
|
49
61
|
id TEXT PRIMARY KEY,
|
|
50
62
|
source_channel TEXT NOT NULL,
|
|
51
63
|
token_hash TEXT NOT NULL,
|
|
52
|
-
|
|
64
|
+
${sessionCol} TEXT,
|
|
53
65
|
note TEXT,
|
|
54
66
|
max_uses INTEGER NOT NULL DEFAULT 1 CHECK (max_uses > 0),
|
|
55
67
|
use_count INTEGER NOT NULL DEFAULT 0 CHECK (use_count >= 0),
|
|
@@ -70,7 +82,7 @@ export function migrateInviteContactId(database: DrizzleDb): void {
|
|
|
70
82
|
);
|
|
71
83
|
|
|
72
84
|
INSERT INTO assistant_ingress_invites_new
|
|
73
|
-
SELECT id, source_channel, token_hash,
|
|
85
|
+
SELECT id, source_channel, token_hash, ${sessionSelect}, note,
|
|
74
86
|
max_uses, use_count, expires_at, status,
|
|
75
87
|
redeemed_by_external_user_id, redeemed_by_external_chat_id,
|
|
76
88
|
redeemed_at, expected_external_user_id, voice_code_hash,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
7
|
+
import * as schema from "../../schema.js";
|
|
8
|
+
import { migrateInviteContactId } from "../157-invite-contact-id.js";
|
|
9
|
+
import { tableExists, tableHasColumn } from "../schema-introspection.js";
|
|
10
|
+
|
|
11
|
+
function createTestDb() {
|
|
12
|
+
const sqlite = new Database(":memory:");
|
|
13
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
14
|
+
return drizzle(sqlite, { schema });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function columnNotNull(raw: Database, name: string): boolean {
|
|
18
|
+
const cols = raw.query(`PRAGMA table_info(assistant_ingress_invites)`).all() as Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
notnull: number;
|
|
21
|
+
}>;
|
|
22
|
+
return cols.some((c) => c.name === name && c.notnull === 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("migrateInviteContactId", () => {
|
|
26
|
+
test("rebuilds a legacy created_by_session_id table with NOT NULL contact_id", () => {
|
|
27
|
+
const db = createTestDb();
|
|
28
|
+
const raw = getSqliteFrom(db);
|
|
29
|
+
raw.exec(/*sql*/ `
|
|
30
|
+
CREATE TABLE assistant_ingress_invites (
|
|
31
|
+
id TEXT PRIMARY KEY,
|
|
32
|
+
source_channel TEXT NOT NULL,
|
|
33
|
+
token_hash TEXT NOT NULL,
|
|
34
|
+
created_by_session_id TEXT,
|
|
35
|
+
note TEXT,
|
|
36
|
+
max_uses INTEGER NOT NULL DEFAULT 1,
|
|
37
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
38
|
+
expires_at INTEGER NOT NULL,
|
|
39
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
40
|
+
redeemed_by_external_user_id TEXT,
|
|
41
|
+
redeemed_by_external_chat_id TEXT,
|
|
42
|
+
redeemed_at INTEGER,
|
|
43
|
+
expected_external_user_id TEXT,
|
|
44
|
+
voice_code_hash TEXT,
|
|
45
|
+
voice_code_digits INTEGER,
|
|
46
|
+
invite_code_hash TEXT,
|
|
47
|
+
friend_name TEXT,
|
|
48
|
+
guardian_name TEXT,
|
|
49
|
+
created_at INTEGER NOT NULL,
|
|
50
|
+
updated_at INTEGER NOT NULL
|
|
51
|
+
)
|
|
52
|
+
`);
|
|
53
|
+
raw.run(
|
|
54
|
+
`INSERT INTO assistant_ingress_invites
|
|
55
|
+
(id, source_channel, token_hash, created_by_session_id, max_uses, use_count,
|
|
56
|
+
expires_at, status, created_at, updated_at)
|
|
57
|
+
VALUES ('inv-keep', 'slack', 'hash-keep', 'conv-1', 1, 0, 9, 'active', 1, 1)`,
|
|
58
|
+
);
|
|
59
|
+
raw.exec(
|
|
60
|
+
`ALTER TABLE assistant_ingress_invites ADD COLUMN contact_id TEXT`,
|
|
61
|
+
);
|
|
62
|
+
raw.run(
|
|
63
|
+
`UPDATE assistant_ingress_invites SET contact_id = 'contact-keep' WHERE id = 'inv-keep'`,
|
|
64
|
+
);
|
|
65
|
+
raw.run(
|
|
66
|
+
`INSERT INTO assistant_ingress_invites
|
|
67
|
+
(id, source_channel, token_hash, max_uses, use_count, expires_at, status,
|
|
68
|
+
created_at, updated_at)
|
|
69
|
+
VALUES ('inv-drop', 'slack', 'hash-drop', 1, 0, 9, 'active', 1, 1)`,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
migrateInviteContactId(db);
|
|
73
|
+
|
|
74
|
+
expect(tableHasColumn(db, "assistant_ingress_invites", "contact_id")).toBe(
|
|
75
|
+
true,
|
|
76
|
+
);
|
|
77
|
+
expect(columnNotNull(raw, "contact_id")).toBe(true);
|
|
78
|
+
expect(
|
|
79
|
+
tableHasColumn(db, "assistant_ingress_invites", "created_by_session_id"),
|
|
80
|
+
).toBe(true);
|
|
81
|
+
const rows = raw
|
|
82
|
+
.query(`SELECT id, contact_id FROM assistant_ingress_invites ORDER BY id`)
|
|
83
|
+
.all() as Array<{ id: string; contact_id: string }>;
|
|
84
|
+
expect(rows).toEqual([{ id: "inv-keep", contact_id: "contact-keep" }]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("rebuilds after created_by_session_id was renamed to source_conversation_id", () => {
|
|
88
|
+
const db = createTestDb();
|
|
89
|
+
const raw = getSqliteFrom(db);
|
|
90
|
+
raw.exec(/*sql*/ `
|
|
91
|
+
CREATE TABLE assistant_ingress_invites (
|
|
92
|
+
id TEXT PRIMARY KEY,
|
|
93
|
+
source_channel TEXT NOT NULL,
|
|
94
|
+
token_hash TEXT NOT NULL,
|
|
95
|
+
source_conversation_id TEXT,
|
|
96
|
+
note TEXT,
|
|
97
|
+
max_uses INTEGER NOT NULL DEFAULT 1,
|
|
98
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
99
|
+
expires_at INTEGER NOT NULL,
|
|
100
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
101
|
+
redeemed_by_external_user_id TEXT,
|
|
102
|
+
redeemed_by_external_chat_id TEXT,
|
|
103
|
+
redeemed_at INTEGER,
|
|
104
|
+
expected_external_user_id TEXT,
|
|
105
|
+
voice_code_hash TEXT,
|
|
106
|
+
voice_code_digits INTEGER,
|
|
107
|
+
invite_code_hash TEXT,
|
|
108
|
+
friend_name TEXT,
|
|
109
|
+
guardian_name TEXT,
|
|
110
|
+
created_at INTEGER NOT NULL,
|
|
111
|
+
updated_at INTEGER NOT NULL
|
|
112
|
+
)
|
|
113
|
+
`);
|
|
114
|
+
raw.exec(
|
|
115
|
+
`ALTER TABLE assistant_ingress_invites ADD COLUMN contact_id TEXT`,
|
|
116
|
+
);
|
|
117
|
+
raw.run(
|
|
118
|
+
`INSERT INTO assistant_ingress_invites
|
|
119
|
+
(id, source_channel, token_hash, source_conversation_id, contact_id,
|
|
120
|
+
max_uses, use_count, expires_at, status, created_at, updated_at)
|
|
121
|
+
VALUES ('inv-1', 'slack', 'hash-1', 'conv-9', 'contact-1', 1, 0, 9, 'active', 1, 1)`,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
migrateInviteContactId(db);
|
|
125
|
+
|
|
126
|
+
expect(columnNotNull(raw, "contact_id")).toBe(true);
|
|
127
|
+
expect(
|
|
128
|
+
tableHasColumn(db, "assistant_ingress_invites", "source_conversation_id"),
|
|
129
|
+
).toBe(true);
|
|
130
|
+
expect(
|
|
131
|
+
tableHasColumn(db, "assistant_ingress_invites", "created_by_session_id"),
|
|
132
|
+
).toBe(false);
|
|
133
|
+
const row = raw
|
|
134
|
+
.query(
|
|
135
|
+
`SELECT source_conversation_id, contact_id FROM assistant_ingress_invites WHERE id = 'inv-1'`,
|
|
136
|
+
)
|
|
137
|
+
.get() as { source_conversation_id: string; contact_id: string };
|
|
138
|
+
expect(row).toEqual({
|
|
139
|
+
source_conversation_id: "conv-9",
|
|
140
|
+
contact_id: "contact-1",
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("is a no-op when the table does not exist", () => {
|
|
145
|
+
const db = createTestDb();
|
|
146
|
+
migrateInviteContactId(db);
|
|
147
|
+
expect(tableExists(db, "assistant_ingress_invites")).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -104,6 +104,11 @@ export const memoryRetrospectiveState = sqliteTable(
|
|
|
104
104
|
conversationId: text("conversation_id").primaryKey(),
|
|
105
105
|
lastProcessedMessageId: text("last_processed_message_id").notNull(),
|
|
106
106
|
lastRunAt: integer("last_run_at").notNull(),
|
|
107
|
+
// `createdAt` of the `last_processed_message_id` row when the pointer was
|
|
108
|
+
// written, so the cursor still bounds reads after that row is deleted.
|
|
109
|
+
// NULL when unknown. Added lazily by `ensureRetrospectiveCursorColumn`
|
|
110
|
+
// (memory-retrospective-state.ts), not by the migration chain.
|
|
111
|
+
lastProcessedCreatedAt: integer("last_processed_created_at"),
|
|
107
112
|
// JSON array of strings — cumulative `remember` contents from prior
|
|
108
113
|
// retrospective passes (capped; see memory-retrospective-state.ts).
|
|
109
114
|
// NULL for rows that predate migration 281 or have no saves yet.
|
|
@@ -24,7 +24,9 @@ mock.module("../providers/platform-proxy/context.js", () => ({
|
|
|
24
24
|
resolveManagedProxyContext: () => mockResolveManagedProxyContext(),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
+
const actualEnv = await import("../config/env.js");
|
|
27
28
|
mock.module("../config/env.js", () => ({
|
|
29
|
+
...actualEnv,
|
|
28
30
|
getPlatformAssistantId: () => mockAssistantId,
|
|
29
31
|
}));
|
|
30
32
|
|
|
@@ -113,11 +115,11 @@ describe("VellumPlatformClient", () => {
|
|
|
113
115
|
platformBaseUrl: "",
|
|
114
116
|
assistantApiKey: "",
|
|
115
117
|
};
|
|
116
|
-
mockAssistantId = "";
|
|
118
|
+
mockAssistantId = "stored-assistant-id";
|
|
117
119
|
mockSecureKeys = {
|
|
118
120
|
"vellum:platform_base_url": "https://stored-platform.example.com/",
|
|
119
121
|
"vellum:assistant_api_key": "stored-api-key",
|
|
120
|
-
"vellum:platform_assistant_id": "
|
|
122
|
+
"vellum:platform_assistant_id": " vault-assistant-id ",
|
|
121
123
|
};
|
|
122
124
|
|
|
123
125
|
const client = await VellumPlatformClient.create();
|
package/src/platform/client.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { PlatformCredentialVerificationStatus } from "@vellumai/service-contracts/platform-credential";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { resolvePlatformAssistantId } from "../config/platform-identity.js";
|
|
11
11
|
import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
|
|
12
12
|
import { credentialKey } from "../security/credential-key.js";
|
|
13
13
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
@@ -63,7 +63,7 @@ async function resolvePlatformClientConfig(): Promise<PlatformClientConfig | nul
|
|
|
63
63
|
|
|
64
64
|
let baseUrl = ctx.enabled ? ctx.platformBaseUrl : "";
|
|
65
65
|
let apiKey = ctx.enabled ? ctx.assistantApiKey : "";
|
|
66
|
-
|
|
66
|
+
const assistantId = await resolvePlatformAssistantId();
|
|
67
67
|
|
|
68
68
|
// Fall back to credential store for values not yet rehydrated (standalone CLI).
|
|
69
69
|
if (!baseUrl) {
|
|
@@ -76,14 +76,6 @@ async function resolvePlatformClientConfig(): Promise<PlatformClientConfig | nul
|
|
|
76
76
|
(await getSecureKeyAsync(credentialKey("vellum", "assistant_api_key"))) ??
|
|
77
77
|
"";
|
|
78
78
|
}
|
|
79
|
-
if (!assistantId) {
|
|
80
|
-
assistantId =
|
|
81
|
-
(
|
|
82
|
-
await getSecureKeyAsync(
|
|
83
|
-
credentialKey("vellum", "platform_assistant_id"),
|
|
84
|
-
)
|
|
85
|
-
)?.trim() ?? "";
|
|
86
|
-
}
|
|
87
79
|
|
|
88
80
|
if (!baseUrl || !apiKey) {
|
|
89
81
|
const level = _missingPrereqsWarned ? "debug" : "warn";
|
|
@@ -34,6 +34,8 @@ export const HOOKS = {
|
|
|
34
34
|
CONVERSATION_DELETED: "conversation-deleted",
|
|
35
35
|
/** Fires once when every conversation is wiped at once (the clear-all reset), after the main tables are cleared. Carries no `conversationId`; cleanup hooks wipe their own per-conversation state wholesale. */
|
|
36
36
|
CONVERSATIONS_CLEARED: "conversations-cleared",
|
|
37
|
+
/** Fires once per deleted message row, after the row is removed, carrying the row's `(createdAt, id)` so a hook can settle state that pointed at it. Fire-and-forget cleanup signal, like `conversation-deleted`; whole-conversation deletes dispatch that hook instead. */
|
|
38
|
+
MESSAGE_DELETED: "message-deleted",
|
|
37
39
|
} as const;
|
|
38
40
|
|
|
39
41
|
/** Union of every hook name declared in {@link HOOKS}. */
|
package/src/plugin-api/index.ts
CHANGED
package/src/plugin-api/types.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type {
|
|
|
16
16
|
ConversationDeletedContext,
|
|
17
17
|
ConversationsClearedContext,
|
|
18
18
|
HookBroadcast,
|
|
19
|
+
MessageDeletedContext,
|
|
19
20
|
PluginLogger,
|
|
20
21
|
PostCompactContext,
|
|
21
22
|
PostModelCallContext,
|
|
@@ -60,6 +61,7 @@ export type {
|
|
|
60
61
|
* - `post-model-call` — {@link PostModelCallContext}
|
|
61
62
|
* - `conversation-deleted` — {@link ConversationDeletedContext}
|
|
62
63
|
* - `conversations-cleared` — {@link ConversationsClearedContext}
|
|
64
|
+
* - `message-deleted`: {@link MessageDeletedContext}
|
|
63
65
|
*/
|
|
64
66
|
export type HookFunction<TCtx = unknown> = (
|
|
65
67
|
ctx: TCtx,
|
|
@@ -65,6 +65,7 @@ import maxTokensContinuePkg from "./max-tokens-continue/package.json" with { typ
|
|
|
65
65
|
import memoryConversationDeleted from "./memory/hooks/conversation-deleted.js";
|
|
66
66
|
import memoryConversationsCleared from "./memory/hooks/conversations-cleared.js";
|
|
67
67
|
import memoryInit from "./memory/hooks/init.js";
|
|
68
|
+
import memoryMessageDeleted from "./memory/hooks/message-deleted.js";
|
|
68
69
|
import memoryPostCompact from "./memory/hooks/post-compact.js";
|
|
69
70
|
import memoryShutdown from "./memory/hooks/shutdown.js";
|
|
70
71
|
import memoryUserPromptSubmit from "./memory/hooks/user-prompt-submit.js";
|
|
@@ -199,6 +200,7 @@ export const defaultMemoryPlugin: Plugin = {
|
|
|
199
200
|
"post-compact": memoryPostCompact,
|
|
200
201
|
"conversation-deleted": memoryConversationDeleted,
|
|
201
202
|
"conversations-cleared": memoryConversationsCleared,
|
|
203
|
+
"message-deleted": memoryMessageDeleted,
|
|
202
204
|
},
|
|
203
205
|
injectors: memoryInjectors,
|
|
204
206
|
};
|
|
@@ -434,3 +434,49 @@ describe("ambient camera frames are not retrospective work", () => {
|
|
|
434
434
|
expect(hasQualifyingUserMessageAfter(CONV, null)).toBe(true);
|
|
435
435
|
});
|
|
436
436
|
});
|
|
437
|
+
|
|
438
|
+
describe("retrospective accounting: a cursor survives its row's deletion", () => {
|
|
439
|
+
beforeEach(() => {
|
|
440
|
+
const db = getDb();
|
|
441
|
+
db.run(`DELETE FROM messages`);
|
|
442
|
+
db.run(`DELETE FROM conversations`);
|
|
443
|
+
createConversation({ id: CONV });
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("a deleted cursor row with a stored timestamp still bounds the count, the slice, and the user-activity probe", () => {
|
|
447
|
+
insertRaw({ role: "user", content: TEXT, createdAt: 1_000 });
|
|
448
|
+
const reply = insertRaw({
|
|
449
|
+
role: "assistant",
|
|
450
|
+
content: TEXT,
|
|
451
|
+
createdAt: 2_000,
|
|
452
|
+
});
|
|
453
|
+
// Regenerate: the reply the cursor sits on is discarded and replaced.
|
|
454
|
+
getDb().delete(messages).where(eq(messages.id, reply)).run();
|
|
455
|
+
const regenerated = insertRaw({
|
|
456
|
+
role: "assistant",
|
|
457
|
+
content: TEXT,
|
|
458
|
+
createdAt: 3_000,
|
|
459
|
+
});
|
|
460
|
+
const followUp = insertRaw({
|
|
461
|
+
role: "user",
|
|
462
|
+
content: TEXT,
|
|
463
|
+
createdAt: 4_000,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
const cursor = { id: reply, createdAt: 2_000 };
|
|
467
|
+
expect(countRetrospectiveMessagesAfter(CONV, cursor)).toBe(2);
|
|
468
|
+
expect(
|
|
469
|
+
getRetrospectiveMessagesAfter(CONV, cursor).map((r) => r.id),
|
|
470
|
+
).toEqual([regenerated, followUp]);
|
|
471
|
+
expect(hasQualifyingUserMessageAfter(CONV, cursor)).toBe(true);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test("a deleted cursor row without a timestamp still reads as no new work", () => {
|
|
475
|
+
insertRaw({ role: "user", content: TEXT, createdAt: 1_000 });
|
|
476
|
+
|
|
477
|
+
const cursor = { id: "gone", createdAt: null };
|
|
478
|
+
expect(countRetrospectiveMessagesAfter(CONV, cursor)).toBe(0);
|
|
479
|
+
expect(getRetrospectiveMessagesAfter(CONV, cursor)).toEqual([]);
|
|
480
|
+
expect(hasQualifyingUserMessageAfter(CONV, cursor)).toBe(false);
|
|
481
|
+
});
|
|
482
|
+
});
|