@vellumai/assistant 0.12.1 → 0.12.2-staging.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 +4 -0
- package/ARCHITECTURE.md +20 -21
- package/docs/architecture/turn-actor.md +15 -8
- package/docs/guardian-request-flow.md +16 -0
- package/openapi.yaml +327 -52
- 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-skill-tools.test.ts +27 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +8 -3
- 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 +212 -3
- package/src/__tests__/credential-security-invariants.test.ts +4 -2
- package/src/__tests__/cu-unified-flow.test.ts +8 -2
- 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 +539 -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 +113 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +18 -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__/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 +19 -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__/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__/agent-process.test.ts +258 -4
- package/src/acp/__tests__/auth-required.test.ts +96 -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__/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/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/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/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 +58 -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 +37 -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 +53 -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 +59 -46
- package/src/calls/voice-triage-escalate.ts +175 -56
- 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/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 +20 -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 +24 -12
- package/src/config/loader.ts +1 -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 +8 -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 +2 -1
- package/src/daemon/conversation-skill-tools.ts +13 -31
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surfaces.ts +15 -9
- package/src/daemon/conversation-tool-setup.ts +37 -43
- 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 +186 -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-metrics.test.ts +0 -3
- package/src/live-voice/__tests__/live-voice-progress.test.ts +15 -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__/runtime-websocket-shell.test.ts +13 -69
- package/src/live-voice/live-voice-photo.ts +25 -6
- package/src/live-voice/live-voice-session.ts +213 -693
- package/src/live-voice/protocol.ts +0 -2
- 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 +54 -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 +73 -0
- package/src/oauth/connection-resolver.ts +95 -2
- package/src/oauth/scope-utils.ts +24 -0
- package/src/oauth/seed-providers.ts +204 -25
- 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-routes.test.ts +159 -4
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +205 -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-routes.test.ts +15 -0
- package/src/runtime/routes/acp-routes.ts +72 -9
- package/src/runtime/routes/app-management-routes.ts +73 -0
- 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 +19 -10
- 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 +36 -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/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-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
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { getSqliteFrom } from "../../db-connection.js";
|
|
7
|
+
import * as schema from "../../schema.js";
|
|
8
|
+
import { migrateInviteContactId } from "../157-invite-contact-id.js";
|
|
9
|
+
import { tableExists, tableHasColumn } from "../schema-introspection.js";
|
|
10
|
+
|
|
11
|
+
function createTestDb() {
|
|
12
|
+
const sqlite = new Database(":memory:");
|
|
13
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
14
|
+
return drizzle(sqlite, { schema });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function columnNotNull(raw: Database, name: string): boolean {
|
|
18
|
+
const cols = raw.query(`PRAGMA table_info(assistant_ingress_invites)`).all() as Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
notnull: number;
|
|
21
|
+
}>;
|
|
22
|
+
return cols.some((c) => c.name === name && c.notnull === 1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("migrateInviteContactId", () => {
|
|
26
|
+
test("rebuilds a legacy created_by_session_id table with NOT NULL contact_id", () => {
|
|
27
|
+
const db = createTestDb();
|
|
28
|
+
const raw = getSqliteFrom(db);
|
|
29
|
+
raw.exec(/*sql*/ `
|
|
30
|
+
CREATE TABLE assistant_ingress_invites (
|
|
31
|
+
id TEXT PRIMARY KEY,
|
|
32
|
+
source_channel TEXT NOT NULL,
|
|
33
|
+
token_hash TEXT NOT NULL,
|
|
34
|
+
created_by_session_id TEXT,
|
|
35
|
+
note TEXT,
|
|
36
|
+
max_uses INTEGER NOT NULL DEFAULT 1,
|
|
37
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
38
|
+
expires_at INTEGER NOT NULL,
|
|
39
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
40
|
+
redeemed_by_external_user_id TEXT,
|
|
41
|
+
redeemed_by_external_chat_id TEXT,
|
|
42
|
+
redeemed_at INTEGER,
|
|
43
|
+
expected_external_user_id TEXT,
|
|
44
|
+
voice_code_hash TEXT,
|
|
45
|
+
voice_code_digits INTEGER,
|
|
46
|
+
invite_code_hash TEXT,
|
|
47
|
+
friend_name TEXT,
|
|
48
|
+
guardian_name TEXT,
|
|
49
|
+
created_at INTEGER NOT NULL,
|
|
50
|
+
updated_at INTEGER NOT NULL
|
|
51
|
+
)
|
|
52
|
+
`);
|
|
53
|
+
raw.run(
|
|
54
|
+
`INSERT INTO assistant_ingress_invites
|
|
55
|
+
(id, source_channel, token_hash, created_by_session_id, max_uses, use_count,
|
|
56
|
+
expires_at, status, created_at, updated_at)
|
|
57
|
+
VALUES ('inv-keep', 'slack', 'hash-keep', 'conv-1', 1, 0, 9, 'active', 1, 1)`,
|
|
58
|
+
);
|
|
59
|
+
raw.exec(
|
|
60
|
+
`ALTER TABLE assistant_ingress_invites ADD COLUMN contact_id TEXT`,
|
|
61
|
+
);
|
|
62
|
+
raw.run(
|
|
63
|
+
`UPDATE assistant_ingress_invites SET contact_id = 'contact-keep' WHERE id = 'inv-keep'`,
|
|
64
|
+
);
|
|
65
|
+
raw.run(
|
|
66
|
+
`INSERT INTO assistant_ingress_invites
|
|
67
|
+
(id, source_channel, token_hash, max_uses, use_count, expires_at, status,
|
|
68
|
+
created_at, updated_at)
|
|
69
|
+
VALUES ('inv-drop', 'slack', 'hash-drop', 1, 0, 9, 'active', 1, 1)`,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
migrateInviteContactId(db);
|
|
73
|
+
|
|
74
|
+
expect(tableHasColumn(db, "assistant_ingress_invites", "contact_id")).toBe(
|
|
75
|
+
true,
|
|
76
|
+
);
|
|
77
|
+
expect(columnNotNull(raw, "contact_id")).toBe(true);
|
|
78
|
+
expect(
|
|
79
|
+
tableHasColumn(db, "assistant_ingress_invites", "created_by_session_id"),
|
|
80
|
+
).toBe(true);
|
|
81
|
+
const rows = raw
|
|
82
|
+
.query(`SELECT id, contact_id FROM assistant_ingress_invites ORDER BY id`)
|
|
83
|
+
.all() as Array<{ id: string; contact_id: string }>;
|
|
84
|
+
expect(rows).toEqual([{ id: "inv-keep", contact_id: "contact-keep" }]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("rebuilds after created_by_session_id was renamed to source_conversation_id", () => {
|
|
88
|
+
const db = createTestDb();
|
|
89
|
+
const raw = getSqliteFrom(db);
|
|
90
|
+
raw.exec(/*sql*/ `
|
|
91
|
+
CREATE TABLE assistant_ingress_invites (
|
|
92
|
+
id TEXT PRIMARY KEY,
|
|
93
|
+
source_channel TEXT NOT NULL,
|
|
94
|
+
token_hash TEXT NOT NULL,
|
|
95
|
+
source_conversation_id TEXT,
|
|
96
|
+
note TEXT,
|
|
97
|
+
max_uses INTEGER NOT NULL DEFAULT 1,
|
|
98
|
+
use_count INTEGER NOT NULL DEFAULT 0,
|
|
99
|
+
expires_at INTEGER NOT NULL,
|
|
100
|
+
status TEXT NOT NULL DEFAULT 'active',
|
|
101
|
+
redeemed_by_external_user_id TEXT,
|
|
102
|
+
redeemed_by_external_chat_id TEXT,
|
|
103
|
+
redeemed_at INTEGER,
|
|
104
|
+
expected_external_user_id TEXT,
|
|
105
|
+
voice_code_hash TEXT,
|
|
106
|
+
voice_code_digits INTEGER,
|
|
107
|
+
invite_code_hash TEXT,
|
|
108
|
+
friend_name TEXT,
|
|
109
|
+
guardian_name TEXT,
|
|
110
|
+
created_at INTEGER NOT NULL,
|
|
111
|
+
updated_at INTEGER NOT NULL
|
|
112
|
+
)
|
|
113
|
+
`);
|
|
114
|
+
raw.exec(
|
|
115
|
+
`ALTER TABLE assistant_ingress_invites ADD COLUMN contact_id TEXT`,
|
|
116
|
+
);
|
|
117
|
+
raw.run(
|
|
118
|
+
`INSERT INTO assistant_ingress_invites
|
|
119
|
+
(id, source_channel, token_hash, source_conversation_id, contact_id,
|
|
120
|
+
max_uses, use_count, expires_at, status, created_at, updated_at)
|
|
121
|
+
VALUES ('inv-1', 'slack', 'hash-1', 'conv-9', 'contact-1', 1, 0, 9, 'active', 1, 1)`,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
migrateInviteContactId(db);
|
|
125
|
+
|
|
126
|
+
expect(columnNotNull(raw, "contact_id")).toBe(true);
|
|
127
|
+
expect(
|
|
128
|
+
tableHasColumn(db, "assistant_ingress_invites", "source_conversation_id"),
|
|
129
|
+
).toBe(true);
|
|
130
|
+
expect(
|
|
131
|
+
tableHasColumn(db, "assistant_ingress_invites", "created_by_session_id"),
|
|
132
|
+
).toBe(false);
|
|
133
|
+
const row = raw
|
|
134
|
+
.query(
|
|
135
|
+
`SELECT source_conversation_id, contact_id FROM assistant_ingress_invites WHERE id = 'inv-1'`,
|
|
136
|
+
)
|
|
137
|
+
.get() as { source_conversation_id: string; contact_id: string };
|
|
138
|
+
expect(row).toEqual({
|
|
139
|
+
source_conversation_id: "conv-9",
|
|
140
|
+
contact_id: "contact-1",
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("is a no-op when the table does not exist", () => {
|
|
145
|
+
const db = createTestDb();
|
|
146
|
+
migrateInviteContactId(db);
|
|
147
|
+
expect(tableExists(db, "assistant_ingress_invites")).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -104,6 +104,11 @@ export const memoryRetrospectiveState = sqliteTable(
|
|
|
104
104
|
conversationId: text("conversation_id").primaryKey(),
|
|
105
105
|
lastProcessedMessageId: text("last_processed_message_id").notNull(),
|
|
106
106
|
lastRunAt: integer("last_run_at").notNull(),
|
|
107
|
+
// `createdAt` of the `last_processed_message_id` row when the pointer was
|
|
108
|
+
// written, so the cursor still bounds reads after that row is deleted.
|
|
109
|
+
// NULL when unknown. Added lazily by `ensureRetrospectiveCursorColumn`
|
|
110
|
+
// (memory-retrospective-state.ts), not by the migration chain.
|
|
111
|
+
lastProcessedCreatedAt: integer("last_processed_created_at"),
|
|
107
112
|
// JSON array of strings — cumulative `remember` contents from prior
|
|
108
113
|
// retrospective passes (capped; see memory-retrospective-state.ts).
|
|
109
114
|
// NULL for rows that predate migration 281 or have no saves yet.
|
|
@@ -24,7 +24,9 @@ mock.module("../providers/platform-proxy/context.js", () => ({
|
|
|
24
24
|
resolveManagedProxyContext: () => mockResolveManagedProxyContext(),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
+
const actualEnv = await import("../config/env.js");
|
|
27
28
|
mock.module("../config/env.js", () => ({
|
|
29
|
+
...actualEnv,
|
|
28
30
|
getPlatformAssistantId: () => mockAssistantId,
|
|
29
31
|
}));
|
|
30
32
|
|
|
@@ -113,11 +115,11 @@ describe("VellumPlatformClient", () => {
|
|
|
113
115
|
platformBaseUrl: "",
|
|
114
116
|
assistantApiKey: "",
|
|
115
117
|
};
|
|
116
|
-
mockAssistantId = "";
|
|
118
|
+
mockAssistantId = "stored-assistant-id";
|
|
117
119
|
mockSecureKeys = {
|
|
118
120
|
"vellum:platform_base_url": "https://stored-platform.example.com/",
|
|
119
121
|
"vellum:assistant_api_key": "stored-api-key",
|
|
120
|
-
"vellum:platform_assistant_id": "
|
|
122
|
+
"vellum:platform_assistant_id": " vault-assistant-id ",
|
|
121
123
|
};
|
|
122
124
|
|
|
123
125
|
const client = await VellumPlatformClient.create();
|
package/src/platform/client.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { PlatformCredentialVerificationStatus } from "@vellumai/service-contracts/platform-credential";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { resolvePlatformAssistantId } from "../config/platform-identity.js";
|
|
11
11
|
import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
|
|
12
12
|
import { credentialKey } from "../security/credential-key.js";
|
|
13
13
|
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
@@ -63,7 +63,7 @@ async function resolvePlatformClientConfig(): Promise<PlatformClientConfig | nul
|
|
|
63
63
|
|
|
64
64
|
let baseUrl = ctx.enabled ? ctx.platformBaseUrl : "";
|
|
65
65
|
let apiKey = ctx.enabled ? ctx.assistantApiKey : "";
|
|
66
|
-
|
|
66
|
+
const assistantId = await resolvePlatformAssistantId();
|
|
67
67
|
|
|
68
68
|
// Fall back to credential store for values not yet rehydrated (standalone CLI).
|
|
69
69
|
if (!baseUrl) {
|
|
@@ -76,14 +76,6 @@ async function resolvePlatformClientConfig(): Promise<PlatformClientConfig | nul
|
|
|
76
76
|
(await getSecureKeyAsync(credentialKey("vellum", "assistant_api_key"))) ??
|
|
77
77
|
"";
|
|
78
78
|
}
|
|
79
|
-
if (!assistantId) {
|
|
80
|
-
assistantId =
|
|
81
|
-
(
|
|
82
|
-
await getSecureKeyAsync(
|
|
83
|
-
credentialKey("vellum", "platform_assistant_id"),
|
|
84
|
-
)
|
|
85
|
-
)?.trim() ?? "";
|
|
86
|
-
}
|
|
87
79
|
|
|
88
80
|
if (!baseUrl || !apiKey) {
|
|
89
81
|
const level = _missingPrereqsWarned ? "debug" : "warn";
|
|
@@ -34,6 +34,8 @@ export const HOOKS = {
|
|
|
34
34
|
CONVERSATION_DELETED: "conversation-deleted",
|
|
35
35
|
/** Fires once when every conversation is wiped at once (the clear-all reset), after the main tables are cleared. Carries no `conversationId`; cleanup hooks wipe their own per-conversation state wholesale. */
|
|
36
36
|
CONVERSATIONS_CLEARED: "conversations-cleared",
|
|
37
|
+
/** Fires once per deleted message row, after the row is removed, carrying the row's `(createdAt, id)` so a hook can settle state that pointed at it. Fire-and-forget cleanup signal, like `conversation-deleted`; whole-conversation deletes dispatch that hook instead. */
|
|
38
|
+
MESSAGE_DELETED: "message-deleted",
|
|
37
39
|
} as const;
|
|
38
40
|
|
|
39
41
|
/** Union of every hook name declared in {@link HOOKS}. */
|
package/src/plugin-api/index.ts
CHANGED
package/src/plugin-api/types.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type {
|
|
|
16
16
|
ConversationDeletedContext,
|
|
17
17
|
ConversationsClearedContext,
|
|
18
18
|
HookBroadcast,
|
|
19
|
+
MessageDeletedContext,
|
|
19
20
|
PluginLogger,
|
|
20
21
|
PostCompactContext,
|
|
21
22
|
PostModelCallContext,
|
|
@@ -60,6 +61,7 @@ export type {
|
|
|
60
61
|
* - `post-model-call` — {@link PostModelCallContext}
|
|
61
62
|
* - `conversation-deleted` — {@link ConversationDeletedContext}
|
|
62
63
|
* - `conversations-cleared` — {@link ConversationsClearedContext}
|
|
64
|
+
* - `message-deleted`: {@link MessageDeletedContext}
|
|
63
65
|
*/
|
|
64
66
|
export type HookFunction<TCtx = unknown> = (
|
|
65
67
|
ctx: TCtx,
|
|
@@ -65,6 +65,7 @@ import maxTokensContinuePkg from "./max-tokens-continue/package.json" with { typ
|
|
|
65
65
|
import memoryConversationDeleted from "./memory/hooks/conversation-deleted.js";
|
|
66
66
|
import memoryConversationsCleared from "./memory/hooks/conversations-cleared.js";
|
|
67
67
|
import memoryInit from "./memory/hooks/init.js";
|
|
68
|
+
import memoryMessageDeleted from "./memory/hooks/message-deleted.js";
|
|
68
69
|
import memoryPostCompact from "./memory/hooks/post-compact.js";
|
|
69
70
|
import memoryShutdown from "./memory/hooks/shutdown.js";
|
|
70
71
|
import memoryUserPromptSubmit from "./memory/hooks/user-prompt-submit.js";
|
|
@@ -199,6 +200,7 @@ export const defaultMemoryPlugin: Plugin = {
|
|
|
199
200
|
"post-compact": memoryPostCompact,
|
|
200
201
|
"conversation-deleted": memoryConversationDeleted,
|
|
201
202
|
"conversations-cleared": memoryConversationsCleared,
|
|
203
|
+
"message-deleted": memoryMessageDeleted,
|
|
202
204
|
},
|
|
203
205
|
injectors: memoryInjectors,
|
|
204
206
|
};
|
|
@@ -434,3 +434,49 @@ describe("ambient camera frames are not retrospective work", () => {
|
|
|
434
434
|
expect(hasQualifyingUserMessageAfter(CONV, null)).toBe(true);
|
|
435
435
|
});
|
|
436
436
|
});
|
|
437
|
+
|
|
438
|
+
describe("retrospective accounting: a cursor survives its row's deletion", () => {
|
|
439
|
+
beforeEach(() => {
|
|
440
|
+
const db = getDb();
|
|
441
|
+
db.run(`DELETE FROM messages`);
|
|
442
|
+
db.run(`DELETE FROM conversations`);
|
|
443
|
+
createConversation({ id: CONV });
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("a deleted cursor row with a stored timestamp still bounds the count, the slice, and the user-activity probe", () => {
|
|
447
|
+
insertRaw({ role: "user", content: TEXT, createdAt: 1_000 });
|
|
448
|
+
const reply = insertRaw({
|
|
449
|
+
role: "assistant",
|
|
450
|
+
content: TEXT,
|
|
451
|
+
createdAt: 2_000,
|
|
452
|
+
});
|
|
453
|
+
// Regenerate: the reply the cursor sits on is discarded and replaced.
|
|
454
|
+
getDb().delete(messages).where(eq(messages.id, reply)).run();
|
|
455
|
+
const regenerated = insertRaw({
|
|
456
|
+
role: "assistant",
|
|
457
|
+
content: TEXT,
|
|
458
|
+
createdAt: 3_000,
|
|
459
|
+
});
|
|
460
|
+
const followUp = insertRaw({
|
|
461
|
+
role: "user",
|
|
462
|
+
content: TEXT,
|
|
463
|
+
createdAt: 4_000,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
const cursor = { id: reply, createdAt: 2_000 };
|
|
467
|
+
expect(countRetrospectiveMessagesAfter(CONV, cursor)).toBe(2);
|
|
468
|
+
expect(
|
|
469
|
+
getRetrospectiveMessagesAfter(CONV, cursor).map((r) => r.id),
|
|
470
|
+
).toEqual([regenerated, followUp]);
|
|
471
|
+
expect(hasQualifyingUserMessageAfter(CONV, cursor)).toBe(true);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
test("a deleted cursor row without a timestamp still reads as no new work", () => {
|
|
475
|
+
insertRaw({ role: "user", content: TEXT, createdAt: 1_000 });
|
|
476
|
+
|
|
477
|
+
const cursor = { id: "gone", createdAt: null };
|
|
478
|
+
expect(countRetrospectiveMessagesAfter(CONV, cursor)).toBe(0);
|
|
479
|
+
expect(getRetrospectiveMessagesAfter(CONV, cursor)).toEqual([]);
|
|
480
|
+
expect(hasQualifyingUserMessageAfter(CONV, cursor)).toBe(false);
|
|
481
|
+
});
|
|
482
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
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,5 +1,7 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import type { MessagesAfterRef } from "../../../../persistence/message-cursor.js";
|
|
4
|
+
|
|
3
5
|
// ---------------------------------------------------------------------------
|
|
4
6
|
// Mock state — reset between tests.
|
|
5
7
|
// ---------------------------------------------------------------------------
|
|
@@ -17,6 +19,7 @@ let cfgThrows = false;
|
|
|
17
19
|
let mockStateRow: {
|
|
18
20
|
conversationId: string;
|
|
19
21
|
lastProcessedMessageId: string;
|
|
22
|
+
lastProcessedCreatedAt: number | null;
|
|
20
23
|
lastRunAt: number;
|
|
21
24
|
rememberedLog: string[];
|
|
22
25
|
} | null = null;
|
|
@@ -24,7 +27,7 @@ let tailQualifies = true;
|
|
|
24
27
|
let gateProbeThrows = false;
|
|
25
28
|
let gateProbeCalls: Array<{
|
|
26
29
|
conversationId: string;
|
|
27
|
-
|
|
30
|
+
after: MessagesAfterRef;
|
|
28
31
|
}> = [];
|
|
29
32
|
|
|
30
33
|
mock.module("../../../../persistence/conversation-crud.js", () => ({
|
|
@@ -68,9 +71,9 @@ mock.module("../memory-retrospective-state.js", () => ({
|
|
|
68
71
|
mock.module("../memory-retrospective-accounting.js", () => ({
|
|
69
72
|
hasQualifyingUserMessageAfter: (
|
|
70
73
|
conversationId: string,
|
|
71
|
-
|
|
74
|
+
after: MessagesAfterRef,
|
|
72
75
|
) => {
|
|
73
|
-
gateProbeCalls.push({ conversationId,
|
|
76
|
+
gateProbeCalls.push({ conversationId, after });
|
|
74
77
|
if (gateProbeThrows) {
|
|
75
78
|
throw new Error("messages table unavailable");
|
|
76
79
|
}
|
|
@@ -170,10 +173,11 @@ describe("enqueueMemoryRetrospectiveIfEnabled", () => {
|
|
|
170
173
|
expect(gateProbeCalls).toHaveLength(0);
|
|
171
174
|
});
|
|
172
175
|
|
|
173
|
-
test("gate probes the tail from the state row's cursor", () => {
|
|
176
|
+
test("gate probes the tail from the state row's cursor, timestamp included", () => {
|
|
174
177
|
mockStateRow = {
|
|
175
178
|
conversationId: "c1",
|
|
176
179
|
lastProcessedMessageId: "m9",
|
|
180
|
+
lastProcessedCreatedAt: 9_000,
|
|
177
181
|
lastRunAt: 1,
|
|
178
182
|
rememberedLog: [],
|
|
179
183
|
};
|
|
@@ -183,7 +187,7 @@ describe("enqueueMemoryRetrospectiveIfEnabled", () => {
|
|
|
183
187
|
});
|
|
184
188
|
|
|
185
189
|
expect(gateProbeCalls).toEqual([
|
|
186
|
-
{ conversationId: "c1",
|
|
190
|
+
{ conversationId: "c1", after: { id: "m9", createdAt: 9_000 } },
|
|
187
191
|
]);
|
|
188
192
|
});
|
|
189
193
|
|
|
@@ -20,6 +20,7 @@ let mockState: StateRow = null;
|
|
|
20
20
|
let stateUpserts: Array<{
|
|
21
21
|
conversationId: string;
|
|
22
22
|
lastProcessedMessageId: string;
|
|
23
|
+
lastProcessedCreatedAt?: number | null;
|
|
23
24
|
lastRunAt: number;
|
|
24
25
|
rememberedLog?: string[];
|
|
25
26
|
}> = [];
|
|
@@ -149,6 +150,7 @@ mock.module("../memory-retrospective-state.js", () => ({
|
|
|
149
150
|
upsertRetrospectiveState: (args: {
|
|
150
151
|
conversationId: string;
|
|
151
152
|
lastProcessedMessageId: string;
|
|
153
|
+
lastProcessedCreatedAt?: number | null;
|
|
152
154
|
lastRunAt: number;
|
|
153
155
|
rememberedLog?: string[];
|
|
154
156
|
}) => {
|
|
@@ -332,15 +334,16 @@ mock.module("../../../../persistence/jobs-store.js", () => ({
|
|
|
332
334
|
},
|
|
333
335
|
}));
|
|
334
336
|
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
339
|
-
let
|
|
337
|
+
// Retrospective skill improvement is distinct from the v3 tier. Keep v3 live
|
|
338
|
+
// while flipping the authoring gate so these tests prove the job reads the
|
|
339
|
+
// dedicated predicate rather than using the broader tier predicate.
|
|
340
|
+
let mockV3TierActive = true;
|
|
341
|
+
let mockSkillImprovementActive = false;
|
|
340
342
|
mock.module("../../../../config/memory-v3-gate.js", () => ({
|
|
341
343
|
isMemoryEnabled: (config?: { memory?: { enabled?: boolean } }) =>
|
|
342
344
|
config?.memory?.enabled !== false,
|
|
343
345
|
isV3TierActive: () => mockV3TierActive,
|
|
346
|
+
isSkillImprovementActive: () => mockSkillImprovementActive,
|
|
344
347
|
isMemoryV3Live: () => mockV3TierActive,
|
|
345
348
|
usesConceptPageMemory: (memory?: {
|
|
346
349
|
enabled?: boolean;
|
|
@@ -504,7 +507,8 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
504
507
|
processingStartedAtById = {};
|
|
505
508
|
mockResolvedUserSlug = "alice";
|
|
506
509
|
resolveUserSlugCalls = [];
|
|
507
|
-
mockV3TierActive =
|
|
510
|
+
mockV3TierActive = true;
|
|
511
|
+
mockSkillImprovementActive = false;
|
|
508
512
|
});
|
|
509
513
|
|
|
510
514
|
test("first-run happy path: no state row, no prior retrospective, both pointer fields set on success", async () => {
|
|
@@ -518,6 +522,11 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
518
522
|
}
|
|
519
523
|
expect(stateUpserts).toHaveLength(1);
|
|
520
524
|
expect(stateUpserts[0]!.lastProcessedMessageId).toBe("m3");
|
|
525
|
+
// The cutoff's `createdAt` rides along so the cursor keeps bounding reads
|
|
526
|
+
// after a regenerate deletes m3.
|
|
527
|
+
expect(stateUpserts[0]!.lastProcessedCreatedAt).toBe(
|
|
528
|
+
Date.parse("2026-05-11T10:10:00Z"),
|
|
529
|
+
);
|
|
521
530
|
expect(lastRunAtBumps).toHaveLength(0);
|
|
522
531
|
expect(wakeCalls).toHaveLength(1);
|
|
523
532
|
// Forks off the source so future runs can find it via
|
|
@@ -1308,7 +1317,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1308
1317
|
});
|
|
1309
1318
|
|
|
1310
1319
|
test("wake allows memory saves + skill authoring and suppresses the internal wake surface", async () => {
|
|
1311
|
-
|
|
1320
|
+
mockSkillImprovementActive = true;
|
|
1312
1321
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1313
1322
|
|
|
1314
1323
|
expect(forkCalls).toHaveLength(1);
|
|
@@ -1331,8 +1340,9 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
1331
1340
|
expect(opts.hintRole).toBe("user");
|
|
1332
1341
|
});
|
|
1333
1342
|
|
|
1334
|
-
test("wake is remember-only when
|
|
1335
|
-
mockV3TierActive =
|
|
1343
|
+
test("wake is remember-only when skill improvement is disabled on a live v3 tier", async () => {
|
|
1344
|
+
mockV3TierActive = true;
|
|
1345
|
+
mockSkillImprovementActive = false;
|
|
1336
1346
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
1337
1347
|
|
|
1338
1348
|
expect(wakeCalls).toHaveLength(1);
|
|
@@ -2538,7 +2548,7 @@ describe("memoryRetrospectiveJob", () => {
|
|
|
2538
2548
|
});
|
|
2539
2549
|
|
|
2540
2550
|
test("proc-to-skills active: instruction carries the pre-check + dedup + companion-file directives", async () => {
|
|
2541
|
-
|
|
2551
|
+
mockSkillImprovementActive = true;
|
|
2542
2552
|
|
|
2543
2553
|
await memoryRetrospectiveJob(makeJob(), stubConfig);
|
|
2544
2554
|
|