vellum 0.2.12 → 0.2.13
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/bun.lock +2 -2
- package/package.json +2 -2
- package/src/__tests__/call-orchestrator.test.ts +58 -0
- package/src/__tests__/config-schema.test.ts +278 -0
- package/src/__tests__/elevenlabs-client.test.ts +209 -0
- package/src/__tests__/trust-store.test.ts +1 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +127 -0
- package/src/calls/call-orchestrator.ts +3 -1
- package/src/calls/elevenlabs-client.ts +89 -0
- package/src/calls/elevenlabs-config.ts +29 -0
- package/src/calls/twilio-routes.ts +55 -6
- package/src/calls/voice-quality.ts +92 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +81 -1
- package/src/config/defaults.ts +18 -0
- package/src/config/schema.ts +110 -0
- package/src/config/types.ts +2 -0
- package/src/permissions/defaults.ts +11 -0
- package/src/runtime/routes/conversation-routes.ts +12 -5
package/src/config/schema.ts
CHANGED
|
@@ -9,6 +9,8 @@ const VALID_SANDBOX_BACKENDS = ['native', 'docker'] as const;
|
|
|
9
9
|
const VALID_DOCKER_NETWORKS = ['none', 'bridge'] as const;
|
|
10
10
|
const VALID_PERMISSIONS_MODES = ['legacy', 'strict'] as const;
|
|
11
11
|
const VALID_CALL_PROVIDERS = ['twilio'] as const;
|
|
12
|
+
const VALID_CALL_VOICE_MODES = ['twilio_standard', 'twilio_elevenlabs_tts', 'elevenlabs_agent'] as const;
|
|
13
|
+
const VALID_CALL_TRANSCRIPTION_PROVIDERS = ['Deepgram', 'Google'] as const;
|
|
12
14
|
|
|
13
15
|
export const TimeoutConfigSchema = z.object({
|
|
14
16
|
shellMaxTimeoutSec: z
|
|
@@ -885,6 +887,75 @@ export const CallsSafetyConfigSchema = z.object({
|
|
|
885
887
|
.default([]),
|
|
886
888
|
});
|
|
887
889
|
|
|
890
|
+
export const CallsElevenLabsConfigSchema = z.object({
|
|
891
|
+
voiceId: z
|
|
892
|
+
.string({ error: 'calls.voice.elevenlabs.voiceId must be a string' })
|
|
893
|
+
.default(''),
|
|
894
|
+
voiceModelId: z
|
|
895
|
+
.string({ error: 'calls.voice.elevenlabs.voiceModelId must be a string' })
|
|
896
|
+
.default('turbo_v2_5'),
|
|
897
|
+
stability: z
|
|
898
|
+
.number({ error: 'calls.voice.elevenlabs.stability must be a number' })
|
|
899
|
+
.min(0, 'calls.voice.elevenlabs.stability must be >= 0')
|
|
900
|
+
.max(1, 'calls.voice.elevenlabs.stability must be <= 1')
|
|
901
|
+
.default(0.5),
|
|
902
|
+
similarityBoost: z
|
|
903
|
+
.number({ error: 'calls.voice.elevenlabs.similarityBoost must be a number' })
|
|
904
|
+
.min(0, 'calls.voice.elevenlabs.similarityBoost must be >= 0')
|
|
905
|
+
.max(1, 'calls.voice.elevenlabs.similarityBoost must be <= 1')
|
|
906
|
+
.default(0.75),
|
|
907
|
+
style: z
|
|
908
|
+
.number({ error: 'calls.voice.elevenlabs.style must be a number' })
|
|
909
|
+
.min(0, 'calls.voice.elevenlabs.style must be >= 0')
|
|
910
|
+
.max(1, 'calls.voice.elevenlabs.style must be <= 1')
|
|
911
|
+
.default(0.0),
|
|
912
|
+
useSpeakerBoost: z
|
|
913
|
+
.boolean({ error: 'calls.voice.elevenlabs.useSpeakerBoost must be a boolean' })
|
|
914
|
+
.default(true),
|
|
915
|
+
agentId: z
|
|
916
|
+
.string({ error: 'calls.voice.elevenlabs.agentId must be a string' })
|
|
917
|
+
.default(''),
|
|
918
|
+
apiBaseUrl: z
|
|
919
|
+
.string({ error: 'calls.voice.elevenlabs.apiBaseUrl must be a string' })
|
|
920
|
+
.default('https://api.elevenlabs.io'),
|
|
921
|
+
registerCallTimeoutMs: z
|
|
922
|
+
.number({ error: 'calls.voice.elevenlabs.registerCallTimeoutMs must be a number' })
|
|
923
|
+
.int('calls.voice.elevenlabs.registerCallTimeoutMs must be an integer')
|
|
924
|
+
.min(1000, 'calls.voice.elevenlabs.registerCallTimeoutMs must be >= 1000')
|
|
925
|
+
.max(15000, 'calls.voice.elevenlabs.registerCallTimeoutMs must be <= 15000')
|
|
926
|
+
.default(5000),
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
export const CallsVoiceConfigSchema = z.object({
|
|
930
|
+
mode: z
|
|
931
|
+
.enum(VALID_CALL_VOICE_MODES, {
|
|
932
|
+
error: `calls.voice.mode must be one of: ${VALID_CALL_VOICE_MODES.join(', ')}`,
|
|
933
|
+
})
|
|
934
|
+
.default('twilio_standard'),
|
|
935
|
+
language: z
|
|
936
|
+
.string({ error: 'calls.voice.language must be a string' })
|
|
937
|
+
.default('en-US'),
|
|
938
|
+
transcriptionProvider: z
|
|
939
|
+
.enum(VALID_CALL_TRANSCRIPTION_PROVIDERS, {
|
|
940
|
+
error: `calls.voice.transcriptionProvider must be one of: ${VALID_CALL_TRANSCRIPTION_PROVIDERS.join(', ')}`,
|
|
941
|
+
})
|
|
942
|
+
.default('Deepgram'),
|
|
943
|
+
fallbackToStandardOnError: z
|
|
944
|
+
.boolean({ error: 'calls.voice.fallbackToStandardOnError must be a boolean' })
|
|
945
|
+
.default(true),
|
|
946
|
+
elevenlabs: CallsElevenLabsConfigSchema.default({
|
|
947
|
+
voiceId: '',
|
|
948
|
+
voiceModelId: 'turbo_v2_5',
|
|
949
|
+
stability: 0.5,
|
|
950
|
+
similarityBoost: 0.75,
|
|
951
|
+
style: 0.0,
|
|
952
|
+
useSpeakerBoost: true,
|
|
953
|
+
agentId: '',
|
|
954
|
+
apiBaseUrl: 'https://api.elevenlabs.io',
|
|
955
|
+
registerCallTimeoutMs: 5000,
|
|
956
|
+
}),
|
|
957
|
+
});
|
|
958
|
+
|
|
888
959
|
export const CallsConfigSchema = z.object({
|
|
889
960
|
enabled: z
|
|
890
961
|
.boolean({ error: 'calls.enabled must be a boolean' })
|
|
@@ -913,6 +984,26 @@ export const CallsConfigSchema = z.object({
|
|
|
913
984
|
safety: CallsSafetyConfigSchema.default({
|
|
914
985
|
denyCategories: [],
|
|
915
986
|
}),
|
|
987
|
+
voice: CallsVoiceConfigSchema.default({
|
|
988
|
+
mode: 'twilio_standard',
|
|
989
|
+
language: 'en-US',
|
|
990
|
+
transcriptionProvider: 'Deepgram',
|
|
991
|
+
fallbackToStandardOnError: true,
|
|
992
|
+
elevenlabs: {
|
|
993
|
+
voiceId: '',
|
|
994
|
+
voiceModelId: 'turbo_v2_5',
|
|
995
|
+
stability: 0.5,
|
|
996
|
+
similarityBoost: 0.75,
|
|
997
|
+
style: 0.0,
|
|
998
|
+
useSpeakerBoost: true,
|
|
999
|
+
agentId: '',
|
|
1000
|
+
apiBaseUrl: 'https://api.elevenlabs.io',
|
|
1001
|
+
registerCallTimeoutMs: 5000,
|
|
1002
|
+
},
|
|
1003
|
+
}),
|
|
1004
|
+
model: z
|
|
1005
|
+
.string({ error: 'calls.model must be a string' })
|
|
1006
|
+
.optional(),
|
|
916
1007
|
});
|
|
917
1008
|
|
|
918
1009
|
export const SkillsConfigSchema = z.object({
|
|
@@ -1178,6 +1269,23 @@ export const AssistantConfigSchema = z.object({
|
|
|
1178
1269
|
safety: {
|
|
1179
1270
|
denyCategories: [],
|
|
1180
1271
|
},
|
|
1272
|
+
voice: {
|
|
1273
|
+
mode: 'twilio_standard',
|
|
1274
|
+
language: 'en-US',
|
|
1275
|
+
transcriptionProvider: 'Deepgram',
|
|
1276
|
+
fallbackToStandardOnError: true,
|
|
1277
|
+
elevenlabs: {
|
|
1278
|
+
voiceId: '',
|
|
1279
|
+
voiceModelId: 'turbo_v2_5',
|
|
1280
|
+
stability: 0.5,
|
|
1281
|
+
similarityBoost: 0.75,
|
|
1282
|
+
style: 0.0,
|
|
1283
|
+
useSpeakerBoost: true,
|
|
1284
|
+
agentId: '',
|
|
1285
|
+
apiBaseUrl: 'https://api.elevenlabs.io',
|
|
1286
|
+
registerCallTimeoutMs: 5000,
|
|
1287
|
+
},
|
|
1288
|
+
},
|
|
1181
1289
|
}),
|
|
1182
1290
|
ingress: IngressConfigSchema.default({
|
|
1183
1291
|
enabled: false,
|
|
@@ -1243,4 +1351,6 @@ export type WorkspaceGitConfig = z.infer<typeof WorkspaceGitConfigSchema>;
|
|
|
1243
1351
|
export type CallsConfig = z.infer<typeof CallsConfigSchema>;
|
|
1244
1352
|
export type CallsDisclosureConfig = z.infer<typeof CallsDisclosureConfigSchema>;
|
|
1245
1353
|
export type CallsSafetyConfig = z.infer<typeof CallsSafetyConfigSchema>;
|
|
1354
|
+
export type CallsVoiceConfig = z.infer<typeof CallsVoiceConfigSchema>;
|
|
1355
|
+
export type CallsElevenLabsConfig = z.infer<typeof CallsElevenLabsConfigSchema>;
|
|
1246
1356
|
export type IngressConfig = z.infer<typeof IngressConfigSchema>;
|
package/src/config/types.ts
CHANGED
|
@@ -225,6 +225,16 @@ export function getDefaultRuleTemplates(): DefaultRuleTemplate[] {
|
|
|
225
225
|
priority: 100,
|
|
226
226
|
};
|
|
227
227
|
|
|
228
|
+
// memory_search is a read-only tool — always allow without prompting.
|
|
229
|
+
const memorySearchRule: DefaultRuleTemplate = {
|
|
230
|
+
id: 'default:allow-memory_search-global',
|
|
231
|
+
tool: 'memory_search',
|
|
232
|
+
pattern: 'memory_search:*',
|
|
233
|
+
scope: 'everywhere',
|
|
234
|
+
decision: 'allow',
|
|
235
|
+
priority: 100,
|
|
236
|
+
};
|
|
237
|
+
|
|
228
238
|
return [
|
|
229
239
|
...hostFileRules,
|
|
230
240
|
hostShellRule,
|
|
@@ -239,5 +249,6 @@ export function getDefaultRuleTemplates(): DefaultRuleTemplate[] {
|
|
|
239
249
|
...browserToolRules,
|
|
240
250
|
...uiSurfaceRules,
|
|
241
251
|
viewImageRule,
|
|
252
|
+
memorySearchRule,
|
|
242
253
|
];
|
|
243
254
|
}
|
|
@@ -46,19 +46,26 @@ export function handleListMessages(
|
|
|
46
46
|
url: URL,
|
|
47
47
|
interfacesDir: string | null,
|
|
48
48
|
): Response {
|
|
49
|
+
const conversationId = url.searchParams.get('conversationId');
|
|
49
50
|
const conversationKey = url.searchParams.get('conversationKey');
|
|
50
|
-
|
|
51
|
+
|
|
52
|
+
let resolvedConversationId: string | undefined;
|
|
53
|
+
if (conversationId) {
|
|
54
|
+
resolvedConversationId = conversationId;
|
|
55
|
+
} else if (conversationKey) {
|
|
56
|
+
const mapping = getConversationByKey(conversationKey);
|
|
57
|
+
resolvedConversationId = mapping?.conversationId;
|
|
58
|
+
} else {
|
|
51
59
|
return Response.json(
|
|
52
|
-
{ error: 'conversationKey query parameter is required' },
|
|
60
|
+
{ error: 'conversationKey or conversationId query parameter is required' },
|
|
53
61
|
{ status: 400 },
|
|
54
62
|
);
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
if (!mapping) {
|
|
65
|
+
if (!resolvedConversationId) {
|
|
59
66
|
return Response.json({ messages: [] });
|
|
60
67
|
}
|
|
61
|
-
const rawMessages = conversationStore.getMessages(
|
|
68
|
+
const rawMessages = conversationStore.getMessages(resolvedConversationId);
|
|
62
69
|
|
|
63
70
|
// Parse content blocks and extract text + tool calls
|
|
64
71
|
const parsed = rawMessages.map((msg) => {
|