@vellumai/vellum-gateway 0.4.50 → 0.4.51

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/knip.json CHANGED
@@ -1,8 +1,5 @@
1
1
  {
2
- "project": [
3
- "src/**/*.ts"
4
- ],
5
- "ignore": [
6
- "src/**/__tests__/**"
7
- ]
2
+ "entry": ["src/**/*.test.ts", "src/**/__tests__/**/*.ts"],
3
+ "project": ["src/**/*.ts"],
4
+ "ignoreDependencies": ["zod"]
8
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/vellum-gateway",
3
- "version": "0.4.50",
3
+ "version": "0.4.51",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./twilio/verify": "./src/twilio/verify.ts",
@@ -16,9 +16,10 @@
16
16
  "format": "prettier --write .",
17
17
  "format:check": "prettier --check .",
18
18
  "lint": "eslint",
19
+ "lint:unused": "knip --include files,dependencies,unlisted",
19
20
  "typecheck": "bunx tsc --noEmit",
20
21
  "prebuild": "cd .. && bun run meta/feature-flags/sync-bundled-copies.ts",
21
- "postinstall": "cd .. && (git config core.hooksPath || git config core.hooksPath .githooks 2>/dev/null || true) && (bun run meta/feature-flags/sync-bundled-copies.ts 2>/dev/null || true)"
22
+ "postinstall": "cd .. && (git config core.hooksPath || git config core.hooksPath .githooks 2>/dev/null || true) && ([ -f meta/feature-flags/sync-bundled-copies.ts ] && bun run meta/feature-flags/sync-bundled-copies.ts 2>/dev/null || true)"
22
23
  },
