@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
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
mkdtempSync,
|
|
16
16
|
readdirSync,
|
|
17
17
|
readFileSync,
|
|
18
|
-
realpathSync,
|
|
19
18
|
rmSync,
|
|
20
19
|
symlinkSync,
|
|
21
20
|
writeFileSync,
|
|
@@ -25,29 +24,9 @@ import { join } from "node:path";
|
|
|
25
24
|
import { afterAll, describe, expect, mock, test } from "bun:test";
|
|
26
25
|
|
|
27
26
|
// Set up temp directories before mocking
|
|
28
|
-
const testDir =
|
|
29
|
-
|
|
30
|
-
);
|
|
31
|
-
const testWorkspaceDir = join(testDir, "workspace");
|
|
32
|
-
const testDbDir = join(testDir, "db");
|
|
33
|
-
const testDbPath = join(testDbDir, "assistant.db");
|
|
34
|
-
|
|
27
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
28
|
+
const testWorkspaceDir = testDir;
|
|
35
29
|
mkdirSync(testWorkspaceDir, { recursive: true });
|
|
36
|
-
mkdirSync(testDbDir, { recursive: true });
|
|
37
|
-
|
|
38
|
-
mock.module("../util/platform.js", () => ({
|
|
39
|
-
getProtectedDir: () => join(testDir, "protected"),
|
|
40
|
-
getDataDir: () => testDir,
|
|
41
|
-
getWorkspaceDir: () => testWorkspaceDir,
|
|
42
|
-
getWorkspaceConfigPath: () => join(testWorkspaceDir, "config.json"),
|
|
43
|
-
isMacOS: () => process.platform === "darwin",
|
|
44
|
-
isLinux: () => process.platform === "linux",
|
|
45
|
-
isWindows: () => process.platform === "win32",
|
|
46
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
47
|
-
getDbPath: () => testDbPath,
|
|
48
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
49
|
-
ensureDataDir: () => {},
|
|
50
|
-
}));
|
|
51
30
|
|
|
52
31
|
mock.module("../util/logger.js", () => ({
|
|
53
32
|
getLogger: () =>
|
|
@@ -68,11 +47,6 @@ initializeDb();
|
|
|
68
47
|
|
|
69
48
|
afterAll(() => {
|
|
70
49
|
resetDb();
|
|
71
|
-
try {
|
|
72
|
-
rmSync(testDir, { recursive: true });
|
|
73
|
-
} catch {
|
|
74
|
-
/* best effort */
|
|
75
|
-
}
|
|
76
50
|
});
|
|
77
51
|
|
|
78
52
|
// ---------------------------------------------------------------------------
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync
|
|
2
|
-
import { mkdtempSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
4
2
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
3
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
6
4
|
|
|
7
5
|
let TEST_DIR = "";
|
|
8
6
|
|
|
@@ -38,22 +36,6 @@ const mockConfig = {
|
|
|
38
36
|
},
|
|
39
37
|
};
|
|
40
38
|
|
|
41
|
-
mock.module("../util/platform.js", () => ({
|
|
42
|
-
getProtectedDir: () => join(TEST_DIR, "protected"),
|
|
43
|
-
getWorkspaceSkillsDir: () => join(TEST_DIR, "skills"),
|
|
44
|
-
getDataDir: () => TEST_DIR,
|
|
45
|
-
ensureDataDir: () => {},
|
|
46
|
-
getPidPath: () => join(TEST_DIR, "vellum.pid"),
|
|
47
|
-
getDbPath: () => join(TEST_DIR, "data", "assistant.db"),
|
|
48
|
-
getLogPath: () => join(TEST_DIR, "logs", "vellum.log"),
|
|
49
|
-
getHistoryPath: () => join(TEST_DIR, "history"),
|
|
50
|
-
isMacOS: () => process.platform === "darwin",
|
|
51
|
-
isLinux: () => process.platform === "linux",
|
|
52
|
-
isWindows: () => process.platform === "win32",
|
|
53
|
-
getPlatformName: () => process.platform,
|
|
54
|
-
getClipboardCommand: () => null,
|
|
55
|
-
}));
|
|
56
|
-
|
|
57
39
|
mock.module("../util/logger.js", () => ({
|
|
58
40
|
getLogger: () =>
|
|
59
41
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -81,7 +63,6 @@ mock.module("../tools/terminal/sandbox.js", () => ({
|
|
|
81
63
|
}));
|
|
82
64
|
|
|
83
65
|
import { loadSkillCatalog } from "../config/skills.js";
|
|
84
|
-
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
85
66
|
import { executeDeleteManagedSkill } from "../tools/skills/delete-managed.js";
|
|
86
67
|
import { SkillLoadTool } from "../tools/skills/load.js";
|
|
87
68
|
import { executeScaffoldManagedSkill } from "../tools/skills/scaffold-managed.js";
|
|
@@ -96,14 +77,10 @@ function makeContext(): ToolContext {
|
|
|
96
77
|
}
|
|
97
78
|
|
|
98
79
|
beforeEach(() => {
|
|
99
|
-
TEST_DIR =
|
|
80
|
+
TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
100
81
|
mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
|
|
101
82
|
});
|
|
102
83
|
|
|
103
|
-
afterEach(() => {
|
|
104
|
-
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
105
|
-
});
|
|
106
|
-
|
|
107
84
|
describe("managed skill lifecycle: scaffold → catalog → prompt → delete", () => {
|
|
108
85
|
test("full lifecycle: create skill, verify in catalog and prompt, then delete", async () => {
|
|
109
86
|
// Step 1: Scaffold a managed skill
|
|
@@ -137,14 +114,7 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
|
|
|
137
114
|
expect(found!.name).toBe("Lifecycle Test");
|
|
138
115
|
expect(found!.description).toBe("Integration test skill.");
|
|
139
116
|
|
|
140
|
-
// Step 4:
|
|
141
|
-
const prompt = buildSystemPrompt();
|
|
142
|
-
expect(prompt).toContain("**lifecycle-test**");
|
|
143
|
-
expect(prompt).toContain("Integration test skill");
|
|
144
|
-
// Dynamic Skill Authoring section moved to tool descriptions; prompt should not contain it
|
|
145
|
-
expect(prompt).not.toContain("## Dynamic Skill Authoring Workflow");
|
|
146
|
-
|
|
147
|
-
// Step 5: Delete the skill
|
|
117
|
+
// Step 4: Delete the skill
|
|
148
118
|
const deleteResult = await executeDeleteManagedSkill(
|
|
149
119
|
{
|
|
150
120
|
skill_id: "lifecycle-test",
|
|
@@ -156,14 +126,14 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
|
|
|
156
126
|
const deleteData = JSON.parse(deleteResult.content as string);
|
|
157
127
|
expect(deleteData.deleted).toBe(true);
|
|
158
128
|
|
|
159
|
-
// Step
|
|
129
|
+
// Step 5: Verify skill is gone from filesystem
|
|
160
130
|
expect(existsSync(skillMdPath)).toBe(false);
|
|
161
131
|
|
|
162
|
-
// Step
|
|
132
|
+
// Step 6: Verify skill no longer in catalog
|
|
163
133
|
const catalogAfter = loadSkillCatalog();
|
|
164
134
|
expect(catalogAfter.find((s) => s.id === "lifecycle-test")).toBeUndefined();
|
|
165
135
|
|
|
166
|
-
// Step
|
|
136
|
+
// Step 7: Verify SKILLS.md index no longer has the entry
|
|
167
137
|
const indexPath = join(TEST_DIR, "skills", "SKILLS.md");
|
|
168
138
|
if (existsSync(indexPath)) {
|
|
169
139
|
const indexContent = readFileSync(indexPath, "utf-8");
|
|
@@ -7,8 +7,6 @@ import {
|
|
|
7
7
|
rmSync,
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from "node:fs";
|
|
10
|
-
import { mkdtempSync } from "node:fs";
|
|
11
|
-
import { tmpdir } from "node:os";
|
|
12
10
|
import { join } from "node:path";
|
|
13
11
|
import {
|
|
14
12
|
afterEach,
|
|
@@ -22,12 +20,7 @@ import {
|
|
|
22
20
|
|
|
23
21
|
import { parse as parseYaml } from "yaml";
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
mock.module("../util/platform.js", () => ({
|
|
28
|
-
getProtectedDir: () => join(TEST_DIR, "protected"),
|
|
29
|
-
getWorkspaceSkillsDir: () => join(TEST_DIR, "skills"),
|
|
30
|
-
}));
|
|
23
|
+
const TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
|
|
31
24
|
|
|
32
25
|
mock.module("../util/logger.js", () => ({
|
|
33
26
|
getLogger: () =>
|
|
@@ -48,12 +41,11 @@ import {
|
|
|
48
41
|
} from "../skills/managed-store.js";
|
|
49
42
|
|
|
50
43
|
beforeEach(() => {
|
|
51
|
-
TEST_DIR = mkdtempSync(join(tmpdir(), "managed-store-test-"));
|
|
52
44
|
mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
|
|
53
45
|
});
|
|
54
46
|
|
|
55
47
|
afterEach(() => {
|
|
56
|
-
rmSync(TEST_DIR, { recursive: true, force: true });
|
|
48
|
+
rmSync(join(TEST_DIR, "skills"), { recursive: true, force: true });
|
|
57
49
|
});
|
|
58
50
|
|
|
59
51
|
describe("validateManagedSkillId", () => {
|
|
@@ -64,16 +64,16 @@ const getConversationMock = mock(
|
|
|
64
64
|
);
|
|
65
65
|
|
|
66
66
|
const syncMessageToDiskMock = mock(
|
|
67
|
-
(
|
|
68
|
-
_conversationId: string,
|
|
69
|
-
_messageId: string,
|
|
70
|
-
_createdAtMs: number,
|
|
71
|
-
) => {},
|
|
67
|
+
(_conversationId: string, _messageId: string, _createdAtMs: number) => {},
|
|
72
68
|
);
|
|
73
69
|
|
|
74
70
|
const getBindingByChannelChatMock = mock(
|
|
75
71
|
(_sourceChannel: string, _externalChatId: string) =>
|
|
76
|
-
null as {
|
|
72
|
+
null as {
|
|
73
|
+
conversationId: string;
|
|
74
|
+
sourceChannel: string;
|
|
75
|
+
externalChatId: string;
|
|
76
|
+
} | null,
|
|
77
77
|
);
|
|
78
78
|
|
|
79
79
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
@@ -20,19 +20,15 @@
|
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
21
|
import {
|
|
22
22
|
mkdirSync,
|
|
23
|
-
mkdtempSync,
|
|
24
23
|
readdirSync,
|
|
25
24
|
readFileSync,
|
|
26
|
-
realpathSync,
|
|
27
25
|
rmSync,
|
|
28
26
|
unlinkSync,
|
|
29
27
|
writeFileSync,
|
|
30
28
|
} from "node:fs";
|
|
31
|
-
import { tmpdir } from "node:os";
|
|
32
29
|
import { join } from "node:path";
|
|
33
30
|
import { gzipSync } from "node:zlib";
|
|
34
31
|
import {
|
|
35
|
-
afterAll,
|
|
36
32
|
afterEach,
|
|
37
33
|
beforeAll,
|
|
38
34
|
beforeEach,
|
|
@@ -50,27 +46,11 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
|
50
46
|
) as ArrayBuffer;
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
const testDir =
|
|
54
|
-
mkdtempSync(join(tmpdir(), "migration-cross-version-test-")),
|
|
55
|
-
);
|
|
49
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
56
50
|
const testDbDir = join(testDir, "data", "db");
|
|
57
51
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
58
52
|
const testConfigPath = join(testDir, "config.json");
|
|
59
53
|
|
|
60
|
-
mock.module("../util/platform.js", () => ({
|
|
61
|
-
getProtectedDir: () => join(testDir, "protected"),
|
|
62
|
-
getDataDir: () => join(testDir, "data"),
|
|
63
|
-
getWorkspaceDir: () => testDir,
|
|
64
|
-
getWorkspaceConfigPath: () => testConfigPath,
|
|
65
|
-
isMacOS: () => process.platform === "darwin",
|
|
66
|
-
isLinux: () => process.platform === "linux",
|
|
67
|
-
isWindows: () => process.platform === "win32",
|
|
68
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
69
|
-
getDbPath: () => testDbPath,
|
|
70
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
71
|
-
ensureDataDir: () => {},
|
|
72
|
-
}));
|
|
73
|
-
|
|
74
54
|
mock.module("../util/logger.js", () => ({
|
|
75
55
|
getLogger: () =>
|
|
76
56
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -137,14 +117,6 @@ beforeAll(() => {
|
|
|
137
117
|
writeFileSync(testConfigPath, JSON.stringify(EXISTING_CONFIG, null, 2));
|
|
138
118
|
});
|
|
139
119
|
|
|
140
|
-
afterAll(() => {
|
|
141
|
-
try {
|
|
142
|
-
rmSync(testDir, { recursive: true });
|
|
143
|
-
} catch {
|
|
144
|
-
/* best effort */
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
120
|
// Restore test files before each test
|
|
149
121
|
beforeEach(() => {
|
|
150
122
|
mkdirSync(testDbDir, { recursive: true });
|
|
@@ -12,39 +12,16 @@
|
|
|
12
12
|
* - Integration: existing routes are unaffected by the new endpoint
|
|
13
13
|
*/
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
|
-
import {
|
|
16
|
-
mkdirSync,
|
|
17
|
-
mkdtempSync,
|
|
18
|
-
realpathSync,
|
|
19
|
-
rmSync,
|
|
20
|
-
writeFileSync,
|
|
21
|
-
} from "node:fs";
|
|
22
|
-
import { tmpdir } from "node:os";
|
|
15
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
23
16
|
import { join } from "node:path";
|
|
24
17
|
import { gunzipSync } from "node:zlib";
|
|
25
|
-
import {
|
|
18
|
+
import { beforeAll, describe, expect, mock, test } from "bun:test";
|
|
26
19
|
|
|
27
|
-
const testDir =
|
|
28
|
-
mkdtempSync(join(tmpdir(), "migration-export-http-test-")),
|
|
29
|
-
);
|
|
20
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
30
21
|
const testDbDir = join(testDir, "data", "db");
|
|
31
22
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
32
23
|
const testConfigPath = join(testDir, "config.json");
|
|
33
24
|
|
|
34
|
-
mock.module("../util/platform.js", () => ({
|
|
35
|
-
getProtectedDir: () => join(testDir, "protected"),
|
|
36
|
-
getDataDir: () => join(testDir, "data"),
|
|
37
|
-
getWorkspaceDir: () => testDir,
|
|
38
|
-
getWorkspaceConfigPath: () => testConfigPath,
|
|
39
|
-
isMacOS: () => process.platform === "darwin",
|
|
40
|
-
isLinux: () => process.platform === "linux",
|
|
41
|
-
isWindows: () => process.platform === "win32",
|
|
42
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
43
|
-
getDbPath: () => testDbPath,
|
|
44
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
45
|
-
ensureDataDir: () => {},
|
|
46
|
-
}));
|
|
47
|
-
|
|
48
25
|
mock.module("../util/logger.js", () => ({
|
|
49
26
|
getLogger: () =>
|
|
50
27
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -101,14 +78,6 @@ beforeAll(() => {
|
|
|
101
78
|
writeFileSync(testConfigPath, JSON.stringify(TEST_CONFIG, null, 2));
|
|
102
79
|
});
|
|
103
80
|
|
|
104
|
-
afterAll(() => {
|
|
105
|
-
try {
|
|
106
|
-
rmSync(testDir, { recursive: true });
|
|
107
|
-
} catch {
|
|
108
|
-
/* best effort */
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
81
|
// ---------------------------------------------------------------------------
|
|
113
82
|
// Tar parsing helper (mirrors vbundle-validator's internal parser)
|
|
114
83
|
// ---------------------------------------------------------------------------
|
|
@@ -15,19 +15,15 @@ import { createHash } from "node:crypto";
|
|
|
15
15
|
import {
|
|
16
16
|
existsSync,
|
|
17
17
|
mkdirSync,
|
|
18
|
-
mkdtempSync,
|
|
19
18
|
readdirSync,
|
|
20
19
|
readFileSync,
|
|
21
|
-
realpathSync,
|
|
22
20
|
rmSync,
|
|
23
21
|
unlinkSync,
|
|
24
22
|
writeFileSync,
|
|
25
23
|
} from "node:fs";
|
|
26
|
-
import { tmpdir } from "node:os";
|
|
27
24
|
import { join } from "node:path";
|
|
28
25
|
import { gzipSync } from "node:zlib";
|
|
29
26
|
import {
|
|
30
|
-
afterAll,
|
|
31
27
|
afterEach,
|
|
32
28
|
beforeAll,
|
|
33
29
|
beforeEach,
|
|
@@ -45,27 +41,11 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
|
45
41
|
) as ArrayBuffer;
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
const testDir =
|
|
49
|
-
mkdtempSync(join(tmpdir(), "migration-import-commit-http-test-")),
|
|
50
|
-
);
|
|
44
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
51
45
|
const testDbDir = join(testDir, "data", "db");
|
|
52
46
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
53
47
|
const testConfigPath = join(testDir, "config.json");
|
|
54
48
|
|
|
55
|
-
mock.module("../util/platform.js", () => ({
|
|
56
|
-
getProtectedDir: () => join(testDir, "protected"),
|
|
57
|
-
getDataDir: () => join(testDir, "data"),
|
|
58
|
-
getWorkspaceDir: () => testDir,
|
|
59
|
-
getWorkspaceConfigPath: () => testConfigPath,
|
|
60
|
-
isMacOS: () => process.platform === "darwin",
|
|
61
|
-
isLinux: () => process.platform === "linux",
|
|
62
|
-
isWindows: () => process.platform === "win32",
|
|
63
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
64
|
-
getDbPath: () => testDbPath,
|
|
65
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
66
|
-
ensureDataDir: () => {},
|
|
67
|
-
}));
|
|
68
|
-
|
|
69
49
|
mock.module("../util/logger.js", () => ({
|
|
70
50
|
getLogger: () =>
|
|
71
51
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -119,14 +99,6 @@ beforeAll(() => {
|
|
|
119
99
|
writeFileSync(testConfigPath, JSON.stringify(EXISTING_CONFIG, null, 2));
|
|
120
100
|
});
|
|
121
101
|
|
|
122
|
-
afterAll(() => {
|
|
123
|
-
try {
|
|
124
|
-
rmSync(testDir, { recursive: true });
|
|
125
|
-
} catch {
|
|
126
|
-
/* best effort */
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
102
|
// Restore test files before each test so mutations from previous tests
|
|
131
103
|
// do not leak across test cases.
|
|
132
104
|
beforeEach(() => {
|
|
@@ -11,17 +11,10 @@
|
|
|
11
11
|
* - Integration: existing routes are unaffected by the new endpoint
|
|
12
12
|
*/
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
-
import {
|
|
15
|
-
mkdirSync,
|
|
16
|
-
mkdtempSync,
|
|
17
|
-
realpathSync,
|
|
18
|
-
rmSync,
|
|
19
|
-
writeFileSync,
|
|
20
|
-
} from "node:fs";
|
|
21
|
-
import { tmpdir } from "node:os";
|
|
14
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
22
15
|
import { join } from "node:path";
|
|
23
16
|
import { gzipSync } from "node:zlib";
|
|
24
|
-
import {
|
|
17
|
+
import { beforeAll, describe, expect, mock, test } from "bun:test";
|
|
25
18
|
|
|
26
19
|
/** Convert a Uint8Array to an ArrayBuffer for BodyInit compatibility. */
|
|
27
20
|
function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
@@ -31,27 +24,11 @@ function toArrayBuffer(data: Uint8Array): ArrayBuffer {
|
|
|
31
24
|
) as ArrayBuffer;
|
|
32
25
|
}
|
|
33
26
|
|
|
34
|
-
const testDir =
|
|
35
|
-
mkdtempSync(join(tmpdir(), "migration-import-preflight-http-test-")),
|
|
36
|
-
);
|
|
27
|
+
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
37
28
|
const testDbDir = join(testDir, "data", "db");
|
|
38
29
|
const testDbPath = join(testDbDir, "assistant.db");
|
|
39
30
|
const testConfigPath = join(testDir, "config.json");
|
|
40
31
|
|
|
41
|
-
mock.module("../util/platform.js", () => ({
|
|
42
|
-
getProtectedDir: () => join(testDir, "protected"),
|
|
43
|
-
getDataDir: () => join(testDir, "data"),
|
|
44
|
-
getWorkspaceDir: () => testDir,
|
|
45
|
-
getWorkspaceConfigPath: () => testConfigPath,
|
|
46
|
-
isMacOS: () => process.platform === "darwin",
|
|
47
|
-
isLinux: () => process.platform === "linux",
|
|
48
|
-
isWindows: () => process.platform === "win32",
|
|
49
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
50
|
-
getDbPath: () => testDbPath,
|
|
51
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
52
|
-
ensureDataDir: () => {},
|
|
53
|
-
}));
|
|
54
|
-
|
|
55
32
|
mock.module("../util/logger.js", () => ({
|
|
56
33
|
getLogger: () =>
|
|
57
34
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -107,14 +84,6 @@ beforeAll(() => {
|
|
|
107
84
|
writeFileSync(testConfigPath, JSON.stringify(EXISTING_CONFIG, null, 2));
|
|
108
85
|
});
|
|
109
86
|
|
|
110
|
-
afterAll(() => {
|
|
111
|
-
try {
|
|
112
|
-
rmSync(testDir, { recursive: true });
|
|
113
|
-
} catch {
|
|
114
|
-
/* best effort */
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
87
|
// ---------------------------------------------------------------------------
|
|
119
88
|
// Tar archive builder helpers (mirrors validate test)
|
|
120
89
|
// ---------------------------------------------------------------------------
|
|
@@ -5,7 +5,8 @@ import { describe, expect, test } from "bun:test";
|
|
|
5
5
|
/**
|
|
6
6
|
* Guard test: domain-specific routing sections must NOT appear in the system
|
|
7
7
|
* prompt source file. Routing cues now live in skill frontmatter
|
|
8
|
-
* (`activation-hints` / `avoid-when`) and are
|
|
8
|
+
* (`activation-hints` / `avoid-when`) and are seeded as capability memories
|
|
9
|
+
* for semantic discovery.
|
|
9
10
|
*
|
|
10
11
|
* If this test fails, you are re-introducing hardcoded routing into the system
|
|
11
12
|
* prompt. Instead, add `activation-hints` to the skill's SKILL.md frontmatter.
|
|
@@ -55,7 +55,6 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
55
55
|
defaultScopes: "[]",
|
|
56
56
|
scopePolicy: "[]",
|
|
57
57
|
extraParams: null,
|
|
58
|
-
callbackTransport: null,
|
|
59
58
|
pingUrl: null,
|
|
60
59
|
pingMethod: null,
|
|
61
60
|
pingHeaders: null,
|
|
@@ -70,6 +69,7 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
70
69
|
identityBody: null,
|
|
71
70
|
identityFormat: null,
|
|
72
71
|
identityOkField: null,
|
|
72
|
+
featureFlag: null,
|
|
73
73
|
createdAt: 1735689500000,
|
|
74
74
|
updatedAt: 1735689550000,
|
|
75
75
|
}
|
|
@@ -88,16 +88,18 @@ mock.module("../oauth/oauth-store.js", () => ({
|
|
|
88
88
|
),
|
|
89
89
|
}));
|
|
90
90
|
|
|
91
|
+
const mockOrchestrateOAuthConnect = mock(() =>
|
|
92
|
+
Promise.resolve({
|
|
93
|
+
success: true,
|
|
94
|
+
deferred: false,
|
|
95
|
+
grantedScopes: [],
|
|
96
|
+
accountInfo: null,
|
|
97
|
+
refreshTokenPresent: false,
|
|
98
|
+
}),
|
|
99
|
+
);
|
|
100
|
+
|
|
91
101
|
mock.module("../oauth/connect-orchestrator.js", () => ({
|
|
92
|
-
orchestrateOAuthConnect:
|
|
93
|
-
Promise.resolve({
|
|
94
|
-
success: true,
|
|
95
|
-
deferred: false,
|
|
96
|
-
grantedScopes: [],
|
|
97
|
-
accountInfo: null,
|
|
98
|
-
refreshTokenPresent: false,
|
|
99
|
-
}),
|
|
100
|
-
),
|
|
102
|
+
orchestrateOAuthConnect: mockOrchestrateOAuthConnect,
|
|
101
103
|
}));
|
|
102
104
|
|
|
103
105
|
import { oauthAppsRouteDefinitions } from "../runtime/routes/oauth-apps.js";
|
|
@@ -202,3 +204,111 @@ describe("GET /v1/oauth/apps", () => {
|
|
|
202
204
|
expect(body.provider).toBeNull();
|
|
203
205
|
});
|
|
204
206
|
});
|
|
207
|
+
|
|
208
|
+
describe("POST /v1/oauth/apps/:appId/connect — callback_transport", () => {
|
|
209
|
+
function connectRequest(body?: unknown) {
|
|
210
|
+
return new Request("http://localhost/v1/oauth/apps/app-1/connect", {
|
|
211
|
+
method: "POST",
|
|
212
|
+
headers: { "Content-Type": "application/json" },
|
|
213
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
test('callback_transport: "gateway" is accepted and passed through', async () => {
|
|
218
|
+
mockOrchestrateOAuthConnect.mockClear();
|
|
219
|
+
const req = connectRequest({ callback_transport: "gateway" });
|
|
220
|
+
const url = new URL(req.url);
|
|
221
|
+
const res = await getRoute("POST", "oauth/apps/:appId/connect").handler({
|
|
222
|
+
req,
|
|
223
|
+
url,
|
|
224
|
+
server: null as never,
|
|
225
|
+
authContext: null as never,
|
|
226
|
+
params: { appId: "app-1" },
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(res.status).toBe(200);
|
|
230
|
+
expect(mockOrchestrateOAuthConnect).toHaveBeenCalledTimes(1);
|
|
231
|
+
const callArgs = (
|
|
232
|
+
mockOrchestrateOAuthConnect.mock.calls as unknown as Array<
|
|
233
|
+
[{ callbackTransport: string }]
|
|
234
|
+
>
|
|
235
|
+
)[0]![0];
|
|
236
|
+
expect(callArgs.callbackTransport).toBe("gateway");
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('callback_transport: "loopback" is accepted and passed through', async () => {
|
|
240
|
+
mockOrchestrateOAuthConnect.mockClear();
|
|
241
|
+
const req = connectRequest({ callback_transport: "loopback" });
|
|
242
|
+
const url = new URL(req.url);
|
|
243
|
+
const res = await getRoute("POST", "oauth/apps/:appId/connect").handler({
|
|
244
|
+
req,
|
|
245
|
+
url,
|
|
246
|
+
server: null as never,
|
|
247
|
+
authContext: null as never,
|
|
248
|
+
params: { appId: "app-1" },
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(res.status).toBe(200);
|
|
252
|
+
expect(mockOrchestrateOAuthConnect).toHaveBeenCalledTimes(1);
|
|
253
|
+
const callArgs = (
|
|
254
|
+
mockOrchestrateOAuthConnect.mock.calls as unknown as Array<
|
|
255
|
+
[{ callbackTransport: string }]
|
|
256
|
+
>
|
|
257
|
+
)[0]![0];
|
|
258
|
+
expect(callArgs.callbackTransport).toBe("loopback");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('omitting callback_transport defaults to "loopback"', async () => {
|
|
262
|
+
mockOrchestrateOAuthConnect.mockClear();
|
|
263
|
+
const req = connectRequest({ scopes: ["email"] });
|
|
264
|
+
const url = new URL(req.url);
|
|
265
|
+
const res = await getRoute("POST", "oauth/apps/:appId/connect").handler({
|
|
266
|
+
req,
|
|
267
|
+
url,
|
|
268
|
+
server: null as never,
|
|
269
|
+
authContext: null as never,
|
|
270
|
+
params: { appId: "app-1" },
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
expect(res.status).toBe(200);
|
|
274
|
+
expect(mockOrchestrateOAuthConnect).toHaveBeenCalledTimes(1);
|
|
275
|
+
const callArgs = (
|
|
276
|
+
mockOrchestrateOAuthConnect.mock.calls as unknown as Array<
|
|
277
|
+
[{ callbackTransport: string }]
|
|
278
|
+
>
|
|
279
|
+
)[0]![0];
|
|
280
|
+
expect(callArgs.callbackTransport).toBe("loopback");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('invalid callback_transport "websocket" returns 400', async () => {
|
|
284
|
+
mockOrchestrateOAuthConnect.mockClear();
|
|
285
|
+
const req = connectRequest({ callback_transport: "websocket" });
|
|
286
|
+
const url = new URL(req.url);
|
|
287
|
+
const res = await getRoute("POST", "oauth/apps/:appId/connect").handler({
|
|
288
|
+
req,
|
|
289
|
+
url,
|
|
290
|
+
server: null as never,
|
|
291
|
+
authContext: null as never,
|
|
292
|
+
params: { appId: "app-1" },
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(res.status).toBe(400);
|
|
296
|
+
expect(mockOrchestrateOAuthConnect).not.toHaveBeenCalled();
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test("invalid callback_transport (number) returns 400", async () => {
|
|
300
|
+
mockOrchestrateOAuthConnect.mockClear();
|
|
301
|
+
const req = connectRequest({ callback_transport: 123 });
|
|
302
|
+
const url = new URL(req.url);
|
|
303
|
+
const res = await getRoute("POST", "oauth/apps/:appId/connect").handler({
|
|
304
|
+
req,
|
|
305
|
+
url,
|
|
306
|
+
server: null as never,
|
|
307
|
+
authContext: null as never,
|
|
308
|
+
params: { appId: "app-1" },
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
expect(res.status).toBe(400);
|
|
312
|
+
expect(mockOrchestrateOAuthConnect).not.toHaveBeenCalled();
|
|
313
|
+
});
|
|
314
|
+
});
|