@vellumai/assistant 0.5.16 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +69 -16
- package/Dockerfile +2 -5
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/knip.json +2 -1
- package/openapi.yaml +1198 -83
- package/package.json +5 -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 +217 -98
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- 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__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +63 -9
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/config-schema.test.ts +6 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -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 +13 -6
- package/src/__tests__/conversation-agent-loop.test.ts +13 -51
- 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 +6 -1
- 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 +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +13 -414
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- 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 +2 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- 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 +192 -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 +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +178 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +293 -0
- 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 +17 -28
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +103 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +164 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +74 -111
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +134 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- 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 +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +3 -3
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- 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-host-parity.test.ts +5 -4
- 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__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +422 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +44 -9
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- 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 +23 -11
- package/src/__tests__/skill-memory.test.ts +2 -741
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +185 -241
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +7 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +277 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +118 -8
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- 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 +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +7 -15
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +37 -84
- package/src/cli/commands/notifications.ts +7 -2
- 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__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +25 -11
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +218 -36
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- 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 +4 -12
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +17 -18
- 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/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +3 -7
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +78 -8
- package/src/config/feature-flag-registry.json +38 -125
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/security.ts +14 -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 +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +143 -82
- package/src/daemon/conversation-agent-loop.ts +236 -108
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +36 -9
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +305 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +59 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +85 -17
- package/src/daemon/handlers/skills.ts +416 -209
- package/src/daemon/lifecycle.ts +212 -131
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +29 -7
- package/src/daemon/message-types/messages.ts +12 -2
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +100 -11
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +50 -8
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +97 -7
- 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/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +42 -75
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +3 -1
- package/src/memory/conversation-crud.ts +211 -288
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +61 -13
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +194 -361
- 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 +521 -0
- package/src/memory/graph/capability-seed.ts +449 -0
- package/src/memory/graph/consolidation.ts +725 -0
- package/src/memory/graph/conversation-graph-memory.ts +659 -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 +74 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1297 -0
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +280 -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 +469 -0
- package/src/memory/graph/inspect.ts +543 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1111 -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 +1098 -0
- package/src/memory/graph/store.ts +838 -0
- package/src/memory/graph/tool-handlers.ts +301 -0
- package/src/memory/graph/tools.ts +97 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +295 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +32 -42
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- 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 +21 -31
- package/src/memory/jobs-worker.ts +180 -129
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +55 -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/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +154 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +58 -61
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +95 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +36 -4
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +10 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +62 -36
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +136 -220
- 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/route-policy.ts +30 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +55 -5
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +22 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +3 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- 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/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- 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/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +170 -247
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +28 -11
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +52 -18
- package/src/security/oauth2.ts +1 -1
- 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 +25 -16
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +29 -18
- package/src/skills/skill-memory.ts +12 -229
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +7 -5
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +85 -1
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +17 -1
- package/src/tools/tool-manifest.ts +9 -3
- package/src/tools/types.ts +2 -0
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- 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/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -372
- package/src/__tests__/clipboard.test.ts +0 -88
- 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/cli/cli-memory.ts +0 -176
- 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 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- 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 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- 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 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
- package/src/util/clipboard.ts +0 -34
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* processes and is not tested here.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
20
20
|
|
|
21
21
|
import { eq } from "drizzle-orm";
|
|
22
22
|
|
|
@@ -59,27 +59,17 @@ mock.module("../config/loader.js", () => ({
|
|
|
59
59
|
invalidateConfigCache: () => {},
|
|
60
60
|
}));
|
|
61
61
|
|
|
62
|
-
import { getDb, initializeDb
|
|
62
|
+
import { getDb, initializeDb } from "../memory/db.js";
|
|
63
63
|
import { indexMessageNow } from "../memory/indexer.js";
|
|
64
|
-
import {
|
|
65
|
-
conversations,
|
|
66
|
-
memoryItems,
|
|
67
|
-
memorySegments,
|
|
68
|
-
messages,
|
|
69
|
-
} from "../memory/schema.js";
|
|
64
|
+
import { conversations, memorySegments, messages } from "../memory/schema.js";
|
|
70
65
|
|
|
71
66
|
// Initialize DB once for the entire file. Each test cleans its own tables.
|
|
72
67
|
initializeDb();
|
|
73
68
|
|
|
74
|
-
afterAll(() => {
|
|
75
|
-
resetDb();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
69
|
function resetTables() {
|
|
79
70
|
const db = getDb();
|
|
80
|
-
db.run("DELETE FROM memory_item_sources");
|
|
81
71
|
db.run("DELETE FROM memory_embeddings");
|
|
82
|
-
db.run("DELETE FROM
|
|
72
|
+
db.run("DELETE FROM memory_graph_nodes");
|
|
83
73
|
db.run("DELETE FROM memory_segments");
|
|
84
74
|
db.run("DELETE FROM memory_jobs");
|
|
85
75
|
db.run("DELETE FROM messages");
|
|
@@ -559,101 +549,3 @@ describe("memory segment job atomicity under repeated indexer invocations", () =
|
|
|
559
549
|
});
|
|
560
550
|
});
|
|
561
551
|
|
|
562
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
563
|
-
// Test suite: memory_items fingerprint uniqueness under race conditions
|
|
564
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
565
|
-
|
|
566
|
-
describe("memory_items fingerprint uniqueness under race conditions", () => {
|
|
567
|
-
beforeEach(() => {
|
|
568
|
-
resetTables();
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
test("duplicate inserts with identical fingerprints produce exactly one row", () => {
|
|
572
|
-
// The memory_items table has a unique constraint on (fingerprint, scope_id).
|
|
573
|
-
// Two sequential inserts for the same fingerprint simulate duplicate extractor
|
|
574
|
-
// runs. Only one INSERT must land; the second must be absorbed by ON CONFLICT.
|
|
575
|
-
const db = getDb();
|
|
576
|
-
const now = Date.now();
|
|
577
|
-
const fingerprint = "fp-race-unique-test-concurrency";
|
|
578
|
-
const scopeId = "default";
|
|
579
|
-
|
|
580
|
-
// Use raw SQL to replicate what the items-extractor would do when a second
|
|
581
|
-
// run tries to INSERT the same fingerprint that already exists.
|
|
582
|
-
const raw = (db as unknown as { $client: import("bun:sqlite").Database })
|
|
583
|
-
.$client;
|
|
584
|
-
|
|
585
|
-
raw.run(`
|
|
586
|
-
INSERT INTO memory_items (
|
|
587
|
-
id, kind, subject, statement, status, confidence, importance,
|
|
588
|
-
fingerprint, verification_state, scope_id, first_seen_at, last_seen_at
|
|
589
|
-
) VALUES (
|
|
590
|
-
'item-race-1', 'preference', 'code style', 'I prefer tabs over spaces.',
|
|
591
|
-
'active', 0.8, 0.6, '${fingerprint}', 'user_reported', '${scopeId}',
|
|
592
|
-
${now}, ${now}
|
|
593
|
-
)
|
|
594
|
-
`);
|
|
595
|
-
|
|
596
|
-
// Second "worker" tries to insert the same fingerprint — must not create a
|
|
597
|
-
// duplicate. INSERT OR IGNORE / ON CONFLICT DO NOTHING is the expected
|
|
598
|
-
// behavior for the unique constraint.
|
|
599
|
-
expect(() => {
|
|
600
|
-
raw.run(`
|
|
601
|
-
INSERT OR IGNORE INTO memory_items (
|
|
602
|
-
id, kind, subject, statement, status, confidence, importance,
|
|
603
|
-
fingerprint, verification_state, scope_id, first_seen_at, last_seen_at
|
|
604
|
-
) VALUES (
|
|
605
|
-
'item-race-2', 'preference', 'code style', 'I prefer tabs over spaces.',
|
|
606
|
-
'active', 0.8, 0.6, '${fingerprint}', 'user_reported', '${scopeId}',
|
|
607
|
-
${now + 1}, ${now + 1}
|
|
608
|
-
)
|
|
609
|
-
`);
|
|
610
|
-
}).not.toThrow();
|
|
611
|
-
|
|
612
|
-
const rows = db
|
|
613
|
-
.select()
|
|
614
|
-
.from(memoryItems)
|
|
615
|
-
.all()
|
|
616
|
-
.filter((r) => r.fingerprint === fingerprint);
|
|
617
|
-
|
|
618
|
-
// Only the first insert must have landed.
|
|
619
|
-
expect(rows).toHaveLength(1);
|
|
620
|
-
expect(rows[0].id).toBe("item-race-1");
|
|
621
|
-
});
|
|
622
|
-
|
|
623
|
-
test("bare INSERT without IGNORE throws on duplicate fingerprint+scopeId", () => {
|
|
624
|
-
// Verify the DB-level unique constraint is actually enforced so that any code
|
|
625
|
-
// path that accidentally omits ON CONFLICT will fail loudly rather than silently
|
|
626
|
-
// producing inconsistent state.
|
|
627
|
-
const db = getDb();
|
|
628
|
-
const now = Date.now();
|
|
629
|
-
const fingerprint = "fp-constraint-enforcement-test";
|
|
630
|
-
|
|
631
|
-
const raw = (db as unknown as { $client: import("bun:sqlite").Database })
|
|
632
|
-
.$client;
|
|
633
|
-
|
|
634
|
-
raw.run(`
|
|
635
|
-
INSERT INTO memory_items (
|
|
636
|
-
id, kind, subject, statement, status, confidence, importance,
|
|
637
|
-
fingerprint, verification_state, scope_id, first_seen_at, last_seen_at
|
|
638
|
-
) VALUES (
|
|
639
|
-
'item-constraint-a', 'preference', 'editor', 'I use VS Code.',
|
|
640
|
-
'active', 0.9, 0.7, '${fingerprint}', 'user_reported', 'default',
|
|
641
|
-
${now}, ${now}
|
|
642
|
-
)
|
|
643
|
-
`);
|
|
644
|
-
|
|
645
|
-
// A bare INSERT (no ON CONFLICT) for the same fingerprint+scope_id must throw.
|
|
646
|
-
expect(() => {
|
|
647
|
-
raw.run(`
|
|
648
|
-
INSERT INTO memory_items (
|
|
649
|
-
id, kind, subject, statement, status, confidence, importance,
|
|
650
|
-
fingerprint, verification_state, scope_id, first_seen_at, last_seen_at
|
|
651
|
-
) VALUES (
|
|
652
|
-
'item-constraint-b', 'preference', 'editor', 'I use VS Code.',
|
|
653
|
-
'active', 0.9, 0.7, '${fingerprint}', 'user_reported', 'default',
|
|
654
|
-
${now + 1}, ${now + 1}
|
|
655
|
-
)
|
|
656
|
-
`);
|
|
657
|
-
}).toThrow();
|
|
658
|
-
});
|
|
659
|
-
});
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for the streaming vbundle export builder.
|
|
3
|
+
*
|
|
4
|
+
* Tests cover:
|
|
5
|
+
* - Format parity: streaming builder produces archives identical to sync builder
|
|
6
|
+
* - SHA-256 integrity: manifest checksums match actual file content
|
|
7
|
+
* - Temp file lifecycle: cleanup removes the temp file
|
|
8
|
+
* - Cleanup idempotency: calling cleanup twice doesn't throw
|
|
9
|
+
* - Round-trip: streamExportVBundle -> validateVBundle succeeds
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { gunzipSync } from "node:zlib";
|
|
15
|
+
import { beforeAll, describe, expect, mock, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
18
|
+
const testDbDir = join(testDir, "data", "db");
|
|
19
|
+
const testDbPath = join(testDbDir, "assistant.db");
|
|
20
|
+
const testConfigPath = join(testDir, "config.json");
|
|
21
|
+
|
|
22
|
+
mock.module("../util/logger.js", () => ({
|
|
23
|
+
getLogger: () =>
|
|
24
|
+
new Proxy({} as Record<string, unknown>, {
|
|
25
|
+
get: () => () => {},
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
mock.module("../permissions/trust-store.js", () => ({
|
|
30
|
+
getAllRules: () => [],
|
|
31
|
+
isStarterBundleAccepted: () => false,
|
|
32
|
+
clearCache: () => {},
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
mock.module("../config/loader.js", () => ({
|
|
36
|
+
getConfig: () => ({
|
|
37
|
+
ui: {},
|
|
38
|
+
model: "test",
|
|
39
|
+
provider: "test",
|
|
40
|
+
memory: { enabled: false },
|
|
41
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
42
|
+
secretDetection: { enabled: false },
|
|
43
|
+
}),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
mock.module("../config/env.js", () => ({
|
|
47
|
+
isHttpAuthDisabled: () => true,
|
|
48
|
+
hasUngatedHttpAuthDisabled: () => false,
|
|
49
|
+
getGatewayInternalBaseUrl: () => "http://127.0.0.1:7830",
|
|
50
|
+
getGatewayPort: () => 7830,
|
|
51
|
+
getRuntimeHttpPort: () => 7821,
|
|
52
|
+
getRuntimeHttpHost: () => "127.0.0.1",
|
|
53
|
+
getRuntimeGatewayOriginSecret: () => undefined,
|
|
54
|
+
getIngressPublicBaseUrl: () => undefined,
|
|
55
|
+
setIngressPublicBaseUrl: () => {},
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
import {
|
|
59
|
+
buildExportVBundle,
|
|
60
|
+
streamExportVBundle,
|
|
61
|
+
} from "../runtime/migrations/vbundle-builder.js";
|
|
62
|
+
import { validateVBundle } from "../runtime/migrations/vbundle-validator.js";
|
|
63
|
+
|
|
64
|
+
// Test fixture data: a minimal SQLite header to simulate a real database file
|
|
65
|
+
const SQLITE_HEADER = new Uint8Array([
|
|
66
|
+
0x53, 0x51, 0x4c, 0x69, 0x74, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
|
|
67
|
+
0x20, 0x33, 0x00,
|
|
68
|
+
]);
|
|
69
|
+
const TEST_CONFIG = { provider: "anthropic", model: "test-model" };
|
|
70
|
+
|
|
71
|
+
// Generate a synthetic large file (~10 MB of repeated data)
|
|
72
|
+
const LARGE_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
73
|
+
const LARGE_FILE_NAME = "large-test-data.txt";
|
|
74
|
+
|
|
75
|
+
function generateLargeFileContent(): Buffer {
|
|
76
|
+
const line =
|
|
77
|
+
"This is a line of repeated test data for streaming export verification.\n";
|
|
78
|
+
const lineBuffer = Buffer.from(line);
|
|
79
|
+
const buf = Buffer.alloc(LARGE_FILE_SIZE);
|
|
80
|
+
for (let offset = 0; offset < LARGE_FILE_SIZE; offset += lineBuffer.length) {
|
|
81
|
+
const remaining = LARGE_FILE_SIZE - offset;
|
|
82
|
+
lineBuffer.copy(buf, offset, 0, Math.min(lineBuffer.length, remaining));
|
|
83
|
+
}
|
|
84
|
+
return buf;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
beforeAll(() => {
|
|
88
|
+
// Write test fixture files so the export reads real data
|
|
89
|
+
mkdirSync(testDbDir, { recursive: true });
|
|
90
|
+
writeFileSync(testDbPath, SQLITE_HEADER);
|
|
91
|
+
writeFileSync(testConfigPath, JSON.stringify(TEST_CONFIG, null, 2));
|
|
92
|
+
writeFileSync(join(testDir, LARGE_FILE_NAME), generateLargeFileContent());
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Tar parsing helper (mirrors vbundle-validator's internal parser)
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
interface TarEntry {
|
|
100
|
+
name: string;
|
|
101
|
+
data: Uint8Array;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function parseTarEntries(gzippedData: Uint8Array): TarEntry[] {
|
|
105
|
+
const tarData = gunzipSync(gzippedData);
|
|
106
|
+
const entries: TarEntry[] = [];
|
|
107
|
+
let offset = 0;
|
|
108
|
+
const BLOCK_SIZE = 512;
|
|
109
|
+
|
|
110
|
+
while (offset + BLOCK_SIZE <= tarData.length) {
|
|
111
|
+
const header = tarData.subarray(offset, offset + BLOCK_SIZE);
|
|
112
|
+
if (header.every((b) => b === 0)) break;
|
|
113
|
+
|
|
114
|
+
let end = 0;
|
|
115
|
+
while (end < 100 && header[end] !== 0) end++;
|
|
116
|
+
const name = new TextDecoder().decode(header.subarray(0, end));
|
|
117
|
+
|
|
118
|
+
let sizeEnd = 124;
|
|
119
|
+
while (sizeEnd < 136 && header[sizeEnd] !== 0) sizeEnd++;
|
|
120
|
+
const sizeStr = new TextDecoder().decode(header.subarray(124, sizeEnd));
|
|
121
|
+
const size = parseInt(sizeStr, 8) || 0;
|
|
122
|
+
|
|
123
|
+
const dataStart = offset + BLOCK_SIZE;
|
|
124
|
+
const data = tarData.subarray(dataStart, dataStart + size);
|
|
125
|
+
const dataBlocks = Math.ceil(size / BLOCK_SIZE);
|
|
126
|
+
|
|
127
|
+
if (header[156] === "0".charCodeAt(0) || header[156] === 0) {
|
|
128
|
+
entries.push({ name, data: new Uint8Array(data) });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
offset = dataStart + dataBlocks * BLOCK_SIZE;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return entries;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function sha256Hex(data: Uint8Array): string {
|
|
138
|
+
return createHash("sha256").update(data).digest("hex");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// Streaming export tests
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
describe("streamExportVBundle", () => {
|
|
146
|
+
test("returns a temp file that exists on disk", async () => {
|
|
147
|
+
const result = await streamExportVBundle({
|
|
148
|
+
workspaceDir: testDir,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
expect(existsSync(result.tempPath)).toBe(true);
|
|
153
|
+
expect(result.size).toBeGreaterThan(0);
|
|
154
|
+
} finally {
|
|
155
|
+
await result.cleanup();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("manifest contains correct SHA-256 checksums for all files", async () => {
|
|
160
|
+
const result = await streamExportVBundle({
|
|
161
|
+
workspaceDir: testDir,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const archiveData = new Uint8Array(readFileSync(result.tempPath));
|
|
166
|
+
const entries = parseTarEntries(archiveData);
|
|
167
|
+
|
|
168
|
+
for (const fileEntry of result.manifest.files) {
|
|
169
|
+
const tarEntry = entries.find((e) => e.name === fileEntry.path);
|
|
170
|
+
expect(tarEntry).toBeDefined();
|
|
171
|
+
expect(sha256Hex(tarEntry!.data)).toBe(fileEntry.sha256);
|
|
172
|
+
}
|
|
173
|
+
} finally {
|
|
174
|
+
await result.cleanup();
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("decompressed archive produces same files as buildExportVBundle", async () => {
|
|
179
|
+
const streamResult = await streamExportVBundle({
|
|
180
|
+
workspaceDir: testDir,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
const syncResult = buildExportVBundle({
|
|
185
|
+
workspaceDir: testDir,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const streamArchiveData = new Uint8Array(
|
|
189
|
+
readFileSync(streamResult.tempPath),
|
|
190
|
+
);
|
|
191
|
+
const streamEntries = parseTarEntries(streamArchiveData);
|
|
192
|
+
const syncEntries = parseTarEntries(syncResult.archive);
|
|
193
|
+
|
|
194
|
+
// Filter out manifest.json — compare only data files
|
|
195
|
+
const streamFileEntries = streamEntries
|
|
196
|
+
.filter((e) => e.name !== "manifest.json")
|
|
197
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
198
|
+
const syncFileEntries = syncEntries
|
|
199
|
+
.filter((e) => e.name !== "manifest.json")
|
|
200
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
201
|
+
|
|
202
|
+
// Same set of files
|
|
203
|
+
expect(streamFileEntries.map((e) => e.name)).toEqual(
|
|
204
|
+
syncFileEntries.map((e) => e.name),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Same content (verified via checksums)
|
|
208
|
+
for (let i = 0; i < streamFileEntries.length; i++) {
|
|
209
|
+
expect(sha256Hex(streamFileEntries[i].data)).toBe(
|
|
210
|
+
sha256Hex(syncFileEntries[i].data),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Same manifest file entries (excluding timing fields)
|
|
215
|
+
const streamManifestFiles = streamResult.manifest.files
|
|
216
|
+
.slice()
|
|
217
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
218
|
+
const syncManifestFiles = syncResult.manifest.files
|
|
219
|
+
.slice()
|
|
220
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
221
|
+
|
|
222
|
+
expect(streamManifestFiles.map((f) => f.path)).toEqual(
|
|
223
|
+
syncManifestFiles.map((f) => f.path),
|
|
224
|
+
);
|
|
225
|
+
expect(streamManifestFiles.map((f) => f.sha256)).toEqual(
|
|
226
|
+
syncManifestFiles.map((f) => f.sha256),
|
|
227
|
+
);
|
|
228
|
+
expect(streamManifestFiles.map((f) => f.size)).toEqual(
|
|
229
|
+
syncManifestFiles.map((f) => f.size),
|
|
230
|
+
);
|
|
231
|
+
} finally {
|
|
232
|
+
await streamResult.cleanup();
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("cleanup removes the temp file", async () => {
|
|
237
|
+
const result = await streamExportVBundle({
|
|
238
|
+
workspaceDir: testDir,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(existsSync(result.tempPath)).toBe(true);
|
|
242
|
+
await result.cleanup();
|
|
243
|
+
expect(existsSync(result.tempPath)).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
// Round-trip test
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
|
|
251
|
+
describe("streamExportVBundle round-trip", () => {
|
|
252
|
+
test("streaming archive passes validateVBundle with matching manifest", async () => {
|
|
253
|
+
const result = await streamExportVBundle({
|
|
254
|
+
workspaceDir: testDir,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const archiveData = new Uint8Array(readFileSync(result.tempPath));
|
|
259
|
+
const validationResult = validateVBundle(archiveData);
|
|
260
|
+
|
|
261
|
+
expect(validationResult.is_valid).toBe(true);
|
|
262
|
+
expect(validationResult.errors).toHaveLength(0);
|
|
263
|
+
expect(validationResult.manifest).toBeDefined();
|
|
264
|
+
|
|
265
|
+
// Verify manifest fields match
|
|
266
|
+
expect(validationResult.manifest!.schema_version).toBe(
|
|
267
|
+
result.manifest.schema_version,
|
|
268
|
+
);
|
|
269
|
+
expect(validationResult.manifest!.manifest_sha256).toBe(
|
|
270
|
+
result.manifest.manifest_sha256,
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
// Verify file entries match
|
|
274
|
+
const validatedFiles = validationResult
|
|
275
|
+
.manifest!.files.slice()
|
|
276
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
277
|
+
const resultFiles = result.manifest.files
|
|
278
|
+
.slice()
|
|
279
|
+
.sort((a, b) => a.path.localeCompare(b.path));
|
|
280
|
+
|
|
281
|
+
expect(validatedFiles).toEqual(resultFiles);
|
|
282
|
+
} finally {
|
|
283
|
+
await result.cleanup();
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
// Cleanup idempotency test
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
describe("streamExportVBundle cleanup", () => {
|
|
293
|
+
test("calling cleanup twice does not throw", async () => {
|
|
294
|
+
const result = await streamExportVBundle({
|
|
295
|
+
workspaceDir: testDir,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
await result.cleanup();
|
|
299
|
+
// Second call should not throw
|
|
300
|
+
await result.cleanup();
|
|
301
|
+
|
|
302
|
+
expect(existsSync(result.tempPath)).toBe(false);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
@@ -355,10 +355,10 @@ describe("handleMigrationImport", () => {
|
|
|
355
355
|
expect(writtenData).toEqual(newDbData);
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
-
test("workspace is cleared before restore —
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
358
|
+
test("workspace is cleared before restore — preserved dirs are overwritten", async () => {
|
|
359
|
+
// New-format bundles (workspace/ prefix) trigger workspace clearing, but
|
|
360
|
+
// data/db/ is preserved during clearing to avoid destroying the database
|
|
361
|
+
// if the import fails partway. The DB file is overwritten (with backup).
|
|
362
362
|
const newDbData = new Uint8Array([0x01, 0x02, 0x03]);
|
|
363
363
|
const vbundle = createValidVBundle([
|
|
364
364
|
{ path: "workspace/data/db/assistant.db", data: newDbData },
|
|
@@ -373,17 +373,18 @@ describe("handleMigrationImport", () => {
|
|
|
373
373
|
const body = (await res.json()) as ImportCommitResponse;
|
|
374
374
|
|
|
375
375
|
expect(body.success).toBe(true);
|
|
376
|
-
expect(body.summary.backups_created).toBe(
|
|
376
|
+
expect(body.summary.backups_created).toBe(1);
|
|
377
377
|
|
|
378
378
|
const dbFile = body.files.find(
|
|
379
379
|
(f) => f.path === "workspace/data/db/assistant.db",
|
|
380
380
|
);
|
|
381
381
|
expect(dbFile).toBeDefined();
|
|
382
|
-
expect(dbFile!.action).toBe("
|
|
382
|
+
expect(dbFile!.action).toBe("overwritten");
|
|
383
383
|
});
|
|
384
384
|
|
|
385
|
-
test("
|
|
386
|
-
//
|
|
385
|
+
test("workspace clear: preserved dirs overwritten, cleared dirs created", async () => {
|
|
386
|
+
// data/db/ is preserved during workspace clearing → DB is "overwritten".
|
|
387
|
+
// config.json lives at the workspace root which IS cleared → "created".
|
|
387
388
|
const newDbData = new Uint8Array([0xaa, 0xbb]);
|
|
388
389
|
const newConfigData = new TextEncoder().encode('{"provider":"openai"}');
|
|
389
390
|
const vbundle = createValidVBundle([
|
|
@@ -408,8 +409,8 @@ describe("handleMigrationImport", () => {
|
|
|
408
409
|
(f) => f.path === "workspace/config.json",
|
|
409
410
|
);
|
|
410
411
|
|
|
411
|
-
//
|
|
412
|
-
expect(dbFile!.action).toBe("
|
|
412
|
+
// data/db/ preserved during clearing → overwritten; config.json cleared → created
|
|
413
|
+
expect(dbFile!.action).toBe("overwritten");
|
|
413
414
|
expect(configFile!.action).toBe("created");
|
|
414
415
|
});
|
|
415
416
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global test utility for mocking `globalThis.fetch`.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* mockFetch("/some/path", { method: "POST" }, { body: { ok: true }, status: 200 });
|
|
6
|
+
*
|
|
7
|
+
* Entries are consumed in order — the first match wins and is removed, so
|
|
8
|
+
* register multiple responses for the same path to simulate sequences.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface MockedResponse {
|
|
12
|
+
body?: unknown;
|
|
13
|
+
status: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface MockFetchEntry {
|
|
17
|
+
path: string;
|
|
18
|
+
init: Partial<RequestInit>;
|
|
19
|
+
response: MockedResponse | Response;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const entries: MockFetchEntry[] = [];
|
|
23
|
+
const calls: { path: string; init: RequestInit }[] = [];
|
|
24
|
+
let originalFetch: typeof globalThis.fetch | undefined;
|
|
25
|
+
|
|
26
|
+
export function mockFetch(
|
|
27
|
+
path: string,
|
|
28
|
+
init: Partial<RequestInit>,
|
|
29
|
+
response: MockedResponse | Response,
|
|
30
|
+
): void {
|
|
31
|
+
if (!originalFetch) {
|
|
32
|
+
originalFetch = globalThis.fetch;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
entries.push({ path, init, response });
|
|
36
|
+
|
|
37
|
+
globalThis.fetch = (async (
|
|
38
|
+
input: RequestInfo | URL,
|
|
39
|
+
actualInit?: RequestInit,
|
|
40
|
+
) => {
|
|
41
|
+
const url = String(input);
|
|
42
|
+
calls.push({ path: url, init: actualInit ?? {} });
|
|
43
|
+
|
|
44
|
+
const idx = entries.findIndex((e) => {
|
|
45
|
+
if (!url.includes(e.path)) return false;
|
|
46
|
+
for (const [key, val] of Object.entries(e.init)) {
|
|
47
|
+
if (
|
|
48
|
+
(actualInit as Record<string, unknown> | undefined)?.[key] !== val
|
|
49
|
+
) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (idx === -1) {
|
|
57
|
+
return new Response(JSON.stringify({ detail: "No mock matched" }), {
|
|
58
|
+
status: 500,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const entry = entries[idx];
|
|
63
|
+
entries.splice(idx, 1);
|
|
64
|
+
|
|
65
|
+
if (entry.response instanceof Response) {
|
|
66
|
+
return entry.response;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return new Response(JSON.stringify(entry.response.body ?? null), {
|
|
70
|
+
status: entry.response.status,
|
|
71
|
+
headers: { "Content-Type": "application/json" },
|
|
72
|
+
});
|
|
73
|
+
}) as unknown as typeof globalThis.fetch;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getMockFetchCalls(): { path: string; init: RequestInit }[] {
|
|
77
|
+
return calls;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function resetMockFetch(): void {
|
|
81
|
+
if (originalFetch) {
|
|
82
|
+
globalThis.fetch = originalFetch;
|
|
83
|
+
originalFetch = undefined;
|
|
84
|
+
}
|
|
85
|
+
entries.length = 0;
|
|
86
|
+
calls.length = 0;
|
|
87
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. Create a guardian approval request for the access request
|
|
8
8
|
* 4. Deduplicate: don't create duplicate requests for repeated messages
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
// Test isolation: in-memory SQLite via temp directory
|
|
@@ -66,17 +66,13 @@ import {
|
|
|
66
66
|
listCanonicalGuardianDeliveries,
|
|
67
67
|
listCanonicalGuardianRequests,
|
|
68
68
|
} from "../memory/canonical-guardian-store.js";
|
|
69
|
-
import { getDb, initializeDb
|
|
69
|
+
import { getDb, initializeDb } from "../memory/db.js";
|
|
70
70
|
import { notifyGuardianOfAccessRequest } from "../runtime/access-request-helper.js";
|
|
71
71
|
import { ensureVellumGuardianBinding } from "../runtime/guardian-vellum-migration.js";
|
|
72
72
|
import { handleChannelInbound } from "../runtime/routes/channel-routes.js";
|
|
73
73
|
|
|
74
74
|
initializeDb();
|
|
75
75
|
|
|
76
|
-
afterAll(() => {
|
|
77
|
-
resetDb();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
76
|
// ---------------------------------------------------------------------------
|
|
81
77
|
// Helpers
|
|
82
78
|
// ---------------------------------------------------------------------------
|