@vellumai/assistant 0.4.31 → 0.4.32
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 +1 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +0 -7
- package/src/__tests__/anthropic-provider.test.ts +86 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/checker.test.ts +37 -98
- package/src/__tests__/commit-message-enrichment-service.test.ts +15 -0
- package/src/__tests__/config-schema.test.ts +6 -5
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/daemon-server-session-init.test.ts +1 -19
- package/src/__tests__/followup-tools.test.ts +0 -30
- package/src/__tests__/gemini-provider.test.ts +79 -1
- package/src/__tests__/ipc-snapshot.test.ts +0 -4
- package/src/__tests__/managed-proxy-context.test.ts +163 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +2 -2
- package/src/__tests__/memory-regressions.test.ts +6 -6
- package/src/__tests__/openai-provider.test.ts +82 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +134 -1
- package/src/__tests__/provider-managed-proxy-integration.test.ts +269 -0
- package/src/__tests__/recurrence-types.test.ts +0 -15
- package/src/__tests__/schedule-tools.test.ts +28 -44
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/task-management-tools.test.ts +111 -0
- package/src/__tests__/twilio-config.test.ts +0 -3
- package/src/amazon/session.ts +30 -91
- package/src/calls/call-controller.ts +423 -571
- package/src/calls/finalize-call.ts +20 -0
- package/src/calls/relay-access-wait.ts +340 -0
- package/src/calls/relay-server.ts +267 -902
- package/src/calls/relay-setup-router.ts +307 -0
- package/src/calls/relay-verification.ts +280 -0
- package/src/calls/twilio-config.ts +1 -8
- package/src/calls/voice-control-protocol.ts +184 -0
- package/src/calls/voice-session-bridge.ts +1 -8
- package/src/config/agent-schema.ts +1 -1
- package/src/config/bundled-skills/followups/TOOLS.json +0 -4
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -10
- package/src/config/core-schema.ts +1 -1
- package/src/config/env.ts +0 -10
- package/src/config/feature-flag-registry.json +1 -1
- package/src/config/loader.ts +19 -0
- package/src/config/schema.ts +2 -2
- package/src/daemon/handlers/session-history.ts +398 -0
- package/src/daemon/handlers/session-user-message.ts +982 -0
- package/src/daemon/handlers/sessions.ts +9 -1338
- package/src/daemon/ipc-contract/sessions.ts +0 -6
- package/src/daemon/ipc-contract-inventory.json +0 -1
- package/src/daemon/lifecycle.ts +0 -29
- package/src/home-base/app-link-store.ts +0 -7
- package/src/memory/conversation-attention-store.ts +1 -1
- package/src/memory/conversation-store.ts +0 -51
- package/src/memory/db-init.ts +5 -1
- package/src/memory/job-handlers/conflict.ts +24 -0
- package/src/memory/migrations/105-contacts-and-triage.ts +4 -7
- package/src/memory/migrations/134-contacts-notes-column.ts +50 -33
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/recall-cache.ts +0 -5
- package/src/memory/schema/calls.ts +274 -0
- package/src/memory/schema/contacts.ts +125 -0
- package/src/memory/schema/conversations.ts +129 -0
- package/src/memory/schema/guardian.ts +172 -0
- package/src/memory/schema/index.ts +8 -0
- package/src/memory/schema/infrastructure.ts +205 -0
- package/src/memory/schema/memory-core.ts +196 -0
- package/src/memory/schema/notifications.ts +191 -0
- package/src/memory/schema/tasks.ts +78 -0
- package/src/memory/schema.ts +1 -1385
- package/src/memory/slack-thread-store.ts +0 -69
- package/src/notifications/decisions-store.ts +2 -105
- package/src/notifications/deliveries-store.ts +0 -11
- package/src/notifications/preferences-store.ts +1 -58
- package/src/permissions/checker.ts +6 -17
- package/src/providers/anthropic/client.ts +6 -2
- package/src/providers/gemini/client.ts +13 -2
- package/src/providers/managed-proxy/constants.ts +55 -0
- package/src/providers/managed-proxy/context.ts +77 -0
- package/src/providers/registry.ts +112 -0
- package/src/runtime/auth/__tests__/guard-tests.test.ts +51 -23
- package/src/runtime/http-server.ts +83 -722
- package/src/runtime/http-types.ts +0 -16
- package/src/runtime/middleware/auth.ts +0 -12
- package/src/runtime/routes/app-routes.ts +33 -0
- package/src/runtime/routes/approval-routes.ts +32 -0
- package/src/runtime/routes/attachment-routes.ts +32 -0
- package/src/runtime/routes/brain-graph-routes.ts +27 -0
- package/src/runtime/routes/call-routes.ts +41 -0
- package/src/runtime/routes/channel-readiness-routes.ts +20 -0
- package/src/runtime/routes/channel-routes.ts +70 -0
- package/src/runtime/routes/contact-routes.ts +63 -0
- package/src/runtime/routes/conversation-attention-routes.ts +15 -0
- package/src/runtime/routes/conversation-routes.ts +190 -193
- package/src/runtime/routes/debug-routes.ts +15 -0
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/global-search-routes.ts +15 -0
- package/src/runtime/routes/guardian-action-routes.ts +22 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +20 -0
- package/src/runtime/routes/guardian-refresh-routes.ts +20 -0
- package/src/runtime/routes/identity-routes.ts +20 -0
- package/src/runtime/routes/inbound-message-handler.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +6 -6
- package/src/runtime/routes/integration-routes.ts +83 -0
- package/src/runtime/routes/invite-routes.ts +31 -0
- package/src/runtime/routes/migration-routes.ts +30 -0
- package/src/runtime/routes/pairing-routes.ts +18 -0
- package/src/runtime/routes/secret-routes.ts +20 -0
- package/src/runtime/routes/surface-action-routes.ts +26 -0
- package/src/runtime/routes/trust-rules-routes.ts +31 -0
- package/src/runtime/routes/twilio-routes.ts +79 -0
- package/src/schedule/recurrence-types.ts +1 -11
- package/src/tools/followups/followup_create.ts +9 -3
- package/src/tools/mcp/mcp-tool-factory.ts +0 -17
- package/src/tools/memory/definitions.ts +0 -6
- package/src/tools/network/script-proxy/session-manager.ts +38 -3
- package/src/tools/schedule/create.ts +1 -3
- package/src/tools/schedule/update.ts +9 -6
- package/src/twitter/session.ts +29 -77
- package/src/util/cookie-session.ts +114 -0
- package/src/__tests__/conversation-routes.test.ts +0 -99
- package/src/__tests__/task-tools.test.ts +0 -685
- package/src/contacts/startup-migration.ts +0 -21
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
startOutbound,
|
|
49
49
|
} from "../guardian-outbound-actions.js";
|
|
50
50
|
import { httpError } from "../http-errors.js";
|
|
51
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
51
52
|
import { guardianVerificationLimiter } from "../verification-rate-limiter.js";
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -296,3 +297,85 @@ export async function handleCancelOutbound(req: Request): Promise<Response> {
|
|
|
296
297
|
const status = result.success ? 200 : 400;
|
|
297
298
|
return Response.json(result, { status });
|
|
298
299
|
}
|
|
300
|
+
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
// Route definitions
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
|
|
305
|
+
export function integrationRouteDefinitions(): RouteDefinition[] {
|
|
306
|
+
return [
|
|
307
|
+
// Telegram
|
|
308
|
+
{
|
|
309
|
+
endpoint: "integrations/telegram/config",
|
|
310
|
+
method: "GET",
|
|
311
|
+
handler: () => handleGetTelegramConfig(),
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
endpoint: "integrations/telegram/config",
|
|
315
|
+
method: "POST",
|
|
316
|
+
handler: async ({ req }) => handleSetTelegramConfig(req),
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
endpoint: "integrations/telegram/config",
|
|
320
|
+
method: "DELETE",
|
|
321
|
+
handler: async () => handleClearTelegramConfig(),
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
endpoint: "integrations/telegram/commands",
|
|
325
|
+
method: "POST",
|
|
326
|
+
handler: async ({ req }) => handleSetTelegramCommands(req),
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
endpoint: "integrations/telegram/setup",
|
|
330
|
+
method: "POST",
|
|
331
|
+
handler: async ({ req }) => handleSetupTelegram(req),
|
|
332
|
+
},
|
|
333
|
+
// Slack
|
|
334
|
+
{
|
|
335
|
+
endpoint: "integrations/slack/channel/config",
|
|
336
|
+
method: "GET",
|
|
337
|
+
handler: () => handleGetSlackChannelConfig(),
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
endpoint: "integrations/slack/channel/config",
|
|
341
|
+
method: "POST",
|
|
342
|
+
handler: async ({ req }) => handleSetSlackChannelConfig(req),
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
endpoint: "integrations/slack/channel/config",
|
|
346
|
+
method: "DELETE",
|
|
347
|
+
handler: () => handleClearSlackChannelConfig(),
|
|
348
|
+
},
|
|
349
|
+
// Guardian
|
|
350
|
+
{
|
|
351
|
+
endpoint: "integrations/guardian/challenge",
|
|
352
|
+
method: "POST",
|
|
353
|
+
handler: async ({ req }) => handleCreateGuardianChallenge(req),
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
endpoint: "integrations/guardian/status",
|
|
357
|
+
method: "GET",
|
|
358
|
+
handler: ({ url }) => handleGetGuardianStatus(url),
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
endpoint: "integrations/guardian/revoke",
|
|
362
|
+
method: "POST",
|
|
363
|
+
handler: async ({ req }) => handleRevokeGuardian(req),
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
endpoint: "integrations/guardian/outbound/start",
|
|
367
|
+
method: "POST",
|
|
368
|
+
handler: async ({ req }) => handleStartOutbound(req),
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
endpoint: "integrations/guardian/outbound/resend",
|
|
372
|
+
method: "POST",
|
|
373
|
+
handler: async ({ req }) => handleResendOutbound(req),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
endpoint: "integrations/guardian/outbound/cancel",
|
|
377
|
+
method: "POST",
|
|
378
|
+
handler: async ({ req }) => handleCancelOutbound(req),
|
|
379
|
+
},
|
|
380
|
+
];
|
|
381
|
+
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* POST /v1/contacts/invites/redeem — redeem an invite (token or voice code)
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
11
12
|
import {
|
|
12
13
|
createIngressInvite,
|
|
13
14
|
listIngressInvites,
|
|
@@ -138,3 +139,33 @@ export async function handleRedeemInvite(req: Request): Promise<Response> {
|
|
|
138
139
|
}
|
|
139
140
|
return Response.json({ ok: true, invite: result.data });
|
|
140
141
|
}
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Route definitions
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
export function inviteRouteDefinitions(): RouteDefinition[] {
|
|
148
|
+
return [
|
|
149
|
+
{
|
|
150
|
+
endpoint: "contacts/invites",
|
|
151
|
+
method: "GET",
|
|
152
|
+
handler: ({ url }) => handleListInvites(url),
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
endpoint: "contacts/invites",
|
|
156
|
+
method: "POST",
|
|
157
|
+
handler: async ({ req }) => handleCreateInvite(req),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
endpoint: "contacts/invites/redeem",
|
|
161
|
+
method: "POST",
|
|
162
|
+
handler: async ({ req }) => handleRedeemInvite(req),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
endpoint: "contacts/invites/:id",
|
|
166
|
+
method: "DELETE",
|
|
167
|
+
policyKey: "contacts/invites",
|
|
168
|
+
handler: ({ params }) => handleRevokeInvite(params.id),
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
}
|
|
@@ -18,6 +18,7 @@ import { resetDb } from "../../memory/db-connection.js";
|
|
|
18
18
|
import { getLogger } from "../../util/logger.js";
|
|
19
19
|
import { getDbPath, getWorkspaceConfigPath } from "../../util/platform.js";
|
|
20
20
|
import { httpError } from "../http-errors.js";
|
|
21
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
21
22
|
import { buildExportVBundle } from "../migrations/vbundle-builder.js";
|
|
22
23
|
import {
|
|
23
24
|
analyzeImport,
|
|
@@ -432,3 +433,32 @@ export async function handleMigrationImport(req: Request): Promise<Response> {
|
|
|
432
433
|
);
|
|
433
434
|
}
|
|
434
435
|
}
|
|
436
|
+
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
// Route definitions
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
|
|
441
|
+
export function migrationRouteDefinitions(): RouteDefinition[] {
|
|
442
|
+
return [
|
|
443
|
+
{
|
|
444
|
+
endpoint: "migrations/validate",
|
|
445
|
+
method: "POST",
|
|
446
|
+
handler: async ({ req }) => handleMigrationValidate(req),
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
endpoint: "migrations/export",
|
|
450
|
+
method: "POST",
|
|
451
|
+
handler: async ({ req }) => handleMigrationExport(req),
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
endpoint: "migrations/import-preflight",
|
|
455
|
+
method: "POST",
|
|
456
|
+
handler: async ({ req }) => handleMigrationImportPreflight(req),
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
endpoint: "migrations/import",
|
|
460
|
+
method: "POST",
|
|
461
|
+
handler: async ({ req }) => handleMigrationImport(req),
|
|
462
|
+
},
|
|
463
|
+
];
|
|
464
|
+
}
|
|
@@ -14,6 +14,7 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
|
14
14
|
import { mintCredentialPair } from "../auth/credential-service.js";
|
|
15
15
|
import { ensureVellumGuardianBinding } from "../guardian-vellum-migration.js";
|
|
16
16
|
import { httpError } from "../http-errors.js";
|
|
17
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
17
18
|
|
|
18
19
|
const log = getLogger("runtime-http");
|
|
19
20
|
|
|
@@ -404,3 +405,20 @@ export function handlePairingStatus(
|
|
|
404
405
|
|
|
405
406
|
return Response.json({ status: entry.status });
|
|
406
407
|
}
|
|
408
|
+
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
// Route definitions
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
|
|
413
|
+
export function pairingRouteDefinitions(deps: {
|
|
414
|
+
getPairingContext: () => PairingHandlerContext;
|
|
415
|
+
}): RouteDefinition[] {
|
|
416
|
+
return [
|
|
417
|
+
{
|
|
418
|
+
endpoint: "pairing/register",
|
|
419
|
+
method: "POST",
|
|
420
|
+
handler: async ({ req }) =>
|
|
421
|
+
handlePairingRegister(req, deps.getPairingContext()),
|
|
422
|
+
},
|
|
423
|
+
];
|
|
424
|
+
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "../../tools/credentials/metadata-store.js";
|
|
13
13
|
import { getLogger } from "../../util/logger.js";
|
|
14
14
|
import { httpError } from "../http-errors.js";
|
|
15
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
15
16
|
|
|
16
17
|
const log = getLogger("runtime-http");
|
|
17
18
|
|
|
@@ -170,3 +171,22 @@ export async function handleDeleteSecret(req: Request): Promise<Response> {
|
|
|
170
171
|
return httpError("INTERNAL_ERROR", message, 500);
|
|
171
172
|
}
|
|
172
173
|
}
|
|
174
|
+
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// Route definitions
|
|
177
|
+
// ---------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
export function secretRouteDefinitions(): RouteDefinition[] {
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
182
|
+
endpoint: "secrets",
|
|
183
|
+
method: "POST",
|
|
184
|
+
handler: async ({ req }) => handleAddSecret(req),
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
endpoint: "secrets",
|
|
188
|
+
method: "DELETE",
|
|
189
|
+
handler: async ({ req }) => handleDeleteSecret(req),
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { getLogger } from "../../util/logger.js";
|
|
8
8
|
import { httpError } from "../http-errors.js";
|
|
9
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
9
10
|
|
|
10
11
|
const log = getLogger("surface-action-routes");
|
|
11
12
|
|
|
@@ -74,3 +75,28 @@ export async function handleSurfaceAction(
|
|
|
74
75
|
return httpError("INTERNAL_ERROR", "Failed to handle surface action", 500);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Route definitions
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
export function surfaceActionRouteDefinitions(deps: {
|
|
84
|
+
findSession?: SessionLookup;
|
|
85
|
+
}): RouteDefinition[] {
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
endpoint: "surface-actions",
|
|
89
|
+
method: "POST",
|
|
90
|
+
handler: async ({ req }) => {
|
|
91
|
+
if (!deps.findSession) {
|
|
92
|
+
return httpError(
|
|
93
|
+
"NOT_IMPLEMENTED",
|
|
94
|
+
"Surface actions not available",
|
|
95
|
+
501,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return handleSurfaceAction(req, deps.findSession);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "../../permissions/trust-store.js";
|
|
14
14
|
import { getLogger } from "../../util/logger.js";
|
|
15
15
|
import { httpError } from "../http-errors.js";
|
|
16
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
16
17
|
|
|
17
18
|
const log = getLogger("trust-rules-routes");
|
|
18
19
|
|
|
@@ -151,3 +152,33 @@ export async function handleUpdateTrustRuleManage(
|
|
|
151
152
|
return httpError("INTERNAL_ERROR", "Failed to update trust rule", 500);
|
|
152
153
|
}
|
|
153
154
|
}
|
|
155
|
+
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
// Route definitions
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
export function trustRulesRouteDefinitions(): RouteDefinition[] {
|
|
161
|
+
return [
|
|
162
|
+
{
|
|
163
|
+
endpoint: "trust-rules/manage",
|
|
164
|
+
method: "GET",
|
|
165
|
+
handler: () => handleListTrustRules(),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
endpoint: "trust-rules/manage",
|
|
169
|
+
method: "POST",
|
|
170
|
+
handler: async ({ req }) => handleAddTrustRuleManage(req),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
endpoint: "trust-rules/manage/:id",
|
|
174
|
+
method: "DELETE",
|
|
175
|
+
handler: ({ params }) => handleRemoveTrustRuleManage(params.id),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
endpoint: "trust-rules/manage/:id",
|
|
179
|
+
method: "PATCH",
|
|
180
|
+
handler: async ({ req, params }) =>
|
|
181
|
+
handleUpdateTrustRuleManage(req, params.id),
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
}
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
upsertCredentialMetadata,
|
|
47
47
|
} from "../../tools/credentials/metadata-store.js";
|
|
48
48
|
import { mintDaemonDeliveryToken } from "../auth/token-service.js";
|
|
49
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
49
50
|
|
|
50
51
|
// ---------------------------------------------------------------------------
|
|
51
52
|
// Shared helpers
|
|
@@ -1126,3 +1127,81 @@ export async function handleSmsDoctor(): Promise<Response> {
|
|
|
1126
1127
|
},
|
|
1127
1128
|
});
|
|
1128
1129
|
}
|
|
1130
|
+
|
|
1131
|
+
// ---------------------------------------------------------------------------
|
|
1132
|
+
// Route definitions
|
|
1133
|
+
// ---------------------------------------------------------------------------
|
|
1134
|
+
|
|
1135
|
+
export function twilioRouteDefinitions(): RouteDefinition[] {
|
|
1136
|
+
return [
|
|
1137
|
+
{
|
|
1138
|
+
endpoint: "integrations/twilio/config",
|
|
1139
|
+
method: "GET",
|
|
1140
|
+
handler: () => handleGetTwilioConfig(),
|
|
1141
|
+
},
|
|
1142
|
+
{
|
|
1143
|
+
endpoint: "integrations/twilio/credentials",
|
|
1144
|
+
method: "POST",
|
|
1145
|
+
handler: async ({ req }) => handleSetTwilioCredentials(req),
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
endpoint: "integrations/twilio/credentials",
|
|
1149
|
+
method: "DELETE",
|
|
1150
|
+
handler: () => handleClearTwilioCredentials(),
|
|
1151
|
+
},
|
|
1152
|
+
{
|
|
1153
|
+
endpoint: "integrations/twilio/numbers",
|
|
1154
|
+
method: "GET",
|
|
1155
|
+
handler: async () => handleListTwilioNumbers(),
|
|
1156
|
+
},
|
|
1157
|
+
{
|
|
1158
|
+
endpoint: "integrations/twilio/numbers/provision",
|
|
1159
|
+
method: "POST",
|
|
1160
|
+
handler: async ({ req }) => handleProvisionTwilioNumber(req),
|
|
1161
|
+
},
|
|
1162
|
+
{
|
|
1163
|
+
endpoint: "integrations/twilio/numbers/assign",
|
|
1164
|
+
method: "POST",
|
|
1165
|
+
handler: async ({ req }) => handleAssignTwilioNumber(req),
|
|
1166
|
+
},
|
|
1167
|
+
{
|
|
1168
|
+
endpoint: "integrations/twilio/numbers/release",
|
|
1169
|
+
method: "POST",
|
|
1170
|
+
handler: async ({ req }) => handleReleaseTwilioNumber(req),
|
|
1171
|
+
},
|
|
1172
|
+
{
|
|
1173
|
+
endpoint: "integrations/twilio/sms/compliance",
|
|
1174
|
+
method: "GET",
|
|
1175
|
+
handler: async () => handleGetSmsCompliance(),
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
endpoint: "integrations/twilio/sms/compliance/tollfree",
|
|
1179
|
+
method: "POST",
|
|
1180
|
+
handler: async ({ req }) => handleSubmitTollfreeVerification(req),
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
endpoint: "integrations/twilio/sms/compliance/tollfree/:sid",
|
|
1184
|
+
method: "PATCH",
|
|
1185
|
+
policyKey: "integrations/twilio/sms/compliance/tollfree",
|
|
1186
|
+
handler: async ({ req, params }) =>
|
|
1187
|
+
handleUpdateTollfreeVerification(req, params.sid),
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
endpoint: "integrations/twilio/sms/compliance/tollfree/:sid",
|
|
1191
|
+
method: "DELETE",
|
|
1192
|
+
policyKey: "integrations/twilio/sms/compliance/tollfree",
|
|
1193
|
+
handler: async ({ params }) =>
|
|
1194
|
+
handleDeleteTollfreeVerification(params.sid),
|
|
1195
|
+
},
|
|
1196
|
+
{
|
|
1197
|
+
endpoint: "integrations/twilio/sms/test",
|
|
1198
|
+
method: "POST",
|
|
1199
|
+
handler: async ({ req }) => handleSmsSendTest(req),
|
|
1200
|
+
},
|
|
1201
|
+
{
|
|
1202
|
+
endpoint: "integrations/twilio/sms/doctor",
|
|
1203
|
+
method: "POST",
|
|
1204
|
+
handler: async () => handleSmsDoctor(),
|
|
1205
|
+
},
|
|
1206
|
+
];
|
|
1207
|
+
}
|
|
@@ -34,13 +34,11 @@ export function detectScheduleSyntax(
|
|
|
34
34
|
* Resolution order:
|
|
35
35
|
* 1. If explicit `syntax` is provided, use it
|
|
36
36
|
* 2. If `expression` is provided, auto-detect from expression
|
|
37
|
-
* 3.
|
|
38
|
-
* 4. Return null if nothing resolved
|
|
37
|
+
* 3. Return null if nothing resolved
|
|
39
38
|
*/
|
|
40
39
|
export function normalizeScheduleSyntax(input: {
|
|
41
40
|
syntax?: ScheduleSyntax;
|
|
42
41
|
expression?: string;
|
|
43
|
-
legacyCronExpression?: string;
|
|
44
42
|
}): { syntax: ScheduleSyntax; expression: string } | null {
|
|
45
43
|
// Explicit syntax + expression
|
|
46
44
|
if (input.syntax && input.expression) {
|
|
@@ -60,13 +58,5 @@ export function normalizeScheduleSyntax(input: {
|
|
|
60
58
|
return null;
|
|
61
59
|
}
|
|
62
60
|
|
|
63
|
-
// Legacy cron_expression fallback
|
|
64
|
-
if (input.legacyCronExpression) {
|
|
65
|
-
return {
|
|
66
|
-
syntax: input.syntax ?? "cron",
|
|
67
|
-
expression: input.legacyCronExpression,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
61
|
return null;
|
|
72
62
|
}
|
|
@@ -47,13 +47,19 @@ export async function executeFollowupCreate(
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
if (input.reminder_cron_id !== undefined) {
|
|
51
|
+
return {
|
|
52
|
+
content:
|
|
53
|
+
'Error: "reminder_cron_id" is deprecated. Use "reminder_schedule_id" instead.',
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
50
58
|
const contactId = input.contact_id as string | undefined;
|
|
51
59
|
const expectedResponseHours = input.expected_response_hours as
|
|
52
60
|
| number
|
|
53
61
|
| undefined;
|
|
54
|
-
|
|
55
|
-
const reminderScheduleId = (input.reminder_schedule_id ??
|
|
56
|
-
input.reminder_cron_id) as string | undefined;
|
|
62
|
+
const reminderScheduleId = input.reminder_schedule_id as string | undefined;
|
|
57
63
|
|
|
58
64
|
// Validate contact exists if provided
|
|
59
65
|
if (contactId) {
|
|
@@ -18,23 +18,6 @@ export function mcpToolName(serverId: string, toolName: string): string {
|
|
|
18
18
|
return `mcp__${serverId}__${toolName}`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/**
|
|
22
|
-
* Parse a namespaced MCP tool name back into serverId and original tool name.
|
|
23
|
-
* Returns null if the name doesn't match the MCP naming convention.
|
|
24
|
-
*/
|
|
25
|
-
export function parseMcpToolName(
|
|
26
|
-
name: string,
|
|
27
|
-
): { serverId: string; toolName: string } | null {
|
|
28
|
-
if (!name.startsWith("mcp__")) return null;
|
|
29
|
-
const prefixRemoved = name.slice(5); // remove 'mcp__'
|
|
30
|
-
const firstSep = prefixRemoved.indexOf("__");
|
|
31
|
-
if (firstSep === -1) return null;
|
|
32
|
-
return {
|
|
33
|
-
serverId: prefixRemoved.slice(0, firstSep),
|
|
34
|
-
toolName: prefixRemoved.slice(firstSep + 2),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
21
|
export interface McpToolMetadata {
|
|
39
22
|
name: string;
|
|
40
23
|
description: string;
|
|
@@ -42,6 +42,32 @@ const DEFAULT_CONFIG: ProxySessionConfig = {
|
|
|
42
42
|
maxSessionsPerConversation: 3,
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Host patterns that are allowed by default through the proxy policy engine,
|
|
47
|
+
* regardless of session configuration. Supports exact matches (e.g.
|
|
48
|
+
* `"localhost"`) and wildcard subdomain patterns (e.g. `"*.vellum.ai"`
|
|
49
|
+
* matches `platform.vellum.ai`, `dev-platform.vellum.ai`, etc.).
|
|
50
|
+
*/
|
|
51
|
+
const ALLOWED_HOST_PATTERNS: readonly string[] = ["*.vellum.ai", "localhost"];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns `true` when `hostname` matches any entry in
|
|
55
|
+
* {@link ALLOWED_HOST_PATTERNS}.
|
|
56
|
+
*/
|
|
57
|
+
function isAllowedHost(hostname: string): boolean {
|
|
58
|
+
for (const pattern of ALLOWED_HOST_PATTERNS) {
|
|
59
|
+
if (pattern.startsWith("*.")) {
|
|
60
|
+
const suffix = pattern.slice(1); // e.g. ".vellum.ai"
|
|
61
|
+
if (hostname.endsWith(suffix) || hostname === pattern.slice(2)) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
} else if (hostname === pattern) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
45
71
|
interface ManagedSession {
|
|
46
72
|
session: ProxySession;
|
|
47
73
|
server: Server | null;
|
|
@@ -309,6 +335,13 @@ export async function startSession(
|
|
|
309
335
|
reqPath: string,
|
|
310
336
|
scheme: "http" | "https",
|
|
311
337
|
) => {
|
|
338
|
+
// Allowed hosts are always passed through the proxy, regardless of
|
|
339
|
+
// session configuration or credential state.
|
|
340
|
+
if (isAllowedHost(hostname)) {
|
|
341
|
+
log.debug({ hostname }, "Allowing always-permitted host");
|
|
342
|
+
return {};
|
|
343
|
+
}
|
|
344
|
+
|
|
312
345
|
const decision = evaluateRequestWithApproval(
|
|
313
346
|
hostname,
|
|
314
347
|
port,
|
|
@@ -506,7 +539,8 @@ export async function getOrStartSession(
|
|
|
506
539
|
): Promise<{ session: ProxySession; created: boolean }> {
|
|
507
540
|
const requestedHost = options?.listenHost ?? "127.0.0.1";
|
|
508
541
|
|
|
509
|
-
// Fast path — session already active with matching credentials and listen
|
|
542
|
+
// Fast path — session already active with matching credentials and listen
|
|
543
|
+
// host, no lock needed.
|
|
510
544
|
const existing = getActiveSession(conversationId);
|
|
511
545
|
if (existing && credentialIdsMatch(existing.credentialIds, credentialIds)) {
|
|
512
546
|
const managed = sessions.get(existing.id);
|
|
@@ -533,8 +567,9 @@ export async function getOrStartSession(
|
|
|
533
567
|
return { session, created: false };
|
|
534
568
|
}
|
|
535
569
|
}
|
|
536
|
-
// Credential or listenHost mismatch — tear down and loop back to
|
|
537
|
-
// whether another waiter has already started a replacement
|
|
570
|
+
// Credential or listenHost mismatch — tear down and loop back to
|
|
571
|
+
// re-check whether another waiter has already started a replacement
|
|
572
|
+
// session.
|
|
538
573
|
await stopSession(session.id);
|
|
539
574
|
}
|
|
540
575
|
|
|
@@ -31,16 +31,14 @@ export async function executeScheduleCreate(
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// Resolve syntax and expression from new or legacy fields
|
|
35
34
|
const resolved = normalizeScheduleSyntax({
|
|
36
35
|
syntax: input.syntax as "cron" | "rrule" | undefined,
|
|
37
36
|
expression: input.expression as string | undefined,
|
|
38
|
-
legacyCronExpression: input.cron_expression as string | undefined,
|
|
39
37
|
});
|
|
40
38
|
|
|
41
39
|
if (!resolved) {
|
|
42
40
|
return {
|
|
43
|
-
content: "Error: expression
|
|
41
|
+
content: "Error: expression is required",
|
|
44
42
|
isError: true,
|
|
45
43
|
};
|
|
46
44
|
}
|
|
@@ -20,6 +20,14 @@ export async function executeScheduleUpdate(
|
|
|
20
20
|
return { content: "Error: job_id is required", isError: true };
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
if (input.cron_expression !== undefined) {
|
|
24
|
+
return {
|
|
25
|
+
content:
|
|
26
|
+
'Error: "cron_expression" is deprecated. Use "expression" instead.',
|
|
27
|
+
isError: true,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
const updates: Record<string, unknown> = {};
|
|
24
32
|
if (input.name !== undefined) updates.name = input.name;
|
|
25
33
|
if (input.timezone !== undefined) updates.timezone = input.timezone;
|
|
@@ -27,13 +35,10 @@ export async function executeScheduleUpdate(
|
|
|
27
35
|
if (input.enabled !== undefined) updates.enabled = input.enabled;
|
|
28
36
|
|
|
29
37
|
// Auto-detect syntax when expression changes without explicit syntax
|
|
30
|
-
|
|
31
|
-
input.expression !== undefined || input.cron_expression !== undefined;
|
|
32
|
-
if (hasExpression || input.syntax !== undefined) {
|
|
38
|
+
if (input.expression !== undefined || input.syntax !== undefined) {
|
|
33
39
|
const resolved = normalizeScheduleSyntax({
|
|
34
40
|
syntax: input.syntax as "cron" | "rrule" | undefined,
|
|
35
41
|
expression: input.expression as string | undefined,
|
|
36
|
-
legacyCronExpression: input.cron_expression as string | undefined,
|
|
37
42
|
});
|
|
38
43
|
if (resolved) {
|
|
39
44
|
updates.syntax = resolved.syntax;
|
|
@@ -42,8 +47,6 @@ export async function executeScheduleUpdate(
|
|
|
42
47
|
updates.expression = input.expression;
|
|
43
48
|
const detected = detectScheduleSyntax(input.expression as string);
|
|
44
49
|
if (detected) updates.syntax = detected;
|
|
45
|
-
} else if (input.cron_expression !== undefined) {
|
|
46
|
-
updates.cronExpression = input.cron_expression;
|
|
47
50
|
}
|
|
48
51
|
// When only syntax is provided (no expression), normalizeScheduleSyntax returns null
|
|
49
52
|
// but we still need to persist the explicit syntax value.
|