@vellumai/assistant 0.4.56 → 0.4.57
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 +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +185 -173
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +237 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +1 -1
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +547 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +139 -274
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +25 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +1 -0
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/emit-signal.ts +4 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +166 -33
- package/src/prompts/templates/IDENTITY.md +8 -23
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +46 -2
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +1 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +63 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +9 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +8 -8
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +263 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +220 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +1 -1
- package/src/telemetry/usage-telemetry-reporter.ts +51 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +2 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
|
+
|
|
6
|
+
const testDir = mkdtempSync(join(tmpdir(), "conv-wipe-test-"));
|
|
7
|
+
|
|
8
|
+
mock.module("../util/platform.js", () => ({
|
|
9
|
+
getDataDir: () => testDir,
|
|
10
|
+
isMacOS: () => process.platform === "darwin",
|
|
11
|
+
isLinux: () => process.platform === "linux",
|
|
12
|
+
isWindows: () => process.platform === "win32",
|
|
13
|
+
getPidPath: () => join(testDir, "test.pid"),
|
|
14
|
+
getDbPath: () => join(testDir, "test.db"),
|
|
15
|
+
getLogPath: () => join(testDir, "test.log"),
|
|
16
|
+
ensureDataDir: () => {},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
mock.module("../util/logger.js", () => ({
|
|
20
|
+
getLogger: () =>
|
|
21
|
+
new Proxy({} as Record<string, unknown>, {
|
|
22
|
+
get: () => () => {},
|
|
23
|
+
}),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
addMessage,
|
|
28
|
+
createConversation,
|
|
29
|
+
getConversation,
|
|
30
|
+
getMessages,
|
|
31
|
+
wipeConversation,
|
|
32
|
+
} from "../memory/conversation-crud.js";
|
|
33
|
+
import { getDb, initializeDb, resetDb } from "../memory/db.js";
|
|
34
|
+
import { enqueueMemoryJob } from "../memory/jobs-store.js";
|
|
35
|
+
|
|
36
|
+
// Initialize db once before all tests
|
|
37
|
+
initializeDb();
|
|
38
|
+
|
|
39
|
+
afterAll(() => {
|
|
40
|
+
resetDb();
|
|
41
|
+
try {
|
|
42
|
+
rmSync(testDir, { recursive: true });
|
|
43
|
+
} catch {
|
|
44
|
+
/* best effort */
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("wipeConversation", () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
const db = getDb();
|
|
51
|
+
db.run(`DELETE FROM memory_item_sources`);
|
|
52
|
+
db.run(`DELETE FROM memory_segments`);
|
|
53
|
+
db.run(`DELETE FROM memory_items`);
|
|
54
|
+
db.run(`DELETE FROM memory_summaries`);
|
|
55
|
+
db.run(`DELETE FROM memory_embeddings`);
|
|
56
|
+
db.run(`DELETE FROM memory_jobs`);
|
|
57
|
+
db.run(`DELETE FROM tool_invocations`);
|
|
58
|
+
db.run(`DELETE FROM llm_request_logs`);
|
|
59
|
+
db.run(`DELETE FROM messages`);
|
|
60
|
+
db.run(`DELETE FROM conversations`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("wipes conversation and all messages", async () => {
|
|
64
|
+
const conv = createConversation("test");
|
|
65
|
+
await addMessage(conv.id, "user", "first message");
|
|
66
|
+
await addMessage(conv.id, "assistant", "second message");
|
|
67
|
+
await addMessage(conv.id, "user", "third message");
|
|
68
|
+
|
|
69
|
+
wipeConversation(conv.id);
|
|
70
|
+
|
|
71
|
+
expect(getConversation(conv.id)).toBeNull();
|
|
72
|
+
expect(getMessages(conv.id)).toEqual([]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("restores explicitly superseded memory items", async () => {
|
|
76
|
+
const convA = createConversation("conversation A");
|
|
77
|
+
const msgA = await addMessage(convA.id, "user", "I like blue");
|
|
78
|
+
|
|
79
|
+
const convB = createConversation("conversation B");
|
|
80
|
+
const msgB = await addMessage(convB.id, "user", "I like red");
|
|
81
|
+
|
|
82
|
+
const db = getDb();
|
|
83
|
+
const now = Date.now();
|
|
84
|
+
|
|
85
|
+
// Insert itemA: active preference about color
|
|
86
|
+
db.run(
|
|
87
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
88
|
+
VALUES ('itemA', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-a', 'default', ${now}, ${now})`,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Insert itemB: active preference about color, supersedes itemA
|
|
92
|
+
db.run(
|
|
93
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
|
|
94
|
+
VALUES ('itemB', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-b', 'default', 'itemA', ${now}, ${now})`,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Mark itemA as superseded by itemB
|
|
98
|
+
db.run(
|
|
99
|
+
`UPDATE memory_items SET status = 'superseded', superseded_by = 'itemB' WHERE id = 'itemA'`,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Link itemA to convA's message, itemB to convB's message
|
|
103
|
+
db.run(
|
|
104
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemA', '${msgA.id}', ${now})`,
|
|
105
|
+
);
|
|
106
|
+
db.run(
|
|
107
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const result = wipeConversation(convB.id);
|
|
111
|
+
|
|
112
|
+
// itemA should be restored to active with superseded_by cleared
|
|
113
|
+
const raw = (
|
|
114
|
+
getDb() as unknown as {
|
|
115
|
+
$client: import("bun:sqlite").Database;
|
|
116
|
+
}
|
|
117
|
+
).$client;
|
|
118
|
+
const itemARow = raw
|
|
119
|
+
.query(
|
|
120
|
+
"SELECT status, superseded_by FROM memory_items WHERE id = 'itemA'",
|
|
121
|
+
)
|
|
122
|
+
.get() as { status: string; superseded_by: string | null } | null;
|
|
123
|
+
expect(itemARow).not.toBeNull();
|
|
124
|
+
expect(itemARow!.status).toBe("active");
|
|
125
|
+
expect(itemARow!.superseded_by).toBeNull();
|
|
126
|
+
|
|
127
|
+
// itemB should no longer exist (orphaned and deleted by deleteConversation)
|
|
128
|
+
const itemBRow = (
|
|
129
|
+
getDb() as unknown as {
|
|
130
|
+
$client: import("bun:sqlite").Database;
|
|
131
|
+
}
|
|
132
|
+
).$client
|
|
133
|
+
.query("SELECT * FROM memory_items WHERE id = 'itemB'")
|
|
134
|
+
.get();
|
|
135
|
+
expect(itemBRow).toBeNull();
|
|
136
|
+
|
|
137
|
+
expect(result.unsupersededItemIds).toContain("itemA");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("does not restore superseded items when superseding item has other sources", async () => {
|
|
141
|
+
const convA = createConversation("conversation A");
|
|
142
|
+
const msgA = await addMessage(convA.id, "user", "I like red in A");
|
|
143
|
+
|
|
144
|
+
const convB = createConversation("conversation B");
|
|
145
|
+
const msgB = await addMessage(convB.id, "user", "I like red in B");
|
|
146
|
+
|
|
147
|
+
const db = getDb();
|
|
148
|
+
const now = Date.now();
|
|
149
|
+
|
|
150
|
+
// Insert itemOld (will be superseded)
|
|
151
|
+
db.run(
|
|
152
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
153
|
+
VALUES ('itemOld', 'active', 'preference', 'color', 'likes blue', 0.8, 'fp-old', 'default', ${now}, ${now})`,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// Insert itemNew (supersedes itemOld)
|
|
157
|
+
db.run(
|
|
158
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, supersedes, first_seen_at, last_seen_at)
|
|
159
|
+
VALUES ('itemNew', 'active', 'preference', 'color', 'likes red', 0.9, 'fp-new', 'default', 'itemOld', ${now}, ${now})`,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Mark itemOld as superseded
|
|
163
|
+
db.run(
|
|
164
|
+
`UPDATE memory_items SET status = 'superseded', superseded_by = 'itemNew' WHERE id = 'itemOld'`,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Link itemNew to BOTH conversations
|
|
168
|
+
db.run(
|
|
169
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgA.id}', ${now})`,
|
|
170
|
+
);
|
|
171
|
+
db.run(
|
|
172
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
wipeConversation(convA.id);
|
|
176
|
+
|
|
177
|
+
const raw = (
|
|
178
|
+
getDb() as unknown as {
|
|
179
|
+
$client: import("bun:sqlite").Database;
|
|
180
|
+
}
|
|
181
|
+
).$client;
|
|
182
|
+
|
|
183
|
+
// itemOld should still be superseded because itemNew has another source (convB)
|
|
184
|
+
const itemOldRow = raw
|
|
185
|
+
.query("SELECT status FROM memory_items WHERE id = 'itemOld'")
|
|
186
|
+
.get() as { status: string } | null;
|
|
187
|
+
expect(itemOldRow).not.toBeNull();
|
|
188
|
+
expect(itemOldRow!.status).toBe("superseded");
|
|
189
|
+
|
|
190
|
+
// itemNew should still exist (has source from convB)
|
|
191
|
+
const itemNewRow = raw
|
|
192
|
+
.query("SELECT * FROM memory_items WHERE id = 'itemNew'")
|
|
193
|
+
.get();
|
|
194
|
+
expect(itemNewRow).not.toBeNull();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("restores orphaned subject-match superseded items", async () => {
|
|
198
|
+
const convB = createConversation("conversation B");
|
|
199
|
+
const msgB = await addMessage(convB.id, "user", "I use vim");
|
|
200
|
+
|
|
201
|
+
const db = getDb();
|
|
202
|
+
const now = Date.now();
|
|
203
|
+
|
|
204
|
+
// Insert itemOld: superseded with no superseded_by link
|
|
205
|
+
db.run(
|
|
206
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
207
|
+
VALUES ('itemOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-old', 'default', ${now}, ${now})`,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// Insert itemNew: active, same kind/subject/scope_id
|
|
211
|
+
db.run(
|
|
212
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
213
|
+
VALUES ('itemNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-new', 'default', ${now}, ${now})`,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
// Link itemNew to convB's message
|
|
217
|
+
db.run(
|
|
218
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemNew', '${msgB.id}', ${now})`,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
wipeConversation(convB.id);
|
|
222
|
+
|
|
223
|
+
const raw = (
|
|
224
|
+
getDb() as unknown as {
|
|
225
|
+
$client: import("bun:sqlite").Database;
|
|
226
|
+
}
|
|
227
|
+
).$client;
|
|
228
|
+
|
|
229
|
+
// itemOld should now be active (restored as orphaned subject-match superseded item)
|
|
230
|
+
const itemOldRow = raw
|
|
231
|
+
.query("SELECT status FROM memory_items WHERE id = 'itemOld'")
|
|
232
|
+
.get() as { status: string } | null;
|
|
233
|
+
expect(itemOldRow).not.toBeNull();
|
|
234
|
+
expect(itemOldRow!.status).toBe("active");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("does not restore superseded items from unrelated conversations", async () => {
|
|
238
|
+
// convA has an item that superseded an older item — convA was previously
|
|
239
|
+
// deleted via regular deleteConversation, leaving the old item superseded
|
|
240
|
+
// with superseded_by = NULL. When we later wipe convB, Step F should NOT
|
|
241
|
+
// restore that unrelated item.
|
|
242
|
+
const convA = createConversation("conversation A");
|
|
243
|
+
const _msgA = await addMessage(convA.id, "user", "I use dark theme");
|
|
244
|
+
|
|
245
|
+
const convB = createConversation("conversation B");
|
|
246
|
+
const msgB = await addMessage(convB.id, "user", "I use vim");
|
|
247
|
+
|
|
248
|
+
const db = getDb();
|
|
249
|
+
const now = Date.now();
|
|
250
|
+
|
|
251
|
+
// unrelatedOld: superseded item from an old conversation (e.g. "uses light theme")
|
|
252
|
+
// Its superseder was deleted in a prior deleteConversation, leaving
|
|
253
|
+
// superseded_by = NULL.
|
|
254
|
+
db.run(
|
|
255
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
256
|
+
VALUES ('unrelatedOld', 'superseded', 'preference', 'theme', 'uses light theme', 0.7, 'fp-unrelated', 'default', ${now}, ${now})`,
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// convA's active item that superseded unrelatedOld — we simulate the
|
|
260
|
+
// case where convA was already deleted, leaving unrelatedOld with
|
|
261
|
+
// superseded_by = NULL and no active replacement.
|
|
262
|
+
// (We don't actually insert the superseder — just leave unrelatedOld
|
|
263
|
+
// as a superseded item with no superseded_by and no active match.)
|
|
264
|
+
|
|
265
|
+
// convB's items — itemOld is superseded by itemNew (subject: editor)
|
|
266
|
+
db.run(
|
|
267
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
268
|
+
VALUES ('editorOld', 'superseded', 'preference', 'editor', 'uses emacs', 0.7, 'fp-editor-old', 'default', ${now}, ${now})`,
|
|
269
|
+
);
|
|
270
|
+
db.run(
|
|
271
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
272
|
+
VALUES ('editorNew', 'active', 'preference', 'editor', 'uses vim', 0.9, 'fp-editor-new', 'default', ${now}, ${now})`,
|
|
273
|
+
);
|
|
274
|
+
db.run(
|
|
275
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('editorNew', '${msgB.id}', ${now})`,
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const result = wipeConversation(convB.id);
|
|
279
|
+
|
|
280
|
+
const raw = (
|
|
281
|
+
getDb() as unknown as {
|
|
282
|
+
$client: import("bun:sqlite").Database;
|
|
283
|
+
}
|
|
284
|
+
).$client;
|
|
285
|
+
|
|
286
|
+
// editorOld SHOULD be restored (its kind+subject matches an orphaned item from convB)
|
|
287
|
+
const editorOldRow = raw
|
|
288
|
+
.query("SELECT status FROM memory_items WHERE id = 'editorOld'")
|
|
289
|
+
.get() as { status: string } | null;
|
|
290
|
+
expect(editorOldRow).not.toBeNull();
|
|
291
|
+
expect(editorOldRow!.status).toBe("active");
|
|
292
|
+
|
|
293
|
+
// unrelatedOld should NOT be restored — it was superseded by a different
|
|
294
|
+
// conversation's item (theme, not editor) and has nothing to do with convB
|
|
295
|
+
const unrelatedOldRow = raw
|
|
296
|
+
.query("SELECT status FROM memory_items WHERE id = 'unrelatedOld'")
|
|
297
|
+
.get() as { status: string } | null;
|
|
298
|
+
expect(unrelatedOldRow).not.toBeNull();
|
|
299
|
+
expect(unrelatedOldRow!.status).toBe("superseded");
|
|
300
|
+
|
|
301
|
+
// Only editorOld should be in the unsuperseded list, not unrelatedOld
|
|
302
|
+
expect(result.unsupersededItemIds).toContain("editorOld");
|
|
303
|
+
expect(result.unsupersededItemIds).not.toContain("unrelatedOld");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("deletes conversation summaries", async () => {
|
|
307
|
+
const conv = createConversation("test");
|
|
308
|
+
await addMessage(conv.id, "user", "hello");
|
|
309
|
+
|
|
310
|
+
const now = Date.now();
|
|
311
|
+
|
|
312
|
+
const raw = (
|
|
313
|
+
getDb() as unknown as {
|
|
314
|
+
$client: import("bun:sqlite").Database;
|
|
315
|
+
}
|
|
316
|
+
).$client;
|
|
317
|
+
|
|
318
|
+
// Insert a conversation-scoped summary
|
|
319
|
+
raw
|
|
320
|
+
.query(
|
|
321
|
+
`INSERT INTO memory_summaries (id, scope, scope_key, summary, token_estimate, version, scope_id, start_at, end_at, created_at, updated_at)
|
|
322
|
+
VALUES ('sum-1', 'conversation', ?, 'test summary', 100, 1, 'default', ?, ?, ?, ?)`,
|
|
323
|
+
)
|
|
324
|
+
.run(conv.id, now, now, now, now);
|
|
325
|
+
|
|
326
|
+
// Insert a corresponding embedding
|
|
327
|
+
raw
|
|
328
|
+
.query(
|
|
329
|
+
`INSERT INTO memory_embeddings (id, target_type, target_id, provider, model, dimensions, created_at, updated_at)
|
|
330
|
+
VALUES ('emb-sum-1', 'summary', 'sum-1', 'test', 'test', 384, ?, ?)`,
|
|
331
|
+
)
|
|
332
|
+
.run(now, now);
|
|
333
|
+
|
|
334
|
+
const result = wipeConversation(conv.id);
|
|
335
|
+
|
|
336
|
+
// Summary should be deleted
|
|
337
|
+
const summaryRow = raw
|
|
338
|
+
.query("SELECT * FROM memory_summaries WHERE id = 'sum-1'")
|
|
339
|
+
.get();
|
|
340
|
+
expect(summaryRow).toBeNull();
|
|
341
|
+
|
|
342
|
+
// Embedding should be deleted
|
|
343
|
+
const embeddingRow = raw
|
|
344
|
+
.query("SELECT * FROM memory_embeddings WHERE id = 'emb-sum-1'")
|
|
345
|
+
.get();
|
|
346
|
+
expect(embeddingRow).toBeNull();
|
|
347
|
+
|
|
348
|
+
expect(result.deletedSummaryIds).toContain("sum-1");
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("cancels pending memory jobs", async () => {
|
|
352
|
+
const conv = createConversation("test");
|
|
353
|
+
const msg = await addMessage(conv.id, "user", "hello", undefined, {
|
|
354
|
+
skipIndexing: true,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// Clear any jobs that might have been created by prior operations
|
|
358
|
+
const db = getDb();
|
|
359
|
+
db.run(`DELETE FROM memory_jobs`);
|
|
360
|
+
|
|
361
|
+
enqueueMemoryJob("extract_items", { messageId: msg.id });
|
|
362
|
+
enqueueMemoryJob("build_conversation_summary", {
|
|
363
|
+
conversationId: conv.id,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
const result = wipeConversation(conv.id);
|
|
367
|
+
|
|
368
|
+
const raw = (
|
|
369
|
+
getDb() as unknown as {
|
|
370
|
+
$client: import("bun:sqlite").Database;
|
|
371
|
+
}
|
|
372
|
+
).$client;
|
|
373
|
+
|
|
374
|
+
// Both jobs should be failed with conversation_wiped error
|
|
375
|
+
const jobs = raw
|
|
376
|
+
.query("SELECT status, last_error FROM memory_jobs")
|
|
377
|
+
.all() as Array<{ status: string; last_error: string | null }>;
|
|
378
|
+
|
|
379
|
+
for (const job of jobs) {
|
|
380
|
+
// Skip embed_item jobs enqueued by wipeConversation's unsupersede logic
|
|
381
|
+
if (job.status === "pending") continue;
|
|
382
|
+
expect(job.status).toBe("failed");
|
|
383
|
+
expect(job.last_error).toContain("conversation_wiped");
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
expect(result.cancelledJobCount).toBeGreaterThanOrEqual(2);
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
test("wipe of empty conversation succeeds", () => {
|
|
390
|
+
const conv = createConversation("empty");
|
|
391
|
+
|
|
392
|
+
const result = wipeConversation(conv.id);
|
|
393
|
+
|
|
394
|
+
expect(getConversation(conv.id)).toBeNull();
|
|
395
|
+
expect(result.segmentIds).toEqual([]);
|
|
396
|
+
expect(result.orphanedItemIds).toEqual([]);
|
|
397
|
+
expect(result.unsupersededItemIds).toEqual([]);
|
|
398
|
+
expect(result.deletedSummaryIds).toEqual([]);
|
|
399
|
+
expect(result.cancelledJobCount).toBe(0);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("does not affect other conversations", async () => {
|
|
403
|
+
const convA = createConversation("conversation A");
|
|
404
|
+
await addMessage(convA.id, "user", "message in A");
|
|
405
|
+
|
|
406
|
+
const convB = createConversation("conversation B");
|
|
407
|
+
const msgB = await addMessage(convB.id, "user", "message in B");
|
|
408
|
+
|
|
409
|
+
const db = getDb();
|
|
410
|
+
const now = Date.now();
|
|
411
|
+
|
|
412
|
+
// Insert a memory item sourced from convB's message
|
|
413
|
+
db.run(
|
|
414
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
415
|
+
VALUES ('itemB', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-b', 'default', ${now}, ${now})`,
|
|
416
|
+
);
|
|
417
|
+
db.run(
|
|
418
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('itemB', '${msgB.id}', ${now})`,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
wipeConversation(convA.id);
|
|
422
|
+
|
|
423
|
+
// convB should still exist
|
|
424
|
+
expect(getConversation(convB.id)).not.toBeNull();
|
|
425
|
+
expect(getMessages(convB.id)).toHaveLength(1);
|
|
426
|
+
|
|
427
|
+
// convB's memory item should still exist
|
|
428
|
+
const raw = (
|
|
429
|
+
getDb() as unknown as {
|
|
430
|
+
$client: import("bun:sqlite").Database;
|
|
431
|
+
}
|
|
432
|
+
).$client;
|
|
433
|
+
const itemBRow = raw
|
|
434
|
+
.query("SELECT * FROM memory_items WHERE id = 'itemB'")
|
|
435
|
+
.get();
|
|
436
|
+
expect(itemBRow).not.toBeNull();
|
|
437
|
+
});
|
|
438
|
+
});
|
|
@@ -35,7 +35,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
35
35
|
compactThreshold: 0.8,
|
|
36
36
|
summaryBudgetRatio: 0.05,
|
|
37
37
|
},
|
|
38
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
38
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
39
39
|
memory: { enabled: false },
|
|
40
40
|
}),
|
|
41
41
|
loadRawConfig: () => ({}),
|
|
@@ -105,8 +105,7 @@ mock.module("../memory/attachments-store.js", () => ({
|
|
|
105
105
|
|
|
106
106
|
mock.module("../memory/retriever.js", () => ({
|
|
107
107
|
buildMemoryRecall: async () => null,
|
|
108
|
-
|
|
109
|
-
stripMemoryRecallMessages: (msgs: Message[]) => msgs,
|
|
108
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
110
109
|
}));
|
|
111
110
|
|
|
112
111
|
mock.module("../memory/query-builder.js", () => ({
|
|
@@ -55,7 +55,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
55
55
|
nonInteractiveLatestTurnCompression: "truncate",
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
58
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
59
59
|
memory: { enabled: false },
|
|
60
60
|
daemon: {
|
|
61
61
|
startupSocketWaitMs: 5000,
|
|
@@ -73,9 +73,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
73
73
|
"image-generation": {
|
|
74
74
|
mode: "your-own",
|
|
75
75
|
provider: "gemini",
|
|
76
|
-
model: "gemini-
|
|
76
|
+
model: "gemini-3.1-flash-image-preview",
|
|
77
77
|
},
|
|
78
|
-
"web-search": { mode: "your-own", provider: "
|
|
78
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
79
79
|
},
|
|
80
80
|
}),
|
|
81
81
|
loadRawConfig: () => ({}),
|
|
@@ -154,8 +154,7 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
154
154
|
latencyMs: 0,
|
|
155
155
|
topCandidates: [],
|
|
156
156
|
}),
|
|
157
|
-
|
|
158
|
-
stripMemoryRecallMessages: (msgs: Message[]) => msgs,
|
|
157
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
159
158
|
}));
|
|
160
159
|
mock.module("../memory/query-builder.js", () => ({
|
|
161
160
|
buildMemoryQuery: () => "",
|
|
@@ -53,7 +53,7 @@ mock.module("../config/loader.js", () => ({
|
|
|
53
53
|
nonInteractiveLatestTurnCompression: "truncate",
|
|
54
54
|
},
|
|
55
55
|
},
|
|
56
|
-
rateLimit: { maxRequestsPerMinute: 0
|
|
56
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
57
57
|
memory: { enabled: false },
|
|
58
58
|
daemon: {
|
|
59
59
|
startupSocketWaitMs: 5000,
|
|
@@ -71,9 +71,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
71
71
|
"image-generation": {
|
|
72
72
|
mode: "your-own",
|
|
73
73
|
provider: "gemini",
|
|
74
|
-
model: "gemini-
|
|
74
|
+
model: "gemini-3.1-flash-image-preview",
|
|
75
75
|
},
|
|
76
|
-
"web-search": { mode: "your-own", provider: "
|
|
76
|
+
"web-search": { mode: "your-own", provider: "inference-provider-native" },
|
|
77
77
|
},
|
|
78
78
|
}),
|
|
79
79
|
loadRawConfig: () => ({}),
|
|
@@ -152,8 +152,7 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
152
152
|
latencyMs: 0,
|
|
153
153
|
topCandidates: [],
|
|
154
154
|
}),
|
|
155
|
-
|
|
156
|
-
stripMemoryRecallMessages: (msgs: Message[]) => msgs,
|
|
155
|
+
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
157
156
|
}));
|
|
158
157
|
mock.module("../memory/query-builder.js", () => ({
|
|
159
158
|
buildMemoryQuery: () => "",
|
|
@@ -15,9 +15,29 @@ const mockConfig = {
|
|
|
15
15
|
timeouts: { permissionTimeoutSec: 300 },
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
function setMockNestedValue(
|
|
19
|
+
obj: Record<string, unknown>,
|
|
20
|
+
path: string,
|
|
21
|
+
value: unknown,
|
|
22
|
+
): void {
|
|
23
|
+
const keys = path.split(".");
|
|
24
|
+
let current = obj;
|
|
25
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
26
|
+
const key = keys[i];
|
|
27
|
+
if (current[key] == null || typeof current[key] !== "object") {
|
|
28
|
+
current[key] = {};
|
|
29
|
+
}
|
|
30
|
+
current = current[key] as Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
current[keys[keys.length - 1]] = value;
|
|
33
|
+
}
|
|
34
|
+
|
|
18
35
|
mock.module("../config/loader.js", () => ({
|
|
19
36
|
getConfig: () => mockConfig,
|
|
20
37
|
loadConfig: () => mockConfig,
|
|
38
|
+
loadRawConfig: () => ({}),
|
|
39
|
+
saveRawConfig: () => {},
|
|
40
|
+
setNestedValue: setMockNestedValue,
|
|
21
41
|
invalidateConfigCache: () => {},
|
|
22
42
|
}));
|
|
23
43
|
|
|
@@ -250,6 +250,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
250
250
|
"daemon/providers-setup.ts", // provider initialization API key lookup
|
|
251
251
|
"tools/claude-code/claude-code.ts", // Claude Code tool API key lookup
|
|
252
252
|
"workspace/migrations/006-services-config.ts", // services config migration reads provider API keys
|
|
253
|
+
"cli/commands/avatar.ts", // CLI avatar generation API key lookup
|
|
253
254
|
]);
|
|
254
255
|
|
|
255
256
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|