23
24
  "dependencies": {
24
25
  "file-type": "^21.3.0",
@@ -246,7 +246,7 @@ describe("telegram webhook handler: gatewayInternalBaseUrl", () => {
246
246
  });
247
247
 
248
248
  describe("telegram webhook handler: /new rejection", () => {
249
- test("/start forwards command intent metadata, does not reset conversation, and sends start acknowledgement", async () => {
249
+ test("/start with payload forwards command intent metadata, does not reset conversation, and suppresses ACK", async () => {
250
250
  const config = makeConfig({
251
251
  routingEntries: [
252
252
  { type: "conversation_id", key: "12345", assistantId: "assistant-a" },
@@ -275,11 +275,11 @@ describe("telegram webhook handler: /new rejection", () => {
275
275
  );
276
276
  expect(resetCall).toBeUndefined();
277
277
 
278
+ // ACK is suppressed when /start has a payload
278
279
  const sendMessageCall = fetchCalls.find((c) =>
279
280
  c.url.includes("/sendMessage"),
280
281
  );
281
- expect(sendMessageCall).toBeDefined();
282
- expect((sendMessageCall!.body as any).text).toContain("Starting up");
282
+ expect(sendMessageCall).toBeUndefined();
283
283
  });
284
284
 
285
285
  test("/start with routing rejection sends setup notice and does not forward", async () => {
@@ -26,16 +26,6 @@ export const SLACK_CHANNEL_TRANSPORT_HINTS = [
26
26
  export const SLACK_CHANNEL_TRANSPORT_UX_BRIEF =
27
27
  "Slack is a threaded channel medium. Replies should stay in-thread when a thread_ts is present. Use plain text or Slack mrkdwn formatting; avoid markdown tables and complex block layouts.";
28
28
 
29
- export function buildSlackTransportMetadata(): {
30
- hints: string[];
31
- uxBrief: string;
32
- } {
33
- return {
34
- hints: [...SLACK_CHANNEL_TRANSPORT_HINTS],
35
- uxBrief: SLACK_CHANNEL_TRANSPORT_UX_BRIEF,
36
- };
37
- }
38
-
39
29
  export const WHATSAPP_CHANNEL_TRANSPORT_HINTS = [
40
30
  "chat-first-medium",
41
31
  "channel-safe-onboarding",
@@ -146,6 +146,10 @@ export function createContactsControlPlaneProxyHandler(config: GatewayConfig) {
146
146
  return proxyToRuntime(req, "/v1/contacts/invites/redeem", "");
147
147
  },
148
148
 
149
+ async handleCallInvite(req: Request, inviteId: string): Promise<Response> {
150
+ return proxyToRuntime(req, `/v1/contacts/invites/${inviteId}/call`, "");
151
+ },
152
+
149
153
  async handleRevokeInvite(
150
154
  req: Request,
151
155
  inviteId: string,
@@ -239,7 +239,7 @@ describe("telegram-webhook callback query acknowledgment", () => {
239
239
  expect(sendTelegramReplyMock).not.toHaveBeenCalled();
240
240
  });
241
241
 
242
- it("forwards /start as channel command-intent metadata and sends start acknowledgement", async () => {
242
+ it("forwards /start with payload as channel command-intent metadata without sending ACK", async () => {
243
243
  const { handler } = createTelegramWebhookHandler(baseConfig, makeCaches());
244
244
  const body = JSON.stringify({
245
245
  update_id: 314,
@@ -263,14 +263,8 @@ describe("telegram-webhook callback query acknowledgment", () => {
263
263
  type: "start",
264
264
  payload: "ref-123",
265
265
  });
266
- expect(sendTelegramReplyMock).toHaveBeenCalledTimes(1);
267
- const sendArgs = sendTelegramReplyMock.mock.calls[0] as unknown as [
268
- GatewayConfig,
269
- string,
270
- string,
271
- ];
272
- expect(sendArgs[1]).toBe("42");
273
- expect(sendArgs[2]).toContain("Starting up");
266
+ // ACK is suppressed when /start has a payload
267
+ expect(sendTelegramReplyMock).not.toHaveBeenCalled();
274
268
  });
275
269
 
276
270
  it("does not call answerCallbackQuery for regular text messages", async () => {
@@ -357,7 +357,9 @@ export function createTelegramWebhookHandler(
357
357
 
358
358
  // Forward to runtime with command-intent metadata so the assistant
359
359
  // generates a natural greeting via the normal agent loop.
360
- if (!normalized.message.callbackQueryId) {
360
+ // Skip the ACK when the /start includes a payload (e.g. invite token)
361
+ // the runtime will send its own contextual reply during ACL enforcement.
362
+ if (!normalized.message.callbackQueryId && !startCmd.payload) {
361
363
  sendTelegramReply(
362
364
  config,
363
365
  normalized.message.conversationExternalId,
package/src/index.ts CHANGED
@@ -466,6 +466,13 @@ async function main() {
466
466
  auth: "edge",
467
467
  handler: (req) => contactsControlPlaneProxy.handleRedeemInvite(req),
468
468
  },
469
+ {
470
+ path: /^\/v1\/contacts\/invites\/([^/]+)\/call$/,
471
+ method: "POST",
472
+ auth: "edge",
473
+ handler: (req, params) =>
474
+ contactsControlPlaneProxy.handleCallInvite(req, params[0]),
475
+ },
469
476
  {
470
477
  path: /^\/v1\/contacts\/invites\/([^/]+)$/,
471
478
  method: "DELETE",
@@ -90,13 +90,6 @@ function cbOnFailure(): void {
90
90
  }
91
91
  }
92
92
 
93
- /** Exported for testing — resets circuit breaker to initial state. */
94
- export function _resetCircuitBreaker(): void {
95
- cbState = CircuitState.CLOSED;
96
- cbConsecutiveFailures = 0;
97
- cbOpenedAt = 0;
98
- }
99
-
100
93
  /**
101
94
  * Build common headers for runtime requests using JWT auth.
102
95
  *
package/src/schema.ts CHANGED
@@ -1145,6 +1145,41 @@ export function buildSchema(): Record<string, unknown> {
1145
1145
  },
1146
1146
  },
1147
1147
  },
1148
+ "/v1/contacts/invites/{inviteId}/call": {
1149
+ post: {
1150
+ summary: "Call a contacts invite",
1151
+ description:
1152
+ "Authenticated gateway endpoint that initiates a call for a contacts invite via the assistant runtime.",
1153
+ operationId: "contactsInvitesCallPost",
1154
+ security: [{ BearerAuth: [] }],
1155
+ parameters: [
1156
+ {
1157
+ name: "inviteId",
1158
+ in: "path",
1159
+ required: true,
1160
+ schema: { type: "string" },
1161
+ },
1162
+ ],
1163
+ requestBody: {
1164
+ required: true,
1165
+ content: {
1166
+ "application/json": {
1167
+ schema: { type: "object", additionalProperties: true },
1168
+ },
1169
+ },
1170
+ },
1171
+ responses: {
1172
+ "200": { description: "Invite call initiated" },
1173
+ "401": {
1174
+ description: "Unauthorized — missing or invalid bearer token",
1175
+ },
1176
+ "404": { description: "Invite not found" },
1177
+ "503": { description: "Bearer token not configured" },
1178
+ "502": { description: "Failed to reach assistant runtime" },
1179
+ "504": { description: "Assistant runtime request timed out" },
1180
+ },
1181
+ },
1182
+ },
1148
1183
  "/v1/contacts/invites/{inviteId}": {
1149
1184
  delete: {
1150
1185
  summary: "Revoke contacts invite",