@vellumai/assistant 0.6.3 → 0.6.4
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/ARCHITECTURE.md +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +5 -13
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- package/src/__tests__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +11 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +138 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- package/src/__tests__/config-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- package/src/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/types.ts +16 -0
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- package/src/cli/program.ts +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +37 -1
- package/src/config/bundled-skills/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +2 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -0
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +12 -3
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +1 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +99 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -5
- package/src/memory/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +92 -56
- package/src/memory/migrations/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -61
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -0
- package/src/runtime/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +82 -23
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +30 -2
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- package/src/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +38 -14
- package/src/tools/browser/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- package/src/tools/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +54 -10
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -5,9 +5,12 @@ import {
|
|
|
5
5
|
computeEffectiveSignificance,
|
|
6
6
|
computeRecencyBoost,
|
|
7
7
|
computeTemporalBoost,
|
|
8
|
+
DEFAULT_WEIGHTS,
|
|
8
9
|
PER_TURN_WEIGHTS,
|
|
10
|
+
PROCEDURAL_WEIGHTS,
|
|
9
11
|
scoreCandidate,
|
|
10
12
|
type ScoringWeights,
|
|
13
|
+
weightsForContextLoad,
|
|
11
14
|
} from "./scoring.js";
|
|
12
15
|
import type { MemoryNode } from "./types.js";
|
|
13
16
|
|
|
@@ -546,3 +549,186 @@ describe("scoreCandidate", () => {
|
|
|
546
549
|
expect(result.scoreBreakdown.effectiveSignificance).toBe(0.7);
|
|
547
550
|
});
|
|
548
551
|
});
|
|
552
|
+
|
|
553
|
+
// ---------------------------------------------------------------------------
|
|
554
|
+
// weightsForContextLoad + PROCEDURAL_WEIGHTS
|
|
555
|
+
// ---------------------------------------------------------------------------
|
|
556
|
+
|
|
557
|
+
describe("weightsForContextLoad", () => {
|
|
558
|
+
test("returns PROCEDURAL_WEIGHTS for procedural nodes", () => {
|
|
559
|
+
const node = makeNode({ type: "procedural" });
|
|
560
|
+
expect(weightsForContextLoad(node)).toBe(PROCEDURAL_WEIGHTS);
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test("returns DEFAULT_WEIGHTS for episodic nodes", () => {
|
|
564
|
+
const node = makeNode({ type: "episodic" });
|
|
565
|
+
expect(weightsForContextLoad(node)).toBe(DEFAULT_WEIGHTS);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
test("returns DEFAULT_WEIGHTS for semantic/emotional/prospective/behavioral/narrative/shared nodes", () => {
|
|
569
|
+
for (const type of [
|
|
570
|
+
"semantic",
|
|
571
|
+
"emotional",
|
|
572
|
+
"prospective",
|
|
573
|
+
"behavioral",
|
|
574
|
+
"narrative",
|
|
575
|
+
"shared",
|
|
576
|
+
] as const) {
|
|
577
|
+
const node = makeNode({ type });
|
|
578
|
+
expect(weightsForContextLoad(node)).toBe(DEFAULT_WEIGHTS);
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
describe("PROCEDURAL_WEIGHTS", () => {
|
|
584
|
+
test("weights sum to 1.0", () => {
|
|
585
|
+
const sum =
|
|
586
|
+
PROCEDURAL_WEIGHTS.semanticSimilarity +
|
|
587
|
+
PROCEDURAL_WEIGHTS.effectiveSignificance +
|
|
588
|
+
PROCEDURAL_WEIGHTS.emotionalIntensity +
|
|
589
|
+
PROCEDURAL_WEIGHTS.temporalBoost +
|
|
590
|
+
PROCEDURAL_WEIGHTS.recencyBoost +
|
|
591
|
+
PROCEDURAL_WEIGHTS.triggerBoost +
|
|
592
|
+
PROCEDURAL_WEIGHTS.activationBoost;
|
|
593
|
+
expect(sum).toBeCloseTo(1.0, 5);
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
test("zeroes out emotionalIntensity and temporalBoost", () => {
|
|
597
|
+
// Procedural memories have no emotional charge and no time-of-day pattern
|
|
598
|
+
// by nature — grading on these signals is just dead weight.
|
|
599
|
+
expect(PROCEDURAL_WEIGHTS.emotionalIntensity).toBe(0);
|
|
600
|
+
expect(PROCEDURAL_WEIGHTS.temporalBoost).toBe(0);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("weights semanticSimilarity and effectiveSignificance more heavily than DEFAULT_WEIGHTS", () => {
|
|
604
|
+
expect(PROCEDURAL_WEIGHTS.semanticSimilarity).toBeGreaterThan(
|
|
605
|
+
DEFAULT_WEIGHTS.semanticSimilarity,
|
|
606
|
+
);
|
|
607
|
+
expect(PROCEDURAL_WEIGHTS.effectiveSignificance).toBeGreaterThan(
|
|
608
|
+
DEFAULT_WEIGHTS.effectiveSignificance,
|
|
609
|
+
);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("procedural node outscores otherwise-identical episodic node under type-aware weights", () => {
|
|
613
|
+
// A procedural memory with zero emotional charge, zero recency, zero
|
|
614
|
+
// trigger — all dead signals — should still surface under PROCEDURAL_WEIGHTS
|
|
615
|
+
// thanks to semantic relevance and significance. Under DEFAULT_WEIGHTS the
|
|
616
|
+
// same signals would leave ~45% of the budget dead.
|
|
617
|
+
const proceduralNode = makeNode({ type: "procedural" });
|
|
618
|
+
const episodicNode = makeNode({ id: "node-2", type: "episodic" });
|
|
619
|
+
|
|
620
|
+
// Components a procedural memory typically carries: semantic hit + stable
|
|
621
|
+
// significance, but no emotional charge, no recency, no trigger.
|
|
622
|
+
const proceduralComponents = {
|
|
623
|
+
semanticSimilarity: 0.8,
|
|
624
|
+
effectiveSignificance: 0.7,
|
|
625
|
+
emotionalIntensity: 0,
|
|
626
|
+
temporalBoost: 0.5, // neutral
|
|
627
|
+
recencyBoost: 0,
|
|
628
|
+
triggerBoost: 0,
|
|
629
|
+
activationBoost: 0,
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const proceduralScore = scoreCandidate(
|
|
633
|
+
proceduralNode,
|
|
634
|
+
proceduralComponents,
|
|
635
|
+
weightsForContextLoad(proceduralNode),
|
|
636
|
+
).score;
|
|
637
|
+
const episodicScore = scoreCandidate(
|
|
638
|
+
episodicNode,
|
|
639
|
+
proceduralComponents,
|
|
640
|
+
weightsForContextLoad(episodicNode),
|
|
641
|
+
).score;
|
|
642
|
+
|
|
643
|
+
expect(proceduralScore).toBeGreaterThan(episodicScore);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
test("episodic node with full signal still outscores procedural with only semantic signal", () => {
|
|
647
|
+
// The change must NOT break episodic retrieval: an episodic memory with
|
|
648
|
+
// emotional charge + recency + moderate significance should still outrank
|
|
649
|
+
// a procedural memory that only has semantic relevance.
|
|
650
|
+
const episodicNode = makeNode({ type: "episodic" });
|
|
651
|
+
const proceduralNode = makeNode({ id: "node-2", type: "procedural" });
|
|
652
|
+
|
|
653
|
+
const episodicComponents = {
|
|
654
|
+
semanticSimilarity: 0.5,
|
|
655
|
+
effectiveSignificance: 0.6,
|
|
656
|
+
emotionalIntensity: 0.7,
|
|
657
|
+
temporalBoost: 0.6,
|
|
658
|
+
recencyBoost: 0.9,
|
|
659
|
+
triggerBoost: 0.4,
|
|
660
|
+
activationBoost: 0.3,
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
const proceduralComponents = {
|
|
664
|
+
semanticSimilarity: 0.5,
|
|
665
|
+
effectiveSignificance: 0.3,
|
|
666
|
+
emotionalIntensity: 0,
|
|
667
|
+
temporalBoost: 0.5,
|
|
668
|
+
recencyBoost: 0,
|
|
669
|
+
triggerBoost: 0,
|
|
670
|
+
activationBoost: 0,
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
const episodicScore = scoreCandidate(
|
|
674
|
+
episodicNode,
|
|
675
|
+
episodicComponents,
|
|
676
|
+
weightsForContextLoad(episodicNode),
|
|
677
|
+
).score;
|
|
678
|
+
const proceduralScore = scoreCandidate(
|
|
679
|
+
proceduralNode,
|
|
680
|
+
proceduralComponents,
|
|
681
|
+
weightsForContextLoad(proceduralNode),
|
|
682
|
+
).score;
|
|
683
|
+
|
|
684
|
+
expect(episodicScore).toBeGreaterThan(proceduralScore);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// ---------------------------------------------------------------------------
|
|
689
|
+
// Regression: DEFAULT_WEIGHTS behavior unchanged
|
|
690
|
+
// ---------------------------------------------------------------------------
|
|
691
|
+
|
|
692
|
+
describe("DEFAULT_WEIGHTS (regression)", () => {
|
|
693
|
+
test("weights sum to 1.0", () => {
|
|
694
|
+
const sum =
|
|
695
|
+
DEFAULT_WEIGHTS.semanticSimilarity +
|
|
696
|
+
DEFAULT_WEIGHTS.effectiveSignificance +
|
|
697
|
+
DEFAULT_WEIGHTS.emotionalIntensity +
|
|
698
|
+
DEFAULT_WEIGHTS.temporalBoost +
|
|
699
|
+
DEFAULT_WEIGHTS.recencyBoost +
|
|
700
|
+
DEFAULT_WEIGHTS.triggerBoost +
|
|
701
|
+
DEFAULT_WEIGHTS.activationBoost;
|
|
702
|
+
expect(sum).toBeCloseTo(1.0, 5);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
test("preserves exact weight values", () => {
|
|
706
|
+
expect(DEFAULT_WEIGHTS).toEqual({
|
|
707
|
+
semanticSimilarity: 0.25,
|
|
708
|
+
effectiveSignificance: 0.15,
|
|
709
|
+
emotionalIntensity: 0.15,
|
|
710
|
+
temporalBoost: 0.05,
|
|
711
|
+
recencyBoost: 0.15,
|
|
712
|
+
triggerBoost: 0.15,
|
|
713
|
+
activationBoost: 0.1,
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
test("scoreCandidate without weights argument uses DEFAULT_WEIGHTS", () => {
|
|
718
|
+
// Backwards-compat: existing callers that pass no weights argument should
|
|
719
|
+
// continue to get DEFAULT_WEIGHTS scoring.
|
|
720
|
+
const node = makeNode({ type: "episodic" });
|
|
721
|
+
const components = {
|
|
722
|
+
semanticSimilarity: 1.0,
|
|
723
|
+
effectiveSignificance: 1.0,
|
|
724
|
+
emotionalIntensity: 1.0,
|
|
725
|
+
temporalBoost: 1.0,
|
|
726
|
+
recencyBoost: 1.0,
|
|
727
|
+
triggerBoost: 1.0,
|
|
728
|
+
activationBoost: 1.0,
|
|
729
|
+
};
|
|
730
|
+
const implicit = scoreCandidate(node, components).score;
|
|
731
|
+
const explicit = scoreCandidate(node, components, DEFAULT_WEIGHTS).score;
|
|
732
|
+
expect(implicit).toBe(explicit);
|
|
733
|
+
});
|
|
734
|
+
});
|
|
@@ -166,7 +166,7 @@ export interface ScoringWeights {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/** Weights for context-load (conversation start): balanced across all signals. */
|
|
169
|
-
const DEFAULT_WEIGHTS: ScoringWeights = {
|
|
169
|
+
export const DEFAULT_WEIGHTS: ScoringWeights = {
|
|
170
170
|
semanticSimilarity: 0.25,
|
|
171
171
|
effectiveSignificance: 0.15,
|
|
172
172
|
emotionalIntensity: 0.15,
|
|
@@ -176,6 +176,28 @@ const DEFAULT_WEIGHTS: ScoringWeights = {
|
|
|
176
176
|
activationBoost: 0.1,
|
|
177
177
|
};
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Weights for context-load of procedural memories (learned skills, how-to).
|
|
181
|
+
* Procedural memories have no emotional charge, no time-of-day pattern, and
|
|
182
|
+
* are often old-but-stable (a workaround learned months ago stays useful).
|
|
183
|
+
* Grading them on DEFAULT_WEIGHTS wastes ~45% of the budget on signals that
|
|
184
|
+
* are structurally ~0 for procedurals, causing them to lose out to episodic
|
|
185
|
+
* and emotional memories that simply have more signals lit up.
|
|
186
|
+
*
|
|
187
|
+
* This redistributes the dead weight onto semantic similarity and
|
|
188
|
+
* significance — the signals that actually differentiate useful procedurals
|
|
189
|
+
* from stale ones.
|
|
190
|
+
*/
|
|
191
|
+
export const PROCEDURAL_WEIGHTS: ScoringWeights = {
|
|
192
|
+
semanticSimilarity: 0.45,
|
|
193
|
+
effectiveSignificance: 0.25,
|
|
194
|
+
emotionalIntensity: 0.0,
|
|
195
|
+
temporalBoost: 0.0,
|
|
196
|
+
recencyBoost: 0.05,
|
|
197
|
+
triggerBoost: 0.1,
|
|
198
|
+
activationBoost: 0.15,
|
|
199
|
+
};
|
|
200
|
+
|
|
179
201
|
/**
|
|
180
202
|
* Weights for per-turn injection: heavily biased toward semantic similarity.
|
|
181
203
|
* Per-turn injections should only surface memories directly relevant to
|
|
@@ -191,6 +213,14 @@ export const PER_TURN_WEIGHTS: ScoringWeights = {
|
|
|
191
213
|
activationBoost: 0.05,
|
|
192
214
|
};
|
|
193
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Pick the appropriate context-load weights for a node based on its type.
|
|
218
|
+
* Procedural nodes use PROCEDURAL_WEIGHTS; everything else uses DEFAULT_WEIGHTS.
|
|
219
|
+
*/
|
|
220
|
+
export function weightsForContextLoad(node: MemoryNode): ScoringWeights {
|
|
221
|
+
return node.type === "procedural" ? PROCEDURAL_WEIGHTS : DEFAULT_WEIGHTS;
|
|
222
|
+
}
|
|
223
|
+
|
|
194
224
|
/**
|
|
195
225
|
* Compute the final retrieval score for a candidate node.
|
|
196
226
|
* All components should be in [0, 1] range before weighting.
|
|
@@ -82,7 +82,7 @@ export const graphRecallDefinition: ToolDefinition = {
|
|
|
82
82
|
export const graphRememberDefinition: ToolDefinition = {
|
|
83
83
|
name: "remember",
|
|
84
84
|
description:
|
|
85
|
-
"Save a fact to your knowledge base. Call this AGGRESSIVELY —
|
|
85
|
+
"Save a fact to your knowledge base. Call this AGGRESSIVELY — capture anything concrete about their life: preferences, locations, names, dates, habits, opinions, health details, plans, relationship facts, routines, commitments. Default to remembering; only skip obvious noise (small talk, hypotheticals, things they're just musing about). Don't judge importance — filing decides that later. Examples: 'Prefers UberEats over DoorDash', 'Lives in NYC, from Texas', 'Takes 45mg nicotine daily, tapering', 'Girlfriend Yen is in Texas', 'Watches vampire show Saturday nights', 'NYU Summit April 10-11'. Call this multiple times per conversation — it's cheap (one line appended to a file). Don't wait until the end. Don't batch. Every new fact, immediately. Remembering too much is infinitely better than forgetting something that mattered. CORRECTIONS are the highest priority — when the user corrects a fact you had wrong, `remember` the correction immediately. The wrong version is already propagated in your prior turns and memory graph; skipping a correction means future-you keeps operating on the old value. Never skip a correction even if you'd skip the equivalent fresh fact.",
|
|
86
86
|
input_schema: {
|
|
87
87
|
type: "object",
|
|
88
88
|
properties: {
|
package/src/memory/group-crud.ts
CHANGED
|
@@ -25,6 +25,11 @@ export interface ConversationGroupRow {
|
|
|
25
25
|
|
|
26
26
|
export function listGroups(): ConversationGroupRow[] {
|
|
27
27
|
ensureGroupMigration();
|
|
28
|
+
// Migration markers are stored as rows in conversation_groups with a leading
|
|
29
|
+
// underscore (e.g. `_backfill_complete`). System groups use the `system:`
|
|
30
|
+
// prefix and custom groups use UUIDs, so no legitimate group id starts with
|
|
31
|
+
// `_`. GLOB treats `_` as literal (unlike LIKE), so `_*` matches any id
|
|
32
|
+
// whose first character is an underscore.
|
|
28
33
|
const rows = rawAll<{
|
|
29
34
|
id: string;
|
|
30
35
|
name: string;
|
|
@@ -33,7 +38,7 @@ export function listGroups(): ConversationGroupRow[] {
|
|
|
33
38
|
created_at: number;
|
|
34
39
|
updated_at: number;
|
|
35
40
|
}>(
|
|
36
|
-
"SELECT id, name, sort_position, is_system_group, created_at, updated_at FROM conversation_groups WHERE id NOT
|
|
41
|
+
"SELECT id, name, sort_position, is_system_group, created_at, updated_at FROM conversation_groups WHERE id NOT GLOB '_*' ORDER BY sort_position ASC",
|
|
37
42
|
);
|
|
38
43
|
return rows.map((r) => ({
|
|
39
44
|
id: r.id,
|
package/src/memory/indexer.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
2
|
import { desc, eq } from "drizzle-orm";
|
|
3
3
|
|
|
4
|
+
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
4
5
|
import { getConfig } from "../config/loader.js";
|
|
5
6
|
import type { MemoryConfig } from "../config/types.js";
|
|
6
7
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
7
8
|
import { getLogger } from "../util/logger.js";
|
|
9
|
+
import { enqueueAutoAnalysisIfEnabled } from "./auto-analysis-enqueue.js";
|
|
10
|
+
import { isAutoAnalysisConversation } from "./auto-analysis-guard.js";
|
|
8
11
|
import { getMemoryCheckpoint, setMemoryCheckpoint } from "./checkpoints.js";
|
|
9
12
|
import { getDb } from "./db.js";
|
|
10
13
|
import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
|
|
@@ -154,6 +157,7 @@ export async function indexMessageNow(
|
|
|
154
157
|
// ── Batch extraction tracking ──────────────────────────────────────
|
|
155
158
|
// Instead of per-message extraction, track pending unextracted messages
|
|
156
159
|
// and trigger batch extraction when the threshold is reached or after idle.
|
|
160
|
+
const isAutoAnalysisSource = isAutoAnalysisConversation(input.conversationId);
|
|
157
161
|
if (
|
|
158
162
|
shouldExtract &&
|
|
159
163
|
isTrustedActor &&
|
|
@@ -163,26 +167,89 @@ export async function indexMessageNow(
|
|
|
163
167
|
const batchSize = config.extraction.batchSize ?? 10;
|
|
164
168
|
const idleTimeoutMs = config.extraction.idleTimeoutMs ?? 300_000;
|
|
165
169
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
170
|
+
// Recursion guard: skip graph extraction + auto-analysis enqueues
|
|
171
|
+
// when the source conversation is itself an auto-analysis
|
|
172
|
+
// conversation. The analysis agent writes memory directly via tools,
|
|
173
|
+
// so extracting from its reflective musings would double-count and
|
|
174
|
+
// analyzing its own output would loop indefinitely.
|
|
175
|
+
// Summaries still run — they feed the graph retrieval pipeline and
|
|
176
|
+
// are not recursion-prone.
|
|
177
|
+
if (!isAutoAnalysisSource) {
|
|
178
|
+
// ── Graph extraction ────────────────────────────────────────────
|
|
179
|
+
const graphPendingKey = `graph_extract:${input.conversationId}:pending_count`;
|
|
180
|
+
const graphCurrentVal = getMemoryCheckpoint(graphPendingKey);
|
|
181
|
+
const graphPendingCount =
|
|
182
|
+
(graphCurrentVal ? parseInt(graphCurrentVal, 10) : 0) + 1;
|
|
183
|
+
setMemoryCheckpoint(graphPendingKey, String(graphPendingCount));
|
|
172
184
|
|
|
173
|
-
|
|
174
|
-
|
|
185
|
+
const graphBatchFired = graphPendingCount >= batchSize;
|
|
186
|
+
if (graphBatchFired) {
|
|
187
|
+
setMemoryCheckpoint(graphPendingKey, "0");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Single pending `graph_extract` row per conversation. If the
|
|
191
|
+
// batch threshold just fired, pull `runAfter` back to now so the
|
|
192
|
+
// job runs immediately; otherwise debounce by the idle timeout.
|
|
193
|
+
// Routing both paths through `upsertDebouncedJob` ensures the
|
|
194
|
+
// row's `runAfter` reflects whichever trigger ran last, so a
|
|
195
|
+
// batch crossing always takes effect immediately.
|
|
196
|
+
upsertDebouncedJob(
|
|
197
|
+
"graph_extract",
|
|
198
|
+
{
|
|
199
|
+
conversationId: input.conversationId,
|
|
200
|
+
scopeId: input.scopeId ?? "default",
|
|
201
|
+
},
|
|
202
|
+
graphBatchFired ? Date.now() : Date.now() + idleTimeoutMs,
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// ── Auto-analysis triggers ─────────────────────────────────────
|
|
206
|
+
// Both triggers route through `upsertDebouncedJob` in the helper,
|
|
207
|
+
// so a single pending row is shared. Order matters: the idle
|
|
208
|
+
// upsert runs first (pushing `runAfter` into the future); the
|
|
209
|
+
// batch trigger runs last so a threshold crossing pulls
|
|
210
|
+
// `runAfter` back to "now" and overrides the idle debounce.
|
|
211
|
+
enqueueAutoAnalysisIfEnabled({
|
|
175
212
|
conversationId: input.conversationId,
|
|
176
|
-
|
|
213
|
+
trigger: "idle",
|
|
177
214
|
});
|
|
178
|
-
setMemoryCheckpoint(graphPendingKey, "0");
|
|
179
|
-
}
|
|
180
215
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
216
|
+
// Auto-analysis cadence is tracked by its own pending-count
|
|
217
|
+
// checkpoint so it fires at `analysis.batchSize` (default 30).
|
|
218
|
+
// Gated behind the `auto-analyze` feature flag so the counter
|
|
219
|
+
// does not accumulate stale counts while the flag is off — if it
|
|
220
|
+
// did, flipping the flag on would trigger an immediate batch from
|
|
221
|
+
// messages buffered during the disabled period. Reading config
|
|
222
|
+
// here is best-effort: if it fails we skip the batch trigger
|
|
223
|
+
// (the idle-debounced enqueue above still runs).
|
|
224
|
+
let analysisConfig: ReturnType<typeof getConfig> | null = null;
|
|
225
|
+
try {
|
|
226
|
+
analysisConfig = getConfig();
|
|
227
|
+
} catch (err) {
|
|
228
|
+
log.debug(
|
|
229
|
+
{ err, conversationId: input.conversationId },
|
|
230
|
+
"Skipping auto-analysis batch trigger: failed to load config",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
if (
|
|
234
|
+
analysisConfig != null &&
|
|
235
|
+
isAssistantFeatureFlagEnabled("auto-analyze", analysisConfig)
|
|
236
|
+
) {
|
|
237
|
+
const analysisBatchSize = analysisConfig.analysis.batchSize;
|
|
238
|
+
const analysisPendingKey = `conversation_analyze:${input.conversationId}:pending_count`;
|
|
239
|
+
const analysisCurrentVal = getMemoryCheckpoint(analysisPendingKey);
|
|
240
|
+
const analysisPendingCount =
|
|
241
|
+
(analysisCurrentVal ? parseInt(analysisCurrentVal, 10) : 0) + 1;
|
|
242
|
+
setMemoryCheckpoint(analysisPendingKey, String(analysisPendingCount));
|
|
243
|
+
|
|
244
|
+
if (analysisPendingCount >= analysisBatchSize) {
|
|
245
|
+
setMemoryCheckpoint(analysisPendingKey, "0");
|
|
246
|
+
enqueueAutoAnalysisIfEnabled({
|
|
247
|
+
conversationId: input.conversationId,
|
|
248
|
+
trigger: "batch",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
186
253
|
|
|
187
254
|
// ── Conversation summarization (independent of extraction) ────────
|
|
188
255
|
// Summaries feed the graph retrieval pipeline via fetchRecentSummaries().
|
|
@@ -228,6 +295,18 @@ export async function indexMessageNow(
|
|
|
228
295
|
);
|
|
229
296
|
}
|
|
230
297
|
|
|
298
|
+
if (
|
|
299
|
+
isAutoAnalysisSource &&
|
|
300
|
+
shouldExtract &&
|
|
301
|
+
isTrustedActor &&
|
|
302
|
+
!input.automated &&
|
|
303
|
+
config.extraction.useLLM
|
|
304
|
+
) {
|
|
305
|
+
log.debug(
|
|
306
|
+
"Skipping graph_extract + auto-analysis enqueues: source is an auto-analysis conversation",
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
231
310
|
const storedSegments = segments.length - skippedShortSegments;
|
|
232
311
|
const enqueuedJobs = storedSegments - skippedEmbedJobs + mediaBlocks.length;
|
|
233
312
|
return {
|
|
@@ -17,15 +17,18 @@ export function pruneOldLlmRequestLogsJob(
|
|
|
17
17
|
job: MemoryJob,
|
|
18
18
|
config: AssistantConfig,
|
|
19
19
|
): void {
|
|
20
|
+
const rawRetention = job.payload.retentionMs;
|
|
20
21
|
const retentionMs =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
rawRetention === null
|
|
23
|
+
? null
|
|
24
|
+
: typeof rawRetention === "number" &&
|
|
25
|
+
Number.isFinite(rawRetention) &&
|
|
26
|
+
rawRetention >= 0
|
|
27
|
+
? rawRetention
|
|
28
|
+
: config.memory.cleanup.llmRequestLogRetentionMs;
|
|
29
|
+
|
|
30
|
+
// null means "keep forever" — skip pruning entirely
|
|
31
|
+
if (retentionMs === null || retentionMs === undefined) return;
|
|
29
32
|
|
|
30
33
|
const cutoffMs = Date.now() - retentionMs;
|
|
31
34
|
|
|
@@ -197,7 +197,7 @@ async function generateStarters(scopeId: string): Promise<GeneratedStarter[]> {
|
|
|
197
197
|
})}`;
|
|
198
198
|
|
|
199
199
|
// Truncate identity context to prevent oversized prompts when SOUL.md /
|
|
200
|
-
// IDENTITY.md /
|
|
200
|
+
// IDENTITY.md / users/<slug>.md are large.
|
|
201
201
|
const rawIdentityContext = buildCoreIdentityContext({
|
|
202
202
|
userPersona: resolveGuardianPersona(),
|
|
203
203
|
});
|
|
@@ -205,11 +205,11 @@ async function generateStarters(scopeId: string): Promise<GeneratedStarter[]> {
|
|
|
205
205
|
? truncate(rawIdentityContext, 2000, "\n…[truncated]")
|
|
206
206
|
: null;
|
|
207
207
|
|
|
208
|
-
const systemPrompt = `You are generating
|
|
208
|
+
const systemPrompt = `You are generating conversation starters for a personal assistant app. These appear as clickable chips on the empty conversation page — the first thing the user sees when they open the app. Clicking a chip sends its prompt as a message from the user.
|
|
209
209
|
|
|
210
210
|
${timeContext}
|
|
211
211
|
|
|
212
|
-
Your goal: suggest the
|
|
212
|
+
Your goal: suggest the most useful things this person could ask you to do right now. Produce 8 candidates, ranked best-first; only the top 4 will be shown.
|
|
213
213
|
|
|
214
214
|
${
|
|
215
215
|
identityContext
|
|
@@ -223,7 +223,7 @@ ${skills}
|
|
|
223
223
|
|
|
224
224
|
## Selection
|
|
225
225
|
|
|
226
|
-
Generate exactly
|
|
226
|
+
Generate exactly 8 starters, ranked #1 (best) to #8. The top 4 will be shown; the rest are fallbacks in case any fail downstream validation (e.g. label too long). Put real effort into every slot — any of them may end up displayed.
|
|
227
227
|
|
|
228
228
|
Start from the user's situation, not from the skill list. Ask yourself:
|
|
229
229
|
- What is this person likely dealing with right now (given the day/time and their context)?
|
|
@@ -250,7 +250,7 @@ Favor what is live over what is merely true. Recent changes matter more than old
|
|
|
250
250
|
## Output format
|
|
251
251
|
|
|
252
252
|
Each starter has:
|
|
253
|
-
- label: 3-6 words, max 40 chars, starts with a verb. Written in the user's voice — something they'd want to do, not something the assistant is offering.
|
|
253
|
+
- label: 3-6 words, max 40 chars, starts with a verb. Written in the user's voice — something they'd want to do, not something the assistant is offering. MUST be a grammatically complete phrase: if it uses an adjective ("quarterly", "weekly"), include the noun it modifies ("quarterly review", "weekly sync"). Never end on a dangling modifier, preposition, or trailing "the/my/a". Prefer completeness over tightness when you have room under 40 chars.
|
|
254
254
|
- prompt: 1-2 natural sentences, as the user would actually say them.
|
|
255
255
|
- category: one of ${CONVERSATION_STARTER_CATEGORIES.join(", ")}
|
|
256
256
|
|
|
@@ -258,9 +258,9 @@ Each starter has:
|
|
|
258
258
|
|
|
259
259
|
**Voice**: The user clicks these chips to send a message. Every label must read as something the user is asking to do, never something the assistant is saying to the user.
|
|
260
260
|
|
|
261
|
-
**Coherence**: The 4 starters should feel like one set — similar abstraction level, no jarring mix of mundane chores and life strategy.
|
|
261
|
+
**Coherence**: The top 4 starters should feel like one set — similar abstraction level, no jarring mix of mundane chores and life strategy. The remaining 4 fallbacks may branch into adjacent topics.
|
|
262
262
|
|
|
263
|
-
**Diversity**: Each chip covers a distinct topic. Never two chips about the same tool, project, or theme.
|
|
263
|
+
**Diversity**: Each chip covers a distinct topic. Never two chips about the same tool, project, or theme. Across all 8 starters, avoid repeating topics.
|
|
264
264
|
|
|
265
265
|
**No setup chips**: Never include a chip whose primary meaning is configuration or "set up X for Y" unless it solves an urgent pain the user is actively feeling. Prefer the outcome over the mechanism.
|
|
266
266
|
|
|
@@ -276,7 +276,12 @@ Bad → Good (ticket-speak → natural):
|
|
|
276
276
|
|
|
277
277
|
Bad → Good (assistant voice → user voice):
|
|
278
278
|
- "You've got a busy week ahead" → "Plan my week ahead"
|
|
279
|
-
- "Let me check your calendar" → "Check my Thursday schedule"
|
|
279
|
+
- "Let me check your calendar" → "Check my Thursday schedule"
|
|
280
|
+
|
|
281
|
+
Bad → Good (incomplete phrase → complete):
|
|
282
|
+
- "Prep for Friday's quarterly" → "Prep for Friday's quarterly review"
|
|
283
|
+
- "Finish the onboarding" → "Finish the onboarding guide"
|
|
284
|
+
- "Draft the release" → "Draft the release notes"`;
|
|
280
285
|
|
|
281
286
|
const { signal, cleanup } = createTimeout(20000);
|
|
282
287
|
try {
|
|
@@ -326,7 +331,7 @@ Bad → Good (assistant voice → user voice):
|
|
|
326
331
|
{
|
|
327
332
|
config: {
|
|
328
333
|
modelIntent: "quality-optimized",
|
|
329
|
-
max_tokens:
|
|
334
|
+
max_tokens: 2048,
|
|
330
335
|
tool_choice: {
|
|
331
336
|
type: "tool" as const,
|
|
332
337
|
name: "store_conversation_starters",
|
|
@@ -356,12 +361,13 @@ Bad → Good (assistant voice → user voice):
|
|
|
356
361
|
(s) =>
|
|
357
362
|
typeof s.label === "string" &&
|
|
358
363
|
s.label.length > 0 &&
|
|
364
|
+
s.label.length <= 40 &&
|
|
359
365
|
typeof s.prompt === "string" &&
|
|
360
366
|
s.prompt.length > 0
|
|
361
367
|
)
|
|
362
368
|
.slice(0, 4)
|
|
363
369
|
.map((s) => ({
|
|
364
|
-
label:
|
|
370
|
+
label: s.label,
|
|
365
371
|
prompt: truncate(s.prompt, 500, ""),
|
|
366
372
|
category:
|
|
367
373
|
typeof s.category === "string" &&
|
package/src/memory/jobs-store.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type MemoryJobType =
|
|
|
18
18
|
| "prune_old_conversations"
|
|
19
19
|
| "prune_old_llm_request_logs"
|
|
20
20
|
| "build_conversation_summary"
|
|
21
|
+
| "conversation_analyze"
|
|
21
22
|
| "backfill"
|
|
22
23
|
| "rebuild_index"
|
|
23
24
|
| "delete_qdrant_vectors"
|
|
@@ -89,15 +90,19 @@ export function enqueueMemoryJob(
|
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
92
|
* Upsert a debounced job: if a pending job of the same type and conversation
|
|
92
|
-
* already exists,
|
|
93
|
-
*
|
|
93
|
+
* already exists, merge the new payload into the existing row and update
|
|
94
|
+
* `runAfter` instead of creating a duplicate. This prevents rapid message
|
|
95
|
+
* indexing from spawning redundant jobs while ensuring the latest payload
|
|
96
|
+
* keys (e.g. `scopeId`) reach the handler — including on upgraded instances
|
|
97
|
+
* where the existing pending row was enqueued by an older build that did
|
|
98
|
+
* not write those keys.
|
|
94
99
|
*
|
|
95
100
|
* Pass a `dbOverride` (transaction handle) to make this call atomic with
|
|
96
101
|
* surrounding writes.
|
|
97
102
|
*/
|
|
98
103
|
export function upsertDebouncedJob(
|
|
99
104
|
type: MemoryJobType,
|
|
100
|
-
payload: { conversationId: string },
|
|
105
|
+
payload: { conversationId: string } & Record<string, unknown>,
|
|
101
106
|
runAfter: number,
|
|
102
107
|
dbOverride?: Parameters<ReturnType<typeof getDb>["transaction"]>[0] extends (
|
|
103
108
|
tx: infer T,
|
|
@@ -118,8 +123,19 @@ export function upsertDebouncedJob(
|
|
|
118
123
|
)
|
|
119
124
|
.get();
|
|
120
125
|
if (existing) {
|
|
126
|
+
let existingPayload: Record<string, unknown> = {};
|
|
127
|
+
try {
|
|
128
|
+
existingPayload = JSON.parse(existing.payload) as Record<string, unknown>;
|
|
129
|
+
} catch {
|
|
130
|
+
existingPayload = {};
|
|
131
|
+
}
|
|
132
|
+
const mergedPayload = { ...existingPayload, ...payload };
|
|
121
133
|
db.update(memoryJobs)
|
|
122
|
-
.set({
|
|
134
|
+
.set({
|
|
135
|
+
payload: JSON.stringify(mergedPayload),
|
|
136
|
+
runAfter,
|
|
137
|
+
updatedAt: Date.now(),
|
|
138
|
+
})
|
|
123
139
|
.where(eq(memoryJobs.id, existing.id))
|
|
124
140
|
.run();
|
|
125
141
|
} else {
|
|
@@ -127,6 +143,50 @@ export function upsertDebouncedJob(
|
|
|
127
143
|
}
|
|
128
144
|
}
|
|
129
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Upsert a pending `conversation_analyze` job keyed by both
|
|
148
|
+
* `conversationId` and `triggerGroup`. Immediate triggers (batch,
|
|
149
|
+
* compaction) and debounced triggers (idle, lifecycle) live in separate
|
|
150
|
+
* rows so an idle enqueue cannot push an already-scheduled immediate
|
|
151
|
+
* row's `runAfter` into the future (and vice versa). Each group still
|
|
152
|
+
* coalesces within itself: two batch crossings, or two idle triggers,
|
|
153
|
+
* collapse to a single pending row.
|
|
154
|
+
*/
|
|
155
|
+
export function upsertAutoAnalysisJob(
|
|
156
|
+
payload: {
|
|
157
|
+
conversationId: string;
|
|
158
|
+
triggerGroup: "immediate" | "debounced";
|
|
159
|
+
},
|
|
160
|
+
runAfter: number,
|
|
161
|
+
dbOverride?: Parameters<ReturnType<typeof getDb>["transaction"]>[0] extends (
|
|
162
|
+
tx: infer T,
|
|
163
|
+
) => unknown
|
|
164
|
+
? T
|
|
165
|
+
: never,
|
|
166
|
+
): void {
|
|
167
|
+
const db = dbOverride ?? getDb();
|
|
168
|
+
const existing = db
|
|
169
|
+
.select()
|
|
170
|
+
.from(memoryJobs)
|
|
171
|
+
.where(
|
|
172
|
+
and(
|
|
173
|
+
eq(memoryJobs.type, "conversation_analyze"),
|
|
174
|
+
eq(memoryJobs.status, "pending"),
|
|
175
|
+
sql`json_extract(${memoryJobs.payload}, '$.conversationId') = ${payload.conversationId}`,
|
|
176
|
+
sql`json_extract(${memoryJobs.payload}, '$.triggerGroup') = ${payload.triggerGroup}`,
|
|
177
|
+
),
|
|
178
|
+
)
|
|
179
|
+
.get();
|
|
180
|
+
if (existing) {
|
|
181
|
+
db.update(memoryJobs)
|
|
182
|
+
.set({ runAfter, updatedAt: Date.now() })
|
|
183
|
+
.where(eq(memoryJobs.id, existing.id))
|
|
184
|
+
.run();
|
|
185
|
+
} else {
|
|
186
|
+
enqueueMemoryJob("conversation_analyze", payload, runAfter, dbOverride);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
130
190
|
/**
|
|
131
191
|
* Check whether a pending or running job of the given type already exists.
|
|
132
192
|
* Used to prevent duplicate enqueues for long-running maintenance jobs.
|