@vellumai/assistant 0.12.1-staging.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +0 -4
- package/ARCHITECTURE.md +12 -11
- package/docs/architecture/turn-actor.md +8 -15
- package/openapi.yaml +1 -291
- package/package.json +1 -1
- package/src/__tests__/always-loaded-tools-guard.test.ts +10 -18
- package/src/__tests__/app-compiler.test.ts +0 -6
- package/src/__tests__/app-control-tool-schemas.test.ts +2 -5
- package/src/__tests__/app-dir-path-guard.test.ts +3 -1
- package/src/__tests__/app-source-watcher.test.ts +218 -0
- package/src/{calls/__tests__ → __tests__}/call-controller.test.ts +82 -503
- package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +5 -17
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +29 -28
- package/src/__tests__/config-watcher-skill-reseed.test.ts +359 -0
- package/src/__tests__/config-watcher.test.ts +8 -23
- 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 +0 -26
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +10 -4
- package/src/__tests__/conversation-queue.test.ts +207 -48
- package/src/__tests__/conversation-retry-discard.test.ts +1 -54
- package/src/__tests__/conversation-skill-tools.test.ts +0 -27
- package/src/__tests__/conversation-surfaces-app-control.test.ts +3 -8
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -67
- package/src/__tests__/conversation-title-service.test.ts +10 -14
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +5 -3
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -51
- package/src/__tests__/credential-record-retire.test.ts +1 -171
- package/src/__tests__/credential-routes.test.ts +3 -212
- package/src/__tests__/credential-security-invariants.test.ts +1 -2
- package/src/__tests__/cu-unified-flow.test.ts +2 -8
- package/src/__tests__/drain-kick-guard.test.ts +2 -0
- package/src/__tests__/drain-requeue-on-contention.test.ts +6 -0
- package/src/__tests__/evict-conversations-for-reload.test.ts +1 -1
- package/src/__tests__/host-bash-proxy.test.ts +45 -26
- package/src/__tests__/host-browser-proxy.test.ts +17 -6
- package/src/__tests__/host-file-proxy-targeted.test.ts +0 -23
- package/src/__tests__/host-file-proxy.test.ts +23 -11
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +9 -30
- package/src/__tests__/llm-catalog-parity.test.ts +0 -25
- package/src/__tests__/mcp-auth-routes.test.ts +9 -6
- package/src/__tests__/mcp-config-secret-boundary.test.ts +136 -78
- package/src/__tests__/mcp-health-check.test.ts +11 -8
- package/src/__tests__/mcp-list-plugin-servers.test.ts +10 -8
- package/src/{calls/__tests__ → __tests__}/media-stream-audio-transcode.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-output.test.ts +8 -37
- package/src/{calls/__tests__ → __tests__}/media-stream-parser.test.ts +1 -1
- package/src/{calls/__tests__ → __tests__}/media-stream-server-integration.test.ts +57 -150
- package/src/{calls/__tests__ → __tests__}/media-stream-stt-session.test.ts +5 -5
- package/src/{calls/__tests__ → __tests__}/media-turn-detector.test.ts +1 -1
- package/src/__tests__/messages-after-tiebreaker.test.ts +0 -91
- package/src/__tests__/messages-read-boundary-guard.test.ts +1 -1
- package/src/__tests__/messaging-send-tool.test.ts +548 -355
- package/src/__tests__/model-intents.test.ts +0 -3
- package/src/__tests__/outlook-messaging-provider.test.ts +6 -6
- package/src/__tests__/platform-client-verify-credential.test.ts +0 -2
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -13
- package/src/__tests__/script-proxy-certs.test.ts +0 -35
- package/src/__tests__/skill-feature-flags-integration.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/speaker-identification.test.ts +56 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +3 -19
- package/src/__tests__/subagent-tools.test.ts +2 -3
- package/src/__tests__/telephony-tts-guard.test.ts +3 -38
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +0 -4
- package/src/{calls/__tests__/voice-session-bridge-turn-flow.test.ts → __tests__/voice-session-bridge.test.ts} +12 -20
- package/src/__tests__/watch-retro-tool-availability.test.ts +17 -8
- package/src/__tests__/workspace-git-service.test.ts +0 -26
- package/src/acp/__tests__/agent-process.test.ts +4 -258
- package/src/acp/__tests__/auth-required.test.ts +0 -96
- package/src/acp/__tests__/client-handler.test.ts +2 -90
- package/src/acp/__tests__/session-manager-persistence.test.ts +1 -5
- package/src/acp/__tests__/session-manager-resume.test.ts +6 -270
- package/src/acp/__tests__/session-manager-usage.test.ts +2 -4
- package/src/acp/__tests__/session-manager.test.ts +4 -895
- package/src/acp/agent-process.test.ts +1 -4
- package/src/acp/agent-process.ts +9 -94
- package/src/acp/auth-required.ts +1 -50
- package/src/acp/auto-install.test.ts +7 -9
- package/src/acp/client-handler.ts +3 -16
- package/src/acp/resolve-agent.test.ts +6 -220
- package/src/acp/resolve-agent.ts +23 -88
- package/src/acp/session-manager.test.ts +3 -196
- package/src/acp/session-manager.ts +18 -441
- package/src/acp/types.ts +5 -50
- package/src/api/events/app-files-changed.ts +3 -4
- package/src/api/index.ts +0 -6
- package/src/apps/app-store.ts +1 -1
- package/src/bundler/app-compiler.ts +0 -15
- package/src/calls/__tests__/voice-control-protocol.test.ts +25 -58
- package/src/calls/__tests__/voice-session-bridge.test.ts +1 -37
- package/src/calls/__tests__/voice-triage-escalate.test.ts +0 -105
- package/src/calls/call-constants.ts +4 -24
- package/src/calls/call-controller.ts +199 -499
- package/src/calls/call-transport.ts +0 -16
- package/src/calls/media-stream-output.ts +0 -32
- package/src/calls/media-stream-server.ts +30 -82
- package/src/calls/media-stream-stt-session.ts +0 -15
- package/src/calls/resolve-call-tts-provider.ts +2 -33
- package/src/calls/speaker-identification.ts +233 -0
- package/src/calls/tts-call-strategy.ts +5 -9
- package/src/calls/types.ts +0 -2
- package/src/calls/voice-control-protocol.ts +21 -53
- package/src/calls/voice-session-bridge.ts +46 -59
- package/src/calls/voice-triage-escalate.ts +56 -175
- package/src/cli/commands/apps.help.ts +13 -67
- package/src/cli/commands/apps.ts +0 -183
- package/src/cli/commands/channels/index.help.ts +2 -70
- package/src/cli/commands/channels/index.ts +3 -16
- package/src/cli/commands/credentials.help.ts +2 -6
- 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/roadmap.help.ts +1 -5
- package/src/cli/commands/roadmap.ts +1 -1
- package/src/cli/lib/bundled-marketplace.json +90 -103
- package/src/config/__tests__/default-profile-catalog.test.ts +0 -22
- package/src/config/__tests__/memory-retrospective-schema.test.ts +1 -15
- package/src/config/__tests__/platform-rehydration.test.ts +0 -93
- package/src/config/acp-defaults.test.ts +3 -13
- package/src/config/acp-defaults.ts +6 -23
- package/src/config/acp-schema.ts +2 -30
- package/src/config/bundled-skills/acp/SKILL.md +14 -22
- package/src/config/bundled-skills/acp/TOOLS.json +1 -5
- package/src/config/bundled-skills/app-builder/SKILL.md +9 -12
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +1 -1
- package/src/config/bundled-skills/messaging/TOOLS.json +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +139 -67
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +1 -1
- package/src/config/bundled-skills/screen-annotation/SKILL.md +1 -8
- package/src/config/bundled-skills/screen-annotation/tools/screen-annotation.test.ts +1 -4
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/default-profile-catalog.ts +1 -1
- package/src/config/env.ts +2 -2
- package/src/config/feature-flag-registry.json +12 -8
- package/src/config/memory-v3-gate.test.ts +0 -31
- package/src/config/memory-v3-gate.ts +16 -20
- package/src/config/platform-rehydration.ts +21 -75
- package/src/config/schema.ts +2 -2
- package/src/config/schemas/__tests__/live-voice.test.ts +158 -8
- package/src/config/schemas/calls.ts +28 -1
- package/src/config/schemas/live-voice.ts +173 -1
- package/src/config/schemas/mcp.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +0 -10
- package/src/config/schemas/skills.ts +14 -0
- package/src/credential-execution/process-manager.test.ts +19 -18
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +121 -66
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +1 -4
- package/src/daemon/app-source-watcher.ts +218 -0
- package/src/daemon/config-watcher.ts +194 -13
- package/src/daemon/conversation-process.ts +64 -35
- package/src/daemon/conversation-skill-tools.ts +31 -13
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +8 -10
- package/src/daemon/conversation-tool-setup.ts +43 -37
- package/src/daemon/conversation.ts +3 -0
- package/src/daemon/host-bash-proxy.ts +12 -8
- package/src/daemon/host-browser-proxy.ts +1 -15
- package/src/daemon/host-file-proxy.ts +10 -8
- package/src/daemon/host-transfer-proxy.ts +18 -14
- package/src/daemon/lifecycle.ts +5 -0
- package/src/daemon/mcp-reload-service.ts +5 -5
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/shutdown-handlers.ts +9 -1
- package/src/daemon/skill-memory-refresh.ts +0 -64
- package/src/daemon/tool-side-effects.ts +18 -7
- package/src/hooks/types.ts +0 -33
- package/src/inbound/platform-callback-registration.ts +5 -4
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +3 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -15
- package/src/live-voice/__tests__/live-voice-vad.test.ts +2 -2
- package/src/{calls → live-voice}/__tests__/progress-narration.test.ts +4 -4
- package/src/{calls → live-voice}/__tests__/progress-phrases.test.ts +1 -1
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +69 -13
- package/src/live-voice/live-voice-session.ts +693 -213
- package/src/{calls → live-voice}/progress-narration.ts +2 -2
- package/src/live-voice/protocol.ts +2 -0
- package/src/mcp/__tests__/effective-config.test.ts +2 -2
- package/src/mcp/effective-config.ts +3 -3
- package/src/mcp/mcp-header-store.ts +45 -29
- package/src/mcp/startup.ts +4 -8
- package/src/messaging/provider.ts +3 -10
- package/src/messaging/providers/callback-routing.ts +0 -18
- package/src/messaging/providers/channel-transport.ts +0 -56
- package/src/messaging/providers/discord/transport.ts +0 -17
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +17 -6
- package/src/messaging/providers/slack/adapter.ts +44 -7
- package/src/messaging/providers/slack/transport.ts +0 -16
- package/src/messaging/providers/telegram-bot/adapter.ts +48 -1
- package/src/messaging/providers/telegram-bot/client.ts +26 -0
- package/src/messaging/providers/telegram-bot/transport.ts +0 -20
- package/src/messaging/providers/whatsapp/adapter.ts +48 -1
- package/src/messaging/providers/whatsapp/client.ts +29 -0
- package/src/messaging/providers/whatsapp/transport.ts +0 -18
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +0 -8
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +0 -55
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +3 -153
- package/src/notifications/assistant-reply-producer.ts +3 -8
- package/src/notifications/conversation-pairing.ts +16 -36
- package/src/notifications/delivered-post-record.ts +1 -11
- package/src/notifications/home-feed-side-effect.ts +7 -55
- package/src/outbound-proxy/certs.ts +5 -3
- package/src/permissions/checker.test.ts +6 -11
- package/src/persistence/compaction-log-store-clickhouse.ts +3 -3
- package/src/persistence/conversation-crud.ts +63 -71
- package/src/persistence/conversation-lineage.ts +4 -12
- package/src/persistence/conversation-title-service.ts +26 -9
- 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-reads.ts +0 -30
- package/src/persistence/migrations/157-invite-contact-id.ts +6 -18
- package/src/persistence/schema/memory-core.ts +0 -5
- package/src/platform/client.test.ts +0 -2
- package/src/platform/client.ts +10 -2
- package/src/plugin-api/constants.ts +0 -2
- package/src/plugin-api/index.ts +0 -1
- package/src/plugin-api/types.ts +0 -2
- package/src/plugins/defaults/index.ts +0 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +0 -46
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +5 -9
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +10 -20
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-state.test.ts +0 -219
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +0 -31
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-trigger-check.test.ts +0 -63
- package/src/plugins/defaults/memory/__tests__/shutdown-hook.test.ts +0 -4
- package/src/plugins/defaults/memory/graph/capability-seed.ts +6 -4
- package/src/plugins/defaults/memory/hooks/init.ts +0 -10
- package/src/plugins/defaults/memory/hooks/shutdown.ts +0 -2
- package/src/plugins/defaults/memory/jobs-worker.ts +0 -13
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +60 -34
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +5 -3
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -12
- package/src/plugins/defaults/memory/memory-retrospective-state.ts +71 -312
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +1 -2
- package/src/plugins/defaults/memory/memory-retrospective-trigger-check.ts +3 -5
- package/src/plugins/defaults/memory/startup.ts +1 -6
- package/src/plugins/defaults/memory/substrate/__tests__/skill-content.test.ts +9 -7
- package/src/plugins/defaults/memory/substrate/skill-content.ts +2 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +0 -89
- package/src/plugins/defaults/memory/worker.ts +0 -6
- package/src/plugins/mcp-servers.ts +75 -15
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -3
- package/src/providers/gemini/client.ts +23 -9
- package/src/providers/model-catalog.ts +0 -34
- package/src/providers/model-intents.ts +5 -10
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +18 -146
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +1 -83
- package/src/providers/openai/chat-completions-provider.ts +78 -192
- package/src/providers/openai/codex-models.ts +4 -0
- package/src/runtime/AGENTS.md +0 -2
- package/src/runtime/auth/same-actor.ts +3 -38
- package/src/runtime/http-server.ts +151 -160
- package/src/runtime/routes/__tests__/acp-routes.test.ts +4 -159
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +1 -108
- package/src/runtime/routes/__tests__/mcp-add-default-risk.test.ts +12 -16
- package/src/runtime/routes/acp-routes.test.ts +0 -15
- package/src/runtime/routes/acp-routes.ts +9 -72
- package/src/runtime/routes/app-management-routes.ts +0 -73
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +0 -40
- package/src/runtime/routes/credential-routes.ts +10 -19
- package/src/runtime/routes/diagnostics-routes.ts +3 -55
- package/src/runtime/routes/index.ts +0 -4
- package/src/runtime/routes/log-export-routes.ts +0 -2
- package/src/runtime/routes/mcp-auth-routes.ts +47 -39
- package/src/runtime/routes/migration-routes.ts +21 -8
- package/src/runtime/routes/platform-routes.ts +15 -18
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -28
- package/src/runtime/services/conversation-serializer.ts +3 -16
- package/src/runtime/sync/resource-sync-events.ts +1 -2
- package/src/schedule/worker.ts +1 -1
- package/src/security/ces-rpc-record-backend.ts +0 -7
- package/src/subagent/consult-context.ts +25 -12
- package/src/tools/acp/list-agents.test.ts +1 -3
- package/src/tools/acp/spawn.test.ts +11 -146
- package/src/tools/acp/spawn.ts +3 -21
- package/src/tools/acp/status.ts +3 -40
- package/src/tools/app-control/skill-proxy-bridge.ts +1 -2
- package/src/tools/capability-offer.test.ts +2 -22
- package/src/tools/capability-offer.ts +1 -16
- package/src/tools/computer-use/definitions.ts +2 -4
- package/src/tools/computer-use/skill-proxy-bridge.ts +1 -2
- package/src/tools/credentials/metadata-store.ts +0 -100
- package/src/tools/host-filesystem/edit.test.ts +1 -2
- package/src/tools/host-filesystem/edit.ts +2 -2
- package/src/tools/host-filesystem/read.test.ts +1 -2
- package/src/tools/host-filesystem/read.ts +2 -2
- package/src/tools/host-filesystem/transfer.test.ts +1 -2
- package/src/tools/host-filesystem/transfer.ts +2 -2
- package/src/tools/host-filesystem/write.test.ts +1 -2
- package/src/tools/host-filesystem/write.ts +2 -2
- package/src/tools/host-terminal/host-shell.test.ts +0 -16
- package/src/tools/host-terminal/host-shell.ts +20 -4
- package/src/tools/policy-context.ts +7 -6
- package/src/tools/subagent/spawn.ts +8 -5
- package/src/tools/watch/watch-retro-report.ts +8 -0
- package/src/util/__tests__/language-subtag.test.ts +1 -23
- package/src/util/fs-watcher-error.ts +36 -0
- package/src/util/language-subtag.ts +0 -16
- package/src/util/platform.ts +0 -8
- package/src/workspace/git-service.ts +54 -5
- package/src/workspace/migrations/registry.ts +0 -6
- package/scripts/smoke-desktop-window-theme.ts +0 -186
- package/src/__tests__/git-porcelain.test.ts +0 -47
- package/src/__tests__/helpers/set-workspace-mcp.ts +0 -11
- package/src/__tests__/message-deleted-hook-dispatch.test.ts +0 -119
- package/src/__tests__/resolve-app.test.ts +0 -135
- package/src/__tests__/source-fingerprint.test.ts +0 -184
- package/src/__tests__/workspace-migration-154-repair-retired-codex-gpt-5-4-model-ids.test.ts +0 -336
- package/src/acp/__tests__/helpers/acp-model-option.ts +0 -51
- package/src/acp/model-config.test.ts +0 -166
- package/src/acp/model-config.ts +0 -138
- package/src/api/events/acp-session-model-update.test.ts +0 -88
- package/src/api/events/acp-session-model-update.ts +0 -46
- package/src/apps/resolve-app.ts +0 -81
- package/src/apps/source-fingerprint.ts +0 -266
- package/src/calls/__tests__/barge-in-guard.test.ts +0 -81
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +0 -229
- package/src/calls/__tests__/voice-progress-cadence.test.ts +0 -274
- package/src/calls/barge-in-guard.ts +0 -118
- package/src/calls/voice-leg-coordinator.ts +0 -189
- package/src/calls/voice-progress-cadence.ts +0 -423
- package/src/cli/commands/__tests__/apps.test.ts +0 -245
- package/src/cli/commands/__tests__/roadmap.help.test.ts +0 -19
- package/src/cli/commands/channels/__tests__/send.test.ts +0 -218
- package/src/cli/commands/channels/send.ts +0 -128
- package/src/config/__tests__/platform-identity.test.ts +0 -117
- package/src/config/acp-agent-write.ts +0 -43
- package/src/config/acp-schema.test.ts +0 -81
- package/src/config/platform-identity.ts +0 -144
- package/src/config/schemas/__tests__/voice.test.ts +0 -148
- package/src/config/schemas/voice.ts +0 -150
- package/src/daemon/__tests__/skill-memory-refresh-mtime.test.ts +0 -133
- package/src/daemon/app-change-notify.ts +0 -45
- package/src/desktop/__tests__/fake-desktop.ts +0 -119
- package/src/desktop/desktop-chrome-policy.test.ts +0 -71
- package/src/desktop/desktop-chrome-policy.ts +0 -42
- package/src/desktop/desktop-dependencies.test.ts +0 -102
- package/src/desktop/desktop-dependencies.ts +0 -306
- package/src/desktop/desktop-feature.test.ts +0 -18
- package/src/desktop/desktop-feature.ts +0 -19
- package/src/desktop/desktop-panel-config.test.ts +0 -135
- package/src/desktop/desktop-panel-config.ts +0 -149
- package/src/desktop/desktop-session-manager.test.ts +0 -627
- package/src/desktop/desktop-session-manager.ts +0 -814
- package/src/desktop/desktop-stream-bridge.test.ts +0 -395
- package/src/desktop/desktop-stream-bridge.ts +0 -214
- package/src/desktop/desktop-wallpaper.test.ts +0 -191
- package/src/desktop/desktop-wallpaper.ts +0 -113
- package/src/desktop/desktop-window-theme.ts +0 -170
- package/src/i18n/AGENTS.md +0 -22
- package/src/i18n/__tests__/i18n.test.ts +0 -108
- package/src/i18n/index.ts +0 -16
- package/src/i18n/locales.ts +0 -93
- package/src/i18n/messages.ts +0 -61
- package/src/i18n/title.ts +0 -25
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +0 -135
- package/src/mcp/spec-schema.ts +0 -111
- package/src/mcp/workspace-mcp-config.ts +0 -188
- package/src/messaging/providers/__tests__/proactive-address.test.ts +0 -95
- package/src/persistence/conversation-title-placeholders.ts +0 -23
- package/src/persistence/message-cursor.ts +0 -78
- package/src/persistence/migrations/__tests__/157-invite-contact-id.test.ts +0 -149
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-backfill.test.ts +0 -118
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-cursor-preserve.test.ts +0 -83
- package/src/plugins/defaults/memory/hooks/message-deleted.ts +0 -21
- package/src/plugins/defaults/memory/memory-retrospective-cursor-backfill.ts +0 -126
- package/src/plugins/defaults/memory/memory-retrospective-cursor-preserve.ts +0 -79
- package/src/plugins/defaults/memory/memory-retrospective-cursor.ts +0 -28
- package/src/plugins/defaults/memory/skill-md-mtime-poll.ts +0 -9
- package/src/providers/gemini-thought-signature.test.ts +0 -63
- package/src/providers/gemini-thought-signature.ts +0 -44
- package/src/providers/openai/__tests__/chat-completions-provider-thought-signature.test.ts +0 -613
- package/src/providers/openai/__tests__/google-thought-signature.test.ts +0 -235
- package/src/providers/openai/google-thought-signature.ts +0 -320
- package/src/runtime/__tests__/channel-send.test.ts +0 -443
- package/src/runtime/__tests__/desktop-stream-upgrade.test.ts +0 -89
- package/src/runtime/__tests__/live-voice-upgrade.test.ts +0 -58
- package/src/runtime/__tests__/runtime-ws-test-utils.ts +0 -72
- package/src/runtime/__tests__/watch-stream-upgrade.test.ts +0 -59
- package/src/runtime/channel-send.ts +0 -316
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +0 -205
- package/src/runtime/routes/__tests__/channel-send-routes.test.ts +0 -127
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
- package/src/runtime/routes/channel-send-routes.ts +0 -85
- package/src/runtime/routes/desktop-setup-routes.test.ts +0 -74
- package/src/runtime/routes/desktop-setup-routes.ts +0 -35
- package/src/security/__tests__/ces-http-record-backend.test.ts +0 -137
- package/src/security/ces-http-record-backend.ts +0 -120
- package/src/skills/__tests__/workspace-skill-md-signature.test.ts +0 -60
- package/src/skills/workspace-skill-md-signature.ts +0 -50
- package/src/tools/acp/status.test.ts +0 -94
- package/src/util/ca-bundle.ts +0 -13
- package/src/workspace/git-porcelain.ts +0 -66
- package/src/workspace/migrations/154-repair-retired-codex-gpt-5-4-model-ids.ts +0 -258
- package/src/workspace/migrations/155-move-front-model-config-to-voice.ts +0 -78
- package/src/workspace/migrations/156-extract-workspace-mcp-json.ts +0 -214
- package/src/workspace/migrations/__tests__/155-move-front-model-config-to-voice.test.ts +0 -102
- package/src/workspace/migrations/__tests__/156-extract-workspace-mcp-json.test.ts +0 -199
- /package/src/{calls → live-voice}/progress-phrases.ts +0 -0
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
// The addressing capability resolves a named chat into exactly what each
|
|
3
|
-
// transport's own operations read: the callback URL `channelForCallback`
|
|
4
|
-
// resolves and the params `deliver` consumes. Resolution is local, so these
|
|
5
|
-
// assertions make no platform call; the tests import the transports'
|
|
6
|
-
// modules, which pull in their provider-API senders, so the senders'
|
|
7
|
-
// credential lookups are stubbed to keep the import inert.
|
|
8
|
-
import { mock } from "bun:test";
|
|
9
|
-
|
|
10
|
-
const actualSecureKeys = await import("../../../security/secure-keys.js");
|
|
11
|
-
mock.module("../../../security/secure-keys.js", () => ({
|
|
12
|
-
...actualSecureKeys,
|
|
13
|
-
getSecureKeyAsync: async () => null,
|
|
14
|
-
getSecureKeyResultAsync: async () => ({ value: null }),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
const { channelForCallback } = await import("../callback-routing.js");
|
|
18
|
-
const { discordTransport } = await import("../discord/transport.js");
|
|
19
|
-
const { slackTransport } = await import("../slack/transport.js");
|
|
20
|
-
const { telegramTransport } = await import("../telegram-bot/transport.js");
|
|
21
|
-
const { whatsappTransport } = await import("../whatsapp/transport.js");
|
|
22
|
-
|
|
23
|
-
describe("addressFor resolves to the channel's own routing state", () => {
|
|
24
|
-
test("slack: a chat, optionally under a thread", () => {
|
|
25
|
-
const room = slackTransport.addressFor!({ kind: "chat", chatId: "C123" });
|
|
26
|
-
expect(room).toEqual({
|
|
27
|
-
ctx: { callbackUrl: "/deliver/slack", params: {} },
|
|
28
|
-
chatId: "C123",
|
|
29
|
-
});
|
|
30
|
-
const thread = slackTransport.addressFor!({
|
|
31
|
-
kind: "chat",
|
|
32
|
-
chatId: "C123",
|
|
33
|
-
threadId: "1690000000.000001",
|
|
34
|
-
});
|
|
35
|
-
expect(thread).toEqual({
|
|
36
|
-
ctx: {
|
|
37
|
-
callbackUrl: "/deliver/slack?threadTs=1690000000.000001",
|
|
38
|
-
params: { threadTs: "1690000000.000001" },
|
|
39
|
-
},
|
|
40
|
-
chatId: "C123",
|
|
41
|
-
threadId: "1690000000.000001",
|
|
42
|
-
});
|
|
43
|
-
expect(channelForCallback(thread!.ctx.callbackUrl)).toBe("slack");
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("telegram: a chat with a topic; binds on send", () => {
|
|
47
|
-
expect(
|
|
48
|
-
telegramTransport.addressFor!({
|
|
49
|
-
kind: "chat",
|
|
50
|
-
chatId: "123",
|
|
51
|
-
threadId: "42",
|
|
52
|
-
}),
|
|
53
|
-
).toEqual({
|
|
54
|
-
ctx: {
|
|
55
|
-
callbackUrl: "/deliver/telegram?threadId=42",
|
|
56
|
-
params: { threadId: "42" },
|
|
57
|
-
},
|
|
58
|
-
chatId: "123",
|
|
59
|
-
threadId: "42",
|
|
60
|
-
});
|
|
61
|
-
expect(telegramTransport.bindsChatOnProactiveSend).toBe(true);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("discord: a chat with a thread, which is its own channel", () => {
|
|
65
|
-
const thread = discordTransport.addressFor!({
|
|
66
|
-
kind: "chat",
|
|
67
|
-
chatId: "C1",
|
|
68
|
-
threadId: "T1",
|
|
69
|
-
});
|
|
70
|
-
expect(thread).toEqual({
|
|
71
|
-
ctx: {
|
|
72
|
-
callbackUrl: "/deliver/discord?threadId=T1",
|
|
73
|
-
params: { threadId: "T1" },
|
|
74
|
-
},
|
|
75
|
-
chatId: "C1",
|
|
76
|
-
threadId: "T1",
|
|
77
|
-
});
|
|
78
|
-
expect(channelForCallback(thread!.ctx.callbackUrl)).toBe("discord");
|
|
79
|
-
expect(discordTransport.bindsChatOnProactiveSend).toBeUndefined();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("whatsapp: the number is the chat; no threads; binds on send", () => {
|
|
83
|
-
expect(
|
|
84
|
-
whatsappTransport.addressFor!({
|
|
85
|
-
kind: "chat",
|
|
86
|
-
chatId: "12125550100",
|
|
87
|
-
threadId: "ignored",
|
|
88
|
-
}),
|
|
89
|
-
).toEqual({
|
|
90
|
-
ctx: { callbackUrl: "/deliver/whatsapp", params: {} },
|
|
91
|
-
chatId: "12125550100",
|
|
92
|
-
});
|
|
93
|
-
expect(whatsappTransport.bindsChatOnProactiveSend).toBe(true);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { isMessageKey } from "../i18n/index.js";
|
|
2
|
-
|
|
3
|
-
const REPLACEABLE_PATTERNS = [
|
|
4
|
-
/^Runtime:\s/,
|
|
5
|
-
/^New Conversation$/,
|
|
6
|
-
/^Untitled$/,
|
|
7
|
-
/^Untitled Conversation$/,
|
|
8
|
-
/^Generating title\.\.\.$/,
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Check whether a title is a system-generated placeholder that can be
|
|
13
|
-
* replaced or omitted without discarding user-provided copy.
|
|
14
|
-
*/
|
|
15
|
-
export function isReplaceableTitle(title: string | null): boolean {
|
|
16
|
-
if (title == null || title.trim() === "") {
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
if (isMessageKey(title.trim())) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
return REPLACEABLE_PATTERNS.some((pattern) => pattern.test(title));
|
|
23
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { setConfig } from "../../../../__tests__/helpers/set-config.js";
|
|
4
|
-
|
|
5
|
-
// Disable memory so persistence writes don't index into the real memory
|
|
6
|
-
// pipeline (both flags default true under the real loader).
|
|
7
|
-
setConfig("memory", { enabled: false, v2: { enabled: false } });
|
|
8
|
-
|
|
9
|
-
import { createConversation } from "../../../../persistence/conversation-crud.js";
|
|
10
|
-
import {
|
|
11
|
-
getDb,
|
|
12
|
-
getMemorySqlite,
|
|
13
|
-
} from "../../../../persistence/db-connection.js";
|
|
14
|
-
import { initializeDb } from "../../../../persistence/db-init.js";
|
|
15
|
-
import { messages } from "../../../../persistence/schema/index.js";
|
|
16
|
-
import { backfillRetrospectiveCursorTimestamps } from "../memory-retrospective-cursor-backfill.js";
|
|
17
|
-
import {
|
|
18
|
-
ensureRetrospectiveCursorColumn,
|
|
19
|
-
getRetrospectiveState,
|
|
20
|
-
} from "../memory-retrospective-state.js";
|
|
21
|
-
|
|
22
|
-
await initializeDb();
|
|
23
|
-
|
|
24
|
-
function resetTables(): void {
|
|
25
|
-
const db = getDb();
|
|
26
|
-
db.run(`DELETE FROM messages`);
|
|
27
|
-
db.run(`DELETE FROM conversations`);
|
|
28
|
-
getMemorySqlite()!.exec(`DELETE FROM memory_retrospective_state`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** A state row as written before the timestamp column existed. */
|
|
32
|
-
function insertLegacyState(conversationId: string, messageId: string): void {
|
|
33
|
-
ensureRetrospectiveCursorColumn("test");
|
|
34
|
-
getMemorySqlite()!
|
|
35
|
-
.query(
|
|
36
|
-
`INSERT INTO memory_retrospective_state
|
|
37
|
-
(conversation_id, last_processed_message_id, last_run_at, remembered_log, last_processed_created_at)
|
|
38
|
-
VALUES (?, ?, 1, NULL, NULL)`,
|
|
39
|
-
)
|
|
40
|
-
.run(conversationId, messageId);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function insertMessage(
|
|
44
|
-
conversationId: string,
|
|
45
|
-
id: string,
|
|
46
|
-
createdAt: number,
|
|
47
|
-
): void {
|
|
48
|
-
getDb()
|
|
49
|
-
.insert(messages)
|
|
50
|
-
.values({
|
|
51
|
-
id,
|
|
52
|
-
conversationId,
|
|
53
|
-
role: "user",
|
|
54
|
-
content: "[]",
|
|
55
|
-
createdAt,
|
|
56
|
-
metadata: null,
|
|
57
|
-
})
|
|
58
|
-
.run();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
describe("backfillRetrospectiveCursorTimestamps", () => {
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
resetTables();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("fills the timestamp from the cursor's message, leaves vanished cursors NULL, and is idempotent", async () => {
|
|
67
|
-
createConversation({ id: "conv-a" });
|
|
68
|
-
createConversation({ id: "conv-b" });
|
|
69
|
-
createConversation({ id: "conv-c" });
|
|
70
|
-
insertMessage("conv-a", "a-m1", 1_000);
|
|
71
|
-
insertLegacyState("conv-a", "a-m1");
|
|
72
|
-
// Cursor row already deleted: nothing left to recover.
|
|
73
|
-
insertLegacyState("conv-b", "gone");
|
|
74
|
-
// The failure-only sentinel is never a cursor, so it is never scanned.
|
|
75
|
-
insertLegacyState("conv-c", "");
|
|
76
|
-
|
|
77
|
-
const first = await backfillRetrospectiveCursorTimestamps();
|
|
78
|
-
|
|
79
|
-
expect(first).toEqual({ scanned: 2, backfilled: 1, unrecoverable: 1 });
|
|
80
|
-
expect(getRetrospectiveState("conv-a")?.lastProcessedCreatedAt).toBe(1_000);
|
|
81
|
-
expect(getRetrospectiveState("conv-b")?.lastProcessedCreatedAt).toBeNull();
|
|
82
|
-
expect(getRetrospectiveState("conv-c")?.lastProcessedCreatedAt).toBeNull();
|
|
83
|
-
|
|
84
|
-
const second = await backfillRetrospectiveCursorTimestamps();
|
|
85
|
-
|
|
86
|
-
expect(second).toEqual({ scanned: 1, backfilled: 0, unrecoverable: 1 });
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("pages through the backlog by conversation id, advancing past unrecoverable rows", async () => {
|
|
90
|
-
for (const id of ["conv-1", "conv-2", "conv-3", "conv-4", "conv-5"]) {
|
|
91
|
-
createConversation({ id });
|
|
92
|
-
}
|
|
93
|
-
insertMessage("conv-1", "m-1", 1_000);
|
|
94
|
-
insertLegacyState("conv-1", "m-1");
|
|
95
|
-
insertLegacyState("conv-2", "gone-2");
|
|
96
|
-
insertMessage("conv-3", "m-3", 3_000);
|
|
97
|
-
insertLegacyState("conv-3", "m-3");
|
|
98
|
-
insertLegacyState("conv-4", "gone-4");
|
|
99
|
-
insertMessage("conv-5", "m-5", 5_000);
|
|
100
|
-
insertLegacyState("conv-5", "m-5");
|
|
101
|
-
|
|
102
|
-
const result = await backfillRetrospectiveCursorTimestamps({ pageSize: 2 });
|
|
103
|
-
|
|
104
|
-
expect(result).toEqual({ scanned: 5, backfilled: 3, unrecoverable: 2 });
|
|
105
|
-
expect(getRetrospectiveState("conv-1")?.lastProcessedCreatedAt).toBe(1_000);
|
|
106
|
-
expect(getRetrospectiveState("conv-3")?.lastProcessedCreatedAt).toBe(3_000);
|
|
107
|
-
expect(getRetrospectiveState("conv-5")?.lastProcessedCreatedAt).toBe(5_000);
|
|
108
|
-
expect(getRetrospectiveState("conv-4")?.lastProcessedCreatedAt).toBeNull();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("a table with nothing to fill is a clean no-op", async () => {
|
|
112
|
-
expect(await backfillRetrospectiveCursorTimestamps()).toEqual({
|
|
113
|
-
scanned: 0,
|
|
114
|
-
backfilled: 0,
|
|
115
|
-
unrecoverable: 0,
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import { setConfig } from "../../../../__tests__/helpers/set-config.js";
|
|
4
|
-
|
|
5
|
-
// Disable memory so persistence writes don't index into the real memory
|
|
6
|
-
// pipeline (both flags default true under the real loader).
|
|
7
|
-
setConfig("memory", { enabled: false, v2: { enabled: false } });
|
|
8
|
-
|
|
9
|
-
import { getMemorySqlite } from "../../../../persistence/db-connection.js";
|
|
10
|
-
import { initializeDb } from "../../../../persistence/db-init.js";
|
|
11
|
-
import { preserveRetrospectiveCursorTimestamps } from "../memory-retrospective-cursor-preserve.js";
|
|
12
|
-
import {
|
|
13
|
-
ensureRetrospectiveCursorColumn,
|
|
14
|
-
getRetrospectiveState,
|
|
15
|
-
upsertRetrospectiveState,
|
|
16
|
-
} from "../memory-retrospective-state.js";
|
|
17
|
-
|
|
18
|
-
await initializeDb();
|
|
19
|
-
|
|
20
|
-
/** A state row as written before the timestamp column existed. */
|
|
21
|
-
function insertLegacyState(conversationId: string, messageId: string): void {
|
|
22
|
-
ensureRetrospectiveCursorColumn("test");
|
|
23
|
-
getMemorySqlite()!
|
|
24
|
-
.query(
|
|
25
|
-
`INSERT INTO memory_retrospective_state
|
|
26
|
-
(conversation_id, last_processed_message_id, last_run_at, remembered_log, last_processed_created_at)
|
|
27
|
-
VALUES (?, ?, 1, NULL, NULL)`,
|
|
28
|
-
)
|
|
29
|
-
.run(conversationId, messageId);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
describe("preserveRetrospectiveCursorTimestamps", () => {
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
getMemorySqlite()!.exec(`DELETE FROM memory_retrospective_state`);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("records the timestamp of a legacy cursor whose row is about to be deleted", async () => {
|
|
38
|
-
insertLegacyState("conv-a", "reply-1");
|
|
39
|
-
|
|
40
|
-
await preserveRetrospectiveCursorTimestamps("conv-a", [
|
|
41
|
-
{ id: "tool-1", createdAt: 1_500 },
|
|
42
|
-
{ id: "reply-1", createdAt: 2_000 },
|
|
43
|
-
]);
|
|
44
|
-
|
|
45
|
-
expect(getRetrospectiveState("conv-a")?.lastProcessedCreatedAt).toBe(2_000);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("leaves a cursor that already has a timestamp, points elsewhere, or does not exist untouched", async () => {
|
|
49
|
-
await upsertRetrospectiveState({
|
|
50
|
-
conversationId: "conv-stamped",
|
|
51
|
-
lastProcessedMessageId: "reply-1",
|
|
52
|
-
lastProcessedCreatedAt: 1_000,
|
|
53
|
-
lastRunAt: 1,
|
|
54
|
-
});
|
|
55
|
-
insertLegacyState("conv-elsewhere", "older-reply");
|
|
56
|
-
|
|
57
|
-
await preserveRetrospectiveCursorTimestamps("conv-stamped", [
|
|
58
|
-
{ id: "reply-1", createdAt: 9_999 },
|
|
59
|
-
]);
|
|
60
|
-
await preserveRetrospectiveCursorTimestamps("conv-elsewhere", [
|
|
61
|
-
{ id: "reply-1", createdAt: 9_999 },
|
|
62
|
-
]);
|
|
63
|
-
await preserveRetrospectiveCursorTimestamps("conv-missing", [
|
|
64
|
-
{ id: "reply-1", createdAt: 9_999 },
|
|
65
|
-
]);
|
|
66
|
-
|
|
67
|
-
expect(getRetrospectiveState("conv-stamped")?.lastProcessedCreatedAt).toBe(
|
|
68
|
-
1_000,
|
|
69
|
-
);
|
|
70
|
-
expect(
|
|
71
|
-
getRetrospectiveState("conv-elsewhere")?.lastProcessedCreatedAt,
|
|
72
|
-
).toBeNull();
|
|
73
|
-
expect(getRetrospectiveState("conv-missing")).toBeNull();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
test("an empty row list is a no-op", async () => {
|
|
77
|
-
insertLegacyState("conv-a", "reply-1");
|
|
78
|
-
|
|
79
|
-
await preserveRetrospectiveCursorTimestamps("conv-a", []);
|
|
80
|
-
|
|
81
|
-
expect(getRetrospectiveState("conv-a")?.lastProcessedCreatedAt).toBeNull();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default `memory` message-deleted hook.
|
|
3
|
-
*
|
|
4
|
-
* Stamps the conversation's retrospective cursor with the deleted row's
|
|
5
|
-
* `createdAt` when the cursor points at that row and was written without a
|
|
6
|
-
* timestamp (by a build before the column existed). The cursor usually sits
|
|
7
|
-
* on the latest assistant reply, which is exactly the row a regenerate
|
|
8
|
-
* deletes, and once the row is gone only the timestamp can place it.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { HookFunction, MessageDeletedContext } from "@vellumai/plugin-api";
|
|
12
|
-
|
|
13
|
-
import { preserveRetrospectiveCursorTimestamps } from "../memory-retrospective-cursor-preserve.js";
|
|
14
|
-
|
|
15
|
-
const messageDeleted: HookFunction<MessageDeletedContext> = async (ctx) => {
|
|
16
|
-
await preserveRetrospectiveCursorTimestamps(ctx.conversationId, [
|
|
17
|
-
{ id: ctx.messageId, createdAt: ctx.createdAt },
|
|
18
|
-
]);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export default messageDeleted;
|