@vellumai/assistant 0.5.4 → 0.5.5
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/Dockerfile +18 -27
- package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +42 -0
- package/package.json +1 -1
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/openai-whisper.test.ts +93 -0
- package/src/__tests__/slack-messaging-token-resolution.test.ts +319 -0
- package/src/__tests__/volume-security-guard.test.ts +155 -0
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +16 -37
- package/src/config/env-registry.ts +9 -0
- package/src/config/feature-flag-registry.json +8 -0
- package/src/credential-execution/managed-catalog.ts +5 -15
- package/src/daemon/config-watcher.ts +4 -1
- package/src/daemon/daemon-control.ts +7 -0
- package/src/daemon/lifecycle.ts +7 -1
- package/src/daemon/providers-setup.ts +2 -1
- package/src/hooks/manager.ts +7 -0
- package/src/instrument.ts +33 -1
- package/src/memory/embedding-local.ts +11 -5
- package/src/memory/job-handlers/conversation-starters.ts +24 -36
- package/src/messaging/provider.ts +9 -0
- package/src/messaging/providers/slack/adapter.ts +29 -2
- package/src/oauth/connection-resolver.test.ts +22 -18
- package/src/oauth/connection-resolver.ts +92 -7
- package/src/oauth/platform-connection.test.ts +78 -69
- package/src/oauth/platform-connection.ts +12 -19
- package/src/permissions/trust-client.ts +343 -0
- package/src/permissions/trust-store-interface.ts +105 -0
- package/src/permissions/trust-store.ts +523 -36
- package/src/platform/client.test.ts +148 -0
- package/src/platform/client.ts +71 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +190 -0
- package/src/providers/speech-to-text/openai-whisper.ts +68 -0
- package/src/providers/speech-to-text/resolve.ts +9 -0
- package/src/providers/speech-to-text/types.ts +17 -0
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/routes/inbound-message-handler.ts +27 -3
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +287 -0
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +122 -0
- package/src/runtime/routes/log-export-routes.ts +1 -0
- package/src/runtime/routes/secret-routes.ts +4 -1
- package/src/security/ces-credential-client.ts +173 -0
- package/src/security/secure-keys.ts +65 -22
- package/src/signals/bash.ts +3 -0
- package/src/signals/cancel.ts +3 -0
- package/src/signals/confirm.ts +3 -0
- package/src/signals/conversation-undo.ts +3 -0
- package/src/signals/event-stream.ts +7 -0
- package/src/signals/shotgun.ts +3 -0
- package/src/signals/trust-rule.ts +3 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +23 -36
- package/src/telemetry/usage-telemetry-reporter.ts +21 -19
- package/src/util/device-id.ts +70 -7
- package/src/util/logger.ts +35 -9
- package/src/util/platform.ts +29 -5
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +113 -0
- package/src/workspace/migrations/registry.ts +2 -0
package/src/util/platform.ts
CHANGED
|
@@ -8,7 +8,11 @@ import {
|
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
getBaseDataDir,
|
|
13
|
+
getIsContainerized,
|
|
14
|
+
getWorkspaceDirOverride,
|
|
15
|
+
} from "../config/env-registry.js";
|
|
12
16
|
|
|
13
17
|
export function isMacOS(): boolean {
|
|
14
18
|
return process.platform === "darwin";
|
|
@@ -237,6 +241,15 @@ export function getInterfacesDir(): string {
|
|
|
237
241
|
return join(getDataDir(), "interfaces");
|
|
238
242
|
}
|
|
239
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Returns the sounds directory (~/.vellum/workspace/data/sounds).
|
|
246
|
+
* Custom sound files and sound configuration live here. Sound files
|
|
247
|
+
* can be large, so this directory is excluded from diagnostic exports.
|
|
248
|
+
*/
|
|
249
|
+
export function getSoundsDir(): string {
|
|
250
|
+
return join(getWorkspaceDir(), "data", "sounds");
|
|
251
|
+
}
|
|
252
|
+
|
|
240
253
|
/**
|
|
241
254
|
* Returns the TCP port the daemon should listen on for iOS clients.
|
|
242
255
|
* Hardcoded default: 8765.
|
|
@@ -356,13 +369,19 @@ export function getSignalsDir(): string {
|
|
|
356
369
|
// Currently not used by call-sites; wired in later PRs.
|
|
357
370
|
|
|
358
371
|
/**
|
|
359
|
-
* Returns
|
|
372
|
+
* Returns the workspace root for user-facing state.
|
|
373
|
+
*
|
|
374
|
+
* When the WORKSPACE_DIR env var is set, returns that value (used in
|
|
375
|
+
* containerized deployments where the workspace is a separate volume).
|
|
376
|
+
* Otherwise falls back to ~/.vellum/workspace.
|
|
360
377
|
*
|
|
361
378
|
* WARNING: The entire workspace directory is included in diagnostic log exports
|
|
362
379
|
* ("Send logs to Vellum"). Do not store secrets, API keys, or sensitive
|
|
363
380
|
* credentials here — use the credential store or ~/.vellum/protected/ instead.
|
|
364
381
|
*/
|
|
365
382
|
export function getWorkspaceDir(): string {
|
|
383
|
+
const override = getWorkspaceDirOverride();
|
|
384
|
+
if (override) return override;
|
|
366
385
|
return join(getRootDir(), "workspace");
|
|
367
386
|
}
|
|
368
387
|
|
|
@@ -413,13 +432,17 @@ export function ensureDataDir(): void {
|
|
|
413
432
|
const root = getRootDir();
|
|
414
433
|
const workspace = getWorkspaceDir();
|
|
415
434
|
const wsData = join(workspace, "data");
|
|
435
|
+
const containerized = getIsContainerized();
|
|
416
436
|
const dirs = [
|
|
417
|
-
// Root-level dirs (runtime
|
|
437
|
+
// Root-level dirs (runtime)
|
|
418
438
|
root,
|
|
419
|
-
|
|
439
|
+
// protected, signals, hooks are local-only — skip in containerized mode
|
|
440
|
+
// (credentials via CES HTTP API, trust via gateway API, no IPC signals)
|
|
441
|
+
...(containerized
|
|
442
|
+
? []
|
|
443
|
+
: [join(root, "protected"), join(root, "signals"), join(root, "hooks")]),
|
|
420
444
|
// Workspace dirs
|
|
421
445
|
workspace,
|
|
422
|
-
join(root, "hooks"),
|
|
423
446
|
join(workspace, "skills"),
|
|
424
447
|
join(workspace, "embedding-models"),
|
|
425
448
|
join(workspace, "conversations"),
|
|
@@ -432,6 +455,7 @@ export function ensureDataDir(): void {
|
|
|
432
455
|
join(wsData, "memory", "knowledge"),
|
|
433
456
|
join(wsData, "apps"),
|
|
434
457
|
join(wsData, "interfaces"),
|
|
458
|
+
join(wsData, "sounds"),
|
|
435
459
|
];
|
|
436
460
|
for (const dir of dirs) {
|
|
437
461
|
if (!existsSync(dir)) {
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace migration: Migrate workspace data from /data to /workspace volume.
|
|
3
|
+
*
|
|
4
|
+
* In the old Docker volume layout, workspace data lived at
|
|
5
|
+
* `$BASE_DATA_DIR/.vellum/workspace`. In the new layout, WORKSPACE_DIR points
|
|
6
|
+
* to a dedicated volume (e.g. `/workspace`). On first boot with the new layout,
|
|
7
|
+
* this migration copies existing workspace data from the old location to the
|
|
8
|
+
* new volume so nothing is lost.
|
|
9
|
+
*
|
|
10
|
+
* Idempotent:
|
|
11
|
+
* - Skips if WORKSPACE_DIR is not set (non-Docker or old layout).
|
|
12
|
+
* - Skips if the workspace volume already has data (config.json exists).
|
|
13
|
+
* - Skips if the sentinel file exists (already migrated).
|
|
14
|
+
* - Skips if the old workspace directory doesn't exist or is empty.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { cpSync, existsSync, readdirSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
getBaseDataDir,
|
|
22
|
+
getWorkspaceDirOverride,
|
|
23
|
+
} from "../../config/env-registry.js";
|
|
24
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
25
|
+
|
|
26
|
+
const SENTINEL_FILENAME = ".workspace-volume-migrated";
|
|
27
|
+
|
|
28
|
+
export const migrateToWorkspaceVolumeMigration: WorkspaceMigration = {
|
|
29
|
+
id: "014-migrate-to-workspace-volume",
|
|
30
|
+
description:
|
|
31
|
+
"Copy workspace data from old /data/.vellum/workspace to new WORKSPACE_DIR volume on first boot",
|
|
32
|
+
|
|
33
|
+
run(workspaceDir: string): void {
|
|
34
|
+
const workspaceDirOverride = getWorkspaceDirOverride();
|
|
35
|
+
|
|
36
|
+
// Only relevant when WORKSPACE_DIR is explicitly set (Docker with separate volume)
|
|
37
|
+
if (!workspaceDirOverride) return;
|
|
38
|
+
|
|
39
|
+
const sentinelPath = join(workspaceDir, SENTINEL_FILENAME);
|
|
40
|
+
|
|
41
|
+
// Already migrated — skip
|
|
42
|
+
if (existsSync(sentinelPath)) return;
|
|
43
|
+
|
|
44
|
+
// If the workspace volume already has data (config.json), assume it's
|
|
45
|
+
// already populated — either by a previous migration or manual setup.
|
|
46
|
+
if (existsSync(join(workspaceDir, "config.json"))) {
|
|
47
|
+
// Write sentinel so we don't re-check on every boot
|
|
48
|
+
writeSentinel(sentinelPath);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Resolve the old workspace location: $BASE_DATA_DIR/.vellum/workspace
|
|
53
|
+
const baseDataDir = getBaseDataDir();
|
|
54
|
+
if (!baseDataDir) {
|
|
55
|
+
// No BASE_DATA_DIR means there's no old location to migrate from
|
|
56
|
+
writeSentinel(sentinelPath);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const oldWorkspaceDir = join(baseDataDir, ".vellum", "workspace");
|
|
61
|
+
|
|
62
|
+
// If the old workspace doesn't exist or is empty, nothing to migrate
|
|
63
|
+
if (!existsSync(oldWorkspaceDir)) {
|
|
64
|
+
writeSentinel(sentinelPath);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
let entries: string[];
|
|
69
|
+
try {
|
|
70
|
+
entries = readdirSync(oldWorkspaceDir);
|
|
71
|
+
} catch {
|
|
72
|
+
// Can't read old workspace — write sentinel and move on
|
|
73
|
+
writeSentinel(sentinelPath);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (entries.length === 0) {
|
|
78
|
+
writeSentinel(sentinelPath);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Copy everything from old workspace to new workspace volume.
|
|
83
|
+
// Use cpSync with recursive to handle nested directories.
|
|
84
|
+
// Copy each entry individually rather than the whole directory to avoid
|
|
85
|
+
// overwriting the target directory itself (which may already have
|
|
86
|
+
// sub-directories created by ensureDataDir).
|
|
87
|
+
for (const entry of entries) {
|
|
88
|
+
const src = join(oldWorkspaceDir, entry);
|
|
89
|
+
const dst = join(workspaceDir, entry);
|
|
90
|
+
|
|
91
|
+
// Skip if destination already exists (partial previous run)
|
|
92
|
+
if (existsSync(dst)) continue;
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
cpSync(src, dst, { recursive: true });
|
|
96
|
+
} catch {
|
|
97
|
+
// Best-effort per entry — continue with remaining items
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Mark migration complete
|
|
102
|
+
writeSentinel(sentinelPath);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function writeSentinel(sentinelPath: string): void {
|
|
107
|
+
try {
|
|
108
|
+
writeFileSync(sentinelPath, new Date().toISOString() + "\n", "utf-8");
|
|
109
|
+
} catch {
|
|
110
|
+
// Best-effort — if we can't write the sentinel, the migration runner's
|
|
111
|
+
// checkpoint will still prevent re-running the migration function.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -10,6 +10,7 @@ import { appDirRenameMigration } from "./010-app-dir-rename.js";
|
|
|
10
10
|
import { backfillInstallationIdMigration } from "./011-backfill-installation-id.js";
|
|
11
11
|
import { renameConversationDiskViewDirsMigration } from "./012-rename-conversation-disk-view-dirs.js";
|
|
12
12
|
import { repairConversationDiskViewMigration } from "./013-repair-conversation-disk-view.js";
|
|
13
|
+
import { migrateToWorkspaceVolumeMigration } from "./migrate-to-workspace-volume.js";
|
|
13
14
|
import type { WorkspaceMigration } from "./types.js";
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -29,4 +30,5 @@ export const WORKSPACE_MIGRATIONS: WorkspaceMigration[] = [
|
|
|
29
30
|
appDirRenameMigration,
|
|
30
31
|
renameConversationDiskViewDirsMigration,
|
|
31
32
|
repairConversationDiskViewMigration,
|
|
33
|
+
migrateToWorkspaceVolumeMigration,
|
|
32
34
|
];
|