@vellumai/assistant 0.3.3 → 0.3.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 +2 -0
- package/README.md +45 -18
- package/package.json +1 -1
- package/scripts/ipc/generate-swift.ts +13 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +100 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +391 -1
- package/src/__tests__/call-routes-http.test.ts +27 -2
- package/src/__tests__/channel-approval-routes.test.ts +397 -135
- package/src/__tests__/channel-approvals.test.ts +99 -3
- package/src/__tests__/channel-delivery-store.test.ts +30 -4
- package/src/__tests__/channel-guardian.test.ts +261 -22
- package/src/__tests__/channel-readiness-service.test.ts +257 -0
- package/src/__tests__/config-schema.test.ts +2 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/daemon-lifecycle.test.ts +636 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +19 -13
- package/src/__tests__/handlers-twilio-config.test.ts +480 -0
- package/src/__tests__/ipc-snapshot.test.ts +63 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +4 -0
- package/src/__tests__/run-orchestrator.test.ts +22 -0
- package/src/__tests__/secret-scanner.test.ts +223 -0
- package/src/__tests__/session-runtime-assembly.test.ts +85 -1
- package/src/__tests__/shell-parser-property.test.ts +357 -2
- package/src/__tests__/sms-messaging-provider.test.ts +125 -0
- package/src/__tests__/system-prompt.test.ts +25 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +34 -1
- package/src/__tests__/twilio-routes.test.ts +39 -3
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +68 -0
- package/src/__tests__/web-search.test.ts +1 -1
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/calls/call-domain.ts +8 -5
- package/src/calls/call-orchestrator.ts +85 -22
- package/src/calls/twilio-config.ts +17 -11
- package/src/calls/twilio-rest.ts +276 -0
- package/src/calls/twilio-routes.ts +39 -1
- package/src/cli/map.ts +6 -0
- package/src/commands/__tests__/cc-command-registry.test.ts +67 -0
- package/src/commands/cc-command-registry.ts +14 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +10 -3
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +199 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +320 -0
- package/src/config/bundled-skills/media-processing/services/capability-registry.ts +137 -0
- package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +280 -0
- package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +144 -0
- package/src/config/bundled-skills/media-processing/services/feedback-store.ts +136 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +261 -0
- package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +95 -0
- package/src/config/bundled-skills/media-processing/services/timeline-service.ts +267 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +301 -0
- package/src/config/bundled-skills/media-processing/tools/detect-events.ts +110 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +190 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +166 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +300 -0
- package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +235 -0
- package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +142 -0
- package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +150 -0
- package/src/config/bundled-skills/messaging/SKILL.md +24 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/defaults.ts +2 -1
- package/src/config/schema.ts +9 -3
- package/src/config/skills.ts +5 -32
- package/src/config/system-prompt.ts +40 -0
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/user-reference.ts +29 -0
- package/src/config/vellum-skills/catalog.json +58 -0
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +3 -3
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +3 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +118 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +6 -1
- package/src/config/vellum-skills/twilio-setup/SKILL.md +76 -6
- package/src/daemon/auth-manager.ts +103 -0
- package/src/daemon/computer-use-session.ts +8 -1
- package/src/daemon/config-watcher.ts +253 -0
- package/src/daemon/handlers/config.ts +819 -22
- package/src/daemon/handlers/dictation.ts +182 -0
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +2 -0
- package/src/daemon/handlers/sessions.ts +2 -0
- package/src/daemon/handlers/shared.ts +3 -0
- package/src/daemon/handlers/skills.ts +6 -7
- package/src/daemon/handlers/work-items.ts +15 -7
- package/src/daemon/ipc-contract-inventory.json +10 -0
- package/src/daemon/ipc-contract.ts +114 -4
- package/src/daemon/ipc-handler.ts +87 -0
- package/src/daemon/lifecycle.ts +18 -4
- package/src/daemon/ride-shotgun-handler.ts +11 -1
- package/src/daemon/server.ts +111 -504
- package/src/daemon/session-agent-loop.ts +10 -15
- package/src/daemon/session-runtime-assembly.ts +115 -44
- package/src/daemon/session-tool-setup.ts +2 -0
- package/src/daemon/session.ts +19 -2
- package/src/inbound/public-ingress-urls.ts +3 -3
- package/src/memory/channel-guardian-store.ts +2 -1
- package/src/memory/db-connection.ts +28 -0
- package/src/memory/db-init.ts +1163 -0
- package/src/memory/db.ts +2 -2007
- package/src/memory/embedding-backend.ts +79 -11
- package/src/memory/indexer.ts +2 -0
- package/src/memory/job-handlers/media-processing.ts +100 -0
- package/src/memory/job-utils.ts +64 -4
- package/src/memory/jobs-store.ts +2 -1
- package/src/memory/jobs-worker.ts +11 -1
- package/src/memory/media-store.ts +759 -0
- package/src/memory/recall-cache.ts +107 -0
- package/src/memory/retriever.ts +36 -2
- package/src/memory/schema-migration.ts +984 -0
- package/src/memory/schema.ts +99 -0
- package/src/memory/search/entity.ts +208 -25
- package/src/memory/search/ranking.ts +6 -1
- package/src/memory/search/types.ts +26 -0
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +204 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/permissions/checker.ts +16 -2
- package/src/permissions/prompter.ts +14 -3
- package/src/permissions/trust-store.ts +7 -0
- package/src/runtime/approval-message-composer.ts +143 -0
- package/src/runtime/channel-approvals.ts +29 -7
- package/src/runtime/channel-guardian-service.ts +44 -18
- package/src/runtime/channel-readiness-service.ts +292 -0
- package/src/runtime/channel-readiness-types.ts +29 -0
- package/src/runtime/gateway-client.ts +2 -1
- package/src/runtime/http-server.ts +65 -28
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/routes/call-routes.ts +2 -1
- package/src/runtime/routes/channel-routes.ts +237 -103
- package/src/runtime/routes/run-routes.ts +7 -1
- package/src/runtime/run-orchestrator.ts +43 -3
- package/src/security/secret-scanner.ts +218 -0
- package/src/skills/frontmatter.ts +63 -0
- package/src/skills/slash-commands.ts +23 -0
- package/src/skills/vellum-catalog-remote.ts +107 -0
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/browser/auto-navigate.ts +132 -24
- package/src/tools/browser/browser-manager.ts +67 -61
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/claude-code/claude-code.ts +55 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +10 -2
- package/src/tools/network/web-search.ts +1 -1
- package/src/tools/skills/vellum-catalog.ts +61 -156
- package/src/tools/terminal/parser.ts +21 -5
- package/src/tools/types.ts +2 -0
- package/src/twitter/router.ts +1 -1
- package/src/util/platform.ts +43 -1
- package/src/util/retry.ts +4 -4
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
-
import { basename, join } from 'node:path';
|
|
3
|
-
|
|
4
1
|
import { RiskLevel } from '../../permissions/types.js';
|
|
5
2
|
import type { ToolDefinition } from '../../providers/types.js';
|
|
3
|
+
import { parseFrontmatterFields } from '../../skills/frontmatter.js';
|
|
6
4
|
import { createManagedSkill } from '../../skills/managed-store.js';
|
|
7
|
-
import {
|
|
5
|
+
import { fetchCatalogEntries, fetchSkillContent, checkVellumSkill } from '../../skills/vellum-catalog-remote.js';
|
|
8
6
|
import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
|
|
9
7
|
|
|
10
|
-
const log = getLogger('vellum-skills-catalog');
|
|
11
|
-
|
|
12
|
-
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
|
|
13
|
-
|
|
14
|
-
function getVellumSkillsDir(): string {
|
|
15
|
-
return join(import.meta.dir, '..', '..', 'config', 'vellum-skills');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
8
|
export interface CatalogEntry {
|
|
19
9
|
id: string;
|
|
20
10
|
name: string;
|
|
@@ -23,123 +13,75 @@ export interface CatalogEntry {
|
|
|
23
13
|
includes?: string[];
|
|
24
14
|
}
|
|
25
15
|
|
|
26
|
-
|
|
27
|
-
const skillFilePath = join(directory, 'SKILL.md');
|
|
28
|
-
if (!existsSync(skillFilePath)) return null;
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const stat = statSync(skillFilePath);
|
|
32
|
-
if (!stat.isFile()) return null;
|
|
33
|
-
|
|
34
|
-
const content = readFileSync(skillFilePath, 'utf-8');
|
|
35
|
-
const match = content.match(FRONTMATTER_REGEX);
|
|
36
|
-
if (!match) return null;
|
|
37
|
-
|
|
38
|
-
const fields: Record<string, string> = {};
|
|
39
|
-
for (const line of match[1].split(/\r?\n/)) {
|
|
40
|
-
const trimmed = line.trim();
|
|
41
|
-
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
42
|
-
const separatorIndex = trimmed.indexOf(':');
|
|
43
|
-
if (separatorIndex === -1) continue;
|
|
44
|
-
|
|
45
|
-
const key = trimmed.slice(0, separatorIndex).trim();
|
|
46
|
-
let value = trimmed.slice(separatorIndex + 1).trim();
|
|
47
|
-
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
48
|
-
value = value.slice(1, -1);
|
|
49
|
-
}
|
|
50
|
-
fields[key] = value;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const name = fields.name?.trim();
|
|
54
|
-
const description = fields.description?.trim();
|
|
55
|
-
if (!name || !description) return null;
|
|
16
|
+
export { fetchCatalogEntries as listCatalogEntries, checkVellumSkill };
|
|
56
17
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
} catch {
|
|
66
|
-
// ignore malformed metadata
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let includes: string[] | undefined;
|
|
71
|
-
const includesRaw = fields.includes?.trim();
|
|
72
|
-
if (includesRaw) {
|
|
73
|
-
try {
|
|
74
|
-
const parsed = JSON.parse(includesRaw);
|
|
75
|
-
if (Array.isArray(parsed) && parsed.every((item: unknown) => typeof item === 'string')) {
|
|
76
|
-
const filtered = (parsed as string[]).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
77
|
-
if (filtered.length > 0) includes = filtered;
|
|
78
|
-
}
|
|
79
|
-
} catch {
|
|
80
|
-
// ignore malformed includes
|
|
81
|
-
}
|
|
82
|
-
}
|
|
18
|
+
/**
|
|
19
|
+
* Install a skill from the vellum-skills catalog by ID.
|
|
20
|
+
* Fetches SKILL.md from GitHub (with bundled fallback) and creates a managed skill.
|
|
21
|
+
* Returns { success, skillName, error }.
|
|
22
|
+
*/
|
|
23
|
+
export async function installFromVellumCatalog(skillId: string, options?: { overwrite?: boolean }): Promise<{ success: boolean; skillName?: string; error?: string }> {
|
|
24
|
+
const trimmedId = skillId.trim();
|
|
83
25
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return
|
|
26
|
+
// Verify skill exists in catalog
|
|
27
|
+
const exists = await checkVellumSkill(trimmedId);
|
|
28
|
+
if (!exists) {
|
|
29
|
+
return { success: false, error: `Skill "${trimmedId}" not found in the Vellum catalog` };
|
|
88
30
|
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function listCatalogEntries(): CatalogEntry[] {
|
|
92
|
-
const catalogDir = getVellumSkillsDir();
|
|
93
|
-
if (!existsSync(catalogDir)) return [];
|
|
94
31
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (!entry.isDirectory()) continue;
|
|
100
|
-
const parsed = parseCatalogEntry(join(catalogDir, entry.name));
|
|
101
|
-
if (parsed) entries.push(parsed);
|
|
102
|
-
}
|
|
103
|
-
} catch (err) {
|
|
104
|
-
log.warn({ err, catalogDir }, 'Failed to list catalog entries');
|
|
32
|
+
// Fetch SKILL.md content (remote with bundled fallback)
|
|
33
|
+
const content = await fetchSkillContent(trimmedId);
|
|
34
|
+
if (!content) {
|
|
35
|
+
return { success: false, error: `Skill "${trimmedId}" SKILL.md not found` };
|
|
105
36
|
}
|
|
106
37
|
|
|
107
|
-
|
|
108
|
-
|
|
38
|
+
const parsed = parseFrontmatterFields(content);
|
|
39
|
+
if (!parsed) {
|
|
40
|
+
return { success: false, error: `Skill "${trimmedId}" has invalid SKILL.md` };
|
|
41
|
+
}
|
|
109
42
|
|
|
110
|
-
|
|
111
|
-
* Install a skill from the vellum-skills catalog by ID.
|
|
112
|
-
* Returns { success, skillName, error }.
|
|
113
|
-
*/
|
|
114
|
-
export function installFromVellumCatalog(skillId: string): { success: boolean; skillName?: string; error?: string } {
|
|
115
|
-
const catalogDir = getVellumSkillsDir();
|
|
116
|
-
const skillDir = join(catalogDir, skillId.trim());
|
|
117
|
-
const skillFilePath = join(skillDir, 'SKILL.md');
|
|
43
|
+
const { fields, body: bodyMarkdown } = parsed;
|
|
118
44
|
|
|
119
|
-
|
|
120
|
-
|
|
45
|
+
const name = fields.name?.trim();
|
|
46
|
+
const description = fields.description?.trim();
|
|
47
|
+
if (!name || !description) {
|
|
48
|
+
return { success: false, error: `Skill "${trimmedId}" has invalid SKILL.md (missing name or description)` };
|
|
121
49
|
}
|
|
122
50
|
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
126
|
-
|
|
51
|
+
let emoji: string | undefined;
|
|
52
|
+
const metadataRaw = fields.metadata?.trim();
|
|
53
|
+
if (metadataRaw) {
|
|
54
|
+
try {
|
|
55
|
+
const metaObj = JSON.parse(metadataRaw);
|
|
56
|
+
if (metaObj?.vellum?.emoji) {
|
|
57
|
+
emoji = metaObj.vellum.emoji as string;
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
// ignore malformed metadata
|
|
61
|
+
}
|
|
127
62
|
}
|
|
128
63
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
64
|
+
let includes: string[] | undefined;
|
|
65
|
+
const includesRaw = fields.includes?.trim();
|
|
66
|
+
if (includesRaw) {
|
|
67
|
+
try {
|
|
68
|
+
const includesObj = JSON.parse(includesRaw);
|
|
69
|
+
if (Array.isArray(includesObj) && includesObj.every((item: unknown) => typeof item === 'string')) {
|
|
70
|
+
const filtered = (includesObj as string[]).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
71
|
+
if (filtered.length > 0) includes = filtered;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// ignore malformed includes
|
|
75
|
+
}
|
|
132
76
|
}
|
|
133
|
-
|
|
134
|
-
const bodyMarkdown = content.slice(match[0].length);
|
|
135
77
|
const result = createManagedSkill({
|
|
136
|
-
id:
|
|
137
|
-
name
|
|
138
|
-
description
|
|
78
|
+
id: trimmedId,
|
|
79
|
+
name,
|
|
80
|
+
description,
|
|
139
81
|
bodyMarkdown,
|
|
140
|
-
emoji
|
|
141
|
-
includes
|
|
142
|
-
overwrite: true,
|
|
82
|
+
emoji,
|
|
83
|
+
includes,
|
|
84
|
+
overwrite: options?.overwrite ?? true,
|
|
143
85
|
addToIndex: true,
|
|
144
86
|
});
|
|
145
87
|
|
|
@@ -147,7 +89,7 @@ export function installFromVellumCatalog(skillId: string): { success: boolean; s
|
|
|
147
89
|
return { success: false, error: result.error };
|
|
148
90
|
}
|
|
149
91
|
|
|
150
|
-
return { success: true, skillName:
|
|
92
|
+
return { success: true, skillName: trimmedId };
|
|
151
93
|
}
|
|
152
94
|
|
|
153
95
|
class VellumSkillsCatalogTool implements Tool {
|
|
@@ -187,7 +129,7 @@ class VellumSkillsCatalogTool implements Tool {
|
|
|
187
129
|
|
|
188
130
|
switch (action) {
|
|
189
131
|
case 'list': {
|
|
190
|
-
const entries =
|
|
132
|
+
const entries = await fetchCatalogEntries();
|
|
191
133
|
if (entries.length === 0) {
|
|
192
134
|
return { content: 'No Vellum-provided skills available in the catalog.', isError: false };
|
|
193
135
|
}
|
|
@@ -200,52 +142,15 @@ class VellumSkillsCatalogTool implements Tool {
|
|
|
200
142
|
return { content: 'Error: skill_id is required for install action', isError: true };
|
|
201
143
|
}
|
|
202
144
|
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
const skillFilePath = join(skillDir, 'SKILL.md');
|
|
206
|
-
|
|
207
|
-
if (!existsSync(skillFilePath)) {
|
|
208
|
-
const available = listCatalogEntries().map((e) => e.id);
|
|
209
|
-
return {
|
|
210
|
-
content: `Error: skill "${skillId}" not found in the Vellum catalog. Available: ${available.join(', ') || 'none'}`,
|
|
211
|
-
isError: true,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const content = readFileSync(skillFilePath, 'utf-8');
|
|
216
|
-
const match = content.match(FRONTMATTER_REGEX);
|
|
217
|
-
if (!match) {
|
|
218
|
-
return { content: `Error: skill "${skillId}" has invalid SKILL.md (missing frontmatter)`, isError: true };
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const entry = parseCatalogEntry(skillDir);
|
|
222
|
-
if (!entry) {
|
|
223
|
-
return { content: `Error: skill "${skillId}" has invalid SKILL.md`, isError: true };
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const bodyMarkdown = content.slice(match[0].length);
|
|
227
|
-
|
|
228
|
-
const result = createManagedSkill({
|
|
229
|
-
id: entry.id,
|
|
230
|
-
name: entry.name,
|
|
231
|
-
description: entry.description,
|
|
232
|
-
bodyMarkdown,
|
|
233
|
-
emoji: entry.emoji,
|
|
234
|
-
includes: entry.includes,
|
|
235
|
-
overwrite: input.overwrite === true,
|
|
236
|
-
addToIndex: true,
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
if (!result.created) {
|
|
145
|
+
const result = await installFromVellumCatalog(skillId, { overwrite: input.overwrite === true });
|
|
146
|
+
if (!result.success) {
|
|
240
147
|
return { content: `Error: ${result.error}`, isError: true };
|
|
241
148
|
}
|
|
242
149
|
|
|
243
150
|
return {
|
|
244
151
|
content: JSON.stringify({
|
|
245
152
|
installed: true,
|
|
246
|
-
skill_id:
|
|
247
|
-
name: entry.name,
|
|
248
|
-
path: result.path,
|
|
153
|
+
skill_id: result.skillName,
|
|
249
154
|
}),
|
|
250
155
|
isError: false,
|
|
251
156
|
};
|
|
@@ -36,7 +36,7 @@ export interface ParsedCommand {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
const SHELL_PROGRAMS = new Set(['sh', 'bash', 'zsh', 'dash', 'ksh', 'fish']);
|
|
39
|
-
const OPAQUE_PROGRAMS = new Set(['eval', 'source']);
|
|
39
|
+
const OPAQUE_PROGRAMS = new Set(['eval', 'source', 'alias']);
|
|
40
40
|
const DANGEROUS_ENV_VARS = new Set([
|
|
41
41
|
'LD_PRELOAD', 'LD_LIBRARY_PATH',
|
|
42
42
|
'DYLD_INSERT_LIBRARIES', 'DYLD_LIBRARY_PATH', 'DYLD_FRAMEWORK_PATH',
|
|
@@ -89,17 +89,33 @@ const initGuard = new PromiseGuard<void>();
|
|
|
89
89
|
*/
|
|
90
90
|
function findWasmPath(pkg: string, file: string): string {
|
|
91
91
|
const dir = import.meta.dirname ?? __dirname;
|
|
92
|
-
const sourcePath = join(dir, '..', '..', '..', 'node_modules', pkg, file);
|
|
93
92
|
|
|
94
|
-
|
|
93
|
+
// In compiled Bun binaries, import.meta.dirname points into the virtual
|
|
94
|
+
// /$bunfs/ filesystem. Prefer bundled WASM assets shipped alongside the
|
|
95
|
+
// executable before falling back to process.cwd(), so we never accidentally
|
|
96
|
+
// pick up a mismatched version from the working directory.
|
|
97
|
+
if (dir.startsWith('/$bunfs/')) {
|
|
95
98
|
const execDir = dirname(process.execPath);
|
|
96
99
|
// macOS .app bundle: binary is in Contents/MacOS/, resources in Contents/Resources/
|
|
97
100
|
const resourcesPath = join(execDir, '..', 'Resources', file);
|
|
98
101
|
if (existsSync(resourcesPath)) return resourcesPath;
|
|
99
|
-
//
|
|
100
|
-
|
|
102
|
+
// Next to the binary itself (non-app-bundle deployments)
|
|
103
|
+
const execDirPath = join(execDir, file);
|
|
104
|
+
if (existsSync(execDirPath)) return execDirPath;
|
|
105
|
+
// Last resort: resolve from process.cwd() (the assistant/ directory)
|
|
106
|
+
const cwdPath = join(process.cwd(), 'node_modules', pkg, file);
|
|
107
|
+
if (existsSync(cwdPath)) return cwdPath;
|
|
108
|
+
return execDirPath;
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
const sourcePath = join(dir, '..', '..', '..', 'node_modules', pkg, file);
|
|
112
|
+
|
|
113
|
+
if (existsSync(sourcePath)) return sourcePath;
|
|
114
|
+
|
|
115
|
+
// Fallback: resolve from process.cwd() (the assistant/ directory).
|
|
116
|
+
const cwdPath = join(process.cwd(), 'node_modules', pkg, file);
|
|
117
|
+
if (existsSync(cwdPath)) return cwdPath;
|
|
118
|
+
|
|
103
119
|
return sourcePath;
|
|
104
120
|
}
|
|
105
121
|
|
package/src/tools/types.ts
CHANGED
|
@@ -87,6 +87,8 @@ export interface ToolContext {
|
|
|
87
87
|
workingDir: string;
|
|
88
88
|
sessionId: string;
|
|
89
89
|
conversationId: string;
|
|
90
|
+
/** Logical assistant scope for multi-assistant routing. */
|
|
91
|
+
assistantId?: string;
|
|
90
92
|
/** When set, the tool execution is part of a task run. Used to retrieve ephemeral permission rules. */
|
|
91
93
|
taskRunId?: string;
|
|
92
94
|
/** Per-message request ID for log correlation across session/connection boundaries. */
|
package/src/twitter/router.ts
CHANGED
|
@@ -41,7 +41,7 @@ export async function routedPostTweet(
|
|
|
41
41
|
if (strategy === 'oauth') {
|
|
42
42
|
// User explicitly wants OAuth
|
|
43
43
|
if (!oauthIsAvailable()) {
|
|
44
|
-
throw Object.assign(new Error('OAuth is not configured.
|
|
44
|
+
throw Object.assign(new Error('OAuth is not configured. Provide your X developer credentials here in the chat to set up OAuth, or switch to browser strategy: `vellum x strategy set browser`.'), {
|
|
45
45
|
pathUsed: 'oauth' as const,
|
|
46
46
|
suggestAlternative: 'browser' as const,
|
|
47
47
|
});
|
package/src/util/platform.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, existsSync, statSync, unlinkSync, renameSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
1
|
+
import { mkdirSync, existsSync, statSync, unlinkSync, renameSync, readFileSync, writeFileSync, readdirSync, chmodSync } from 'node:fs';
|
|
2
2
|
import { join, dirname } from 'node:path';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
/**
|
|
@@ -45,6 +45,41 @@ export function getClipboardCommand(): string | null {
|
|
|
45
45
|
return null;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Read and parse the lockfile, trying the primary path (~/.vellum.lock.json)
|
|
50
|
+
* first, then falling back to the legacy path (~/.vellum.lockfile.json).
|
|
51
|
+
* Respects BASE_DATA_DIR for non-standard home directories.
|
|
52
|
+
* Returns null if neither file exists or both are malformed.
|
|
53
|
+
*/
|
|
54
|
+
export function readLockfile(): Record<string, unknown> | null {
|
|
55
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
56
|
+
const candidates = [
|
|
57
|
+
join(base, '.vellum.lock.json'),
|
|
58
|
+
join(base, '.vellum.lockfile.json'),
|
|
59
|
+
];
|
|
60
|
+
for (const lockPath of candidates) {
|
|
61
|
+
if (!existsSync(lockPath)) continue;
|
|
62
|
+
try {
|
|
63
|
+
const raw = JSON.parse(readFileSync(lockPath, 'utf-8'));
|
|
64
|
+
if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
|
|
65
|
+
return raw as Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
// malformed JSON — try next
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Write data to the primary lockfile (~/.vellum.lock.json).
|
|
76
|
+
* Respects BASE_DATA_DIR for non-standard home directories.
|
|
77
|
+
*/
|
|
78
|
+
export function writeLockfile(data: Record<string, unknown>): void {
|
|
79
|
+
const base = process.env.BASE_DATA_DIR?.trim() || homedir();
|
|
80
|
+
writeFileSync(join(base, '.vellum.lock.json'), JSON.stringify(data, null, 2) + '\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
48
83
|
/**
|
|
49
84
|
* Returns the root ~/.vellum directory. User-facing files (config, prompt
|
|
50
85
|
* files, skills) and runtime files (socket, PID) live here.
|
|
@@ -565,6 +600,13 @@ export function ensureDataDir(): void {
|
|
|
565
600
|
mkdirSync(dir, { recursive: true });
|
|
566
601
|
}
|
|
567
602
|
}
|
|
603
|
+
// Lock down the root directory so only the owner can traverse it.
|
|
604
|
+
// Runtime files (socket, session token, PID) live directly under root.
|
|
605
|
+
try {
|
|
606
|
+
chmodSync(root, 0o700);
|
|
607
|
+
} catch {
|
|
608
|
+
// Non-fatal: some filesystems don't support Unix permissions
|
|
609
|
+
}
|
|
568
610
|
}
|
|
569
611
|
|
|
570
612
|
/**
|
package/src/util/retry.ts
CHANGED
|
@@ -58,10 +58,10 @@ export function getHttpRetryDelay(
|
|
|
58
58
|
const parsed = parseRetryAfterMs(retryAfter);
|
|
59
59
|
if (parsed !== undefined) return parsed;
|
|
60
60
|
}
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
return Math.max(baseDelayMs, computeRetryDelay(attempt,
|
|
61
|
+
// For attempt 0, double the base so jitter range [baseDelayMs, 2*baseDelayMs) stays above the floor.
|
|
62
|
+
// For attempt >= 1, use the original base — jitter is already above baseDelayMs.
|
|
63
|
+
const effectiveBase = attempt === 0 ? baseDelayMs * 2 : baseDelayMs;
|
|
64
|
+
return Math.max(baseDelayMs, computeRetryDelay(attempt, effectiveBase));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|