@vellumai/assistant 0.5.15 → 0.6.0
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 +3 -3
- package/Dockerfile +0 -3
- package/docs/architecture/integrations.md +15 -14
- package/knip.json +4 -1
- package/openapi.yaml +670 -122
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +57 -3
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +5 -377
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/ces-rpc-credential-backend.test.ts +4 -1
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +84 -3
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +108 -6
- package/src/__tests__/cli-memory.test.ts +377 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -2
- package/src/__tests__/config-schema.test.ts +1 -3
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/config-watcher-feature-flags.test.ts +211 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
- package/src/__tests__/conversation-agent-loop.test.ts +9 -0
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +5 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +2 -6
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-usage.test.ts +3 -6
- package/src/__tests__/conversation-wipe.test.ts +11 -408
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +6 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +7 -73
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -7
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +343 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/heartbeat-service.test.ts +1 -3
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +154 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +292 -0
- package/src/__tests__/intent-routing.test.ts +6 -18
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/llm-context-route-provider.test.ts +2 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +4 -34
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -37
- package/src/__tests__/managed-store.test.ts +40 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +2 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/messaging-send-tool.test.ts +6 -6
- package/src/__tests__/migration-cross-version-compatibility.test.ts +1 -29
- package/src/__tests__/migration-export-http.test.ts +3 -34
- package/src/__tests__/migration-import-commit-http.test.ts +1 -29
- package/src/__tests__/migration-import-preflight-http.test.ts +3 -34
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +2 -1
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-apps-routes.test.ts +120 -10
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth-connect-orchestrator.test.ts +709 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +2 -1
- package/src/__tests__/oauth-provider-visibility.test.ts +149 -0
- package/src/__tests__/oauth-providers-routes.test.ts +5 -2
- package/src/__tests__/oauth-store.test.ts +0 -5
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +1071 -0
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/path-policy.test.ts +2 -17
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/platform-callback-registration.test.ts +7 -11
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +21 -2
- package/src/__tests__/qdrant-manager.test.ts +68 -21
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/require-fresh-approval.test.ts +64 -3
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/sandbox-diagnostics.test.ts +20 -29
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/search-skills-unified.test.ts +421 -0
- package/src/__tests__/secret-allowlist.test.ts +20 -35
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +2 -6
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/shell-credential-ref.test.ts +0 -5
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +13 -54
- package/src/__tests__/skill-load-inline-command.test.ts +3 -65
- package/src/__tests__/skill-load-inline-includes.test.ts +3 -65
- package/src/__tests__/skill-load-tool.test.ts +3 -67
- package/src/__tests__/skill-memory.test.ts +480 -195
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +23 -50
- package/src/__tests__/slack-channel-config.test.ts +2 -21
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/starter-bundle.test.ts +2 -8
- package/src/__tests__/stt-hints.test.ts +7 -2
- package/src/__tests__/system-prompt.test.ts +25 -45
- package/src/__tests__/task-compiler.test.ts +2 -27
- package/src/__tests__/task-management-tools.test.ts +2 -27
- package/src/__tests__/task-memory-cleanup.test.ts +173 -250
- package/src/__tests__/task-runner.test.ts +2 -27
- package/src/__tests__/task-scheduler.test.ts +2 -27
- package/src/__tests__/terminal-tools.test.ts +1 -17
- package/src/__tests__/test-preload.ts +3 -0
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +0 -79
- package/src/__tests__/tool-approval-handler.test.ts +4 -27
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -11
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -25
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +4 -27
- package/src/__tests__/tool-preview-lifecycle.test.ts +0 -20
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
- package/src/__tests__/trust-store.test.ts +10 -42
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -30
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +3 -27
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -28
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -28
- package/src/__tests__/trusted-contact-verification.test.ts +2 -28
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -34
- package/src/__tests__/twilio-provider.test.ts +0 -16
- package/src/__tests__/twilio-routes-twiml.test.ts +7 -12
- package/src/__tests__/twilio-routes.test.ts +0 -24
- package/src/__tests__/update-bulletin.test.ts +17 -89
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -26
- package/src/__tests__/usage-routes.test.ts +2 -27
- package/src/__tests__/user-reference.test.ts +1 -5
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +4 -34
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -53
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -27
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -30
- package/src/__tests__/voice-session-bridge.test.ts +2 -27
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -29
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +2 -2
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +4 -29
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-migration-down-functions.test.ts +0 -6
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +1 -15
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -24
- package/src/cli/cli-memory.ts +179 -0
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/memory.ts +41 -55
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +26 -6
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/providers.ts +49 -42
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +3 -49
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +3 -49
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -55
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/skills.ts +88 -16
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/TOOLS.json +7 -7
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +2 -10
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +26 -19
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/slack/SKILL.md +1 -7
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +15 -8
- package/src/config/feature-flag-registry.json +29 -116
- package/src/config/loader.ts +4 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +8 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +32 -9
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/config-watcher.ts +51 -0
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
- package/src/daemon/conversation-agent-loop.ts +99 -63
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-lifecycle.ts +33 -0
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-process.ts +1 -0
- package/src/daemon/conversation-runtime-assembly.ts +5 -0
- package/src/daemon/conversation-usage.ts +1 -0
- package/src/daemon/conversation.ts +41 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/handlers/shared.ts +22 -12
- package/src/daemon/handlers/skills.ts +423 -201
- package/src/daemon/lifecycle.ts +52 -4
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +5 -1
- package/src/daemon/message-types/messages.ts +3 -1
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/providers-setup.ts +7 -0
- package/src/daemon/server.ts +35 -22
- package/src/daemon/tool-side-effects.ts +27 -5
- package/src/events/domain-events.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/memory/admin.ts +11 -45
- package/src/memory/conversation-bootstrap.ts +2 -0
- package/src/memory/conversation-crud.ts +242 -348
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +4 -2
- package/src/memory/db-init.ts +39 -3
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +512 -0
- package/src/memory/graph/capability-seed.ts +297 -0
- package/src/memory/graph/consolidation.ts +691 -0
- package/src/memory/graph/conversation-graph-memory.ts +630 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +69 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1254 -0
- package/src/memory/graph/graph-search.ts +266 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +439 -0
- package/src/memory/graph/inspect.ts +534 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1008 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1050 -0
- package/src/memory/graph/store.ts +699 -0
- package/src/memory/graph/tool-handlers.ts +426 -0
- package/src/memory/graph/tools.ts +141 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +271 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +0 -53
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.test.ts +3 -27
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +50 -70
- package/src/memory/jobs-worker.ts +147 -112
- package/src/memory/llm-usage-store.ts +35 -2
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/201-oauth-providers-feature-flag.ts +11 -0
- package/src/memory/migrations/202-drop-callback-transport-column.ts +13 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/qdrant-manager.ts +26 -5
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/memory-graph.ts +139 -0
- package/src/memory/schema/oauth.ts +1 -1
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/slack-thread-store.ts +17 -0
- package/src/memory/task-memory-cleanup.ts +28 -50
- package/src/messaging/providers/outlook/adapter.ts +200 -0
- package/src/messaging/providers/outlook/client.ts +610 -0
- package/src/messaging/providers/outlook/types.ts +201 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/adapters/slack.ts +1 -1
- package/src/notifications/copy-composer.ts +9 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +10 -3
- package/src/oauth/oauth-store.ts +10 -11
- package/src/oauth/provider-serializer.ts +3 -0
- package/src/oauth/provider-visibility.ts +16 -0
- package/src/oauth/seed-providers.ts +50 -17
- package/src/permissions/checker.ts +62 -9
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/types.ts +2 -4
- package/src/permissions/workspace-policy.ts +1 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +6 -93
- package/src/prompts/templates/UPDATES.md +6 -0
- package/src/providers/anthropic/client.ts +47 -19
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -27
- package/src/runtime/auth/__tests__/token-service.test.ts +1 -25
- package/src/runtime/auth/route-policy.ts +7 -4
- package/src/runtime/guardian-reply-router.ts +10 -2
- package/src/runtime/http-server.ts +23 -3
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +8 -0
- package/src/runtime/routes/conversation-management-routes.ts +2 -0
- package/src/runtime/routes/conversation-query-routes.ts +2 -58
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +43 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/memory-item-routes.test.ts +2 -31
- package/src/runtime/routes/memory-item-routes.ts +385 -341
- package/src/runtime/routes/oauth-apps.ts +18 -1
- package/src/runtime/routes/oauth-providers.ts +13 -1
- package/src/runtime/routes/schedule-routes.ts +2 -0
- package/src/runtime/routes/settings-routes.ts +1 -0
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/usage-routes.ts +19 -2
- package/src/runtime/routes/work-items-routes.test.ts +2 -27
- package/src/runtime/routes/workspace-routes.test.ts +3 -27
- package/src/schedule/scheduler.ts +8 -1
- package/src/security/oauth2.ts +1 -1
- package/src/security/secret-allowlist.ts +4 -4
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +15 -14
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +27 -16
- package/src/skills/skill-memory.ts +210 -96
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/tasks/task-runner.ts +3 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +7 -19
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/skills/skill-script-runner.ts +1 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/tool-manifest.ts +3 -3
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/util/device-id.ts +3 -65
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/git-service.ts +27 -6
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -741
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -383
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1590
- package/src/memory/retriever.ts +0 -1323
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -136
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -156
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -590
- package/src/tools/memory/handlers.ts +0 -434
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
evaluateEventTriggers,
|
|
5
|
+
evaluateSemanticTriggers,
|
|
6
|
+
evaluateTemporalTriggers,
|
|
7
|
+
} from "./triggers.js";
|
|
8
|
+
import type { MemoryTrigger } from "./types.js";
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helpers
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
const DAY_MS = 1000 * 60 * 60 * 24;
|
|
15
|
+
|
|
16
|
+
function makeTrigger(overrides: Partial<MemoryTrigger> = {}): MemoryTrigger {
|
|
17
|
+
return {
|
|
18
|
+
id: "trigger-1",
|
|
19
|
+
nodeId: "node-1",
|
|
20
|
+
type: "temporal",
|
|
21
|
+
schedule: null,
|
|
22
|
+
condition: null,
|
|
23
|
+
conditionEmbedding: null,
|
|
24
|
+
threshold: null,
|
|
25
|
+
eventDate: null,
|
|
26
|
+
rampDays: null,
|
|
27
|
+
followUpDays: null,
|
|
28
|
+
recurring: false,
|
|
29
|
+
consumed: false,
|
|
30
|
+
cooldownMs: null,
|
|
31
|
+
lastFired: null,
|
|
32
|
+
...overrides,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// evaluateTemporalTriggers
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
describe("evaluateTemporalTriggers", () => {
|
|
41
|
+
test("fires day-of-week trigger on matching day", () => {
|
|
42
|
+
// 2025-06-16 is a Monday
|
|
43
|
+
const monday = new Date("2025-06-16T10:00:00Z");
|
|
44
|
+
const trigger = makeTrigger({
|
|
45
|
+
type: "temporal",
|
|
46
|
+
schedule: "day-of-week:monday",
|
|
47
|
+
});
|
|
48
|
+
const results = evaluateTemporalTriggers([trigger], monday);
|
|
49
|
+
expect(results).toHaveLength(1);
|
|
50
|
+
expect(results[0].boost).toBe(1.0);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("does not fire day-of-week trigger on non-matching day", () => {
|
|
54
|
+
// 2025-06-17 is a Tuesday
|
|
55
|
+
const tuesday = new Date("2025-06-17T10:00:00Z");
|
|
56
|
+
const trigger = makeTrigger({
|
|
57
|
+
type: "temporal",
|
|
58
|
+
schedule: "day-of-week:monday",
|
|
59
|
+
});
|
|
60
|
+
const results = evaluateTemporalTriggers([trigger], tuesday);
|
|
61
|
+
expect(results).toHaveLength(0);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("fires date trigger on matching date", () => {
|
|
65
|
+
const april8 = new Date("2025-04-08T15:00:00Z");
|
|
66
|
+
const trigger = makeTrigger({
|
|
67
|
+
type: "temporal",
|
|
68
|
+
schedule: "date:04-08",
|
|
69
|
+
});
|
|
70
|
+
const results = evaluateTemporalTriggers([trigger], april8);
|
|
71
|
+
expect(results).toHaveLength(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("does not fire date trigger on non-matching date", () => {
|
|
75
|
+
const april9 = new Date("2025-04-09T15:00:00Z");
|
|
76
|
+
const trigger = makeTrigger({
|
|
77
|
+
type: "temporal",
|
|
78
|
+
schedule: "date:04-08",
|
|
79
|
+
});
|
|
80
|
+
const results = evaluateTemporalTriggers([trigger], april9);
|
|
81
|
+
expect(results).toHaveLength(0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("fires time:morning trigger between 5-11", () => {
|
|
85
|
+
const morning = new Date("2025-06-15T08:00:00"); // local hour 8
|
|
86
|
+
const trigger = makeTrigger({
|
|
87
|
+
type: "temporal",
|
|
88
|
+
schedule: "time:morning",
|
|
89
|
+
});
|
|
90
|
+
const results = evaluateTemporalTriggers([trigger], morning);
|
|
91
|
+
expect(results).toHaveLength(1);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("fires time:afternoon trigger between 12-16", () => {
|
|
95
|
+
const afternoon = new Date("2025-06-15T14:00:00"); // local hour 14
|
|
96
|
+
const trigger = makeTrigger({
|
|
97
|
+
type: "temporal",
|
|
98
|
+
schedule: "time:afternoon",
|
|
99
|
+
});
|
|
100
|
+
const results = evaluateTemporalTriggers([trigger], afternoon);
|
|
101
|
+
expect(results).toHaveLength(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("fires time:evening trigger between 17-20", () => {
|
|
105
|
+
const evening = new Date("2025-06-15T18:00:00"); // local hour 18
|
|
106
|
+
const trigger = makeTrigger({
|
|
107
|
+
type: "temporal",
|
|
108
|
+
schedule: "time:evening",
|
|
109
|
+
});
|
|
110
|
+
const results = evaluateTemporalTriggers([trigger], evening);
|
|
111
|
+
expect(results).toHaveLength(1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("fires time:night trigger at 22:00", () => {
|
|
115
|
+
const night = new Date("2025-06-15T22:00:00"); // local hour 22
|
|
116
|
+
const trigger = makeTrigger({
|
|
117
|
+
type: "temporal",
|
|
118
|
+
schedule: "time:night",
|
|
119
|
+
});
|
|
120
|
+
const results = evaluateTemporalTriggers([trigger], night);
|
|
121
|
+
expect(results).toHaveLength(1);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test("fires time:night trigger at 3 AM (wraps past midnight)", () => {
|
|
125
|
+
const lateNight = new Date("2025-06-15T03:00:00"); // local hour 3
|
|
126
|
+
const trigger = makeTrigger({
|
|
127
|
+
type: "temporal",
|
|
128
|
+
schedule: "time:night",
|
|
129
|
+
});
|
|
130
|
+
const results = evaluateTemporalTriggers([trigger], lateNight);
|
|
131
|
+
expect(results).toHaveLength(1);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("does not fire time:morning at 14:00", () => {
|
|
135
|
+
const afternoon = new Date("2025-06-15T14:00:00");
|
|
136
|
+
const trigger = makeTrigger({
|
|
137
|
+
type: "temporal",
|
|
138
|
+
schedule: "time:morning",
|
|
139
|
+
});
|
|
140
|
+
const results = evaluateTemporalTriggers([trigger], afternoon);
|
|
141
|
+
expect(results).toHaveLength(0);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("skips non-temporal triggers", () => {
|
|
145
|
+
const now = new Date("2025-06-16T10:00:00Z");
|
|
146
|
+
const trigger = makeTrigger({
|
|
147
|
+
type: "semantic",
|
|
148
|
+
schedule: "day-of-week:monday",
|
|
149
|
+
});
|
|
150
|
+
const results = evaluateTemporalTriggers([trigger], now);
|
|
151
|
+
expect(results).toHaveLength(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("skips triggers without a schedule", () => {
|
|
155
|
+
const now = new Date("2025-06-16T10:00:00Z");
|
|
156
|
+
const trigger = makeTrigger({
|
|
157
|
+
type: "temporal",
|
|
158
|
+
schedule: null,
|
|
159
|
+
});
|
|
160
|
+
const results = evaluateTemporalTriggers([trigger], now);
|
|
161
|
+
expect(results).toHaveLength(0);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("respects cooldown for recurring triggers", () => {
|
|
165
|
+
const now = new Date("2025-06-16T10:00:00Z");
|
|
166
|
+
const trigger = makeTrigger({
|
|
167
|
+
type: "temporal",
|
|
168
|
+
schedule: "day-of-week:monday",
|
|
169
|
+
recurring: true,
|
|
170
|
+
cooldownMs: DAY_MS,
|
|
171
|
+
lastFired: now.getTime() - DAY_MS / 2, // fired 12h ago, cooldown 24h
|
|
172
|
+
});
|
|
173
|
+
const results = evaluateTemporalTriggers([trigger], now);
|
|
174
|
+
expect(results).toHaveLength(0);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("fires recurring trigger after cooldown expires", () => {
|
|
178
|
+
const now = new Date("2025-06-16T10:00:00Z");
|
|
179
|
+
const trigger = makeTrigger({
|
|
180
|
+
type: "temporal",
|
|
181
|
+
schedule: "day-of-week:monday",
|
|
182
|
+
recurring: true,
|
|
183
|
+
cooldownMs: DAY_MS,
|
|
184
|
+
lastFired: now.getTime() - DAY_MS * 2, // fired 2 days ago, cooldown 24h
|
|
185
|
+
});
|
|
186
|
+
const results = evaluateTemporalTriggers([trigger], now);
|
|
187
|
+
expect(results).toHaveLength(1);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("non-recurring triggers bypass cooldown check", () => {
|
|
191
|
+
const now = new Date("2025-06-16T10:00:00Z");
|
|
192
|
+
const trigger = makeTrigger({
|
|
193
|
+
type: "temporal",
|
|
194
|
+
schedule: "day-of-week:monday",
|
|
195
|
+
recurring: false,
|
|
196
|
+
lastFired: now.getTime() - 1000, // fired 1s ago
|
|
197
|
+
});
|
|
198
|
+
const results = evaluateTemporalTriggers([trigger], now);
|
|
199
|
+
expect(results).toHaveLength(1);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("handles case-insensitive schedule matching", () => {
|
|
203
|
+
const monday = new Date("2025-06-16T10:00:00Z");
|
|
204
|
+
const trigger = makeTrigger({
|
|
205
|
+
type: "temporal",
|
|
206
|
+
schedule: "Day-Of-Week:Monday",
|
|
207
|
+
});
|
|
208
|
+
const results = evaluateTemporalTriggers([trigger], monday);
|
|
209
|
+
expect(results).toHaveLength(1);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("evaluates multiple triggers independently", () => {
|
|
213
|
+
const monday = new Date("2025-06-16T10:00:00Z");
|
|
214
|
+
const triggers = [
|
|
215
|
+
makeTrigger({
|
|
216
|
+
id: "t1",
|
|
217
|
+
type: "temporal",
|
|
218
|
+
schedule: "day-of-week:monday",
|
|
219
|
+
}),
|
|
220
|
+
makeTrigger({
|
|
221
|
+
id: "t2",
|
|
222
|
+
type: "temporal",
|
|
223
|
+
schedule: "day-of-week:tuesday",
|
|
224
|
+
}),
|
|
225
|
+
makeTrigger({
|
|
226
|
+
id: "t3",
|
|
227
|
+
type: "temporal",
|
|
228
|
+
schedule: "day-of-week:monday",
|
|
229
|
+
}),
|
|
230
|
+
];
|
|
231
|
+
const results = evaluateTemporalTriggers(triggers, monday);
|
|
232
|
+
expect(results).toHaveLength(2);
|
|
233
|
+
expect(results.map((r) => r.trigger.id)).toEqual(["t1", "t3"]);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// evaluateSemanticTriggers
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
describe("evaluateSemanticTriggers", () => {
|
|
242
|
+
test("fires when cosine similarity exceeds threshold", () => {
|
|
243
|
+
// Two identical unit vectors → similarity = 1.0
|
|
244
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
245
|
+
const trigger = makeTrigger({
|
|
246
|
+
type: "semantic",
|
|
247
|
+
conditionEmbedding: new Float32Array([1, 0, 0]),
|
|
248
|
+
threshold: 0.5,
|
|
249
|
+
});
|
|
250
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
251
|
+
expect(results).toHaveLength(1);
|
|
252
|
+
expect(results[0].boost).toBeGreaterThanOrEqual(0.5);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("does not fire when similarity is below threshold", () => {
|
|
256
|
+
// Orthogonal vectors → similarity = 0
|
|
257
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
258
|
+
const trigger = makeTrigger({
|
|
259
|
+
type: "semantic",
|
|
260
|
+
conditionEmbedding: new Float32Array([0, 1, 0]),
|
|
261
|
+
threshold: 0.5,
|
|
262
|
+
});
|
|
263
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
264
|
+
expect(results).toHaveLength(0);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
test("boost scales by how far above threshold", () => {
|
|
268
|
+
// Identical vectors: similarity = 1.0, threshold = 0.5
|
|
269
|
+
// boost = (1.0 - 0.5) / (1 - 0.5 + 0.001) ≈ 1.0
|
|
270
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
271
|
+
const trigger = makeTrigger({
|
|
272
|
+
type: "semantic",
|
|
273
|
+
conditionEmbedding: new Float32Array([1, 0, 0]),
|
|
274
|
+
threshold: 0.5,
|
|
275
|
+
});
|
|
276
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
277
|
+
expect(results[0].boost).toBeCloseTo(1.0, 1);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
test("boost has minimum of 0.5", () => {
|
|
281
|
+
// Barely above threshold — boost would be near 0, but floored to 0.5
|
|
282
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
283
|
+
const trigger = makeTrigger({
|
|
284
|
+
type: "semantic",
|
|
285
|
+
conditionEmbedding: new Float32Array([0.98, 0.2, 0]),
|
|
286
|
+
threshold: 0.97,
|
|
287
|
+
});
|
|
288
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
289
|
+
if (results.length > 0) {
|
|
290
|
+
expect(results[0].boost).toBeGreaterThanOrEqual(0.5);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("skips consumed triggers", () => {
|
|
295
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
296
|
+
const trigger = makeTrigger({
|
|
297
|
+
type: "semantic",
|
|
298
|
+
conditionEmbedding: new Float32Array([1, 0, 0]),
|
|
299
|
+
threshold: 0.5,
|
|
300
|
+
consumed: true,
|
|
301
|
+
});
|
|
302
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
303
|
+
expect(results).toHaveLength(0);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("skips triggers without embeddings", () => {
|
|
307
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
308
|
+
const trigger = makeTrigger({
|
|
309
|
+
type: "semantic",
|
|
310
|
+
conditionEmbedding: null,
|
|
311
|
+
threshold: 0.5,
|
|
312
|
+
});
|
|
313
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
314
|
+
expect(results).toHaveLength(0);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("skips triggers without threshold", () => {
|
|
318
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
319
|
+
const trigger = makeTrigger({
|
|
320
|
+
type: "semantic",
|
|
321
|
+
conditionEmbedding: new Float32Array([1, 0, 0]),
|
|
322
|
+
threshold: null,
|
|
323
|
+
});
|
|
324
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
325
|
+
expect(results).toHaveLength(0);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("skips non-semantic triggers", () => {
|
|
329
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
330
|
+
const trigger = makeTrigger({
|
|
331
|
+
type: "temporal",
|
|
332
|
+
conditionEmbedding: new Float32Array([1, 0, 0]),
|
|
333
|
+
threshold: 0.5,
|
|
334
|
+
});
|
|
335
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
336
|
+
expect(results).toHaveLength(0);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test("handles mismatched vector lengths gracefully", () => {
|
|
340
|
+
const embedding = new Float32Array([1, 0, 0]);
|
|
341
|
+
const trigger = makeTrigger({
|
|
342
|
+
type: "semantic",
|
|
343
|
+
conditionEmbedding: new Float32Array([1, 0]),
|
|
344
|
+
threshold: 0.5,
|
|
345
|
+
});
|
|
346
|
+
// cosineSimilarity returns 0 for mismatched lengths → no fire
|
|
347
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
348
|
+
expect(results).toHaveLength(0);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("accepts number[] as query embedding", () => {
|
|
352
|
+
const embedding = [1, 0, 0];
|
|
353
|
+
const trigger = makeTrigger({
|
|
354
|
+
type: "semantic",
|
|
355
|
+
conditionEmbedding: new Float32Array([1, 0, 0]),
|
|
356
|
+
threshold: 0.5,
|
|
357
|
+
});
|
|
358
|
+
const results = evaluateSemanticTriggers([trigger], embedding);
|
|
359
|
+
expect(results).toHaveLength(1);
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// ---------------------------------------------------------------------------
|
|
364
|
+
// evaluateEventTriggers
|
|
365
|
+
// ---------------------------------------------------------------------------
|
|
366
|
+
|
|
367
|
+
describe("evaluateEventTriggers", () => {
|
|
368
|
+
test("returns background boost (0.05) for events far in the future", () => {
|
|
369
|
+
const now = new Date("2025-06-15T10:00:00Z");
|
|
370
|
+
const trigger = makeTrigger({
|
|
371
|
+
type: "event",
|
|
372
|
+
eventDate: now.getTime() + 30 * DAY_MS, // 30 days away
|
|
373
|
+
rampDays: 7,
|
|
374
|
+
followUpDays: 2,
|
|
375
|
+
});
|
|
376
|
+
const results = evaluateEventTriggers([trigger], now);
|
|
377
|
+
expect(results).toHaveLength(1);
|
|
378
|
+
expect(results[0].boost).toBeCloseTo(0.05, 5);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("ramps linearly from 0.05 to 1.0 as event approaches", () => {
|
|
382
|
+
const now = new Date("2025-06-15T10:00:00Z");
|
|
383
|
+
const eventDate = now.getTime() + 7 * DAY_MS; // exactly at rampDays boundary
|
|
384
|
+
const trigger = makeTrigger({
|
|
385
|
+
type: "event",
|
|
386
|
+
eventDate,
|
|
387
|
+
rampDays: 7,
|
|
388
|
+
followUpDays: 2,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
// At rampDays boundary: daysUntil = 7, rampDays = 7 → boost = 0.05
|
|
392
|
+
const resultsAtBoundary = evaluateEventTriggers([trigger], now);
|
|
393
|
+
expect(resultsAtBoundary[0].boost).toBeCloseTo(0.05, 5);
|
|
394
|
+
|
|
395
|
+
// Halfway through ramp: 3.5 days before event
|
|
396
|
+
const halfwayNow = new Date(now.getTime() + 3.5 * DAY_MS);
|
|
397
|
+
const resultsHalfway = evaluateEventTriggers([trigger], halfwayNow);
|
|
398
|
+
expect(resultsHalfway[0].boost).toBeCloseTo(0.525, 1);
|
|
399
|
+
|
|
400
|
+
// Day before: daysUntil ≈ 0 → boost ≈ 1.0
|
|
401
|
+
const dayBeforeNow = new Date(eventDate - 0.1 * DAY_MS);
|
|
402
|
+
const resultsDayBefore = evaluateEventTriggers([trigger], dayBeforeNow);
|
|
403
|
+
expect(resultsDayBefore[0].boost).toBeGreaterThan(0.9);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test("returns full boost (1.0) on the day of the event", () => {
|
|
407
|
+
const now = new Date("2025-06-15T10:00:00Z");
|
|
408
|
+
const eventDate = now.getTime(); // right now
|
|
409
|
+
const trigger = makeTrigger({
|
|
410
|
+
type: "event",
|
|
411
|
+
eventDate,
|
|
412
|
+
rampDays: 7,
|
|
413
|
+
followUpDays: 2,
|
|
414
|
+
});
|
|
415
|
+
// daysUntil = 0, which is > -1 → day-of boost = 1.0
|
|
416
|
+
const results = evaluateEventTriggers([trigger], now);
|
|
417
|
+
expect(results).toHaveLength(1);
|
|
418
|
+
expect(results[0].boost).toBe(1.0);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test("decays exponentially after the event", () => {
|
|
422
|
+
const eventDate = new Date("2025-06-15T10:00:00Z").getTime();
|
|
423
|
+
const trigger = makeTrigger({
|
|
424
|
+
type: "event",
|
|
425
|
+
eventDate,
|
|
426
|
+
rampDays: 7,
|
|
427
|
+
followUpDays: 2,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// 1.5 days after event
|
|
431
|
+
const afterNow = new Date(eventDate + 1.5 * DAY_MS);
|
|
432
|
+
const results = evaluateEventTriggers([trigger], afterNow);
|
|
433
|
+
expect(results).toHaveLength(1);
|
|
434
|
+
expect(results[0].boost).toBeGreaterThan(0.01);
|
|
435
|
+
expect(results[0].boost).toBeLessThan(1.0);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("returns 0 after followUpDays", () => {
|
|
439
|
+
const eventDate = new Date("2025-06-15T10:00:00Z").getTime();
|
|
440
|
+
const trigger = makeTrigger({
|
|
441
|
+
type: "event",
|
|
442
|
+
eventDate,
|
|
443
|
+
rampDays: 7,
|
|
444
|
+
followUpDays: 2,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// 3 days after event (beyond followUpDays=2)
|
|
448
|
+
const longAfter = new Date(eventDate + 3 * DAY_MS);
|
|
449
|
+
const results = evaluateEventTriggers([trigger], longAfter);
|
|
450
|
+
expect(results).toHaveLength(0);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test("skips non-event triggers", () => {
|
|
454
|
+
const now = new Date("2025-06-15T10:00:00Z");
|
|
455
|
+
const trigger = makeTrigger({
|
|
456
|
+
type: "temporal",
|
|
457
|
+
eventDate: now.getTime() + DAY_MS,
|
|
458
|
+
});
|
|
459
|
+
const results = evaluateEventTriggers([trigger], now);
|
|
460
|
+
expect(results).toHaveLength(0);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test("skips triggers without eventDate", () => {
|
|
464
|
+
const now = new Date("2025-06-15T10:00:00Z");
|
|
465
|
+
const trigger = makeTrigger({
|
|
466
|
+
type: "event",
|
|
467
|
+
eventDate: null,
|
|
468
|
+
});
|
|
469
|
+
const results = evaluateEventTriggers([trigger], now);
|
|
470
|
+
expect(results).toHaveLength(0);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test("uses default rampDays=7 and followUpDays=2 when not specified", () => {
|
|
474
|
+
const now = new Date("2025-06-15T10:00:00Z");
|
|
475
|
+
const eventDate = now.getTime() + 5 * DAY_MS; // 5 days away (inside default 7-day ramp)
|
|
476
|
+
const trigger = makeTrigger({
|
|
477
|
+
type: "event",
|
|
478
|
+
eventDate,
|
|
479
|
+
rampDays: null, // triggers default of 7
|
|
480
|
+
followUpDays: null, // triggers default of 2
|
|
481
|
+
});
|
|
482
|
+
const results = evaluateEventTriggers([trigger], now);
|
|
483
|
+
expect(results).toHaveLength(1);
|
|
484
|
+
// Should be in the ramp-up phase with boost > background
|
|
485
|
+
expect(results[0].boost).toBeGreaterThan(0.05);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Memory Graph — Trigger evaluation
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { MemoryTrigger } from "./types.js";
|
|
6
|
+
|
|
7
|
+
export interface TriggeredResult {
|
|
8
|
+
trigger: MemoryTrigger;
|
|
9
|
+
/** Relevance boost from this trigger (0–1). */
|
|
10
|
+
boost: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Temporal triggers — pure date math
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Evaluate temporal triggers against the current time.
|
|
19
|
+
* Schedule patterns:
|
|
20
|
+
* "day-of-week:monday" → fires every Monday
|
|
21
|
+
* "date:04-08" → fires on April 8 every year
|
|
22
|
+
* "time:morning" → fires between 5 AM and 11 AM
|
|
23
|
+
* "time:afternoon" → fires between 12 PM and 5 PM
|
|
24
|
+
* "time:evening" → fires between 5 PM and 9 PM
|
|
25
|
+
* "time:night" → fires between 9 PM and 5 AM
|
|
26
|
+
*/
|
|
27
|
+
export function evaluateTemporalTriggers(
|
|
28
|
+
triggers: MemoryTrigger[],
|
|
29
|
+
now: Date,
|
|
30
|
+
): TriggeredResult[] {
|
|
31
|
+
const results: TriggeredResult[] = [];
|
|
32
|
+
const dayNames = [
|
|
33
|
+
"sunday",
|
|
34
|
+
"monday",
|
|
35
|
+
"tuesday",
|
|
36
|
+
"wednesday",
|
|
37
|
+
"thursday",
|
|
38
|
+
"friday",
|
|
39
|
+
"saturday",
|
|
40
|
+
];
|
|
41
|
+
const currentDay = dayNames[now.getDay()];
|
|
42
|
+
const currentMonth = String(now.getMonth() + 1).padStart(2, "0");
|
|
43
|
+
const currentDate = String(now.getDate()).padStart(2, "0");
|
|
44
|
+
const currentHour = now.getHours();
|
|
45
|
+
const dateStr = `${currentMonth}-${currentDate}`;
|
|
46
|
+
|
|
47
|
+
for (const trigger of triggers) {
|
|
48
|
+
if (trigger.type !== "temporal" || !trigger.schedule) continue;
|
|
49
|
+
if (!passesCooldown(trigger, now)) continue;
|
|
50
|
+
|
|
51
|
+
const schedule = trigger.schedule.toLowerCase();
|
|
52
|
+
let fired = false;
|
|
53
|
+
|
|
54
|
+
if (schedule.startsWith("day-of-week:")) {
|
|
55
|
+
const target = schedule.slice("day-of-week:".length).trim();
|
|
56
|
+
fired = currentDay === target;
|
|
57
|
+
} else if (schedule.startsWith("date:")) {
|
|
58
|
+
const target = schedule.slice("date:".length).trim();
|
|
59
|
+
fired = dateStr === target;
|
|
60
|
+
} else if (schedule.startsWith("time:")) {
|
|
61
|
+
const period = schedule.slice("time:".length).trim();
|
|
62
|
+
fired = matchesTimePeriod(period, currentHour);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (fired) {
|
|
66
|
+
results.push({ trigger, boost: 1.0 });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return results;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function matchesTimePeriod(period: string, hour: number): boolean {
|
|
74
|
+
switch (period) {
|
|
75
|
+
case "morning":
|
|
76
|
+
return hour >= 5 && hour < 12;
|
|
77
|
+
case "afternoon":
|
|
78
|
+
return hour >= 12 && hour < 17;
|
|
79
|
+
case "evening":
|
|
80
|
+
return hour >= 17 && hour < 21;
|
|
81
|
+
case "night":
|
|
82
|
+
return hour >= 21 || hour < 5;
|
|
83
|
+
default:
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// Semantic triggers — cosine similarity against pre-computed embeddings
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Evaluate semantic triggers by computing cosine similarity between
|
|
94
|
+
* the current conversational context embedding and each trigger's
|
|
95
|
+
* pre-computed condition embedding.
|
|
96
|
+
*
|
|
97
|
+
* Fast: ~5ms for 50 triggers (just vector math, no LLM).
|
|
98
|
+
*/
|
|
99
|
+
export function evaluateSemanticTriggers(
|
|
100
|
+
triggers: MemoryTrigger[],
|
|
101
|
+
queryEmbedding: number[] | Float32Array,
|
|
102
|
+
): TriggeredResult[] {
|
|
103
|
+
const results: TriggeredResult[] = [];
|
|
104
|
+
|
|
105
|
+
for (const trigger of triggers) {
|
|
106
|
+
if (trigger.type !== "semantic") continue;
|
|
107
|
+
if (!trigger.conditionEmbedding || trigger.threshold == null) continue;
|
|
108
|
+
if (trigger.consumed) continue;
|
|
109
|
+
if (!passesCooldown(trigger, new Date())) continue;
|
|
110
|
+
|
|
111
|
+
const similarity = cosineSimilarity(
|
|
112
|
+
queryEmbedding,
|
|
113
|
+
trigger.conditionEmbedding,
|
|
114
|
+
);
|
|
115
|
+
if (similarity >= trigger.threshold) {
|
|
116
|
+
// Scale boost by how far above threshold (0 at threshold, 1 at similarity=1)
|
|
117
|
+
const boost = Math.min(
|
|
118
|
+
1.0,
|
|
119
|
+
(similarity - trigger.threshold) / (1 - trigger.threshold + 0.001),
|
|
120
|
+
);
|
|
121
|
+
results.push({ trigger, boost: Math.max(0.5, boost) });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Cosine similarity between two vectors. Returns value in [-1, 1].
|
|
130
|
+
*/
|
|
131
|
+
function cosineSimilarity(
|
|
132
|
+
a: number[] | Float32Array,
|
|
133
|
+
b: number[] | Float32Array,
|
|
134
|
+
): number {
|
|
135
|
+
if (a.length !== b.length) return 0;
|
|
136
|
+
let dot = 0;
|
|
137
|
+
let normA = 0;
|
|
138
|
+
let normB = 0;
|
|
139
|
+
for (let i = 0; i < a.length; i++) {
|
|
140
|
+
dot += a[i] * b[i];
|
|
141
|
+
normA += a[i] * a[i];
|
|
142
|
+
normB += b[i] * b[i];
|
|
143
|
+
}
|
|
144
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
145
|
+
return denom === 0 ? 0 : dot / denom;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Event triggers — ramp function for future events
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Evaluate event triggers with a relevance ramp:
|
|
154
|
+
*
|
|
155
|
+
* announced → low bg (0.05) → ramping (linear 0.05→1.0) → day-of (1.0)
|
|
156
|
+
* → decay (exponential) → 0
|
|
157
|
+
*/
|
|
158
|
+
export function evaluateEventTriggers(
|
|
159
|
+
triggers: MemoryTrigger[],
|
|
160
|
+
now: Date,
|
|
161
|
+
): TriggeredResult[] {
|
|
162
|
+
const results: TriggeredResult[] = [];
|
|
163
|
+
const nowMs = now.getTime();
|
|
164
|
+
|
|
165
|
+
for (const trigger of triggers) {
|
|
166
|
+
if (trigger.type !== "event" || trigger.eventDate == null) continue;
|
|
167
|
+
|
|
168
|
+
const boost = computeEventRelevance(
|
|
169
|
+
trigger.eventDate,
|
|
170
|
+
trigger.rampDays ?? 7,
|
|
171
|
+
trigger.followUpDays ?? 2,
|
|
172
|
+
nowMs,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (boost > 0.01) {
|
|
176
|
+
results.push({ trigger, boost });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return results;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Compute the relevance boost for an event at the current time.
|
|
185
|
+
*/
|
|
186
|
+
function computeEventRelevance(
|
|
187
|
+
eventDateMs: number,
|
|
188
|
+
rampDays: number,
|
|
189
|
+
followUpDays: number,
|
|
190
|
+
nowMs: number,
|
|
191
|
+
): number {
|
|
192
|
+
const msPerDay = 1000 * 60 * 60 * 24;
|
|
193
|
+
const daysUntil = (eventDateMs - nowMs) / msPerDay;
|
|
194
|
+
|
|
195
|
+
if (daysUntil > rampDays) {
|
|
196
|
+
// Background awareness — the event exists and is coming
|
|
197
|
+
return 0.05;
|
|
198
|
+
}
|
|
199
|
+
if (daysUntil > 0) {
|
|
200
|
+
// Linear ramp from 0.05 to 1.0 over the ramp period
|
|
201
|
+
return 0.05 + 0.95 * (1 - daysUntil / rampDays);
|
|
202
|
+
}
|
|
203
|
+
if (daysUntil > -1) {
|
|
204
|
+
// Day-of: full boost
|
|
205
|
+
return 1.0;
|
|
206
|
+
}
|
|
207
|
+
if (-daysUntil <= followUpDays) {
|
|
208
|
+
// Rapid exponential decay after the event
|
|
209
|
+
return Math.exp(-(-daysUntil - 1));
|
|
210
|
+
}
|
|
211
|
+
// Event is over
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Helpers
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
function passesCooldown(trigger: MemoryTrigger, now: Date): boolean {
|
|
220
|
+
if (!trigger.recurring) return true;
|
|
221
|
+
if (!trigger.lastFired || !trigger.cooldownMs) return true;
|
|
222
|
+
return now.getTime() - trigger.lastFired >= trigger.cooldownMs;
|
|
223
|
+
}
|