@vellumai/assistant 0.5.1 → 0.5.3
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 +163 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/docs/skills.md +100 -0
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +156 -5
- package/src/__tests__/conversation-agent-loop.test.ts +297 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-wipe.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +32 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +7 -7
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +699 -0
- package/src/__tests__/memory-reducer.test.ts +698 -0
- package/src/__tests__/memory-regressions.test.ts +6 -4
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/parse-identity-fields.test.ts +129 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +24 -0
- package/src/config/loader.ts +65 -0
- package/src/config/raw-config-utils.ts +58 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +20 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +8 -6
- package/src/config/skills.ts +50 -4
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
- package/src/daemon/conversation-agent-loop.ts +127 -20
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +50 -16
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +22 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +170 -24
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +69 -30
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +156 -0
- package/src/daemon/handlers/config-model.ts +62 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +115 -65
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +18 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/followups/followup-store.ts +47 -1
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/archive-store.ts +400 -0
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +161 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +591 -8
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +40 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +114 -21
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +2 -4
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +6 -0
- package/src/memory/jobs-worker.ts +12 -0
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +99 -0
- package/src/memory/reducer.ts +453 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/schema/oauth.ts +6 -0
- package/src/memory/search/semantic.ts +17 -4
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +99 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +160 -14
- package/src/permissions/defaults.ts +14 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +74 -0
- package/src/runtime/routes/conversation-query-routes.ts +187 -10
- package/src/runtime/routes/conversation-routes.ts +269 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1212 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +94 -5
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +30 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +30 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/tasks/task-store.ts +43 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +140 -6
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +24 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +11 -1
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
* Regression tests for app surface refresh and eventing side effects in
|
|
3
3
|
* createToolExecutor (conversation-tool-setup.ts).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* skill-origin tools. These tests verify that contract.
|
|
5
|
+
* The app_refresh hook is the sole hook for app change refresh. These tests
|
|
6
|
+
* verify that app_refresh, app_create, and app_delete hooks fire correctly,
|
|
7
|
+
* and that removed hooks (app_update, app_file_edit, app_file_write) no
|
|
8
|
+
* longer trigger side effects.
|
|
10
9
|
*/
|
|
11
10
|
|
|
12
11
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -69,6 +68,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
|
|
|
69
68
|
{ surfaceType: SurfaceType; data: SurfaceData; title?: string }
|
|
70
69
|
>(),
|
|
71
70
|
surfaceUndoStacks: new Map(),
|
|
71
|
+
accumulatedSurfaceState: new Map(),
|
|
72
72
|
surfaceActionRequestIds: new Set<string>(),
|
|
73
73
|
currentTurnSurfaces: [],
|
|
74
74
|
isProcessing: () => false,
|
|
@@ -109,9 +109,9 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
109
109
|
updatePublishedSpy.mockClear();
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
// ──
|
|
112
|
+
// ── app_refresh ─────────────────────────────────────────────────────
|
|
113
113
|
|
|
114
|
-
describe("
|
|
114
|
+
describe("app_refresh", () => {
|
|
115
115
|
test("triggers refreshSurfacesForApp when result is not an error", async () => {
|
|
116
116
|
const ctx = makeCtx();
|
|
117
117
|
const executor = makeFakeExecutor({
|
|
@@ -129,7 +129,7 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
129
129
|
broadcastSpy,
|
|
130
130
|
);
|
|
131
131
|
|
|
132
|
-
await toolFn("
|
|
132
|
+
await toolFn("app_refresh", { app_id: "app-1" });
|
|
133
133
|
|
|
134
134
|
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
135
135
|
expect((refreshSpy.mock.calls as unknown[][])[0][0]).toBe(ctx);
|
|
@@ -150,7 +150,7 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
150
150
|
broadcastSpy,
|
|
151
151
|
);
|
|
152
152
|
|
|
153
|
-
await toolFn("
|
|
153
|
+
await toolFn("app_refresh", { app_id: "app-42" });
|
|
154
154
|
|
|
155
155
|
expect(broadcastSpy).toHaveBeenCalledTimes(1);
|
|
156
156
|
expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
|
|
@@ -172,7 +172,7 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
172
172
|
mock(() => {}),
|
|
173
173
|
);
|
|
174
174
|
|
|
175
|
-
await toolFn("
|
|
175
|
+
await toolFn("app_refresh", { app_id: "app-publish" });
|
|
176
176
|
|
|
177
177
|
// updatePublishedAppDeployment is called with void (fire-and-forget),
|
|
178
178
|
// so just verify it was invoked.
|
|
@@ -199,7 +199,7 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
199
199
|
broadcastSpy,
|
|
200
200
|
);
|
|
201
201
|
|
|
202
|
-
await toolFn("
|
|
202
|
+
await toolFn("app_refresh", { app_id: "app-err" });
|
|
203
203
|
|
|
204
204
|
expect(refreshSpy).not.toHaveBeenCalled();
|
|
205
205
|
expect(broadcastSpy).not.toHaveBeenCalled();
|
|
@@ -220,303 +220,13 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
220
220
|
broadcastSpy,
|
|
221
221
|
);
|
|
222
222
|
|
|
223
|
-
await toolFn("
|
|
223
|
+
await toolFn("app_refresh", {});
|
|
224
224
|
|
|
225
225
|
expect(refreshSpy).not.toHaveBeenCalled();
|
|
226
226
|
expect(broadcastSpy).not.toHaveBeenCalled();
|
|
227
227
|
});
|
|
228
228
|
});
|
|
229
229
|
|
|
230
|
-
// ── app_file_edit ───────────────────────────────────────────────────
|
|
231
|
-
|
|
232
|
-
describe("app_file_edit", () => {
|
|
233
|
-
test("triggers refreshSurfacesForApp with fileChange flag", async () => {
|
|
234
|
-
const ctx = makeCtx();
|
|
235
|
-
const executor = makeFakeExecutor({
|
|
236
|
-
content: '{"ok":true}',
|
|
237
|
-
isError: false,
|
|
238
|
-
});
|
|
239
|
-
const broadcastSpy = mock(() => {});
|
|
240
|
-
|
|
241
|
-
const toolFn = createToolExecutor(
|
|
242
|
-
executor as unknown as ToolExecutor,
|
|
243
|
-
noopPrompter,
|
|
244
|
-
noopSecretPrompter,
|
|
245
|
-
ctx,
|
|
246
|
-
noopLifecycleHandler,
|
|
247
|
-
broadcastSpy,
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
await toolFn("app_file_edit", {
|
|
251
|
-
app_id: "app-edit",
|
|
252
|
-
path: "index.html",
|
|
253
|
-
old_string: "old",
|
|
254
|
-
new_string: "new",
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
258
|
-
expect((refreshSpy.mock.calls as unknown[][])[0][1]).toBe("app-edit");
|
|
259
|
-
// Verify opts include fileChange: true
|
|
260
|
-
expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
|
|
261
|
-
fileChange: true,
|
|
262
|
-
status: undefined,
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
test("propagates status field through refresh opts", async () => {
|
|
267
|
-
const ctx = makeCtx();
|
|
268
|
-
const executor = makeFakeExecutor({
|
|
269
|
-
content: '{"ok":true}',
|
|
270
|
-
isError: false,
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
const toolFn = createToolExecutor(
|
|
274
|
-
executor as unknown as ToolExecutor,
|
|
275
|
-
noopPrompter,
|
|
276
|
-
noopSecretPrompter,
|
|
277
|
-
ctx,
|
|
278
|
-
noopLifecycleHandler,
|
|
279
|
-
mock(() => {}),
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
await toolFn("app_file_edit", {
|
|
283
|
-
app_id: "app-status",
|
|
284
|
-
path: "styles.css",
|
|
285
|
-
old_string: "x",
|
|
286
|
-
new_string: "y",
|
|
287
|
-
status: "updating styles",
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
291
|
-
expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
|
|
292
|
-
fileChange: true,
|
|
293
|
-
status: "updating styles",
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
test("broadcasts app_files_changed for file edit", async () => {
|
|
298
|
-
const ctx = makeCtx();
|
|
299
|
-
const executor = makeFakeExecutor({ content: "{}", isError: false });
|
|
300
|
-
const broadcastSpy = mock(() => {});
|
|
301
|
-
|
|
302
|
-
const toolFn = createToolExecutor(
|
|
303
|
-
executor as unknown as ToolExecutor,
|
|
304
|
-
noopPrompter,
|
|
305
|
-
noopSecretPrompter,
|
|
306
|
-
ctx,
|
|
307
|
-
noopLifecycleHandler,
|
|
308
|
-
broadcastSpy,
|
|
309
|
-
);
|
|
310
|
-
|
|
311
|
-
await toolFn("app_file_edit", {
|
|
312
|
-
app_id: "app-edit-bc",
|
|
313
|
-
path: "f",
|
|
314
|
-
old_string: "a",
|
|
315
|
-
new_string: "b",
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
expect(broadcastSpy).toHaveBeenCalledTimes(1);
|
|
319
|
-
expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
|
|
320
|
-
type: "app_files_changed",
|
|
321
|
-
appId: "app-edit-bc",
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
test("calls updatePublishedAppDeployment for file edit", async () => {
|
|
326
|
-
const ctx = makeCtx();
|
|
327
|
-
const executor = makeFakeExecutor({ content: "{}", isError: false });
|
|
328
|
-
|
|
329
|
-
const toolFn = createToolExecutor(
|
|
330
|
-
executor as unknown as ToolExecutor,
|
|
331
|
-
noopPrompter,
|
|
332
|
-
noopSecretPrompter,
|
|
333
|
-
ctx,
|
|
334
|
-
noopLifecycleHandler,
|
|
335
|
-
mock(() => {}),
|
|
336
|
-
);
|
|
337
|
-
|
|
338
|
-
await toolFn("app_file_edit", {
|
|
339
|
-
app_id: "app-pub-edit",
|
|
340
|
-
path: "f",
|
|
341
|
-
old_string: "a",
|
|
342
|
-
new_string: "b",
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
expect(updatePublishedSpy).toHaveBeenCalledTimes(1);
|
|
346
|
-
expect((updatePublishedSpy.mock.calls as unknown[][])[0][0]).toBe(
|
|
347
|
-
"app-pub-edit",
|
|
348
|
-
);
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
test("skips side effects when result is an error", async () => {
|
|
352
|
-
const ctx = makeCtx();
|
|
353
|
-
const executor = makeFakeExecutor({ content: "Error", isError: true });
|
|
354
|
-
const broadcastSpy = mock(() => {});
|
|
355
|
-
|
|
356
|
-
const toolFn = createToolExecutor(
|
|
357
|
-
executor as unknown as ToolExecutor,
|
|
358
|
-
noopPrompter,
|
|
359
|
-
noopSecretPrompter,
|
|
360
|
-
ctx,
|
|
361
|
-
noopLifecycleHandler,
|
|
362
|
-
broadcastSpy,
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
await toolFn("app_file_edit", {
|
|
366
|
-
app_id: "app-err",
|
|
367
|
-
path: "f",
|
|
368
|
-
old_string: "a",
|
|
369
|
-
new_string: "b",
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
expect(refreshSpy).not.toHaveBeenCalled();
|
|
373
|
-
expect(broadcastSpy).not.toHaveBeenCalled();
|
|
374
|
-
expect(updatePublishedSpy).not.toHaveBeenCalled();
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
// ── app_file_write ──────────────────────────────────────────────────
|
|
379
|
-
|
|
380
|
-
describe("app_file_write", () => {
|
|
381
|
-
test("triggers refreshSurfacesForApp with fileChange flag", async () => {
|
|
382
|
-
const ctx = makeCtx();
|
|
383
|
-
const executor = makeFakeExecutor({
|
|
384
|
-
content: '{"written":true}',
|
|
385
|
-
isError: false,
|
|
386
|
-
});
|
|
387
|
-
const broadcastSpy = mock(() => {});
|
|
388
|
-
|
|
389
|
-
const toolFn = createToolExecutor(
|
|
390
|
-
executor as unknown as ToolExecutor,
|
|
391
|
-
noopPrompter,
|
|
392
|
-
noopSecretPrompter,
|
|
393
|
-
ctx,
|
|
394
|
-
noopLifecycleHandler,
|
|
395
|
-
broadcastSpy,
|
|
396
|
-
);
|
|
397
|
-
|
|
398
|
-
await toolFn("app_file_write", {
|
|
399
|
-
app_id: "app-write",
|
|
400
|
-
path: "new.html",
|
|
401
|
-
content: "<div/>",
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
405
|
-
expect((refreshSpy.mock.calls as unknown[][])[0][1]).toBe("app-write");
|
|
406
|
-
expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
|
|
407
|
-
fileChange: true,
|
|
408
|
-
status: undefined,
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
test("propagates status field through refresh opts", async () => {
|
|
413
|
-
const ctx = makeCtx();
|
|
414
|
-
const executor = makeFakeExecutor({
|
|
415
|
-
content: '{"written":true}',
|
|
416
|
-
isError: false,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
const toolFn = createToolExecutor(
|
|
420
|
-
executor as unknown as ToolExecutor,
|
|
421
|
-
noopPrompter,
|
|
422
|
-
noopSecretPrompter,
|
|
423
|
-
ctx,
|
|
424
|
-
noopLifecycleHandler,
|
|
425
|
-
mock(() => {}),
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
await toolFn("app_file_write", {
|
|
429
|
-
app_id: "app-ws",
|
|
430
|
-
path: "f.txt",
|
|
431
|
-
content: "hi",
|
|
432
|
-
status: "adding dark mode",
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
436
|
-
expect((refreshSpy.mock.calls as unknown[][])[0][2]).toEqual({
|
|
437
|
-
fileChange: true,
|
|
438
|
-
status: "adding dark mode",
|
|
439
|
-
});
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
test("broadcasts app_files_changed for file write", async () => {
|
|
443
|
-
const ctx = makeCtx();
|
|
444
|
-
const executor = makeFakeExecutor({ content: "{}", isError: false });
|
|
445
|
-
const broadcastSpy = mock(() => {});
|
|
446
|
-
|
|
447
|
-
const toolFn = createToolExecutor(
|
|
448
|
-
executor as unknown as ToolExecutor,
|
|
449
|
-
noopPrompter,
|
|
450
|
-
noopSecretPrompter,
|
|
451
|
-
ctx,
|
|
452
|
-
noopLifecycleHandler,
|
|
453
|
-
broadcastSpy,
|
|
454
|
-
);
|
|
455
|
-
|
|
456
|
-
await toolFn("app_file_write", {
|
|
457
|
-
app_id: "app-write-bc",
|
|
458
|
-
path: "f",
|
|
459
|
-
content: "x",
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
expect(broadcastSpy).toHaveBeenCalledTimes(1);
|
|
463
|
-
expect((broadcastSpy.mock.calls as unknown[][])[0][0]).toEqual({
|
|
464
|
-
type: "app_files_changed",
|
|
465
|
-
appId: "app-write-bc",
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test("calls updatePublishedAppDeployment for file write", async () => {
|
|
470
|
-
const ctx = makeCtx();
|
|
471
|
-
const executor = makeFakeExecutor({ content: "{}", isError: false });
|
|
472
|
-
|
|
473
|
-
const toolFn = createToolExecutor(
|
|
474
|
-
executor as unknown as ToolExecutor,
|
|
475
|
-
noopPrompter,
|
|
476
|
-
noopSecretPrompter,
|
|
477
|
-
ctx,
|
|
478
|
-
noopLifecycleHandler,
|
|
479
|
-
mock(() => {}),
|
|
480
|
-
);
|
|
481
|
-
|
|
482
|
-
await toolFn("app_file_write", {
|
|
483
|
-
app_id: "app-pub-write",
|
|
484
|
-
path: "f",
|
|
485
|
-
content: "x",
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
expect(updatePublishedSpy).toHaveBeenCalledTimes(1);
|
|
489
|
-
expect((updatePublishedSpy.mock.calls as unknown[][])[0][0]).toBe(
|
|
490
|
-
"app-pub-write",
|
|
491
|
-
);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
test("skips side effects when result is an error", async () => {
|
|
495
|
-
const ctx = makeCtx();
|
|
496
|
-
const executor = makeFakeExecutor({ content: "Error", isError: true });
|
|
497
|
-
const broadcastSpy = mock(() => {});
|
|
498
|
-
|
|
499
|
-
const toolFn = createToolExecutor(
|
|
500
|
-
executor as unknown as ToolExecutor,
|
|
501
|
-
noopPrompter,
|
|
502
|
-
noopSecretPrompter,
|
|
503
|
-
ctx,
|
|
504
|
-
noopLifecycleHandler,
|
|
505
|
-
broadcastSpy,
|
|
506
|
-
);
|
|
507
|
-
|
|
508
|
-
await toolFn("app_file_write", {
|
|
509
|
-
app_id: "app-err",
|
|
510
|
-
path: "f",
|
|
511
|
-
content: "x",
|
|
512
|
-
});
|
|
513
|
-
|
|
514
|
-
expect(refreshSpy).not.toHaveBeenCalled();
|
|
515
|
-
expect(broadcastSpy).not.toHaveBeenCalled();
|
|
516
|
-
expect(updatePublishedSpy).not.toHaveBeenCalled();
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
|
|
520
230
|
// ── app_create side effects ─────────────────────────────────────────
|
|
521
231
|
|
|
522
232
|
describe("app_create side effects", () => {
|
|
@@ -616,7 +326,7 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
616
326
|
|
|
617
327
|
describe("name-based hooks fire for skill-origin tools", () => {
|
|
618
328
|
test("hooks fire purely on tool name, regardless of tool origin", async () => {
|
|
619
|
-
// The key invariant: createToolExecutor uses `name === '
|
|
329
|
+
// The key invariant: createToolExecutor uses `name === 'app_refresh'`
|
|
620
330
|
// string comparison, not tool metadata or origin. This means skill-
|
|
621
331
|
// projected tools with the same name trigger the same afterExecute
|
|
622
332
|
// hooks as core tools.
|
|
@@ -633,22 +343,14 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
633
343
|
broadcastSpy,
|
|
634
344
|
);
|
|
635
345
|
|
|
636
|
-
// Simulate calling
|
|
637
|
-
for (const toolName of [
|
|
638
|
-
"app_update",
|
|
639
|
-
"app_file_edit",
|
|
640
|
-
"app_file_write",
|
|
641
|
-
]) {
|
|
346
|
+
// Simulate calling app_refresh by name (as the agent loop does)
|
|
347
|
+
for (const toolName of ["app_refresh"]) {
|
|
642
348
|
refreshSpy.mockClear();
|
|
643
349
|
broadcastSpy.mockClear();
|
|
644
350
|
updatePublishedSpy.mockClear();
|
|
645
351
|
|
|
646
352
|
await toolFn(toolName, {
|
|
647
353
|
app_id: "skill-app",
|
|
648
|
-
path: "f",
|
|
649
|
-
old_string: "a",
|
|
650
|
-
new_string: "b",
|
|
651
|
-
content: "x",
|
|
652
354
|
});
|
|
653
355
|
|
|
654
356
|
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
@@ -675,7 +377,15 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
675
377
|
broadcastSpy,
|
|
676
378
|
);
|
|
677
379
|
|
|
678
|
-
for (const toolName of [
|
|
380
|
+
for (const toolName of [
|
|
381
|
+
"read_file",
|
|
382
|
+
"write_file",
|
|
383
|
+
"shell",
|
|
384
|
+
"app_list",
|
|
385
|
+
"app_update",
|
|
386
|
+
"app_file_edit",
|
|
387
|
+
"app_file_write",
|
|
388
|
+
]) {
|
|
679
389
|
refreshSpy.mockClear();
|
|
680
390
|
broadcastSpy.mockClear();
|
|
681
391
|
updatePublishedSpy.mockClear();
|
|
@@ -706,7 +416,7 @@ describe("session-tool-setup app refresh side effects", () => {
|
|
|
706
416
|
);
|
|
707
417
|
|
|
708
418
|
// Should not throw even though broadcastToAllClients is undefined
|
|
709
|
-
const result = await toolFn("
|
|
419
|
+
const result = await toolFn("app_refresh", { app_id: "app-no-bc" });
|
|
710
420
|
|
|
711
421
|
expect(result.isError).toBe(false);
|
|
712
422
|
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
|
@@ -56,6 +56,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
|
|
|
56
56
|
{ surfaceType: SurfaceType; data: SurfaceData; title?: string }
|
|
57
57
|
>(),
|
|
58
58
|
surfaceUndoStacks: new Map(),
|
|
59
|
+
accumulatedSurfaceState: new Map(),
|
|
59
60
|
surfaceActionRequestIds: new Set<string>(),
|
|
60
61
|
currentTurnSurfaces: [],
|
|
61
62
|
isProcessing: () => false,
|
|
@@ -56,6 +56,7 @@ function makeCtx(overrides: Partial<ToolSetupContext> = {}): ToolSetupContext {
|
|
|
56
56
|
{ surfaceType: SurfaceType; data: SurfaceData; title?: string }
|
|
57
57
|
>(),
|
|
58
58
|
surfaceUndoStacks: new Map(),
|
|
59
|
+
accumulatedSurfaceState: new Map(),
|
|
59
60
|
surfaceActionRequestIds: new Set<string>(),
|
|
60
61
|
currentTurnSurfaces: [],
|
|
61
62
|
isProcessing: () => false,
|
|
@@ -26,6 +26,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
26
26
|
import {
|
|
27
27
|
addMessage,
|
|
28
28
|
createConversation,
|
|
29
|
+
deleteConversation,
|
|
29
30
|
getConversation,
|
|
30
31
|
getMessages,
|
|
31
32
|
wipeConversation,
|
|
@@ -436,3 +437,228 @@ describe("wipeConversation", () => {
|
|
|
436
437
|
expect(itemBRow).not.toBeNull();
|
|
437
438
|
});
|
|
438
439
|
});
|
|
440
|
+
|
|
441
|
+
describe("deleteConversation — private scope cleanup", () => {
|
|
442
|
+
beforeEach(() => {
|
|
443
|
+
const db = getDb();
|
|
444
|
+
db.run(`DELETE FROM conversation_starters`);
|
|
445
|
+
db.run(`DELETE FROM memory_item_sources`);
|
|
446
|
+
db.run(`DELETE FROM memory_segments`);
|
|
447
|
+
db.run(`DELETE FROM memory_items`);
|
|
448
|
+
db.run(`DELETE FROM memory_summaries`);
|
|
449
|
+
db.run(`DELETE FROM memory_embeddings`);
|
|
450
|
+
db.run(`DELETE FROM memory_jobs`);
|
|
451
|
+
db.run(`DELETE FROM tool_invocations`);
|
|
452
|
+
db.run(`DELETE FROM llm_request_logs`);
|
|
453
|
+
db.run(`DELETE FROM messages`);
|
|
454
|
+
db.run(`DELETE FROM conversations`);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
test("sourceless items cleaned up", () => {
|
|
458
|
+
const conv = createConversation({ conversationType: "private" });
|
|
459
|
+
const scopeId = conv.memoryScopeId;
|
|
460
|
+
const now = Date.now();
|
|
461
|
+
|
|
462
|
+
const raw = (
|
|
463
|
+
getDb() as unknown as {
|
|
464
|
+
$client: import("bun:sqlite").Database;
|
|
465
|
+
}
|
|
466
|
+
).$client;
|
|
467
|
+
|
|
468
|
+
// Insert a memory item with matching scopeId but no memory_item_sources
|
|
469
|
+
raw
|
|
470
|
+
.query(
|
|
471
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
472
|
+
VALUES ('priv-item-1', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-1', ?, ?, ?)`,
|
|
473
|
+
)
|
|
474
|
+
.run(scopeId, now, now);
|
|
475
|
+
|
|
476
|
+
const result = deleteConversation(conv.id);
|
|
477
|
+
|
|
478
|
+
// Item should be gone
|
|
479
|
+
const itemRow = raw
|
|
480
|
+
.query("SELECT * FROM memory_items WHERE id = 'priv-item-1'")
|
|
481
|
+
.get();
|
|
482
|
+
expect(itemRow).toBeNull();
|
|
483
|
+
|
|
484
|
+
// Its ID should be in orphanedItemIds
|
|
485
|
+
expect(result.orphanedItemIds).toContain("priv-item-1");
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
test("summaries cleaned up", () => {
|
|
489
|
+
const conv = createConversation({ conversationType: "private" });
|
|
490
|
+
const scopeId = conv.memoryScopeId;
|
|
491
|
+
const now = Date.now();
|
|
492
|
+
|
|
493
|
+
const raw = (
|
|
494
|
+
getDb() as unknown as {
|
|
495
|
+
$client: import("bun:sqlite").Database;
|
|
496
|
+
}
|
|
497
|
+
).$client;
|
|
498
|
+
|
|
499
|
+
// Insert a memory summary with matching scopeId
|
|
500
|
+
raw
|
|
501
|
+
.query(
|
|
502
|
+
`INSERT INTO memory_summaries (id, scope, scope_key, summary, token_estimate, version, scope_id, start_at, end_at, created_at, updated_at)
|
|
503
|
+
VALUES ('priv-sum-1', 'global', 'all', 'private summary', 100, 1, ?, ?, ?, ?, ?)`,
|
|
504
|
+
)
|
|
505
|
+
.run(scopeId, now, now, now, now);
|
|
506
|
+
|
|
507
|
+
const result = deleteConversation(conv.id);
|
|
508
|
+
|
|
509
|
+
// Summary should be gone
|
|
510
|
+
const summaryRow = raw
|
|
511
|
+
.query("SELECT * FROM memory_summaries WHERE id = 'priv-sum-1'")
|
|
512
|
+
.get();
|
|
513
|
+
expect(summaryRow).toBeNull();
|
|
514
|
+
|
|
515
|
+
// Its ID should be in deletedSummaryIds
|
|
516
|
+
expect(result.deletedSummaryIds).toContain("priv-sum-1");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test("standard conversations unaffected", async () => {
|
|
520
|
+
const conv = createConversation("standard test");
|
|
521
|
+
const now = Date.now();
|
|
522
|
+
|
|
523
|
+
const raw = (
|
|
524
|
+
getDb() as unknown as {
|
|
525
|
+
$client: import("bun:sqlite").Database;
|
|
526
|
+
}
|
|
527
|
+
).$client;
|
|
528
|
+
|
|
529
|
+
// Insert items with scopeId = "default"
|
|
530
|
+
raw
|
|
531
|
+
.query(
|
|
532
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
533
|
+
VALUES ('default-item-1', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-default', 'default', ?, ?)`,
|
|
534
|
+
)
|
|
535
|
+
.run(now, now);
|
|
536
|
+
|
|
537
|
+
deleteConversation(conv.id);
|
|
538
|
+
|
|
539
|
+
// Default-scope items should still exist
|
|
540
|
+
const itemRow = raw
|
|
541
|
+
.query("SELECT * FROM memory_items WHERE id = 'default-item-1'")
|
|
542
|
+
.get();
|
|
543
|
+
expect(itemRow).not.toBeNull();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
test("embeddings cleaned up", () => {
|
|
547
|
+
const conv = createConversation({ conversationType: "private" });
|
|
548
|
+
const scopeId = conv.memoryScopeId;
|
|
549
|
+
const now = Date.now();
|
|
550
|
+
|
|
551
|
+
const raw = (
|
|
552
|
+
getDb() as unknown as {
|
|
553
|
+
$client: import("bun:sqlite").Database;
|
|
554
|
+
}
|
|
555
|
+
).$client;
|
|
556
|
+
|
|
557
|
+
// Insert a memory item with matching scopeId
|
|
558
|
+
raw
|
|
559
|
+
.query(
|
|
560
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
561
|
+
VALUES ('priv-item-emb', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-emb', ?, ?, ?)`,
|
|
562
|
+
)
|
|
563
|
+
.run(scopeId, now, now);
|
|
564
|
+
|
|
565
|
+
// Insert a corresponding embedding
|
|
566
|
+
raw
|
|
567
|
+
.query(
|
|
568
|
+
`INSERT INTO memory_embeddings (id, target_type, target_id, provider, model, dimensions, created_at, updated_at)
|
|
569
|
+
VALUES ('emb-priv-item', 'item', 'priv-item-emb', 'test', 'test', 384, ?, ?)`,
|
|
570
|
+
)
|
|
571
|
+
.run(now, now);
|
|
572
|
+
|
|
573
|
+
deleteConversation(conv.id);
|
|
574
|
+
|
|
575
|
+
// Both item and embedding should be deleted
|
|
576
|
+
const itemRow = raw
|
|
577
|
+
.query("SELECT * FROM memory_items WHERE id = 'priv-item-emb'")
|
|
578
|
+
.get();
|
|
579
|
+
expect(itemRow).toBeNull();
|
|
580
|
+
|
|
581
|
+
const embeddingRow = raw
|
|
582
|
+
.query("SELECT * FROM memory_embeddings WHERE id = 'emb-priv-item'")
|
|
583
|
+
.get();
|
|
584
|
+
expect(embeddingRow).toBeNull();
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
test("conversationStarters cleaned up", () => {
|
|
588
|
+
const conv = createConversation({ conversationType: "private" });
|
|
589
|
+
const scopeId = conv.memoryScopeId;
|
|
590
|
+
const now = Date.now();
|
|
591
|
+
|
|
592
|
+
const raw = (
|
|
593
|
+
getDb() as unknown as {
|
|
594
|
+
$client: import("bun:sqlite").Database;
|
|
595
|
+
}
|
|
596
|
+
).$client;
|
|
597
|
+
|
|
598
|
+
// Insert a conversation_starters row with the private scopeId
|
|
599
|
+
raw
|
|
600
|
+
.query(
|
|
601
|
+
`INSERT INTO conversation_starters (id, label, prompt, generation_batch, scope_id, card_type, created_at)
|
|
602
|
+
VALUES ('starter-1', 'Test starter', 'Tell me about tests', 1, ?, 'chip', ?)`,
|
|
603
|
+
)
|
|
604
|
+
.run(scopeId, now);
|
|
605
|
+
|
|
606
|
+
// Also insert a default-scope starter that should NOT be deleted
|
|
607
|
+
raw
|
|
608
|
+
.query(
|
|
609
|
+
`INSERT INTO conversation_starters (id, label, prompt, generation_batch, scope_id, card_type, created_at)
|
|
610
|
+
VALUES ('starter-default', 'Default starter', 'Hello', 1, 'default', 'chip', ?)`,
|
|
611
|
+
)
|
|
612
|
+
.run(now);
|
|
613
|
+
|
|
614
|
+
deleteConversation(conv.id);
|
|
615
|
+
|
|
616
|
+
// Private-scope starter should be gone
|
|
617
|
+
const starterRow = raw
|
|
618
|
+
.query("SELECT * FROM conversation_starters WHERE id = 'starter-1'")
|
|
619
|
+
.get();
|
|
620
|
+
expect(starterRow).toBeNull();
|
|
621
|
+
|
|
622
|
+
// Default-scope starter should still exist
|
|
623
|
+
const defaultStarterRow = raw
|
|
624
|
+
.query("SELECT * FROM conversation_starters WHERE id = 'starter-default'")
|
|
625
|
+
.get();
|
|
626
|
+
expect(defaultStarterRow).not.toBeNull();
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
test("no duplicate IDs", async () => {
|
|
630
|
+
const conv = createConversation({ conversationType: "private" });
|
|
631
|
+
const scopeId = conv.memoryScopeId;
|
|
632
|
+
const msg = await addMessage(conv.id, "user", "hello");
|
|
633
|
+
const now = Date.now();
|
|
634
|
+
|
|
635
|
+
const raw = (
|
|
636
|
+
getDb() as unknown as {
|
|
637
|
+
$client: import("bun:sqlite").Database;
|
|
638
|
+
}
|
|
639
|
+
).$client;
|
|
640
|
+
|
|
641
|
+
// Insert a memory item with the private scopeId AND a source linking to the message
|
|
642
|
+
raw
|
|
643
|
+
.query(
|
|
644
|
+
`INSERT INTO memory_items (id, status, kind, subject, statement, confidence, fingerprint, scope_id, first_seen_at, last_seen_at)
|
|
645
|
+
VALUES ('priv-item-dup', 'active', 'fact', 'test', 'test fact', 0.8, 'fp-priv-dup', ?, ?, ?)`,
|
|
646
|
+
)
|
|
647
|
+
.run(scopeId, now, now);
|
|
648
|
+
|
|
649
|
+
raw
|
|
650
|
+
.query(
|
|
651
|
+
`INSERT INTO memory_item_sources (memory_item_id, message_id, created_at) VALUES ('priv-item-dup', ?, ?)`,
|
|
652
|
+
)
|
|
653
|
+
.run(msg.id, now);
|
|
654
|
+
|
|
655
|
+
const result = deleteConversation(conv.id);
|
|
656
|
+
|
|
657
|
+
// The item ID should appear exactly once in orphanedItemIds (caught by
|
|
658
|
+
// source-based cleanup, not double-counted by scope sweep).
|
|
659
|
+
const count = result.orphanedItemIds.filter(
|
|
660
|
+
(id) => id === "priv-item-dup",
|
|
661
|
+
).length;
|
|
662
|
+
expect(count).toBe(1);
|
|
663
|
+
});
|
|
664
|
+
});
|