@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
|
@@ -12,6 +12,8 @@ mock.module("../memory-retrospective-enqueue.js", () => ({
|
|
|
12
12
|
},
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
|
+
import { eq } from "drizzle-orm";
|
|
16
|
+
|
|
15
17
|
import type { AssistantConfig } from "../../../../config/types.js";
|
|
16
18
|
import { createConversation } from "../../../../persistence/conversation-crud.js";
|
|
17
19
|
import {
|
|
@@ -307,3 +309,64 @@ describe("maybeEnqueueRetrospective — kind-aware accounting", () => {
|
|
|
307
309
|
]);
|
|
308
310
|
});
|
|
309
311
|
});
|
|
312
|
+
|
|
313
|
+
// ---------------------------------------------------------------------------
|
|
314
|
+
// maybeEnqueueRetrospective: the cursor survives a regenerated reply.
|
|
315
|
+
//
|
|
316
|
+
// The cursor normally lands on the latest assistant reply, and a regenerate
|
|
317
|
+
// deletes exactly that row. With its `createdAt` persisted the cursor still
|
|
318
|
+
// bounds the count, so the conversation keeps getting retrospectives.
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
|
|
321
|
+
describe("maybeEnqueueRetrospective: cursor survives a regenerated reply", () => {
|
|
322
|
+
beforeEach(() => {
|
|
323
|
+
resetTables();
|
|
324
|
+
enqueueCalls = [];
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("a regenerate that deletes the cursor's row does not stall the conversation", async () => {
|
|
328
|
+
const conv = createConversation("conv");
|
|
329
|
+
insertMessage(conv.id, { createdAt: 1_000 });
|
|
330
|
+
const reply = insertMessage(conv.id, {
|
|
331
|
+
role: "assistant",
|
|
332
|
+
createdAt: 2_000,
|
|
333
|
+
});
|
|
334
|
+
await upsertRetrospectiveState({
|
|
335
|
+
conversationId: conv.id,
|
|
336
|
+
lastProcessedMessageId: reply,
|
|
337
|
+
lastProcessedCreatedAt: 2_000,
|
|
338
|
+
lastRunAt: Date.now() - 24 * 60 * 60_000,
|
|
339
|
+
});
|
|
340
|
+
// Regenerate: the retry route discards the latest assistant turn, then
|
|
341
|
+
// a fresh reply and the next user message land.
|
|
342
|
+
getDb().delete(messages).where(eq(messages.id, reply)).run();
|
|
343
|
+
insertMessage(conv.id, { role: "assistant", createdAt: 3_000 });
|
|
344
|
+
insertMessage(conv.id, { createdAt: 4_000 });
|
|
345
|
+
|
|
346
|
+
maybeEnqueueRetrospective(conv.id, makeConfig());
|
|
347
|
+
|
|
348
|
+
expect(enqueueCalls).toEqual([
|
|
349
|
+
{ conversationId: conv.id, trigger: "interval" },
|
|
350
|
+
]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("a cursor written without a timestamp still goes quiet once its row is gone", async () => {
|
|
354
|
+
const conv = createConversation("conv");
|
|
355
|
+
insertMessage(conv.id, { createdAt: 1_000 });
|
|
356
|
+
const reply = insertMessage(conv.id, {
|
|
357
|
+
role: "assistant",
|
|
358
|
+
createdAt: 2_000,
|
|
359
|
+
});
|
|
360
|
+
await upsertRetrospectiveState({
|
|
361
|
+
conversationId: conv.id,
|
|
362
|
+
lastProcessedMessageId: reply,
|
|
363
|
+
lastRunAt: Date.now() - 24 * 60 * 60_000,
|
|
364
|
+
});
|
|
365
|
+
getDb().delete(messages).where(eq(messages.id, reply)).run();
|
|
366
|
+
insertMessage(conv.id, { createdAt: 4_000 });
|
|
367
|
+
|
|
368
|
+
maybeEnqueueRetrospective(conv.id, makeConfig());
|
|
369
|
+
|
|
370
|
+
expect(enqueueCalls).toEqual([]);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
@@ -14,6 +14,10 @@ mock.module("../worker-control.js", () => ({
|
|
|
14
14
|
},
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
+
mock.module("../skill-md-mtime-poll.js", () => ({
|
|
18
|
+
stopWorkspaceSkillMdMtimePoll: () => {},
|
|
19
|
+
}));
|
|
20
|
+
|
|
17
21
|
const memoryShutdown = (await import("../hooks/shutdown.js")).default;
|
|
18
22
|
|
|
19
23
|
describe("memory shutdown hook", () => {
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
import { getConfiguredProvider } from "@vellumai/plugin-api";
|
|
8
8
|
|
|
9
9
|
import { redactSecrets } from "../../../../security/secret-scanner.js";
|
|
10
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
10
11
|
import {
|
|
11
12
|
buildRecallAgentPromptBundle,
|
|
12
13
|
FINISH_RECALL_TOOL_DEFINITION,
|
|
@@ -862,9 +863,9 @@ function compactText(text: string, maxChars: number): string {
|
|
|
862
863
|
return compacted;
|
|
863
864
|
}
|
|
864
865
|
if (maxChars <= 3) {
|
|
865
|
-
return compacted
|
|
866
|
+
return safeStringSlice(compacted, 0, maxChars);
|
|
866
867
|
}
|
|
867
|
-
return `${compacted
|
|
868
|
+
return `${safeStringSlice(compacted, 0, maxChars - 3).trimEnd()}...`;
|
|
868
869
|
}
|
|
869
870
|
|
|
870
871
|
function dedupeEvidenceById(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
1
2
|
import type {
|
|
2
3
|
DeterministicRecallSearchResult,
|
|
3
4
|
DeterministicRecallSourceNote,
|
|
@@ -139,9 +140,9 @@ function compactText(text: string, maxChars: number): string {
|
|
|
139
140
|
return compacted;
|
|
140
141
|
}
|
|
141
142
|
if (maxChars <= 3) {
|
|
142
|
-
return compacted
|
|
143
|
+
return safeStringSlice(compacted, 0, maxChars);
|
|
143
144
|
}
|
|
144
|
-
return `${compacted
|
|
145
|
+
return `${safeStringSlice(compacted, 0, maxChars - 3).trimEnd()}...`;
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
function dedupeStrings(values: readonly string[]): string[] {
|
|
@@ -27,6 +27,7 @@ import { readdir, readFile, realpath, stat } from "node:fs/promises";
|
|
|
27
27
|
import { extname, join } from "node:path";
|
|
28
28
|
|
|
29
29
|
import { embedWithRetry } from "../../../../../persistence/embeddings/embed.js";
|
|
30
|
+
import { safeStringSlice } from "../../host-utils.js";
|
|
30
31
|
import { getLogger } from "../../logging.js";
|
|
31
32
|
import { isPathInsideRoot } from "../../path-containment.js";
|
|
32
33
|
import { getEdgeIndex } from "../../substrate/edge-index.js";
|
|
@@ -518,9 +519,11 @@ function buildLexicalExcerpt(
|
|
|
518
519
|
if (focusedLine.length <= MEMORY_V2_LEXICAL_EXCERPT_MAX_CHARS) {
|
|
519
520
|
return focusedLine;
|
|
520
521
|
}
|
|
521
|
-
return `${
|
|
522
|
-
|
|
523
|
-
|
|
522
|
+
return `${safeStringSlice(
|
|
523
|
+
focusedLine,
|
|
524
|
+
0,
|
|
525
|
+
MEMORY_V2_LEXICAL_EXCERPT_MAX_CHARS - 3,
|
|
526
|
+
).trimEnd()}...`;
|
|
524
527
|
}
|
|
525
528
|
|
|
526
529
|
function toLexicalEvidence(match: MemoryV2LexicalMatch): RecallEvidence {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile, realpath, stat } from "node:fs/promises";
|
|
2
2
|
import { extname, isAbsolute, join, relative, sep } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { safeStringSlice } from "../../host-utils.js";
|
|
4
5
|
import { isPathInsideRoot } from "../../path-containment.js";
|
|
5
6
|
import type {
|
|
6
7
|
RecallEvidence,
|
|
@@ -924,7 +925,7 @@ function buildExcerpt(lines: readonly string[], lineIndex: number): string {
|
|
|
924
925
|
return focusedLine;
|
|
925
926
|
}
|
|
926
927
|
|
|
927
|
-
return `${focusedLine
|
|
928
|
+
return `${safeStringSlice(focusedLine, 0, EXCERPT_MAX_CHARS - 3).trimEnd()}...`;
|
|
928
929
|
}
|
|
929
930
|
|
|
930
931
|
function buildSectionExcerpt(
|
|
@@ -942,7 +943,7 @@ function buildSectionExcerpt(
|
|
|
942
943
|
return excerpt;
|
|
943
944
|
}
|
|
944
945
|
|
|
945
|
-
return `${excerpt
|
|
946
|
+
return `${safeStringSlice(excerpt, 0, SECTION_EXCERPT_MAX_CHARS - 3).trimEnd()}...`;
|
|
946
947
|
}
|
|
947
948
|
|
|
948
949
|
function buildStructuredJsonExcerpt(contents: string): string {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
loadSkillCatalog,
|
|
17
17
|
type SkillSummary,
|
|
18
18
|
} from "../../../../config/skills.js";
|
|
19
|
+
import { loadWorkspaceMcpConfig } from "../../../../mcp/workspace-mcp-config.js";
|
|
19
20
|
import {
|
|
20
21
|
enqueueMemoryJob,
|
|
21
22
|
upsertEmbedGraphNodeJob,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
getCachedCatalogSync,
|
|
26
27
|
getCatalog,
|
|
27
28
|
} from "../../../../skills/catalog-cache.js";
|
|
29
|
+
import { safeStringSlice } from "../host-utils.js";
|
|
28
30
|
import { getLogger } from "../logging.js";
|
|
29
31
|
import { memoryDbOrNull } from "../memory-db.js";
|
|
30
32
|
import type { SkillCapabilityInput } from "../substrate/skill-content.js";
|
|
@@ -131,12 +133,9 @@ export function seedSkillGraphNodes(): void {
|
|
|
131
133
|
const input = fromSkillSummary(summary);
|
|
132
134
|
|
|
133
135
|
if (summary.id === "mcp-setup") {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
|
|
137
|
-
if (names.length > 0) {
|
|
138
|
-
input.description += ` Configured: ${names.join(", ")}`;
|
|
139
|
-
}
|
|
136
|
+
const names = Object.keys(loadWorkspaceMcpConfig().servers);
|
|
137
|
+
if (names.length > 0) {
|
|
138
|
+
input.description += ` Configured: ${names.join(", ")}`;
|
|
140
139
|
}
|
|
141
140
|
}
|
|
142
141
|
|
|
@@ -247,7 +246,7 @@ function buildSkillContent(input: SkillCapabilityInput): string {
|
|
|
247
246
|
content += ` Avoid when: ${input.avoidWhen.join("; ")}.`;
|
|
248
247
|
}
|
|
249
248
|
if (content.length > 500) {
|
|
250
|
-
content = content
|
|
249
|
+
content = safeStringSlice(content, 0, 500);
|
|
251
250
|
}
|
|
252
251
|
return content;
|
|
253
252
|
}
|
|
@@ -17,6 +17,7 @@ import type { HookFunction, InitContext } from "@vellumai/plugin-api";
|
|
|
17
17
|
import { getConfig } from "../../../../config/loader.js";
|
|
18
18
|
import { registerMemoryPluginJobHandlers } from "../job-handler-registration.js";
|
|
19
19
|
import { getLogger } from "../logging.js";
|
|
20
|
+
import { backfillRetrospectiveCursorTimestamps } from "../memory-retrospective-cursor-backfill.js";
|
|
20
21
|
import { runMemoryStartup } from "../startup.js";
|
|
21
22
|
import { ensureMemoryV3PluginSchema } from "../v3/plugin-schema.js";
|
|
22
23
|
|
|
@@ -30,6 +31,15 @@ const init: HookFunction<InitContext> = async () => {
|
|
|
30
31
|
// against an empty table and failed as an unknown type.
|
|
31
32
|
registerMemoryPluginJobHandlers();
|
|
32
33
|
|
|
34
|
+
// Retrospective cursors written before the timestamp column existed can
|
|
35
|
+
// only be completed while their message rows still exist, so start the
|
|
36
|
+
// fill at boot, ahead of any client regenerating a reply. Detached and
|
|
37
|
+
// fail-open like the rest of memory startup; the memory worker runs the
|
|
38
|
+
// same idempotent pass as a second chance.
|
|
39
|
+
void backfillRetrospectiveCursorTimestamps().catch((err) =>
|
|
40
|
+
log.warn({ err }, "Retrospective cursor timestamp backfill failed"),
|
|
41
|
+
);
|
|
42
|
+
|
|
33
43
|
// The memory-v3 plugin-owned schema on the memory connection
|
|
34
44
|
// (`v3/plugin-schema.ts`): the pools and injected-sections tables and the
|
|
35
45
|
// selection log's `section_key` column, ensured idempotently on every
|
|
@@ -0,0 +1,21 @@
|
|
|
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;
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
import type { HookFunction, ShutdownContext } from "@vellumai/plugin-api";
|
|
11
11
|
|
|
12
|
+
import { stopWorkspaceSkillMdMtimePoll } from "../skill-md-mtime-poll.js";
|
|
12
13
|
import { stopMemoryWorkerProcess } from "../worker-control.js";
|
|
13
14
|
|
|
14
15
|
const shutdown: HookFunction<ShutdownContext> = async () => {
|
|
16
|
+
stopWorkspaceSkillMdMtimePoll();
|
|
15
17
|
stopMemoryWorkerProcess();
|
|
16
18
|
};
|
|
17
19
|
|
|
@@ -25,6 +25,7 @@ import type { SqliteRetryOptions } from "../../../util/sqlite-retry.js";
|
|
|
25
25
|
import * as hostSqliteRetry from "../../../util/sqlite-retry.js";
|
|
26
26
|
import * as hostStripCommentLines from "../../../util/strip-comment-lines.js";
|
|
27
27
|
import * as hostTruncate from "../../../util/truncate.js";
|
|
28
|
+
import * as hostUnicode from "../../../util/unicode.js";
|
|
28
29
|
import * as hostWorkerCompute from "../../../util/worker-compute.js";
|
|
29
30
|
import * as hostWorkerMemory from "../../../util/worker-memory.js";
|
|
30
31
|
|
|
@@ -76,6 +77,15 @@ export function truncate(str: string, maxLen: number, suffix?: string): string {
|
|
|
76
77
|
: hostTruncate.truncate(str, maxLen, suffix);
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
/** `String.prototype.slice` that never cuts a UTF-16 surrogate pair in half. */
|
|
81
|
+
export function safeStringSlice(
|
|
82
|
+
str: string,
|
|
83
|
+
start?: number,
|
|
84
|
+
end?: number,
|
|
85
|
+
): string {
|
|
86
|
+
return hostUnicode.safeStringSlice(str, start, end);
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
export function workerMemoryEnv(): Record<string, string | undefined> {
|
|
80
90
|
return hostWorkerMemory.workerMemoryEnv();
|
|
81
91
|
}
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
import { isJobQueueResolution, type JobHandler } from "../../types.js";
|
|
63
63
|
import { sweepOrphanConversationMemoryTables } from "./conversation-memory-orphan-sweep.js";
|
|
64
64
|
import { getLogger } from "./logging.js";
|
|
65
|
+
import { backfillRetrospectiveCursorTimestamps } from "./memory-retrospective-cursor-backfill.js";
|
|
65
66
|
import { sweepOrphanMemoryRetrospectiveConversations } from "./memory-retrospective-startup-cleanup.js";
|
|
66
67
|
import { getWorkspaceDir } from "./paths.js";
|
|
67
68
|
// SUBSTRATE (v2+v3) — feeds `enqueueSubstrateMaintenanceJobs`.
|
|
@@ -259,6 +260,18 @@ export function startMemoryJobsWorkerLoop(): MemoryJobsWorker {
|
|
|
259
260
|
);
|
|
260
261
|
});
|
|
261
262
|
|
|
263
|
+
// Second chance for the cursor timestamp backfill the daemon's memory init
|
|
264
|
+
// hook starts at boot: rows written before the column existed get their
|
|
265
|
+
// `createdAt` so a later deletion of the row they point at (a regenerated
|
|
266
|
+
// reply) cannot stall the conversation's retrospectives. Idempotent,
|
|
267
|
+
// detached, and best-effort, same as the sweeps.
|
|
268
|
+
void backfillRetrospectiveCursorTimestamps().catch((err: unknown) => {
|
|
269
|
+
log.warn(
|
|
270
|
+
{ err },
|
|
271
|
+
"Retrospective cursor timestamp backfill failed; continuing worker startup",
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
|
|
262
275
|
// Also catch up on relocated conversation-keyed tables orphaned while the
|
|
263
276
|
// plugin was disabled: with memory off, the conversation-deleted hook never
|
|
264
277
|
// fired, so deletes during that window left rows behind (the pre-Wave-2 main
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
// accounting; once newer real messages arrive it is copied into the next
|
|
32
32
|
// run's fork as inert prefix context.
|
|
33
33
|
|
|
34
|
-
import { and, eq,
|
|
34
|
+
import { and, eq, like, or } from "drizzle-orm";
|
|
35
35
|
|
|
36
36
|
import {
|
|
37
37
|
countMessagesAfter,
|
|
@@ -43,6 +43,11 @@ import {
|
|
|
43
43
|
SIGHT_FRAME_ATTACHMENT_IDS_KEY,
|
|
44
44
|
} from "../../../persistence/conversation-types.js";
|
|
45
45
|
import { getDb } from "../../../persistence/db-connection.js";
|
|
46
|
+
import {
|
|
47
|
+
messagesAfterBoundFilter,
|
|
48
|
+
type MessagesAfterRef,
|
|
49
|
+
resolveMessagesAfterBound,
|
|
50
|
+
} from "../../../persistence/message-cursor.js";
|
|
46
51
|
import { messages } from "../../../persistence/schema/index.js";
|
|
47
52
|
import { SKILL_CARD_MESSAGE_KIND } from "./memory-retrospective-constants.js";
|
|
48
53
|
|
|
@@ -98,9 +103,9 @@ export function isSkillCardMessage(row: { metadata: string | null }): boolean {
|
|
|
98
103
|
*/
|
|
99
104
|
export function getRetrospectiveMessagesAfter(
|
|
100
105
|
conversationId: string,
|
|
101
|
-
|
|
106
|
+
after: MessagesAfterRef,
|
|
102
107
|
): MessageRow[] {
|
|
103
|
-
const rows = getMessagesAfter(conversationId,
|
|
108
|
+
const rows = getMessagesAfter(conversationId, after);
|
|
104
109
|
const firstUnfinalized = rows.findIndex((row) => row.finalized !== 1);
|
|
105
110
|
const bounded =
|
|
106
111
|
firstUnfinalized === -1 ? rows : rows.slice(0, firstUnfinalized);
|
|
@@ -116,13 +121,13 @@ export function getRetrospectiveMessagesAfter(
|
|
|
116
121
|
*/
|
|
117
122
|
export function countRetrospectiveMessagesAfter(
|
|
118
123
|
conversationId: string,
|
|
119
|
-
|
|
124
|
+
after: MessagesAfterRef,
|
|
120
125
|
): number {
|
|
121
|
-
const total = countMessagesAfter(conversationId,
|
|
126
|
+
const total = countMessagesAfter(conversationId, after);
|
|
122
127
|
if (total === 0) {
|
|
123
128
|
return 0;
|
|
124
129
|
}
|
|
125
|
-
const excluded = countExcludedMessagesAfter(conversationId,
|
|
130
|
+
const excluded = countExcludedMessagesAfter(conversationId, after);
|
|
126
131
|
return Math.max(0, total - excluded);
|
|
127
132
|
}
|
|
128
133
|
|
|
@@ -167,7 +172,8 @@ export function messagesHaveUserActivity(
|
|
|
167
172
|
* a non-tool_result block. Powers the `memory.retrospective.requireUserActivity`
|
|
168
173
|
* enqueue gate, so only user rows are loaded. Cursor semantics mirror
|
|
169
174
|
* `countMessagesAfter`: a null/`""` reference scans the whole conversation,
|
|
170
|
-
*
|
|
175
|
+
* a cursor whose row is gone falls back to the timestamp it carries, and a
|
|
176
|
+
* bare id whose row is gone means no new work.
|
|
171
177
|
*
|
|
172
178
|
* Operates on the raw `content` column: rows that do not parse to a block
|
|
173
179
|
* array (file-backed `{ ref }` rows, legacy plain strings) count as
|
|
@@ -177,37 +183,15 @@ export function messagesHaveUserActivity(
|
|
|
177
183
|
*/
|
|
178
184
|
export function hasQualifyingUserMessageAfter(
|
|
179
185
|
conversationId: string,
|
|
180
|
-
|
|
186
|
+
after: MessagesAfterRef,
|
|
181
187
|
): boolean {
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
let ref: { createdAt: number } | undefined;
|
|
187
|
-
if (cursorId !== null) {
|
|
188
|
-
ref = db
|
|
189
|
-
.select({ createdAt: messages.createdAt })
|
|
190
|
-
.from(messages)
|
|
191
|
-
.where(eq(messages.id, cursorId))
|
|
192
|
-
.get();
|
|
193
|
-
if (!ref) {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
188
|
+
const start = resolveMessagesAfterBound(after);
|
|
189
|
+
if (start.kind === "vanished") {
|
|
190
|
+
return false;
|
|
196
191
|
}
|
|
197
|
-
|
|
198
192
|
const afterCursor =
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
or(
|
|
202
|
-
gt(messages.createdAt, ref.createdAt),
|
|
203
|
-
and(
|
|
204
|
-
eq(messages.createdAt, ref.createdAt),
|
|
205
|
-
gt(messages.id, cursorId),
|
|
206
|
-
),
|
|
207
|
-
),
|
|
208
|
-
]
|
|
209
|
-
: [];
|
|
210
|
-
const rows = db
|
|
193
|
+
start.kind === "after" ? [messagesAfterBoundFilter(start.bound)] : [];
|
|
194
|
+
const rows = getDb()
|
|
211
195
|
.select({ content: messages.content, metadata: messages.metadata })
|
|
212
196
|
.from(messages)
|
|
213
197
|
.where(
|
|
@@ -248,18 +232,14 @@ function rawUserContentCarriesActivity(raw: string | null): boolean {
|
|
|
248
232
|
* — at most one per successful retrospective run); each candidate is then
|
|
249
233
|
* JSON-verified by {@link isSkillCardMessage} so an incidental substring
|
|
250
234
|
* match in unrelated metadata can never exclude a real message from
|
|
251
|
-
* accounting. The cursor
|
|
252
|
-
*
|
|
253
|
-
*
|
|
235
|
+
* accounting. The cursor resolves exactly as `countMessagesAfter`'s does,
|
|
236
|
+
* including the null/`""` "count everything" cases and the vanished-reference
|
|
237
|
+
* "no new work" case.
|
|
254
238
|
*/
|
|
255
239
|
function countExcludedMessagesAfter(
|
|
256
240
|
conversationId: string,
|
|
257
|
-
|
|
241
|
+
after: MessagesAfterRef,
|
|
258
242
|
): number {
|
|
259
|
-
// The `""` sentinel (failure-only state rows) counts everything, matching
|
|
260
|
-
// `countMessagesAfter`.
|
|
261
|
-
const cursorId =
|
|
262
|
-
afterMessageId === null || afterMessageId === "" ? null : afterMessageId;
|
|
263
243
|
const db = getDb();
|
|
264
244
|
const candidates = db
|
|
265
245
|
.select({
|
|
@@ -282,18 +262,11 @@ function countExcludedMessagesAfter(
|
|
|
282
262
|
return 0;
|
|
283
263
|
}
|
|
284
264
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
.where(eq(messages.id, cursorId))
|
|
291
|
-
.get();
|
|
292
|
-
// Vanished reference: `countMessagesAfter` reported 0, so there is
|
|
293
|
-
// nothing to subtract from.
|
|
294
|
-
if (!ref) {
|
|
295
|
-
return 0;
|
|
296
|
-
}
|
|
265
|
+
const start = resolveMessagesAfterBound(after);
|
|
266
|
+
// Vanished reference: `countMessagesAfter` reported 0, so there is nothing
|
|
267
|
+
// to subtract from.
|
|
268
|
+
if (start.kind === "vanished") {
|
|
269
|
+
return 0;
|
|
297
270
|
}
|
|
298
271
|
|
|
299
272
|
let count = 0;
|
|
@@ -301,11 +274,12 @@ function countExcludedMessagesAfter(
|
|
|
301
274
|
if (!isExcludedFromRetrospectiveAccounting(row)) {
|
|
302
275
|
continue;
|
|
303
276
|
}
|
|
304
|
-
if (
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
277
|
+
if (start.kind === "after") {
|
|
278
|
+
const { createdAt, id } = start.bound;
|
|
279
|
+
const isAfter =
|
|
280
|
+
row.createdAt > createdAt ||
|
|
281
|
+
(row.createdAt === createdAt && row.id > id);
|
|
282
|
+
if (!isAfter) {
|
|
309
283
|
continue;
|
|
310
284
|
}
|
|
311
285
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Memory retrospective: cursor timestamp backfill.
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// State rows written before `last_processed_created_at` existed carry an
|
|
6
|
+
// id-only cursor, which stops bounding reads the moment its row is deleted
|
|
7
|
+
// (see `memory-retrospective-state.ts`). This pass copies `createdAt` from
|
|
8
|
+
// each such row's message while the message still exists. Rows whose message
|
|
9
|
+
// is already gone stay NULL: nothing remains to recover, and they are counted
|
|
10
|
+
// so the stall is visible in the log. Runs detached at daemon init (ahead of
|
|
11
|
+
// any client regenerating a reply) and again at memory-worker startup;
|
|
12
|
+
// idempotent, keyset-paginated by conversation id, and yielding between pages.
|
|
13
|
+
|
|
14
|
+
import { and, asc, eq, gt, isNull, ne } from "drizzle-orm";
|
|
15
|
+
|
|
16
|
+
import { messageCreatedAtByIds } from "../../../persistence/message-reads.js";
|
|
17
|
+
import { memoryRetrospectiveState } from "../../../persistence/schema/index.js";
|
|
18
|
+
import { withSqliteRetry } from "./host-utils.js";
|
|
19
|
+
import { getLogger } from "./logging.js";
|
|
20
|
+
import { memoryDbOrNull } from "./memory-db.js";
|
|
21
|
+
import { ensureRetrospectiveCursorColumn } from "./memory-retrospective-state.js";
|
|
22
|
+
|
|
23
|
+
const log = getLogger("memory-retrospective-cursor-backfill");
|
|
24
|
+
|
|
25
|
+
const DEFAULT_PAGE_SIZE = 500;
|
|
26
|
+
|
|
27
|
+
export interface CursorBackfillResult {
|
|
28
|
+
/** Rows with a cursor id but no timestamp. */
|
|
29
|
+
scanned: number;
|
|
30
|
+
/** Rows whose message still exists and received its `createdAt`. */
|
|
31
|
+
backfilled: number;
|
|
32
|
+
/** Rows whose cursor message no longer exists; left NULL. */
|
|
33
|
+
unrecoverable: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Fill `last_processed_created_at` on every state row that has a cursor id
|
|
38
|
+
* but no timestamp. Best-effort: a missing memory connection or column is a
|
|
39
|
+
* no-op, and a row the job rewrites mid-pass is skipped by the update's
|
|
40
|
+
* `IS NULL` guard rather than stomped. Pages by conversation id so a large
|
|
41
|
+
* backlog never materializes at once or holds the event loop.
|
|
42
|
+
*/
|
|
43
|
+
export async function backfillRetrospectiveCursorTimestamps(opts?: {
|
|
44
|
+
pageSize?: number;
|
|
45
|
+
}): Promise<CursorBackfillResult> {
|
|
46
|
+
const pageSize = opts?.pageSize ?? DEFAULT_PAGE_SIZE;
|
|
47
|
+
const result: CursorBackfillResult = {
|
|
48
|
+
scanned: 0,
|
|
49
|
+
backfilled: 0,
|
|
50
|
+
unrecoverable: 0,
|
|
51
|
+
};
|
|
52
|
+
const context = "backfillRetrospectiveCursorTimestamps";
|
|
53
|
+
const mdb = memoryDbOrNull(context);
|
|
54
|
+
if (!mdb || !ensureRetrospectiveCursorColumn(context)) {
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let afterConversationId = "";
|
|
59
|
+
for (;;) {
|
|
60
|
+
const page = mdb
|
|
61
|
+
.select({
|
|
62
|
+
conversationId: memoryRetrospectiveState.conversationId,
|
|
63
|
+
messageId: memoryRetrospectiveState.lastProcessedMessageId,
|
|
64
|
+
})
|
|
65
|
+
.from(memoryRetrospectiveState)
|
|
66
|
+
.where(
|
|
67
|
+
and(
|
|
68
|
+
isNull(memoryRetrospectiveState.lastProcessedCreatedAt),
|
|
69
|
+
ne(memoryRetrospectiveState.lastProcessedMessageId, ""),
|
|
70
|
+
gt(memoryRetrospectiveState.conversationId, afterConversationId),
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
.orderBy(asc(memoryRetrospectiveState.conversationId))
|
|
74
|
+
.limit(pageSize)
|
|
75
|
+
.all();
|
|
76
|
+
if (page.length === 0) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
afterConversationId = page[page.length - 1]!.conversationId;
|
|
80
|
+
result.scanned += page.length;
|
|
81
|
+
|
|
82
|
+
const createdAtById = messageCreatedAtByIds(
|
|
83
|
+
page.map((row) => row.messageId),
|
|
84
|
+
);
|
|
85
|
+
const recoverable = page.flatMap((row) => {
|
|
86
|
+
const createdAt = createdAtById.get(row.messageId);
|
|
87
|
+
return createdAt === undefined ? [] : [{ ...row, createdAt }];
|
|
88
|
+
});
|
|
89
|
+
result.unrecoverable += page.length - recoverable.length;
|
|
90
|
+
|
|
91
|
+
if (recoverable.length > 0) {
|
|
92
|
+
await withSqliteRetry(
|
|
93
|
+
() => {
|
|
94
|
+
mdb.transaction((tx) => {
|
|
95
|
+
for (const row of recoverable) {
|
|
96
|
+
tx.update(memoryRetrospectiveState)
|
|
97
|
+
.set({ lastProcessedCreatedAt: row.createdAt })
|
|
98
|
+
.where(
|
|
99
|
+
and(
|
|
100
|
+
eq(
|
|
101
|
+
memoryRetrospectiveState.conversationId,
|
|
102
|
+
row.conversationId,
|
|
103
|
+
),
|
|
104
|
+
eq(
|
|
105
|
+
memoryRetrospectiveState.lastProcessedMessageId,
|
|
106
|
+
row.messageId,
|
|
107
|
+
),
|
|
108
|
+
isNull(memoryRetrospectiveState.lastProcessedCreatedAt),
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
.run();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
{ op: context, context: { afterConversationId } },
|
|
116
|
+
);
|
|
117
|
+
result.backfilled += recoverable.length;
|
|
118
|
+
}
|
|
119
|
+
await Bun.sleep(0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (result.backfilled > 0 || result.unrecoverable > 0) {
|
|
123
|
+
log.info(result, "Retrospective cursor timestamps backfilled");
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|