@vellumai/assistant 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +68 -15
- package/Dockerfile +2 -2
- package/bun.lock +6 -2
- package/docker-entrypoint.sh +32 -1
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/memory.md +21 -24
- package/openapi.yaml +538 -3
- package/package.json +5 -1
- package/src/__tests__/anthropic-provider.test.ts +160 -95
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +47 -1
- package/src/__tests__/app-source-watcher.test.ts +159 -0
- package/src/__tests__/checker.test.ts +38 -6
- package/src/__tests__/config-schema.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -6
- package/src/__tests__/conversation-agent-loop.test.ts +4 -51
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +653 -832
- package/src/__tests__/conversation-runtime-workspace.test.ts +1 -93
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +17 -4
- package/src/__tests__/conversation-wipe.test.ts +2 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -12
- package/src/__tests__/conversation-workspace-injection.test.ts +25 -26
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/copy-composer-tc-templates.test.ts +335 -0
- package/src/__tests__/date-context.test.ts +76 -210
- package/src/__tests__/db-schedule-syntax-migration.test.ts +16 -1
- package/src/__tests__/file-list-tool.test.ts +219 -0
- package/src/__tests__/first-greeting.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +180 -3
- package/src/__tests__/identity-routes.test.ts +328 -0
- package/src/__tests__/injection-block.test.ts +24 -0
- package/src/__tests__/install-skill-routing.test.ts +7 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +15 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +300 -0
- package/src/__tests__/llm-context-normalization.test.ts +18 -18
- package/src/__tests__/llm-context-route-provider.test.ts +101 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +162 -0
- package/src/__tests__/log-export-workspace.test.ts +72 -105
- package/src/__tests__/mcp-abort-signal.test.ts +5 -0
- package/src/__tests__/mcp-client-auth.test.ts +5 -0
- package/src/__tests__/memory-recall-log-store.test.ts +132 -0
- package/src/__tests__/migration-export-streaming.test.ts +304 -0
- package/src/__tests__/migration-import-commit-http.test.ts +11 -10
- package/src/__tests__/mock-fetch.ts +87 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +282 -0
- package/src/__tests__/onboarding-template-contract.test.ts +62 -14
- package/src/__tests__/parser.test.ts +32 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +452 -0
- package/src/__tests__/permission-controls-v2-flag.test.ts +55 -0
- package/src/__tests__/permission-mode-sse.test.ts +418 -0
- package/src/__tests__/permission-mode-store.test.ts +277 -0
- package/src/__tests__/permission-mode.test.ts +101 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +359 -0
- package/src/__tests__/profiler-routes.test.ts +502 -0
- package/src/__tests__/profiler-run-store.test.ts +441 -0
- package/src/__tests__/proxy-approval-callback.test.ts +4 -75
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/sandbox-host-parity.test.ts +5 -4
- package/src/__tests__/scheduler-reuse-conversation.test.ts +368 -0
- package/src/__tests__/scrub-corrupted-image-attachments.test.ts +278 -0
- package/src/__tests__/search-skills-unified.test.ts +4 -3
- package/src/__tests__/send-endpoint-busy.test.ts +42 -3
- package/src/__tests__/set-permission-mode.test.ts +274 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +12 -0
- package/src/__tests__/skill-memory.test.ts +2 -783
- package/src/__tests__/strip-memory-injections.test.ts +187 -0
- package/src/__tests__/subagent-detail.test.ts +84 -0
- package/src/__tests__/subagent-disposal.test.ts +308 -0
- package/src/__tests__/subagent-manager-notify.test.ts +19 -10
- package/src/__tests__/subagent-notify-parent.test.ts +390 -0
- package/src/__tests__/subagent-role-registry.test.ts +108 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +71 -0
- package/src/__tests__/subagent-tools.test.ts +464 -4
- package/src/__tests__/system-prompt-ask-mode.test.ts +139 -0
- package/src/__tests__/task-memory-cleanup.test.ts +12 -12
- package/src/__tests__/terminal-tools.test.ts +17 -27
- package/src/__tests__/test-preload.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +4 -26
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/top-level-renderer.test.ts +10 -13
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +116 -2
- package/src/__tests__/workspace-migration-028-recover-conversations-from-disk-view.test.ts +387 -0
- package/src/agent/loop.ts +6 -0
- package/src/approvals/guardian-request-resolvers.ts +24 -0
- package/src/avatar/traits-png-sync.ts +3 -3
- package/src/cli/__tests__/run-assistant-command.ts +29 -0
- package/src/cli/commands/__tests__/email-download.test.ts +245 -0
- package/src/cli/commands/__tests__/email-list.test.ts +192 -0
- package/src/cli/commands/__tests__/email-register.test.ts +186 -0
- package/src/cli/commands/__tests__/email-send.test.ts +291 -0
- package/src/cli/commands/__tests__/email-status.test.ts +181 -0
- package/src/cli/commands/__tests__/email-unregister.test.ts +139 -0
- package/src/cli/commands/__tests__/routes.test.ts +562 -0
- package/src/cli/commands/conversations.ts +1 -8
- package/src/cli/commands/email.ts +584 -835
- package/src/cli/commands/memory.ts +1 -34
- package/src/cli/commands/notifications.ts +7 -2
- package/src/cli/commands/oauth/connect.ts +14 -5
- package/src/cli/commands/routes.ts +396 -0
- package/src/cli/commands/skills.ts +130 -20
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +1 -120
- package/src/config/bundled-skills/app-builder/SKILL.md +4 -1
- package/src/config/bundled-skills/gmail/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/SKILL.md +7 -0
- package/src/config/bundled-skills/schedule/SKILL.md +22 -2
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/settings/tools/avatar-get.ts +3 -13
- package/src/config/bundled-skills/settings/tools/avatar-remove.ts +2 -4
- package/src/config/bundled-skills/settings/tools/avatar-update.ts +5 -2
- package/src/config/bundled-skills/slack/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/SKILL.md +43 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +29 -4
- package/src/config/env-registry.ts +63 -0
- package/src/config/feature-flag-registry.json +17 -1
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/filing.ts +51 -0
- package/src/config/schemas/heartbeat.ts +15 -12
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/security.ts +14 -0
- package/src/daemon/app-source-watcher.ts +93 -0
- package/src/daemon/config-watcher.ts +79 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +20 -0
- package/src/daemon/conversation-agent-loop.ts +158 -65
- package/src/daemon/conversation-history.ts +4 -19
- package/src/daemon/conversation-lifecycle.ts +8 -14
- package/src/daemon/conversation-process.ts +13 -7
- package/src/daemon/conversation-runtime-assembly.ts +300 -306
- package/src/daemon/conversation-tool-setup.ts +44 -14
- package/src/daemon/conversation-workspace.ts +1 -2
- package/src/daemon/conversation.ts +18 -0
- package/src/daemon/date-context.ts +26 -53
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/conversations.ts +4 -7
- package/src/daemon/handlers/shared.test.ts +143 -0
- package/src/daemon/handlers/shared.ts +63 -5
- package/src/daemon/handlers/skills.ts +11 -18
- package/src/daemon/lifecycle.ts +199 -157
- package/src/daemon/message-types/conversations.ts +25 -6
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/settings.ts +6 -0
- package/src/daemon/profiler-run-store.ts +557 -0
- package/src/daemon/server.ts +89 -9
- package/src/daemon/shutdown-handlers.ts +5 -0
- package/src/daemon/tool-side-effects.ts +23 -3
- package/src/export/transcript-formatter.ts +148 -0
- package/src/filing/filing-service.ts +228 -0
- package/src/heartbeat/heartbeat-service.ts +96 -7
- package/src/mcp/client.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +149 -27
- package/src/memory/admin.ts +33 -32
- package/src/memory/app-store.ts +69 -0
- package/src/memory/conversation-bootstrap.ts +1 -1
- package/src/memory/conversation-crud.ts +136 -107
- package/src/memory/conversation-group-migration.ts +1 -1
- package/src/memory/conversation-queries.ts +58 -12
- package/src/memory/conversation-title-service.ts +1 -0
- package/src/memory/db-init.ts +182 -376
- package/src/memory/graph/bootstrap.ts +75 -66
- package/src/memory/graph/capability-seed.ts +167 -15
- package/src/memory/graph/consolidation.ts +38 -4
- package/src/memory/graph/conversation-graph-memory.ts +133 -104
- package/src/memory/graph/extraction-job.ts +9 -4
- package/src/memory/graph/extraction.ts +66 -23
- package/src/memory/graph/graph-memory-state-store.ts +37 -0
- package/src/memory/graph/graph-search.ts +29 -15
- package/src/memory/graph/injection.ts +38 -8
- package/src/memory/graph/inspect.ts +12 -3
- package/src/memory/graph/retriever.ts +365 -262
- package/src/memory/graph/store.test.ts +48 -0
- package/src/memory/graph/store.ts +150 -11
- package/src/memory/graph/tool-handlers.ts +84 -209
- package/src/memory/graph/tools.ts +8 -52
- package/src/memory/graph/types.ts +24 -0
- package/src/memory/job-handlers/cleanup.ts +44 -1
- package/src/memory/jobs-store.ts +70 -60
- package/src/memory/jobs-worker.ts +44 -28
- package/src/memory/llm-request-log-store.ts +96 -12
- package/src/memory/memory-recall-log-store.ts +49 -5
- package/src/memory/migrations/203-drop-memory-items-tables.ts +33 -1
- package/src/memory/migrations/206-memory-graph-node-edits.ts +19 -0
- package/src/memory/migrations/206-scrub-corrupted-image-attachments.ts +131 -0
- package/src/memory/migrations/207-conversation-graph-memory-state.ts +20 -0
- package/src/memory/migrations/208-conversations-last-message-at.ts +35 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +85 -0
- package/src/memory/migrations/210-schedule-reuse-conversation.ts +13 -0
- package/src/memory/migrations/211-memory-recall-logs-query-context.ts +21 -0
- package/src/memory/migrations/212-llm-request-logs-created-at-index.ts +19 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +14 -0
- package/src/memory/schema/infrastructure.ts +8 -1
- package/src/memory/schema/memory-core.ts +0 -51
- package/src/memory/schema/memory-graph.ts +15 -0
- package/src/memory/task-memory-cleanup.ts +30 -11
- package/src/notifications/copy-composer.ts +86 -0
- package/src/notifications/decision-engine.ts +35 -0
- package/src/permissions/checker.ts +12 -1
- package/src/permissions/permission-mode-store.ts +180 -0
- package/src/permissions/permission-mode.ts +31 -0
- package/src/permissions/workspace-policy.ts +9 -0
- package/src/prompts/system-prompt.ts +59 -7
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +100 -0
- package/src/prompts/templates/BOOTSTRAP.md +70 -165
- package/src/prompts/templates/HEARTBEAT.md +3 -1
- package/src/prompts/templates/SOUL.md +25 -4
- package/src/prompts/templates/UPDATES.md +8 -0
- package/src/providers/anthropic/client.ts +107 -219
- package/src/runtime/auth/route-policy.ts +23 -0
- package/src/runtime/http-server.ts +32 -2
- package/src/runtime/http-types.ts +12 -1
- package/src/runtime/migrations/vbundle-builder.ts +389 -3
- package/src/runtime/migrations/vbundle-importer.ts +8 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +378 -0
- package/src/runtime/routes/app-management-routes.ts +1 -11
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +26 -0
- package/src/runtime/routes/archive-utils.ts +29 -0
- package/src/runtime/routes/avatar-routes.ts +2 -9
- package/src/runtime/routes/btw-routes.ts +14 -1
- package/src/runtime/routes/conversation-analysis-routes.ts +173 -0
- package/src/runtime/routes/conversation-management-routes.ts +1 -14
- package/src/runtime/routes/conversation-query-routes.ts +49 -3
- package/src/runtime/routes/conversation-routes.ts +264 -44
- package/src/runtime/routes/heartbeat-routes.ts +4 -10
- package/src/runtime/routes/identity-routes.ts +53 -18
- package/src/runtime/routes/llm-context-normalization.ts +14 -10
- package/src/runtime/routes/log-export-routes.ts +23 -275
- package/src/runtime/routes/memory-item-routes.test.ts +168 -233
- package/src/runtime/routes/migration-routes.ts +18 -7
- package/src/runtime/routes/profiler-routes.ts +350 -0
- package/src/runtime/routes/schedule-routes.ts +27 -12
- package/src/runtime/routes/settings-routes.ts +95 -8
- package/src/runtime/routes/subagents-routes.ts +28 -7
- package/src/runtime/routes/user-route-dispatcher.ts +223 -0
- package/src/runtime/routes/user-routes.ts +41 -0
- package/src/runtime/routes/workspace-routes.ts +0 -1
- package/src/schedule/schedule-store.ts +30 -0
- package/src/schedule/scheduler.ts +45 -18
- package/src/skills/catalog-install.ts +10 -2
- package/src/skills/managed-store.ts +2 -2
- package/src/skills/skill-memory.ts +1 -293
- package/src/subagent/index.ts +13 -3
- package/src/subagent/manager.ts +308 -29
- package/src/subagent/types.ts +68 -0
- package/src/tasks/task-runner.ts +4 -4
- package/src/tools/apps/executors.ts +29 -4
- package/src/tools/filesystem/list.ts +93 -0
- package/src/tools/permission-checker.ts +78 -0
- package/src/tools/registry.ts +4 -0
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +1 -0
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/shared/filesystem/errors.ts +5 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +90 -2
- package/src/tools/shared/filesystem/types.ts +17 -0
- package/src/tools/shared/shell-output.ts +31 -2
- package/src/tools/subagent/abort.ts +12 -2
- package/src/tools/subagent/message.ts +9 -2
- package/src/tools/subagent/notify-parent.ts +79 -0
- package/src/tools/subagent/read.ts +29 -8
- package/src/tools/subagent/resolve.ts +21 -0
- package/src/tools/subagent/spawn.ts +2 -0
- package/src/tools/subagent/status.ts +11 -1
- package/src/tools/system/avatar-generator.ts +3 -3
- package/src/tools/system/register.ts +23 -0
- package/src/tools/system/set-permission-mode.ts +103 -0
- package/src/tools/terminal/parser.ts +30 -5
- package/src/tools/terminal/safe-env.ts +16 -1
- package/src/tools/tool-manifest.ts +6 -0
- package/src/tools/types.ts +2 -0
- package/src/util/logger.ts +1 -1
- package/src/util/platform.ts +50 -17
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +2 -2
- package/src/workspace/migrations/028-recover-conversations-from-disk-view.ts +270 -0
- package/src/workspace/migrations/029-seed-pkb.ts +84 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +5 -9
- package/src/__tests__/cli-memory.test.ts +0 -377
- package/src/__tests__/clipboard.test.ts +0 -88
- package/src/cli/cli-memory.ts +0 -179
- package/src/util/clipboard.ts +0 -34
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* - audit-data.json with tool invocation records
|
|
6
6
|
* - daemon-logs/ with log file contents
|
|
7
7
|
* - config-snapshot.json with sanitized config
|
|
8
|
-
* - workspace/ with text files, SQL dumps for .db files, and proper
|
|
9
|
-
* filtering (excluded directories, binary files, symlinks).
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
10
|
import { spawnSync } from "node:child_process";
|
|
@@ -16,7 +14,6 @@ import {
|
|
|
16
14
|
readdirSync,
|
|
17
15
|
readFileSync,
|
|
18
16
|
rmSync,
|
|
19
|
-
symlinkSync,
|
|
20
17
|
writeFileSync,
|
|
21
18
|
} from "node:fs";
|
|
22
19
|
import { tmpdir } from "node:os";
|
|
@@ -24,8 +21,7 @@ import { join } from "node:path";
|
|
|
24
21
|
import { describe, expect, mock, test } from "bun:test";
|
|
25
22
|
|
|
26
23
|
// Set up temp directories before mocking
|
|
27
|
-
const
|
|
28
|
-
const testWorkspaceDir = testDir;
|
|
24
|
+
const testWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
29
25
|
mkdirSync(testWorkspaceDir, { recursive: true });
|
|
30
26
|
|
|
31
27
|
mock.module("../util/logger.js", () => ({
|
|
@@ -52,11 +48,13 @@ initializeDb();
|
|
|
52
48
|
const routes = logExportRouteDefinitions();
|
|
53
49
|
const exportRoute = routes.find((r) => r.endpoint === "export")!;
|
|
54
50
|
|
|
55
|
-
async function callExport(
|
|
51
|
+
async function callExport(
|
|
52
|
+
body: Record<string, unknown> = {},
|
|
53
|
+
): Promise<Response> {
|
|
56
54
|
const req = new Request("http://localhost/v1/export", {
|
|
57
55
|
method: "POST",
|
|
58
56
|
headers: { "Content-Type": "application/json" },
|
|
59
|
-
body: JSON.stringify(
|
|
57
|
+
body: JSON.stringify(body),
|
|
60
58
|
});
|
|
61
59
|
const url = new URL(req.url);
|
|
62
60
|
return exportRoute.handler({
|
|
@@ -85,73 +83,37 @@ async function extractArchive(res: Response): Promise<string> {
|
|
|
85
83
|
return extractDir;
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
/** Recursively lists all files under a directory as relative paths. */
|
|
89
|
-
function listFiles(dir: string, base = dir): string[] {
|
|
90
|
-
const result: string[] = [];
|
|
91
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
92
|
-
const full = join(dir, entry.name);
|
|
93
|
-
if (entry.isDirectory()) {
|
|
94
|
-
result.push(...listFiles(full, base));
|
|
95
|
-
} else {
|
|
96
|
-
result.push(full.slice(base.length + 1));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
86
|
// ---------------------------------------------------------------------------
|
|
103
|
-
// Seed
|
|
87
|
+
// Seed test data
|
|
104
88
|
// ---------------------------------------------------------------------------
|
|
105
89
|
|
|
106
|
-
//
|
|
107
|
-
writeFileSync(join(testWorkspaceDir, "IDENTITY.md"), "# My Identity\nHello");
|
|
108
|
-
mkdirSync(join(testWorkspaceDir, "notes"), { recursive: true });
|
|
109
|
-
writeFileSync(join(testWorkspaceDir, "notes", "daily.txt"), "Some daily notes");
|
|
110
|
-
|
|
111
|
-
// SQLite DB file — should be dumped as .sql
|
|
112
|
-
mkdirSync(join(testWorkspaceDir, "data", "db"), { recursive: true });
|
|
113
|
-
// Create a real sqlite db with a table
|
|
114
|
-
import { Database } from "bun:sqlite";
|
|
115
|
-
const wsDbPath = join(testWorkspaceDir, "data", "db", "assistant.db");
|
|
116
|
-
const wsDb = new Database(wsDbPath);
|
|
117
|
-
wsDb.run("CREATE TABLE test_table (id INTEGER PRIMARY KEY, name TEXT)");
|
|
118
|
-
wsDb.run("INSERT INTO test_table (name) VALUES ('hello')");
|
|
119
|
-
wsDb.close();
|
|
120
|
-
|
|
121
|
-
// Excluded directory: embedding-models/
|
|
122
|
-
mkdirSync(join(testWorkspaceDir, "embedding-models"), { recursive: true });
|
|
90
|
+
// config.json at workspace root — needed for config-snapshot test
|
|
123
91
|
writeFileSync(
|
|
124
|
-
join(testWorkspaceDir, "
|
|
125
|
-
|
|
92
|
+
join(testWorkspaceDir, "config.json"),
|
|
93
|
+
JSON.stringify({ provider: "anthropic" }),
|
|
126
94
|
);
|
|
127
95
|
|
|
128
|
-
//
|
|
129
|
-
|
|
96
|
+
// Daemon log files — used for date filtering tests
|
|
97
|
+
const logsDir = join(testWorkspaceDir, "data", "logs");
|
|
98
|
+
mkdirSync(logsDir, { recursive: true });
|
|
130
99
|
writeFileSync(
|
|
131
|
-
join(
|
|
132
|
-
"
|
|
100
|
+
join(logsDir, "assistant-2025-01-10.log"),
|
|
101
|
+
"log entry from Jan 10\n",
|
|
133
102
|
);
|
|
134
|
-
|
|
135
|
-
// Binary file — should be skipped
|
|
136
103
|
writeFileSync(
|
|
137
|
-
join(
|
|
138
|
-
|
|
104
|
+
join(logsDir, "assistant-2025-01-15.log"),
|
|
105
|
+
"log entry from Jan 15\n",
|
|
139
106
|
);
|
|
140
|
-
|
|
141
|
-
// config.json at workspace root — should be skipped (already in configSnapshot)
|
|
142
107
|
writeFileSync(
|
|
143
|
-
join(
|
|
144
|
-
|
|
108
|
+
join(logsDir, "assistant-2025-01-20.log"),
|
|
109
|
+
"log entry from Jan 20\n",
|
|
145
110
|
);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
} catch {
|
|
153
|
-
// Symlink creation may fail on some platforms; tests will still pass
|
|
154
|
-
}
|
|
111
|
+
writeFileSync(
|
|
112
|
+
join(logsDir, "assistant-2025-01-25.log"),
|
|
113
|
+
"log entry from Jan 25\n",
|
|
114
|
+
);
|
|
115
|
+
// Non-dated log file — should always be included regardless of time filter
|
|
116
|
+
writeFileSync(join(logsDir, "vellum.log"), "non-dated log content\n");
|
|
155
117
|
|
|
156
118
|
// ---------------------------------------------------------------------------
|
|
157
119
|
// Tests
|
|
@@ -185,90 +147,95 @@ describe("POST /v1/export — tar.gz archive", () => {
|
|
|
185
147
|
}
|
|
186
148
|
});
|
|
187
149
|
|
|
188
|
-
test("archive contains
|
|
150
|
+
test("archive contains config-snapshot.json when config exists", async () => {
|
|
189
151
|
const res = await callExport();
|
|
190
152
|
const dir = await extractArchive(res);
|
|
191
153
|
try {
|
|
192
|
-
const
|
|
193
|
-
join(dir, "
|
|
194
|
-
"utf-8",
|
|
195
|
-
);
|
|
196
|
-
expect(identity).toBe("# My Identity\nHello");
|
|
197
|
-
|
|
198
|
-
const daily = readFileSync(
|
|
199
|
-
join(dir, "workspace", "notes", "daily.txt"),
|
|
154
|
+
const configContent = readFileSync(
|
|
155
|
+
join(dir, "config-snapshot.json"),
|
|
200
156
|
"utf-8",
|
|
201
157
|
);
|
|
202
|
-
|
|
158
|
+
const parsed = JSON.parse(configContent);
|
|
159
|
+
expect(parsed.provider).toBe("anthropic");
|
|
203
160
|
} finally {
|
|
204
161
|
rmSync(dir, { recursive: true, force: true });
|
|
205
162
|
}
|
|
206
163
|
});
|
|
164
|
+
});
|
|
207
165
|
|
|
208
|
-
|
|
209
|
-
|
|
166
|
+
describe("POST /v1/export — daemon log date filtering", () => {
|
|
167
|
+
test("excludes log files before startTime", async () => {
|
|
168
|
+
// startTime = Jan 14 — should exclude assistant-2025-01-10.log
|
|
169
|
+
const startTime = new Date("2025-01-14T00:00:00.000Z").getTime();
|
|
170
|
+
const res = await callExport({ startTime });
|
|
210
171
|
const dir = await extractArchive(res);
|
|
211
172
|
try {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
);
|
|
216
|
-
expect(
|
|
217
|
-
expect(sqlContent).toContain("test_table");
|
|
173
|
+
const logFiles = readdirSync(join(dir, "daemon-logs"));
|
|
174
|
+
expect(logFiles).not.toContain("assistant-2025-01-10.log");
|
|
175
|
+
expect(logFiles).toContain("assistant-2025-01-15.log");
|
|
176
|
+
expect(logFiles).toContain("assistant-2025-01-20.log");
|
|
177
|
+
expect(logFiles).toContain("assistant-2025-01-25.log");
|
|
218
178
|
} finally {
|
|
219
179
|
rmSync(dir, { recursive: true, force: true });
|
|
220
180
|
}
|
|
221
181
|
});
|
|
222
182
|
|
|
223
|
-
test("
|
|
224
|
-
|
|
183
|
+
test("excludes log files after endTime", async () => {
|
|
184
|
+
// endTime = Jan 22 — should exclude assistant-2025-01-25.log
|
|
185
|
+
const endTime = new Date("2025-01-22T00:00:00.000Z").getTime();
|
|
186
|
+
const res = await callExport({ endTime });
|
|
225
187
|
const dir = await extractArchive(res);
|
|
226
188
|
try {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
expect(embeddingFiles).toHaveLength(0);
|
|
233
|
-
expect(qdrantFiles).toHaveLength(0);
|
|
189
|
+
const logFiles = readdirSync(join(dir, "daemon-logs"));
|
|
190
|
+
expect(logFiles).toContain("assistant-2025-01-10.log");
|
|
191
|
+
expect(logFiles).toContain("assistant-2025-01-15.log");
|
|
192
|
+
expect(logFiles).toContain("assistant-2025-01-20.log");
|
|
193
|
+
expect(logFiles).not.toContain("assistant-2025-01-25.log");
|
|
234
194
|
} finally {
|
|
235
195
|
rmSync(dir, { recursive: true, force: true });
|
|
236
196
|
}
|
|
237
197
|
});
|
|
238
198
|
|
|
239
|
-
test("
|
|
240
|
-
|
|
199
|
+
test("filters log files by both startTime and endTime", async () => {
|
|
200
|
+
// startTime = Jan 14, endTime = Jan 22 — should only include Jan 15 and Jan 20
|
|
201
|
+
const startTime = new Date("2025-01-14T00:00:00.000Z").getTime();
|
|
202
|
+
const endTime = new Date("2025-01-22T00:00:00.000Z").getTime();
|
|
203
|
+
const res = await callExport({ startTime, endTime });
|
|
241
204
|
const dir = await extractArchive(res);
|
|
242
205
|
try {
|
|
243
|
-
const
|
|
244
|
-
expect(
|
|
245
|
-
expect(
|
|
206
|
+
const logFiles = readdirSync(join(dir, "daemon-logs"));
|
|
207
|
+
expect(logFiles).not.toContain("assistant-2025-01-10.log");
|
|
208
|
+
expect(logFiles).toContain("assistant-2025-01-15.log");
|
|
209
|
+
expect(logFiles).toContain("assistant-2025-01-20.log");
|
|
210
|
+
expect(logFiles).not.toContain("assistant-2025-01-25.log");
|
|
246
211
|
} finally {
|
|
247
212
|
rmSync(dir, { recursive: true, force: true });
|
|
248
213
|
}
|
|
249
214
|
});
|
|
250
215
|
|
|
251
|
-
test("
|
|
252
|
-
const
|
|
216
|
+
test("always includes non-dated log files regardless of time filter", async () => {
|
|
217
|
+
const startTime = new Date("2025-01-14T00:00:00.000Z").getTime();
|
|
218
|
+
const endTime = new Date("2025-01-22T00:00:00.000Z").getTime();
|
|
219
|
+
const res = await callExport({ startTime, endTime });
|
|
253
220
|
const dir = await extractArchive(res);
|
|
254
221
|
try {
|
|
255
|
-
const
|
|
256
|
-
expect(
|
|
222
|
+
const logFiles = readdirSync(join(dir, "daemon-logs"));
|
|
223
|
+
expect(logFiles).toContain("vellum.log");
|
|
257
224
|
} finally {
|
|
258
225
|
rmSync(dir, { recursive: true, force: true });
|
|
259
226
|
}
|
|
260
227
|
});
|
|
261
228
|
|
|
262
|
-
test("
|
|
229
|
+
test("includes all log files when no time filter is specified", async () => {
|
|
263
230
|
const res = await callExport();
|
|
264
231
|
const dir = await extractArchive(res);
|
|
265
232
|
try {
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
expect(
|
|
233
|
+
const logFiles = readdirSync(join(dir, "daemon-logs"));
|
|
234
|
+
expect(logFiles).toContain("assistant-2025-01-10.log");
|
|
235
|
+
expect(logFiles).toContain("assistant-2025-01-15.log");
|
|
236
|
+
expect(logFiles).toContain("assistant-2025-01-20.log");
|
|
237
|
+
expect(logFiles).toContain("assistant-2025-01-25.log");
|
|
238
|
+
expect(logFiles).toContain("vellum.log");
|
|
272
239
|
} finally {
|
|
273
240
|
rmSync(dir, { recursive: true, force: true });
|
|
274
241
|
}
|
|
@@ -7,6 +7,11 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
7
7
|
deleteSecureKeyAsync: jest.fn().mockResolvedValue("deleted"),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
+
// Mock platform-callback-registration (imported by client.ts)
|
|
11
|
+
mock.module("../inbound/platform-callback-registration.js", () => ({
|
|
12
|
+
shouldUsePlatformCallbacks: jest.fn().mockReturnValue(false),
|
|
13
|
+
}));
|
|
14
|
+
|
|
10
15
|
const { McpClient } = await import("../mcp/client.js");
|
|
11
16
|
const { McpServerManager } = await import("../mcp/manager.js");
|
|
12
17
|
const { createMcpTool } = await import("../tools/mcp/mcp-tool-factory.js");
|
|
@@ -7,6 +7,11 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
7
7
|
deleteSecureKeyAsync: jest.fn().mockResolvedValue("deleted"),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
+
// Mock platform-callback-registration (imported by client.ts)
|
|
11
|
+
mock.module("../inbound/platform-callback-registration.js", () => ({
|
|
12
|
+
shouldUsePlatformCallbacks: jest.fn().mockReturnValue(false),
|
|
13
|
+
}));
|
|
14
|
+
|
|
10
15
|
const { McpClient } = await import("../mcp/client.js");
|
|
11
16
|
|
|
12
17
|
/**
|
|
@@ -22,6 +22,7 @@ import { getDb, initializeDb } from "../memory/db.js";
|
|
|
22
22
|
import {
|
|
23
23
|
backfillMemoryRecallLogMessageId,
|
|
24
24
|
getMemoryRecallLogByMessageIds,
|
|
25
|
+
normalizeTopCandidates,
|
|
25
26
|
recordMemoryRecallLog,
|
|
26
27
|
} from "../memory/memory-recall-log-store.js";
|
|
27
28
|
import { memoryRecallLogs } from "../memory/schema.js";
|
|
@@ -61,6 +62,7 @@ describe("memory-recall-log-store", () => {
|
|
|
61
62
|
topCandidatesJson: [{ id: "c1", score: 0.9 }],
|
|
62
63
|
injectedText: "some memory context",
|
|
63
64
|
reason: "user query matched memories",
|
|
65
|
+
queryContext: "what is the weather like",
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
backfillMemoryRecallLogMessageId(conversationId, messageId);
|
|
@@ -84,6 +86,34 @@ describe("memory-recall-log-store", () => {
|
|
|
84
86
|
expect(result!.topCandidates).toEqual([{ id: "c1", score: 0.9 }]);
|
|
85
87
|
expect(result!.injectedText).toBe("some memory context");
|
|
86
88
|
expect(result!.reason).toBe("user query matched memories");
|
|
89
|
+
expect(result!.queryContext).toBe("what is the weather like");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("queryContext defaults to null when omitted", () => {
|
|
93
|
+
const conversationId = "conv-no-query-ctx";
|
|
94
|
+
const messageId = "msg-no-query-ctx";
|
|
95
|
+
|
|
96
|
+
recordMemoryRecallLog({
|
|
97
|
+
conversationId,
|
|
98
|
+
enabled: true,
|
|
99
|
+
degraded: false,
|
|
100
|
+
semanticHits: 1,
|
|
101
|
+
mergedCount: 1,
|
|
102
|
+
selectedCount: 1,
|
|
103
|
+
tier1Count: 1,
|
|
104
|
+
tier2Count: 0,
|
|
105
|
+
hybridSearchLatencyMs: 50,
|
|
106
|
+
sparseVectorUsed: false,
|
|
107
|
+
injectedTokens: 100,
|
|
108
|
+
latencyMs: 80,
|
|
109
|
+
topCandidatesJson: [],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
backfillMemoryRecallLogMessageId(conversationId, messageId);
|
|
113
|
+
|
|
114
|
+
const result = getMemoryRecallLogByMessageIds([messageId]);
|
|
115
|
+
expect(result).not.toBeNull();
|
|
116
|
+
expect(result!.queryContext).toBeNull();
|
|
87
117
|
});
|
|
88
118
|
|
|
89
119
|
test("returns null when no log exists for a messageId", () => {
|
|
@@ -148,4 +178,106 @@ describe("memory-recall-log-store", () => {
|
|
|
148
178
|
expect(secondLog).not.toBeNull();
|
|
149
179
|
expect(secondLog!.degraded).toBe(true);
|
|
150
180
|
});
|
|
181
|
+
|
|
182
|
+
test("normalizes SSE-event format candidates to inspector format on read", () => {
|
|
183
|
+
const conversationId = "conv-normalize-sse";
|
|
184
|
+
const messageId = "msg-normalize-sse";
|
|
185
|
+
|
|
186
|
+
// Store candidates in SSE-event format (key/finalScore/semantic/recency/kind)
|
|
187
|
+
recordMemoryRecallLog({
|
|
188
|
+
conversationId,
|
|
189
|
+
enabled: true,
|
|
190
|
+
degraded: false,
|
|
191
|
+
semanticHits: 2,
|
|
192
|
+
mergedCount: 1,
|
|
193
|
+
selectedCount: 1,
|
|
194
|
+
tier1Count: 1,
|
|
195
|
+
tier2Count: 0,
|
|
196
|
+
hybridSearchLatencyMs: 100,
|
|
197
|
+
sparseVectorUsed: false,
|
|
198
|
+
injectedTokens: 200,
|
|
199
|
+
latencyMs: 120,
|
|
200
|
+
topCandidatesJson: [
|
|
201
|
+
{
|
|
202
|
+
key: "node-abc",
|
|
203
|
+
finalScore: 0.85,
|
|
204
|
+
semantic: 0.9,
|
|
205
|
+
recency: 0.1,
|
|
206
|
+
kind: "episode",
|
|
207
|
+
type: "episodic",
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
backfillMemoryRecallLogMessageId(conversationId, messageId);
|
|
213
|
+
|
|
214
|
+
const result = getMemoryRecallLogByMessageIds([messageId]);
|
|
215
|
+
expect(result).not.toBeNull();
|
|
216
|
+
const candidates = result!.topCandidates as Array<Record<string, unknown>>;
|
|
217
|
+
expect(candidates).toHaveLength(1);
|
|
218
|
+
expect(candidates[0]).toEqual({
|
|
219
|
+
nodeId: "node-abc",
|
|
220
|
+
score: 0.85,
|
|
221
|
+
semanticSimilarity: 0.9,
|
|
222
|
+
recencyBoost: 0.1,
|
|
223
|
+
type: "episodic",
|
|
224
|
+
});
|
|
225
|
+
// kind should be stripped
|
|
226
|
+
expect(candidates[0]).not.toHaveProperty("kind");
|
|
227
|
+
// Old field names should not be present
|
|
228
|
+
expect(candidates[0]).not.toHaveProperty("key");
|
|
229
|
+
expect(candidates[0]).not.toHaveProperty("finalScore");
|
|
230
|
+
expect(candidates[0]).not.toHaveProperty("semantic");
|
|
231
|
+
expect(candidates[0]).not.toHaveProperty("recency");
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("passes through candidates already in inspector format unchanged", () => {
|
|
235
|
+
const conversationId = "conv-normalize-inspector";
|
|
236
|
+
const messageId = "msg-normalize-inspector";
|
|
237
|
+
|
|
238
|
+
// Store candidates already in inspector format (nodeId/score/semanticSimilarity/recencyBoost)
|
|
239
|
+
recordMemoryRecallLog({
|
|
240
|
+
conversationId,
|
|
241
|
+
enabled: true,
|
|
242
|
+
degraded: false,
|
|
243
|
+
semanticHits: 1,
|
|
244
|
+
mergedCount: 1,
|
|
245
|
+
selectedCount: 1,
|
|
246
|
+
tier1Count: 1,
|
|
247
|
+
tier2Count: 0,
|
|
248
|
+
hybridSearchLatencyMs: 80,
|
|
249
|
+
sparseVectorUsed: false,
|
|
250
|
+
injectedTokens: 100,
|
|
251
|
+
latencyMs: 90,
|
|
252
|
+
topCandidatesJson: [
|
|
253
|
+
{
|
|
254
|
+
nodeId: "node-xyz",
|
|
255
|
+
score: 0.92,
|
|
256
|
+
semanticSimilarity: 0.88,
|
|
257
|
+
recencyBoost: 0.05,
|
|
258
|
+
type: "semantic",
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
backfillMemoryRecallLogMessageId(conversationId, messageId);
|
|
264
|
+
|
|
265
|
+
const result = getMemoryRecallLogByMessageIds([messageId]);
|
|
266
|
+
expect(result).not.toBeNull();
|
|
267
|
+
const candidates = result!.topCandidates as Array<Record<string, unknown>>;
|
|
268
|
+
expect(candidates).toHaveLength(1);
|
|
269
|
+
expect(candidates[0]).toEqual({
|
|
270
|
+
nodeId: "node-xyz",
|
|
271
|
+
score: 0.92,
|
|
272
|
+
semanticSimilarity: 0.88,
|
|
273
|
+
recencyBoost: 0.05,
|
|
274
|
+
type: "semantic",
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("normalizeTopCandidates handles non-array input", () => {
|
|
279
|
+
expect(normalizeTopCandidates(null)).toBeNull();
|
|
280
|
+
expect(normalizeTopCandidates("not-an-array")).toBe("not-an-array");
|
|
281
|
+
expect(normalizeTopCandidates(42)).toBe(42);
|
|
282
|
+
});
|
|
151
283
|
});
|