@vellumai/assistant 0.5.1 → 0.5.2
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 +54 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- 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 +149 -5
- package/src/__tests__/conversation-agent-loop.test.ts +290 -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-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-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__/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__/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-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-recall-quality.test.ts +5 -5
- 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__/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__/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__/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/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 +8 -0
- package/src/config/loader.ts +64 -0
- package/src/config/raw-config-utils.ts +30 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/services.ts +8 -6
- 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 +48 -7
- package/src/daemon/conversation-agent-loop.ts +56 -19
- 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 +39 -15
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +21 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +143 -20
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +51 -29
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +148 -0
- package/src/daemon/handlers/config-model.ts +71 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +106 -64
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +19 -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/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/attachments-store.ts +558 -130
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +442 -3
- 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 +28 -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 +9 -7
- 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 +1 -4
- 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/index.ts +7 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +6 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/oauth.ts +6 -0
- 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 +97 -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 +71 -8
- 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 +68 -0
- package/src/runtime/routes/conversation-query-routes.ts +180 -10
- package/src/runtime/routes/conversation-routes.ts +222 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1199 -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 +4 -0
- 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 +28 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +9 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/swarm/backend-claude-code.ts +3 -6
- 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/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/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 +6 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
- 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/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 +10 -0
- 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
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Database } from "bun:sqlite";
|
|
5
|
+
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
6
|
+
|
|
7
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
8
|
+
|
|
9
|
+
const testDir = mkdtempSync(join(tmpdir(), "conversation-fork-lineage-"));
|
|
10
|
+
const dbPath = join(testDir, "test.db");
|
|
11
|
+
const originalBunTest = process.env.BUN_TEST;
|
|
12
|
+
|
|
13
|
+
mock.module("../util/platform.js", () => ({
|
|
14
|
+
getDataDir: () => testDir,
|
|
15
|
+
isMacOS: () => process.platform === "darwin",
|
|
16
|
+
isLinux: () => process.platform === "linux",
|
|
17
|
+
isWindows: () => process.platform === "win32",
|
|
18
|
+
getPidPath: () => join(testDir, "test.pid"),
|
|
19
|
+
getDbPath: () => dbPath,
|
|
20
|
+
getLogPath: () => join(testDir, "test.log"),
|
|
21
|
+
ensureDataDir: () => {},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
mock.module("../util/logger.js", () => ({
|
|
25
|
+
getLogger: () =>
|
|
26
|
+
new Proxy({} as Record<string, unknown>, {
|
|
27
|
+
get: () => () => {},
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { initializeDb, resetDb } from "../memory/db.js";
|
|
32
|
+
import { getSqliteFrom } from "../memory/db-connection.js";
|
|
33
|
+
import { migrateConversationForkLineage } from "../memory/migrations/183-add-conversation-fork-lineage.js";
|
|
34
|
+
import * as schema from "../memory/schema.js";
|
|
35
|
+
|
|
36
|
+
function createTestDb() {
|
|
37
|
+
const sqlite = new Database(":memory:");
|
|
38
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
39
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
40
|
+
return drizzle(sqlite, { schema });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getColumnNames(raw: Database): string[] {
|
|
44
|
+
return (
|
|
45
|
+
raw.query(`PRAGMA table_info(conversations)`).all() as Array<{ name: string }>
|
|
46
|
+
).map((column) => column.name);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hasIndex(raw: Database, indexName: string): boolean {
|
|
50
|
+
const row = raw
|
|
51
|
+
.query(`SELECT 1 FROM sqlite_master WHERE type = 'index' AND name = ?`)
|
|
52
|
+
.get(indexName);
|
|
53
|
+
return row != null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function bootstrapPreLineageConversations(raw: Database): void {
|
|
57
|
+
raw.exec(/*sql*/ `
|
|
58
|
+
CREATE TABLE conversations (
|
|
59
|
+
id TEXT PRIMARY KEY,
|
|
60
|
+
title TEXT,
|
|
61
|
+
created_at INTEGER NOT NULL,
|
|
62
|
+
updated_at INTEGER NOT NULL,
|
|
63
|
+
total_input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
64
|
+
total_output_tokens INTEGER NOT NULL DEFAULT 0,
|
|
65
|
+
total_estimated_cost REAL NOT NULL DEFAULT 0,
|
|
66
|
+
context_summary TEXT,
|
|
67
|
+
context_compacted_message_count INTEGER NOT NULL DEFAULT 0,
|
|
68
|
+
context_compacted_at INTEGER,
|
|
69
|
+
conversation_type TEXT NOT NULL DEFAULT 'standard',
|
|
70
|
+
source TEXT NOT NULL DEFAULT 'user',
|
|
71
|
+
memory_scope_id TEXT NOT NULL DEFAULT 'default',
|
|
72
|
+
origin_channel TEXT,
|
|
73
|
+
origin_interface TEXT,
|
|
74
|
+
is_auto_title INTEGER NOT NULL DEFAULT 1,
|
|
75
|
+
schedule_job_id TEXT
|
|
76
|
+
)
|
|
77
|
+
`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function removeTestDbFiles(): void {
|
|
81
|
+
resetDb();
|
|
82
|
+
rmSync(dbPath, { force: true });
|
|
83
|
+
rmSync(`${dbPath}-shm`, { force: true });
|
|
84
|
+
rmSync(`${dbPath}-wal`, { force: true });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
describe("conversation fork lineage migration", () => {
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
process.env.BUN_TEST = "0";
|
|
90
|
+
removeTestDbFiles();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterAll(() => {
|
|
94
|
+
process.env.BUN_TEST = originalBunTest;
|
|
95
|
+
removeTestDbFiles();
|
|
96
|
+
try {
|
|
97
|
+
rmSync(testDir, { recursive: true });
|
|
98
|
+
} catch {
|
|
99
|
+
/* best effort */
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("fresh DB initialization includes nullable lineage columns and parent lookup index", () => {
|
|
104
|
+
initializeDb();
|
|
105
|
+
|
|
106
|
+
const raw = new Database(dbPath);
|
|
107
|
+
const columns = getColumnNames(raw);
|
|
108
|
+
|
|
109
|
+
expect(columns).toContain("fork_parent_conversation_id");
|
|
110
|
+
expect(columns).toContain("fork_parent_message_id");
|
|
111
|
+
expect(hasIndex(raw, "idx_conversations_fork_parent_conversation_id")).toBe(
|
|
112
|
+
true,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const forkColumns = (
|
|
116
|
+
raw.query(`PRAGMA table_info(conversations)`).all() as Array<{
|
|
117
|
+
name: string;
|
|
118
|
+
notnull: number;
|
|
119
|
+
}>
|
|
120
|
+
).filter(
|
|
121
|
+
(column) =>
|
|
122
|
+
column.name === "fork_parent_conversation_id" ||
|
|
123
|
+
column.name === "fork_parent_message_id",
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(forkColumns).toHaveLength(2);
|
|
127
|
+
expect(forkColumns.every((column) => column.notnull === 0)).toBe(true);
|
|
128
|
+
raw.close();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("migration upgrades the previous schema without disturbing existing conversation rows", () => {
|
|
132
|
+
const db = createTestDb();
|
|
133
|
+
const raw = getSqliteFrom(db);
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
|
|
136
|
+
bootstrapPreLineageConversations(raw);
|
|
137
|
+
raw.exec(/*sql*/ `
|
|
138
|
+
INSERT INTO conversations (
|
|
139
|
+
id,
|
|
140
|
+
title,
|
|
141
|
+
created_at,
|
|
142
|
+
updated_at,
|
|
143
|
+
conversation_type,
|
|
144
|
+
source,
|
|
145
|
+
memory_scope_id,
|
|
146
|
+
is_auto_title
|
|
147
|
+
) VALUES (
|
|
148
|
+
'conv-upgrade',
|
|
149
|
+
'Existing conversation',
|
|
150
|
+
${now},
|
|
151
|
+
${now},
|
|
152
|
+
'standard',
|
|
153
|
+
'user',
|
|
154
|
+
'default',
|
|
155
|
+
1
|
|
156
|
+
)
|
|
157
|
+
`);
|
|
158
|
+
|
|
159
|
+
migrateConversationForkLineage(db);
|
|
160
|
+
|
|
161
|
+
expect(getColumnNames(raw)).toContain("fork_parent_conversation_id");
|
|
162
|
+
expect(getColumnNames(raw)).toContain("fork_parent_message_id");
|
|
163
|
+
expect(hasIndex(raw, "idx_conversations_fork_parent_conversation_id")).toBe(
|
|
164
|
+
true,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const row = raw
|
|
168
|
+
.query(
|
|
169
|
+
`SELECT id, title, fork_parent_conversation_id, fork_parent_message_id FROM conversations WHERE id = 'conv-upgrade'`,
|
|
170
|
+
)
|
|
171
|
+
.get() as {
|
|
172
|
+
id: string;
|
|
173
|
+
title: string | null;
|
|
174
|
+
fork_parent_conversation_id: string | null;
|
|
175
|
+
fork_parent_message_id: string | null;
|
|
176
|
+
} | null;
|
|
177
|
+
|
|
178
|
+
expect(row).toEqual({
|
|
179
|
+
id: "conv-upgrade",
|
|
180
|
+
title: "Existing conversation",
|
|
181
|
+
fork_parent_conversation_id: null,
|
|
182
|
+
fork_parent_message_id: null,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("re-running the migration preserves existing lineage data", () => {
|
|
187
|
+
const db = createTestDb();
|
|
188
|
+
const raw = getSqliteFrom(db);
|
|
189
|
+
const now = Date.now();
|
|
190
|
+
|
|
191
|
+
bootstrapPreLineageConversations(raw);
|
|
192
|
+
raw.exec(/*sql*/ `
|
|
193
|
+
INSERT INTO conversations (
|
|
194
|
+
id,
|
|
195
|
+
title,
|
|
196
|
+
created_at,
|
|
197
|
+
updated_at,
|
|
198
|
+
conversation_type,
|
|
199
|
+
source,
|
|
200
|
+
memory_scope_id,
|
|
201
|
+
is_auto_title
|
|
202
|
+
) VALUES (
|
|
203
|
+
'conv-rerun',
|
|
204
|
+
'Forked conversation',
|
|
205
|
+
${now},
|
|
206
|
+
${now},
|
|
207
|
+
'standard',
|
|
208
|
+
'user',
|
|
209
|
+
'default',
|
|
210
|
+
1
|
|
211
|
+
)
|
|
212
|
+
`);
|
|
213
|
+
|
|
214
|
+
migrateConversationForkLineage(db);
|
|
215
|
+
raw.exec(/*sql*/ `
|
|
216
|
+
UPDATE conversations
|
|
217
|
+
SET fork_parent_conversation_id = 'conv-parent',
|
|
218
|
+
fork_parent_message_id = 'msg-parent'
|
|
219
|
+
WHERE id = 'conv-rerun'
|
|
220
|
+
`);
|
|
221
|
+
|
|
222
|
+
expect(() => migrateConversationForkLineage(db)).not.toThrow();
|
|
223
|
+
|
|
224
|
+
const row = raw
|
|
225
|
+
.query(
|
|
226
|
+
`SELECT fork_parent_conversation_id, fork_parent_message_id FROM conversations WHERE id = 'conv-rerun'`,
|
|
227
|
+
)
|
|
228
|
+
.get() as {
|
|
229
|
+
fork_parent_conversation_id: string | null;
|
|
230
|
+
fork_parent_message_id: string | null;
|
|
231
|
+
} | null;
|
|
232
|
+
|
|
233
|
+
expect(row).toEqual({
|
|
234
|
+
fork_parent_conversation_id: "conv-parent",
|
|
235
|
+
fork_parent_message_id: "msg-parent",
|
|
236
|
+
});
|
|
237
|
+
expect(hasIndex(raw, "idx_conversations_fork_parent_conversation_id")).toBe(
|
|
238
|
+
true,
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Database } from "bun:sqlite";
|
|
5
|
+
import { afterAll, afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
6
|
+
|
|
7
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
8
|
+
|
|
9
|
+
const testDir = mkdtempSync(join(tmpdir(), "llm-request-log-provider-"));
|
|
10
|
+
const dbPath = join(testDir, "test.db");
|
|
11
|
+
const originalBunTest = process.env.BUN_TEST;
|
|
12
|
+
|
|
13
|
+
mock.module("../util/platform.js", () => ({
|
|
14
|
+
getDataDir: () => testDir,
|
|
15
|
+
isMacOS: () => process.platform === "darwin",
|
|
16
|
+
isLinux: () => process.platform === "linux",
|
|
17
|
+
isWindows: () => process.platform === "win32",
|
|
18
|
+
getPidPath: () => join(testDir, "test.pid"),
|
|
19
|
+
getDbPath: () => dbPath,
|
|
20
|
+
getLogPath: () => join(testDir, "test.log"),
|
|
21
|
+
ensureDataDir: () => {},
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
mock.module("../util/logger.js", () => ({
|
|
25
|
+
getLogger: () =>
|
|
26
|
+
new Proxy({} as Record<string, unknown>, {
|
|
27
|
+
get: () => () => {},
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { initializeDb, resetDb } from "../memory/db.js";
|
|
32
|
+
import { getSqliteFrom } from "../memory/db-connection.js";
|
|
33
|
+
import { migrateLlmRequestLogProvider } from "../memory/migrations/184-llm-request-log-provider.js";
|
|
34
|
+
import * as schema from "../memory/schema.js";
|
|
35
|
+
|
|
36
|
+
function createTestDb() {
|
|
37
|
+
const sqlite = new Database(":memory:");
|
|
38
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
39
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
40
|
+
return drizzle(sqlite, { schema });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getColumnInfo(raw: Database): Array<{ name: string; notnull: number }> {
|
|
44
|
+
return raw.query(`PRAGMA table_info(llm_request_logs)`).all() as Array<{
|
|
45
|
+
name: string;
|
|
46
|
+
notnull: number;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function bootstrapPreProviderLlmRequestLogs(raw: Database): void {
|
|
51
|
+
raw.exec(/*sql*/ `
|
|
52
|
+
CREATE TABLE llm_request_logs (
|
|
53
|
+
id TEXT PRIMARY KEY,
|
|
54
|
+
conversation_id TEXT NOT NULL,
|
|
55
|
+
message_id TEXT,
|
|
56
|
+
request_payload TEXT NOT NULL,
|
|
57
|
+
response_payload TEXT NOT NULL,
|
|
58
|
+
created_at INTEGER NOT NULL
|
|
59
|
+
)
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function removeTestDbFiles(): void {
|
|
64
|
+
rmSync(dbPath, { force: true });
|
|
65
|
+
rmSync(`${dbPath}-shm`, { force: true });
|
|
66
|
+
rmSync(`${dbPath}-wal`, { force: true });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe("llm_request_logs provider migration", () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
process.env.BUN_TEST = "0";
|
|
72
|
+
resetDb();
|
|
73
|
+
removeTestDbFiles();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
resetDb();
|
|
78
|
+
removeTestDbFiles();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterAll(() => {
|
|
82
|
+
if (originalBunTest === undefined) {
|
|
83
|
+
delete process.env.BUN_TEST;
|
|
84
|
+
} else {
|
|
85
|
+
process.env.BUN_TEST = originalBunTest;
|
|
86
|
+
}
|
|
87
|
+
resetDb();
|
|
88
|
+
removeTestDbFiles();
|
|
89
|
+
try {
|
|
90
|
+
rmSync(testDir, { recursive: true });
|
|
91
|
+
} catch {
|
|
92
|
+
/* best effort */
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("fresh DB initialization includes llm_request_logs.provider", () => {
|
|
97
|
+
initializeDb();
|
|
98
|
+
|
|
99
|
+
const raw = new Database(dbPath);
|
|
100
|
+
const columns = getColumnInfo(raw);
|
|
101
|
+
|
|
102
|
+
expect(columns.some((column) => column.name === "provider")).toBe(true);
|
|
103
|
+
expect(columns.find((column) => column.name === "provider")?.notnull).toBe(
|
|
104
|
+
0,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
raw.close();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("migration upgrades the pre-provider schema without disturbing rows", () => {
|
|
111
|
+
const db = createTestDb();
|
|
112
|
+
const raw = getSqliteFrom(db);
|
|
113
|
+
|
|
114
|
+
bootstrapPreProviderLlmRequestLogs(raw);
|
|
115
|
+
raw.exec(/*sql*/ `
|
|
116
|
+
INSERT INTO llm_request_logs (
|
|
117
|
+
id,
|
|
118
|
+
conversation_id,
|
|
119
|
+
message_id,
|
|
120
|
+
request_payload,
|
|
121
|
+
response_payload,
|
|
122
|
+
created_at
|
|
123
|
+
) VALUES (
|
|
124
|
+
'log-upgrade',
|
|
125
|
+
'conv-1',
|
|
126
|
+
'msg-1',
|
|
127
|
+
'{}',
|
|
128
|
+
'{"ok":true}',
|
|
129
|
+
1000
|
|
130
|
+
)
|
|
131
|
+
`);
|
|
132
|
+
|
|
133
|
+
migrateLlmRequestLogProvider(db);
|
|
134
|
+
|
|
135
|
+
expect(getColumnInfo(raw).some((column) => column.name === "provider")).toBe(
|
|
136
|
+
true,
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const row = raw
|
|
140
|
+
.query(
|
|
141
|
+
`SELECT id, conversation_id, message_id, provider, request_payload, response_payload, created_at
|
|
142
|
+
FROM llm_request_logs
|
|
143
|
+
WHERE id = 'log-upgrade'`,
|
|
144
|
+
)
|
|
145
|
+
.get() as
|
|
146
|
+
| {
|
|
147
|
+
id: string;
|
|
148
|
+
conversation_id: string;
|
|
149
|
+
message_id: string | null;
|
|
150
|
+
provider: string | null;
|
|
151
|
+
request_payload: string;
|
|
152
|
+
response_payload: string;
|
|
153
|
+
created_at: number;
|
|
154
|
+
}
|
|
155
|
+
| null;
|
|
156
|
+
|
|
157
|
+
expect(row).toEqual({
|
|
158
|
+
id: "log-upgrade",
|
|
159
|
+
conversation_id: "conv-1",
|
|
160
|
+
message_id: "msg-1",
|
|
161
|
+
provider: null,
|
|
162
|
+
request_payload: "{}",
|
|
163
|
+
response_payload: '{"ok":true}',
|
|
164
|
+
created_at: 1000,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
raw.close();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("re-running the migration preserves populated provider values", () => {
|
|
171
|
+
const db = createTestDb();
|
|
172
|
+
const raw = getSqliteFrom(db);
|
|
173
|
+
|
|
174
|
+
bootstrapPreProviderLlmRequestLogs(raw);
|
|
175
|
+
raw.exec(/*sql*/ `
|
|
176
|
+
INSERT INTO llm_request_logs (
|
|
177
|
+
id,
|
|
178
|
+
conversation_id,
|
|
179
|
+
message_id,
|
|
180
|
+
request_payload,
|
|
181
|
+
response_payload,
|
|
182
|
+
created_at
|
|
183
|
+
) VALUES (
|
|
184
|
+
'log-rerun',
|
|
185
|
+
'conv-2',
|
|
186
|
+
'msg-2',
|
|
187
|
+
'{}',
|
|
188
|
+
'{"ok":true}',
|
|
189
|
+
2000
|
|
190
|
+
)
|
|
191
|
+
`);
|
|
192
|
+
|
|
193
|
+
migrateLlmRequestLogProvider(db);
|
|
194
|
+
raw.exec(/*sql*/ `
|
|
195
|
+
UPDATE llm_request_logs
|
|
196
|
+
SET provider = 'anthropic'
|
|
197
|
+
WHERE id = 'log-rerun'
|
|
198
|
+
`);
|
|
199
|
+
|
|
200
|
+
expect(() => migrateLlmRequestLogProvider(db)).not.toThrow();
|
|
201
|
+
|
|
202
|
+
const row = raw
|
|
203
|
+
.query(
|
|
204
|
+
`SELECT provider FROM llm_request_logs WHERE id = 'log-rerun'`,
|
|
205
|
+
)
|
|
206
|
+
.get() as { provider: string | null } | null;
|
|
207
|
+
|
|
208
|
+
expect(row).toEqual({
|
|
209
|
+
provider: "anthropic",
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
raw.close();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* (specific ID → most recent assistant → any message → empty conversation).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
8
|
+
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
12
|
|
|
13
|
+
import JSZip from "jszip";
|
|
14
|
+
|
|
13
15
|
const testDir = mkdtempSync(join(tmpdir(), "diagnostics-export-test-"));
|
|
14
16
|
|
|
15
17
|
mock.module("../util/platform.js", () => ({
|
|
@@ -90,6 +92,27 @@ function seedMessage(
|
|
|
90
92
|
);
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
function seedLlmRequestLog(
|
|
96
|
+
id: string,
|
|
97
|
+
conversationId: string,
|
|
98
|
+
provider: string | null,
|
|
99
|
+
requestPayload: unknown,
|
|
100
|
+
responsePayload: unknown,
|
|
101
|
+
createdAt: number,
|
|
102
|
+
): void {
|
|
103
|
+
db().run(
|
|
104
|
+
"INSERT INTO llm_request_logs (id, conversation_id, provider, request_payload, response_payload, created_at) VALUES (?, ?, ?, ?, ?, ?)",
|
|
105
|
+
[
|
|
106
|
+
id,
|
|
107
|
+
conversationId,
|
|
108
|
+
provider,
|
|
109
|
+
JSON.stringify(requestPayload),
|
|
110
|
+
JSON.stringify(responsePayload),
|
|
111
|
+
createdAt,
|
|
112
|
+
],
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
function seedConversationKey(
|
|
94
117
|
conversationKey: string,
|
|
95
118
|
conversationId: string,
|
|
@@ -102,6 +125,7 @@ function seedConversationKey(
|
|
|
102
125
|
|
|
103
126
|
function cleanDb(): void {
|
|
104
127
|
db().run("DELETE FROM messages");
|
|
128
|
+
db().run("DELETE FROM llm_request_logs");
|
|
105
129
|
db().run("DELETE FROM conversation_keys");
|
|
106
130
|
db().run("DELETE FROM conversations");
|
|
107
131
|
}
|
|
@@ -216,4 +240,49 @@ describe("diagnostics export", () => {
|
|
|
216
240
|
const json = (await res.json()) as { success: boolean };
|
|
217
241
|
expect(json.success).toBe(true);
|
|
218
242
|
});
|
|
243
|
+
|
|
244
|
+
test("preserves llm request provider identity in the exported JSONL", async () => {
|
|
245
|
+
const convId = "conv-7";
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
|
|
248
|
+
seedConversation(convId);
|
|
249
|
+
seedMessage("msg-user-7", convId, "user", "hello", now - 1000);
|
|
250
|
+
seedMessage("msg-assistant-7", convId, "assistant", "world", now);
|
|
251
|
+
seedLlmRequestLog(
|
|
252
|
+
"log-7",
|
|
253
|
+
convId,
|
|
254
|
+
"openrouter",
|
|
255
|
+
{ model: "openai/gpt-4.1-mini", input: "hello" },
|
|
256
|
+
{ output: "world" },
|
|
257
|
+
now,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
const res = await callExport({ conversationId: convId });
|
|
261
|
+
expect(res.status).toBe(200);
|
|
262
|
+
const json = (await res.json()) as { success: boolean; filePath: string };
|
|
263
|
+
expect(json.success).toBe(true);
|
|
264
|
+
|
|
265
|
+
const zip = await JSZip.loadAsync(readFileSync(json.filePath));
|
|
266
|
+
const llmRequests = zip.file("llm_requests.jsonl");
|
|
267
|
+
expect(llmRequests).not.toBeNull();
|
|
268
|
+
|
|
269
|
+
const lines = (await llmRequests!.async("string")).trim().split("\n");
|
|
270
|
+
expect(lines).toHaveLength(1);
|
|
271
|
+
|
|
272
|
+
const row = JSON.parse(lines[0]) as {
|
|
273
|
+
id: string;
|
|
274
|
+
conversationId: string;
|
|
275
|
+
provider?: string | null;
|
|
276
|
+
request: unknown;
|
|
277
|
+
response: unknown;
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
expect(row).toMatchObject({
|
|
281
|
+
id: "log-7",
|
|
282
|
+
conversationId: convId,
|
|
283
|
+
provider: "openrouter",
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
rmSync(json.filePath, { force: true });
|
|
287
|
+
});
|
|
219
288
|
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
|
|
5
|
+
|
|
6
|
+
let tempDir: string;
|
|
7
|
+
|
|
8
|
+
mock.module("../util/platform.js", () => ({
|
|
9
|
+
getWorkspacePromptPath: mock((file: string) => join(tempDir, file)),
|
|
10
|
+
getWorkspaceDir: () => tempDir,
|
|
11
|
+
getRootDir: () => tempDir,
|
|
12
|
+
getDataDir: () => join(tempDir, "data"),
|
|
13
|
+
getPlatformName: () => "darwin",
|
|
14
|
+
isMacOS: () => false,
|
|
15
|
+
isLinux: () => false,
|
|
16
|
+
isWindows: () => false,
|
|
17
|
+
ensureDataDir: () => {},
|
|
18
|
+
getDbPath: () => "",
|
|
19
|
+
getLogPath: () => "",
|
|
20
|
+
getHistoryPath: () => "",
|
|
21
|
+
getHooksDir: () => "",
|
|
22
|
+
getSessionTokenPath: () => "",
|
|
23
|
+
getPlatformTokenPath: () => "",
|
|
24
|
+
getPidPath: () => "",
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const { isWakeUpGreeting, getCannedFirstGreeting, CANNED_FIRST_GREETING } =
|
|
28
|
+
await import("../daemon/first-greeting.js");
|
|
29
|
+
|
|
30
|
+
describe("first-greeting", () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
tempDir = join(tmpdir(), `first-greeting-test-${Date.now()}`);
|
|
33
|
+
mkdirSync(tempDir, { recursive: true });
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("isWakeUpGreeting", () => {
|
|
41
|
+
it("returns true for wake-up greeting with 0 messages and BOOTSTRAP.md present", () => {
|
|
42
|
+
writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
|
|
43
|
+
expect(isWakeUpGreeting("Wake up, my friend.", 0)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("returns true for case variations", () => {
|
|
47
|
+
writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
|
|
48
|
+
expect(isWakeUpGreeting("wake up, my friend.", 0)).toBe(true);
|
|
49
|
+
expect(isWakeUpGreeting("WAKE UP, MY FRIEND.", 0)).toBe(true);
|
|
50
|
+
expect(isWakeUpGreeting("Wake Up, My Friend.", 0)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns false when content doesn't match wake-up greeting", () => {
|
|
54
|
+
writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
|
|
55
|
+
expect(isWakeUpGreeting("Hello", 0)).toBe(false);
|
|
56
|
+
expect(isWakeUpGreeting("Hey there", 0)).toBe(false);
|
|
57
|
+
expect(isWakeUpGreeting("Wake up", 0)).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("returns false when conversationMessageCount > 0", () => {
|
|
61
|
+
writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
|
|
62
|
+
expect(isWakeUpGreeting("Wake up, my friend.", 1)).toBe(false);
|
|
63
|
+
expect(isWakeUpGreeting("Wake up, my friend.", 5)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("returns false when BOOTSTRAP.md doesn't exist", () => {
|
|
67
|
+
expect(existsSync(join(tempDir, "BOOTSTRAP.md"))).toBe(false);
|
|
68
|
+
expect(isWakeUpGreeting("Wake up, my friend.", 0)).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("getCannedFirstGreeting", () => {
|
|
73
|
+
it("returns the expected greeting string", () => {
|
|
74
|
+
const greeting = getCannedFirstGreeting();
|
|
75
|
+
expect(greeting).toBe(CANNED_FIRST_GREETING);
|
|
76
|
+
expect(greeting).toContain("brand new");
|
|
77
|
+
expect(greeting).toContain("no name, no memories");
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -24,6 +24,7 @@ const ALLOWLIST = new Set([
|
|
|
24
24
|
// --- Intentional local daemon-control paths ---
|
|
25
25
|
"assistant/src/cli/commands/conversations.ts", // CLI wipe talks to runtime directly
|
|
26
26
|
"clients/shared/Network/DaemonClient.swift",
|
|
27
|
+
"clients/shared/App/Auth/PlatformOAuthService.swift", // comment explaining runtimeUrl vs platformUrl
|
|
27
28
|
"clients/macos/vellum-assistant/App/AppDelegate.swift",
|
|
28
29
|
"clients/macos/vellum-assistant/Features/Settings/SettingsConnectTab.swift",
|
|
29
30
|
".claude/skills/update/SKILL.md", // daemon health check script
|
|
@@ -268,13 +268,9 @@ describe("handleConfirmationResponse canonical status sync", () => {
|
|
|
268
268
|
undefined,
|
|
269
269
|
{ source: "button" },
|
|
270
270
|
]);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
{
|
|
275
|
-
status: "approved",
|
|
276
|
-
},
|
|
277
|
-
);
|
|
271
|
+
// Canonical status sync is now handled inside Conversation.handleConfirmationResponse,
|
|
272
|
+
// which this test mocks out — so the handler itself no longer calls resolveCanonicalGuardianRequest.
|
|
273
|
+
expect(resolveCanonicalGuardianRequestMock).not.toHaveBeenCalled();
|
|
278
274
|
expect(resolveMock).toHaveBeenCalledWith("req-confirm-allow");
|
|
279
275
|
});
|
|
280
276
|
});
|