@vellumai/assistant 0.5.15 → 0.5.16
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 +2 -2
- package/docs/architecture/integrations.md +15 -14
- package/knip.json +3 -1
- package/openapi.yaml +11 -43
- package/package.json +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -375
- package/src/__tests__/ces-rpc-credential-backend.test.ts +4 -1
- package/src/__tests__/checker.test.ts +59 -0
- package/src/__tests__/cli-command-risk-guard.test.ts +98 -10
- package/src/__tests__/cli-memory.test.ts +372 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +12 -2
- package/src/__tests__/config-schema.test.ts +0 -2
- package/src/__tests__/config-watcher-feature-flags.test.ts +211 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +2 -6
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +4 -1
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +7 -73
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +6 -7
- package/src/__tests__/guardian-routing-invariants.test.ts +151 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -3
- package/src/__tests__/intent-routing.test.ts +6 -18
- package/src/__tests__/log-export-workspace.test.ts +2 -28
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -37
- package/src/__tests__/managed-store.test.ts +2 -10
- package/src/__tests__/messaging-send-tool.test.ts +6 -6
- package/src/__tests__/migration-cross-version-compatibility.test.ts +1 -29
- package/src/__tests__/migration-export-http.test.ts +3 -34
- package/src/__tests__/migration-import-commit-http.test.ts +1 -29
- package/src/__tests__/migration-import-preflight-http.test.ts +3 -34
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +2 -1
- package/src/__tests__/oauth-apps-routes.test.ts +120 -10
- package/src/__tests__/oauth-connect-orchestrator.test.ts +709 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +2 -1
- package/src/__tests__/oauth-provider-visibility.test.ts +149 -0
- package/src/__tests__/oauth-providers-routes.test.ts +5 -2
- package/src/__tests__/oauth-store.test.ts +0 -5
- package/src/__tests__/outlook-messaging-provider.test.ts +576 -0
- package/src/__tests__/path-policy.test.ts +2 -17
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/platform-callback-registration.test.ts +3 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/provider-error-scenarios.test.ts +0 -2
- package/src/__tests__/qdrant-manager.test.ts +68 -21
- package/src/__tests__/require-fresh-approval.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +20 -29
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/secret-allowlist.test.ts +20 -35
- package/src/__tests__/shell-credential-ref.test.ts +0 -5
- package/src/__tests__/skill-load-feature-flag.test.ts +2 -43
- package/src/__tests__/skill-load-inline-command.test.ts +3 -65
- package/src/__tests__/skill-load-inline-includes.test.ts +3 -65
- package/src/__tests__/skill-load-tool.test.ts +3 -67
- package/src/__tests__/skill-memory.test.ts +362 -119
- package/src/__tests__/skills.test.ts +22 -49
- package/src/__tests__/slack-channel-config.test.ts +2 -21
- package/src/__tests__/starter-bundle.test.ts +2 -8
- package/src/__tests__/stt-hints.test.ts +7 -2
- package/src/__tests__/system-prompt.test.ts +25 -45
- package/src/__tests__/task-compiler.test.ts +0 -21
- package/src/__tests__/task-management-tools.test.ts +0 -21
- package/src/__tests__/task-memory-cleanup.test.ts +0 -21
- package/src/__tests__/task-runner.test.ts +0 -21
- package/src/__tests__/task-scheduler.test.ts +0 -21
- package/src/__tests__/terminal-tools.test.ts +1 -17
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +0 -79
- package/src/__tests__/tool-approval-handler.test.ts +1 -20
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +2 -11
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -25
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -20
- package/src/__tests__/tool-preview-lifecycle.test.ts +0 -20
- package/src/__tests__/trust-store.test.ts +9 -41
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -30
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -21
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -22
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -22
- package/src/__tests__/trusted-contact-verification.test.ts +0 -22
- package/src/__tests__/turn-boundary-resolution.test.ts +0 -28
- package/src/__tests__/twilio-provider.test.ts +0 -16
- package/src/__tests__/twilio-routes-twiml.test.ts +7 -12
- package/src/__tests__/twilio-routes.test.ts +0 -24
- package/src/__tests__/update-bulletin.test.ts +17 -89
- package/src/__tests__/usage-cache-backfill-migration.test.ts +0 -20
- package/src/__tests__/usage-routes.test.ts +0 -21
- package/src/__tests__/user-reference.test.ts +1 -5
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +4 -34
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +2 -53
- package/src/__tests__/voice-invite-redemption.test.ts +0 -21
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -24
- package/src/__tests__/voice-session-bridge.test.ts +0 -21
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -23
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +2 -2
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -23
- package/src/__tests__/workspace-migration-down-functions.test.ts +0 -6
- package/src/acp/client-handler.ts +1 -2
- package/src/cli/__tests__/notifications.test.ts +0 -22
- package/src/cli/cli-memory.ts +176 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +15 -0
- package/src/cli/commands/oauth/providers.ts +49 -42
- package/src/cli/commands/platform/__tests__/connect.test.ts +2 -48
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +2 -48
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -50
- package/src/config/bundled-skills/computer-use/TOOLS.json +7 -7
- package/src/config/bundled-skills/messaging/SKILL.md +17 -2
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +4 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +8 -0
- package/src/context/window-manager.ts +28 -9
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/daemon/config-watcher.ts +51 -0
- package/src/daemon/conversation-agent-loop.ts +3 -2
- package/src/daemon/conversation-process.ts +1 -0
- package/src/daemon/conversation-usage.ts +1 -0
- package/src/daemon/handlers/skills.ts +9 -1
- package/src/daemon/lifecycle.ts +13 -4
- package/src/daemon/message-types/conversations.ts +1 -0
- package/src/daemon/providers-setup.ts +2 -0
- package/src/daemon/server.ts +26 -22
- package/src/events/domain-events.ts +1 -2
- package/src/memory/db-init.ts +9 -0
- package/src/memory/job-handlers/batch-extraction.ts +16 -4
- package/src/memory/job-handlers/embedding.test.ts +3 -27
- package/src/memory/job-handlers/journal-carry-forward.test.ts +1 -29
- package/src/memory/llm-usage-store.ts +35 -2
- package/src/memory/migrations/201-oauth-providers-feature-flag.ts +11 -0
- package/src/memory/migrations/202-drop-callback-transport-column.ts +13 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/qdrant-manager.ts +26 -5
- package/src/memory/query-expansion.ts +1 -1
- package/src/memory/retriever.test.ts +22 -20
- package/src/memory/retriever.ts +10 -2
- package/src/memory/schema/oauth.ts +1 -1
- package/src/memory/search/mmr.ts +8 -5
- package/src/memory/slack-thread-store.ts +17 -0
- package/src/messaging/providers/outlook/adapter.ts +193 -0
- package/src/messaging/providers/outlook/client.ts +311 -0
- package/src/messaging/providers/outlook/types.ts +83 -0
- package/src/notifications/adapters/slack.ts +1 -1
- package/src/oauth/__tests__/identity-verifier.test.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +10 -3
- package/src/oauth/oauth-store.ts +10 -11
- package/src/oauth/provider-serializer.ts +3 -0
- package/src/oauth/provider-visibility.ts +16 -0
- package/src/oauth/seed-providers.ts +49 -17
- package/src/permissions/checker.ts +39 -7
- package/src/permissions/types.ts +2 -4
- package/src/prompts/journal-context.ts +9 -11
- package/src/prompts/system-prompt.ts +3 -64
- package/src/prompts/templates/UPDATES.md +6 -0
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -27
- package/src/runtime/auth/__tests__/token-service.test.ts +1 -25
- package/src/runtime/auth/route-policy.ts +0 -4
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/routes/conversation-query-routes.ts +2 -58
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +43 -2
- package/src/runtime/routes/memory-item-routes.test.ts +0 -17
- package/src/runtime/routes/memory-item-routes.ts +103 -12
- package/src/runtime/routes/oauth-apps.ts +18 -1
- package/src/runtime/routes/oauth-providers.ts +13 -1
- package/src/runtime/routes/settings-routes.ts +1 -0
- package/src/runtime/routes/usage-routes.ts +19 -2
- package/src/runtime/routes/work-items-routes.test.ts +0 -21
- package/src/runtime/routes/workspace-routes.test.ts +3 -27
- package/src/security/secret-allowlist.ts +4 -4
- package/src/skills/skill-memory.ts +62 -23
- package/src/tools/memory/handlers.test.ts +1 -29
- package/src/tools/permission-checker.ts +0 -18
- package/src/tools/skills/skill-script-runner.ts +1 -1
- package/src/util/device-id.ts +3 -65
- package/src/workspace/git-service.ts +27 -6
|
@@ -10,37 +10,7 @@ import { tmpdir } from "node:os";
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
12
|
|
|
13
|
-
const TEST_DIR =
|
|
14
|
-
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
-
const realPlatform = require("../util/platform.js");
|
|
17
|
-
mock.module("../util/platform.js", () => ({
|
|
18
|
-
...realPlatform,
|
|
19
|
-
getProtectedDir: () => join(TEST_DIR, "protected"),
|
|
20
|
-
getDataDir: () => TEST_DIR,
|
|
21
|
-
|
|
22
|
-
getSandboxRootDir: () => join(TEST_DIR, "sandbox"),
|
|
23
|
-
getSandboxWorkingDir: () => TEST_DIR,
|
|
24
|
-
getInterfacesDir: () => join(TEST_DIR, "interfaces"),
|
|
25
|
-
ensureDataDir: () => {},
|
|
26
|
-
getPidPath: () => join(TEST_DIR, "vellum.pid"),
|
|
27
|
-
getDbPath: () => join(TEST_DIR, "data", "assistant.db"),
|
|
28
|
-
getLogPath: () => join(TEST_DIR, "logs", "vellum.log"),
|
|
29
|
-
getHistoryPath: () => join(TEST_DIR, "history"),
|
|
30
|
-
isMacOS: () => process.platform === "darwin",
|
|
31
|
-
isLinux: () => process.platform === "linux",
|
|
32
|
-
isWindows: () => process.platform === "win32",
|
|
33
|
-
getPlatformName: () => process.platform,
|
|
34
|
-
getClipboardCommand: () => null,
|
|
35
|
-
getWorkspaceConfigPath: () => join(TEST_DIR, "config.json"),
|
|
36
|
-
getWorkspaceSkillsDir: () => join(TEST_DIR, "skills"),
|
|
37
|
-
getWorkspaceHooksDir: () => join(TEST_DIR, "hooks"),
|
|
38
|
-
getHooksDir: () => join(TEST_DIR, "hooks"),
|
|
39
|
-
getWorkspaceDir: () => TEST_DIR,
|
|
40
|
-
getWorkspacePromptPath: (file: string) => join(TEST_DIR, file),
|
|
41
|
-
readSessionToken: () => null,
|
|
42
|
-
normalizeAssistantId: (id: string) => id,
|
|
43
|
-
}));
|
|
13
|
+
const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
44
14
|
|
|
45
15
|
const noopLogger = {
|
|
46
16
|
info: () => {},
|
|
@@ -91,9 +61,9 @@ describe("skills catalog loading", () => {
|
|
|
91
61
|
});
|
|
92
62
|
|
|
93
63
|
afterEach(() => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
64
|
+
const skillsDir = join(TEST_DIR, "skills");
|
|
65
|
+
if (existsSync(skillsDir))
|
|
66
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
97
67
|
});
|
|
98
68
|
|
|
99
69
|
test("parses markdown list path entries from SKILLS.md", () => {
|
|
@@ -221,9 +191,12 @@ describe("workspace skills", () => {
|
|
|
221
191
|
});
|
|
222
192
|
|
|
223
193
|
afterEach(() => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
194
|
+
const skillsDir = join(TEST_DIR, "skills");
|
|
195
|
+
if (existsSync(skillsDir))
|
|
196
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
197
|
+
const outsideDir = join(TEST_DIR, "outside");
|
|
198
|
+
if (existsSync(outsideDir))
|
|
199
|
+
rmSync(outsideDir, { recursive: true, force: true });
|
|
227
200
|
if (existsSync(WORKSPACE_DIR)) {
|
|
228
201
|
rmSync(WORKSPACE_DIR, { recursive: true, force: true });
|
|
229
202
|
}
|
|
@@ -282,9 +255,9 @@ describe("tool manifest detection", () => {
|
|
|
282
255
|
});
|
|
283
256
|
|
|
284
257
|
afterEach(() => {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
258
|
+
const skillsDir = join(TEST_DIR, "skills");
|
|
259
|
+
if (existsSync(skillsDir))
|
|
260
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
288
261
|
});
|
|
289
262
|
|
|
290
263
|
test("attaches toolManifest metadata when valid TOOLS.json is present", () => {
|
|
@@ -455,9 +428,9 @@ describe("includes frontmatter parsing", () => {
|
|
|
455
428
|
});
|
|
456
429
|
|
|
457
430
|
afterEach(() => {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
431
|
+
const skillsDir = join(TEST_DIR, "skills");
|
|
432
|
+
if (existsSync(skillsDir))
|
|
433
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
461
434
|
});
|
|
462
435
|
|
|
463
436
|
function writeSkillWithIncludes(skillId: string, includes: string): void {
|
|
@@ -553,9 +526,9 @@ describe("bundled browser skill", () => {
|
|
|
553
526
|
});
|
|
554
527
|
|
|
555
528
|
afterEach(() => {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
529
|
+
const skillsDir = join(TEST_DIR, "skills");
|
|
530
|
+
if (existsSync(skillsDir))
|
|
531
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
559
532
|
});
|
|
560
533
|
|
|
561
534
|
test("browser skill appears in full catalog (including bundled)", () => {
|
|
@@ -679,9 +652,9 @@ describe("bundled computer-use skill", () => {
|
|
|
679
652
|
});
|
|
680
653
|
|
|
681
654
|
afterEach(() => {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
655
|
+
const skillsDir = join(TEST_DIR, "skills");
|
|
656
|
+
if (existsSync(skillsDir))
|
|
657
|
+
rmSync(skillsDir, { recursive: true, force: true });
|
|
685
658
|
});
|
|
686
659
|
|
|
687
660
|
test("computer-use skill appears in full catalog (including bundled)", () => {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
1
|
+
import { rmSync } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
4
|
|
|
6
|
-
const testDir =
|
|
5
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
7
6
|
const secureStorePath = join(testDir, "keys.enc");
|
|
8
7
|
const metadataPath = join(testDir, "metadata.json");
|
|
9
8
|
const originalVellumDev = process.env.VELLUM_DEV;
|
|
@@ -59,19 +58,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
59
58
|
setNestedValue,
|
|
60
59
|
}));
|
|
61
60
|
|
|
62
|
-
mock.module("../util/platform.js", () => ({
|
|
63
|
-
getProtectedDir: () => join(testDir, "protected"),
|
|
64
|
-
getDataDir: () => testDir,
|
|
65
|
-
|
|
66
|
-
isMacOS: () => process.platform === "darwin",
|
|
67
|
-
isLinux: () => process.platform === "linux",
|
|
68
|
-
isWindows: () => process.platform === "win32",
|
|
69
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
70
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
71
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
72
|
-
ensureDataDir: () => {},
|
|
73
|
-
}));
|
|
74
|
-
|
|
75
61
|
mock.module("../util/logger.js", () => ({
|
|
76
62
|
getLogger: () => ({
|
|
77
63
|
info: () => {},
|
|
@@ -182,11 +168,6 @@ afterAll(() => {
|
|
|
182
168
|
} else {
|
|
183
169
|
process.env.VELLUM_DEV = originalVellumDev;
|
|
184
170
|
}
|
|
185
|
-
try {
|
|
186
|
-
rmSync(testDir, { recursive: true });
|
|
187
|
-
} catch {
|
|
188
|
-
/* best effort */
|
|
189
|
-
}
|
|
190
171
|
});
|
|
191
172
|
|
|
192
173
|
describe("Slack channel config handler", () => {
|
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Set up a temp directory before importing trust-store
|
|
6
6
|
const TEST_ROOT = join(
|
|
7
7
|
import.meta.dirname ?? __dirname,
|
|
8
8
|
"..",
|
|
@@ -11,23 +11,17 @@ const TEST_ROOT = join(
|
|
|
11
11
|
);
|
|
12
12
|
const TRUST_PATH = join(TEST_ROOT, "protected", "trust.json");
|
|
13
13
|
|
|
14
|
-
// We need to mock getRootDir before importing trust-store
|
|
15
14
|
import { mock } from "bun:test";
|
|
16
15
|
|
|
17
16
|
// Point the file-based trust backend at the test temp dir.
|
|
18
17
|
process.env.GATEWAY_SECURITY_DIR = join(TEST_ROOT, "protected");
|
|
19
18
|
|
|
20
|
-
// Mock the platform module to use our test root
|
|
21
|
-
mock.module("../util/platform.js", () => ({
|
|
22
|
-
getProtectedDir: () => join(TEST_ROOT, "protected"),
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
19
|
// Mock the skills config module used by defaults.ts
|
|
26
20
|
mock.module("../config/skills.js", () => ({
|
|
27
21
|
getBundledSkillsDir: () => join(TEST_ROOT, "bundled-skills"),
|
|
28
22
|
}));
|
|
29
23
|
|
|
30
|
-
// Now import trust-store (which uses
|
|
24
|
+
// Now import trust-store (which uses GATEWAY_SECURITY_DIR)
|
|
31
25
|
import {
|
|
32
26
|
acceptStarterBundle,
|
|
33
27
|
clearCache,
|
|
@@ -64,7 +64,11 @@ mock.module("pino", () => ({ default: mockPinoLogger }));
|
|
|
64
64
|
mock.module("pino-pretty", () => ({ default: () => ({}) }));
|
|
65
65
|
|
|
66
66
|
// Import after mocking
|
|
67
|
-
import {
|
|
67
|
+
import {
|
|
68
|
+
buildSttHints,
|
|
69
|
+
resolveCallHints,
|
|
70
|
+
type SttHintsInput,
|
|
71
|
+
} from "../calls/stt-hints.js";
|
|
68
72
|
|
|
69
73
|
function emptyInput(): SttHintsInput {
|
|
70
74
|
return {
|
|
@@ -159,7 +163,8 @@ describe("buildSttHints", () => {
|
|
|
159
163
|
|
|
160
164
|
test("proper nouns extracted across sentence boundaries", () => {
|
|
161
165
|
const input = emptyInput();
|
|
162
|
-
input.taskDescription =
|
|
166
|
+
input.taskDescription =
|
|
167
|
+
"Meet with Alice. Then call Bob! Ask Charlie? Done.";
|
|
163
168
|
const result = buildSttHints(input);
|
|
164
169
|
expect(result).toContain("Alice");
|
|
165
170
|
expect(result).toContain("Bob");
|
|
@@ -5,45 +5,14 @@ import {
|
|
|
5
5
|
rmSync,
|
|
6
6
|
writeFileSync,
|
|
7
7
|
} from "node:fs";
|
|
8
|
-
import { tmpdir } from "node:os";
|
|
9
8
|
import { join } from "node:path";
|
|
10
9
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
11
10
|
|
|
12
11
|
// Mock platform to use a temp directory
|
|
13
|
-
const TEST_DIR =
|
|
12
|
+
const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
14
13
|
|
|
15
14
|
import { mock } from "bun:test";
|
|
16
15
|
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
18
|
-
const realPlatform = require("../util/platform.js");
|
|
19
|
-
mock.module("../util/platform.js", () => ({
|
|
20
|
-
...realPlatform,
|
|
21
|
-
getProtectedDir: () => join(TEST_DIR, "protected"),
|
|
22
|
-
getDataDir: () => TEST_DIR,
|
|
23
|
-
getWorkspaceDir: () => TEST_DIR,
|
|
24
|
-
getWorkspaceConfigPath: () => join(TEST_DIR, "config.json"),
|
|
25
|
-
getWorkspaceSkillsDir: () => join(TEST_DIR, "skills"),
|
|
26
|
-
getWorkspaceHooksDir: () => join(TEST_DIR, "hooks"),
|
|
27
|
-
getConversationsDir: () => join(TEST_DIR, "conversations"),
|
|
28
|
-
getWorkspacePromptPath: (file: string) => join(TEST_DIR, file),
|
|
29
|
-
ensureDataDir: () => {},
|
|
30
|
-
getPidPath: () => join(TEST_DIR, "vellum.pid"),
|
|
31
|
-
getDbPath: () => join(TEST_DIR, "data", "assistant.db"),
|
|
32
|
-
getLogPath: () => join(TEST_DIR, "logs", "vellum.log"),
|
|
33
|
-
getHistoryPath: () => join(TEST_DIR, "history"),
|
|
34
|
-
getHooksDir: () => join(TEST_DIR, "hooks"),
|
|
35
|
-
|
|
36
|
-
getSandboxRootDir: () => join(TEST_DIR, "sandbox"),
|
|
37
|
-
getSandboxWorkingDir: () => TEST_DIR,
|
|
38
|
-
getInterfacesDir: () => join(TEST_DIR, "interfaces"),
|
|
39
|
-
isMacOS: () => process.platform === "darwin",
|
|
40
|
-
isLinux: () => process.platform === "linux",
|
|
41
|
-
isWindows: () => process.platform === "win32",
|
|
42
|
-
getPlatformName: () => process.platform,
|
|
43
|
-
getClipboardCommand: () => null,
|
|
44
|
-
readSessionToken: () => null,
|
|
45
|
-
}));
|
|
46
|
-
|
|
47
16
|
const noopLogger: Record<string, unknown> = new Proxy(
|
|
48
17
|
{} as Record<string, unknown>,
|
|
49
18
|
{
|
|
@@ -123,7 +92,6 @@ function basePrompt(result: string): string {
|
|
|
123
92
|
for (const heading of [
|
|
124
93
|
"## Configuration",
|
|
125
94
|
"## Skills Catalog",
|
|
126
|
-
"## Available Skills",
|
|
127
95
|
"## External Communications Identity",
|
|
128
96
|
"## Connected Services",
|
|
129
97
|
"## Dynamic Skill Authoring Workflow",
|
|
@@ -144,8 +112,16 @@ describe("buildSystemPrompt", () => {
|
|
|
144
112
|
});
|
|
145
113
|
|
|
146
114
|
afterEach(() => {
|
|
147
|
-
|
|
148
|
-
|
|
115
|
+
for (const name of [
|
|
116
|
+
"IDENTITY.md",
|
|
117
|
+
"SOUL.md",
|
|
118
|
+
"USER.md",
|
|
119
|
+
"BOOTSTRAP.md",
|
|
120
|
+
"UPDATES.md",
|
|
121
|
+
"skills",
|
|
122
|
+
]) {
|
|
123
|
+
const p = join(TEST_DIR, name);
|
|
124
|
+
if (existsSync(p)) rmSync(p, { recursive: true, force: true });
|
|
149
125
|
}
|
|
150
126
|
});
|
|
151
127
|
|
|
@@ -196,7 +172,7 @@ describe("buildSystemPrompt", () => {
|
|
|
196
172
|
expect(basePrompt(result)).toBe("Be kind");
|
|
197
173
|
});
|
|
198
174
|
|
|
199
|
-
test("
|
|
175
|
+
test("does not include skills catalog in system prompt", () => {
|
|
200
176
|
const skillsDir = join(TEST_DIR, "skills");
|
|
201
177
|
mkdirSync(join(skillsDir, "release-checklist"), { recursive: true });
|
|
202
178
|
writeFileSync(
|
|
@@ -208,11 +184,11 @@ describe("buildSystemPrompt", () => {
|
|
|
208
184
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Custom identity");
|
|
209
185
|
const result = buildSystemPrompt();
|
|
210
186
|
expect(result).toContain("Custom identity");
|
|
211
|
-
expect(result).toContain("## Available Skills");
|
|
212
|
-
expect(result).toContain("**release-checklist
|
|
187
|
+
expect(result).not.toContain("## Available Skills");
|
|
188
|
+
expect(result).not.toContain("**release-checklist**");
|
|
213
189
|
});
|
|
214
190
|
|
|
215
|
-
test("keeps SOUL.md and IDENTITY.md additive
|
|
191
|
+
test("keeps SOUL.md and IDENTITY.md additive without skills catalog", () => {
|
|
216
192
|
const skillsDir = join(TEST_DIR, "skills");
|
|
217
193
|
mkdirSync(join(skillsDir, "incident-response"), { recursive: true });
|
|
218
194
|
writeFileSync(
|
|
@@ -225,10 +201,7 @@ describe("buildSystemPrompt", () => {
|
|
|
225
201
|
|
|
226
202
|
const result = buildSystemPrompt();
|
|
227
203
|
expect(result).toContain("Identity content\n\nSoul content");
|
|
228
|
-
expect(result).toContain("## Available Skills");
|
|
229
|
-
expect(result.indexOf("Soul content")).toBeLessThan(
|
|
230
|
-
result.indexOf("## Available Skills"),
|
|
231
|
-
);
|
|
204
|
+
expect(result).not.toContain("## Available Skills");
|
|
232
205
|
});
|
|
233
206
|
|
|
234
207
|
test("includes external service access section", () => {
|
|
@@ -492,8 +465,15 @@ describe("ensurePromptFiles", () => {
|
|
|
492
465
|
});
|
|
493
466
|
|
|
494
467
|
afterEach(() => {
|
|
495
|
-
|
|
496
|
-
|
|
468
|
+
for (const name of [
|
|
469
|
+
"IDENTITY.md",
|
|
470
|
+
"SOUL.md",
|
|
471
|
+
"USER.md",
|
|
472
|
+
"BOOTSTRAP.md",
|
|
473
|
+
"conversations",
|
|
474
|
+
]) {
|
|
475
|
+
const p = join(TEST_DIR, name);
|
|
476
|
+
if (existsSync(p)) rmSync(p, { recursive: true, force: true });
|
|
497
477
|
}
|
|
498
478
|
});
|
|
499
479
|
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterAll, beforeEach, describe, expect, test } from "bun:test";
|
|
5
2
|
import { mock } from "bun:test";
|
|
6
3
|
|
|
7
|
-
const testDir = mkdtempSync(join(tmpdir(), "task-compiler-test-"));
|
|
8
|
-
|
|
9
|
-
mock.module("../util/platform.js", () => ({
|
|
10
|
-
getDataDir: () => testDir,
|
|
11
|
-
isMacOS: () => process.platform === "darwin",
|
|
12
|
-
isLinux: () => process.platform === "linux",
|
|
13
|
-
isWindows: () => process.platform === "win32",
|
|
14
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
15
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
16
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
17
|
-
ensureDataDir: () => {},
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
4
|
mock.module("../util/logger.js", () => ({
|
|
21
5
|
getLogger: () =>
|
|
22
6
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -49,11 +33,6 @@ initializeDb();
|
|
|
49
33
|
|
|
50
34
|
afterAll(() => {
|
|
51
35
|
resetDb();
|
|
52
|
-
try {
|
|
53
|
-
rmSync(testDir, { recursive: true });
|
|
54
|
-
} catch {
|
|
55
|
-
/* best effort */
|
|
56
|
-
}
|
|
57
36
|
});
|
|
58
37
|
|
|
59
38
|
// ── Helpers ──────────────────────────────────────────────────────────
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
2
|
|
|
6
|
-
const testDir = mkdtempSync(join(tmpdir(), "task-mgmt-test-"));
|
|
7
|
-
|
|
8
|
-
mock.module("../util/platform.js", () => ({
|
|
9
|
-
getDataDir: () => testDir,
|
|
10
|
-
isMacOS: () => process.platform === "darwin",
|
|
11
|
-
isLinux: () => process.platform === "linux",
|
|
12
|
-
isWindows: () => process.platform === "win32",
|
|
13
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
14
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
15
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
16
|
-
ensureDataDir: () => {},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
3
|
mock.module("../util/logger.js", () => ({
|
|
20
4
|
getLogger: () =>
|
|
21
5
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -76,11 +60,6 @@ initializeDb();
|
|
|
76
60
|
|
|
77
61
|
afterAll(() => {
|
|
78
62
|
resetDb();
|
|
79
|
-
try {
|
|
80
|
-
rmSync(testDir, { recursive: true });
|
|
81
|
-
} catch {
|
|
82
|
-
/* best effort */
|
|
83
|
-
}
|
|
84
63
|
});
|
|
85
64
|
|
|
86
65
|
const ctx: ToolContext = {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import {
|
|
5
2
|
afterAll,
|
|
6
3
|
beforeAll,
|
|
@@ -15,19 +12,6 @@ import { eq } from "drizzle-orm";
|
|
|
15
12
|
|
|
16
13
|
import { DEFAULT_CONFIG } from "../config/defaults.js";
|
|
17
14
|
|
|
18
|
-
const testDir = mkdtempSync(join(tmpdir(), "task-memory-cleanup-"));
|
|
19
|
-
|
|
20
|
-
mock.module("../util/platform.js", () => ({
|
|
21
|
-
getDataDir: () => testDir,
|
|
22
|
-
isMacOS: () => process.platform === "darwin",
|
|
23
|
-
isLinux: () => process.platform === "linux",
|
|
24
|
-
isWindows: () => process.platform === "win32",
|
|
25
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
26
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
27
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
28
|
-
ensureDataDir: () => {},
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
15
|
mock.module("../util/logger.js", () => ({
|
|
32
16
|
getLogger: () =>
|
|
33
17
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -105,11 +89,6 @@ describe("invalidateAssistantInferredItemsForConversation", () => {
|
|
|
105
89
|
|
|
106
90
|
afterAll(() => {
|
|
107
91
|
resetDb();
|
|
108
|
-
try {
|
|
109
|
-
rmSync(testDir, { recursive: true });
|
|
110
|
-
} catch {
|
|
111
|
-
// best effort
|
|
112
|
-
}
|
|
113
92
|
});
|
|
114
93
|
|
|
115
94
|
function seedConversations() {
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
2
|
|
|
6
|
-
const testDir = mkdtempSync(join(tmpdir(), "task-runner-test-"));
|
|
7
|
-
|
|
8
|
-
mock.module("../util/platform.js", () => ({
|
|
9
|
-
getDataDir: () => testDir,
|
|
10
|
-
isMacOS: () => process.platform === "darwin",
|
|
11
|
-
isLinux: () => process.platform === "linux",
|
|
12
|
-
isWindows: () => process.platform === "win32",
|
|
13
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
14
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
15
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
16
|
-
ensureDataDir: () => {},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
3
|
mock.module("../util/logger.js", () => ({
|
|
20
4
|
getLogger: () =>
|
|
21
5
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -32,11 +16,6 @@ initializeDb();
|
|
|
32
16
|
|
|
33
17
|
afterAll(() => {
|
|
34
18
|
resetDb();
|
|
35
|
-
try {
|
|
36
|
-
rmSync(testDir, { recursive: true });
|
|
37
|
-
} catch {
|
|
38
|
-
/* best effort */
|
|
39
|
-
}
|
|
40
19
|
});
|
|
41
20
|
|
|
42
21
|
// ── renderTemplate ──────────────────────────────────────────────────
|
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
2
|
|
|
6
|
-
const testDir = mkdtempSync(join(tmpdir(), "task-scheduler-test-"));
|
|
7
|
-
|
|
8
|
-
mock.module("../util/platform.js", () => ({
|
|
9
|
-
getDataDir: () => testDir,
|
|
10
|
-
isMacOS: () => process.platform === "darwin",
|
|
11
|
-
isLinux: () => process.platform === "linux",
|
|
12
|
-
isWindows: () => process.platform === "win32",
|
|
13
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
14
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
15
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
16
|
-
ensureDataDir: () => {},
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
3
|
mock.module("../util/logger.js", () => ({
|
|
20
4
|
getLogger: () =>
|
|
21
5
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -51,11 +35,6 @@ function forceScheduleDue(scheduleId: string): void {
|
|
|
51
35
|
|
|
52
36
|
afterAll(() => {
|
|
53
37
|
resetDb();
|
|
54
|
-
try {
|
|
55
|
-
rmSync(testDir, { recursive: true });
|
|
56
|
-
} catch {
|
|
57
|
-
/* best effort */
|
|
58
|
-
}
|
|
59
38
|
});
|
|
60
39
|
|
|
61
40
|
// ── scheduleTask helper ─────────────────────────────────────────────
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { mkdtempSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
2
|
|
|
6
3
|
import type { ShellOutputResult } from "../tools/shared/shell-output.js";
|
|
@@ -16,20 +13,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
16
13
|
}),
|
|
17
14
|
}));
|
|
18
15
|
|
|
19
|
-
const testTmpDir =
|
|
20
|
-
|
|
21
|
-
mock.module("../util/platform.js", () => ({
|
|
22
|
-
getProtectedDir: () => join(testTmpDir, "protected"),
|
|
23
|
-
getDataDir: () => join(testTmpDir, "data"),
|
|
24
|
-
getSandboxWorkingDir: () => join(testTmpDir, "sandbox"),
|
|
25
|
-
isMacOS: () => process.platform === "darwin",
|
|
26
|
-
isLinux: () => process.platform === "linux",
|
|
27
|
-
isWindows: () => process.platform === "win32",
|
|
28
|
-
getPidPath: () => join(testTmpDir, "test.pid"),
|
|
29
|
-
getDbPath: () => join(testTmpDir, "test.db"),
|
|
30
|
-
getLogPath: () => join(testTmpDir, "test.log"),
|
|
31
|
-
ensureDataDir: () => {},
|
|
32
|
-
}));
|
|
16
|
+
const testTmpDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
33
17
|
|
|
34
18
|
mock.module("../config/loader.js", () => ({
|
|
35
19
|
getConfig: () => ({
|
|
@@ -216,85 +216,6 @@ function makeSystemPrompt(size: "small" | "production" = "small"): string {
|
|
|
216
216
|
"Each MCP server entry requires: name, command, args, and optional env.",
|
|
217
217
|
);
|
|
218
218
|
|
|
219
|
-
// Dynamic skills catalog (~5K chars)
|
|
220
|
-
sections.push("", "## Available Skills", "<available_skills>");
|
|
221
|
-
const skillCategories = [
|
|
222
|
-
{
|
|
223
|
-
id: "gmail",
|
|
224
|
-
name: "Gmail",
|
|
225
|
-
desc: "Send, search, draft, and manage Gmail messages",
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
id: "calendar",
|
|
229
|
-
name: "Google Calendar",
|
|
230
|
-
desc: "Create, list, update, and delete calendar events",
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
id: "slack",
|
|
234
|
-
name: "Slack",
|
|
235
|
-
desc: "Send messages, search channels, manage threads",
|
|
236
|
-
},
|
|
237
|
-
{ id: "contacts", name: "Contacts", desc: "Search and manage contacts" },
|
|
238
|
-
{
|
|
239
|
-
id: "tasks",
|
|
240
|
-
name: "Tasks",
|
|
241
|
-
desc: "Create, list, update, and complete tasks",
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
id: "browser",
|
|
245
|
-
name: "Browser",
|
|
246
|
-
desc: "Navigate web pages, take screenshots, interact with web content",
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
id: "schedule",
|
|
250
|
-
name: "Schedule",
|
|
251
|
-
desc: "Set reminders and schedule recurring tasks",
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
id: "messaging",
|
|
255
|
-
name: "Messaging",
|
|
256
|
-
desc: "Send iMessage and SMS messages",
|
|
257
|
-
},
|
|
258
|
-
{
|
|
259
|
-
id: "sequences",
|
|
260
|
-
name: "Sequences",
|
|
261
|
-
desc: "Create and manage multi-step automation workflows",
|
|
262
|
-
},
|
|
263
|
-
{
|
|
264
|
-
id: "playbooks",
|
|
265
|
-
name: "Playbooks",
|
|
266
|
-
desc: "Execute pre-defined operational playbooks",
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
id: "notes",
|
|
270
|
-
name: "Notes",
|
|
271
|
-
desc: "Create and manage notes in Apple Notes",
|
|
272
|
-
},
|
|
273
|
-
{ id: "music", name: "Music", desc: "Control Apple Music playback" },
|
|
274
|
-
{
|
|
275
|
-
id: "photos",
|
|
276
|
-
name: "Photos",
|
|
277
|
-
desc: "Search and manage photos in Apple Photos",
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
id: "maps",
|
|
281
|
-
name: "Maps",
|
|
282
|
-
desc: "Search locations, get directions, find nearby places",
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
id: "weather",
|
|
286
|
-
name: "Weather",
|
|
287
|
-
desc: "Get current weather and forecasts",
|
|
288
|
-
},
|
|
289
|
-
];
|
|
290
|
-
for (const skill of skillCategories) {
|
|
291
|
-
sections.push(
|
|
292
|
-
` <skill id="${skill.id}" name="${skill.name}" description="${skill.desc}" ` +
|
|
293
|
-
`credential_setup="oauth" enabled="true" />`,
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
sections.push("</available_skills>");
|
|
297
|
-
|
|
298
219
|
// Attachment handling (~1K chars)
|
|
299
220
|
sections.push(
|
|
300
221
|
"",
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
5
2
|
|
|
6
|
-
const testDir =
|
|
7
|
-
|
|
8
|
-
mock.module("../util/platform.js", () => ({
|
|
9
|
-
getDataDir: () => testDir,
|
|
10
|
-
isMacOS: () => process.platform === "darwin",
|
|
11
|
-
isLinux: () => process.platform === "linux",
|
|
12
|
-
isWindows: () => process.platform === "win32",
|
|
13
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
14
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
15
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
16
|
-
ensureDataDir: () => {},
|
|
17
|
-
}));
|
|
3
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
18
4
|
|
|
19
5
|
mock.module("../util/logger.js", () => ({
|
|
20
6
|
getLogger: () =>
|
|
@@ -72,11 +58,6 @@ function clearTables(): void {
|
|
|
72
58
|
|
|
73
59
|
afterAll(() => {
|
|
74
60
|
resetDb();
|
|
75
|
-
try {
|
|
76
|
-
rmSync(testDir, { recursive: true });
|
|
77
|
-
} catch {
|
|
78
|
-
/* best effort */
|
|
79
|
-
}
|
|
80
61
|
});
|
|
81
62
|
|
|
82
63
|
// ---------------------------------------------------------------------------
|