@vellumai/vellum-gateway 0.7.0 → 0.7.2
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/AGENTS.md +4 -0
- package/ARCHITECTURE.md +67 -25
- package/Dockerfile +2 -0
- package/README.md +50 -13
- package/bun.lock +16 -2
- package/knip.json +3 -1
- package/package.json +3 -1
- package/src/__tests__/auto-approve-thresholds.test.ts +49 -22
- package/src/__tests__/channel-verification-session-proxy.test.ts +0 -1
- package/src/__tests__/config-file-watcher.test.ts +181 -0
- package/src/__tests__/config.test.ts +0 -1
- package/src/__tests__/contacts-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/credential-watcher-managed-bootstrap.test.ts +10 -2
- package/src/__tests__/credential-watcher.test.ts +30 -2
- package/src/__tests__/db-connection-isolation.test.ts +157 -0
- package/src/__tests__/fake-assistant-ipc.ts +39 -0
- package/src/__tests__/feature-flags-route.test.ts +8 -8
- package/src/__tests__/guardian-init-lockfile.test.ts +30 -4
- package/src/__tests__/ipc-feature-flag-routes.test.ts +1 -1
- package/src/__tests__/live-voice-websocket.test.ts +0 -1
- package/src/__tests__/load-guards.test.ts +0 -1
- package/src/__tests__/migration-teleport-gcs-proxy.test.ts +0 -1
- package/src/__tests__/oauth-callback.test.ts +0 -1
- package/src/__tests__/pair-origin-allowlist.test.ts +155 -0
- package/src/__tests__/rate-limit-loopback.test.ts +1 -1
- package/src/__tests__/remote-feature-flag-sync.test.ts +47 -7
- package/src/__tests__/resolve-assistant.test.ts +0 -1
- package/src/__tests__/route-schema-guard.test.ts +42 -6
- package/src/__tests__/runtime-client.test.ts +0 -1
- package/src/__tests__/runtime-health-proxy.test.ts +0 -1
- package/src/__tests__/runtime-proxy-auth.test.ts +0 -1
- package/src/__tests__/runtime-proxy.test.ts +0 -1
- package/src/__tests__/slack-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/slack-display-name.test.ts +66 -1
- package/src/__tests__/slack-normalize.test.ts +158 -4
- package/src/__tests__/slack-reaction-normalize.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-catchup.test.ts +857 -0
- package/src/__tests__/slack-socket-mode-scopes.test.ts +52 -0
- package/src/__tests__/slack-socket-mode-thread-tracking.test.ts +654 -0
- package/src/__tests__/stt-stream-websocket.test.ts +0 -1
- package/src/__tests__/telegram-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/telegram-send-attachments.test.ts +0 -1
- package/src/__tests__/telegram-webhook-handler.test.ts +0 -1
- package/src/__tests__/text-verification-helpers.test.ts +136 -0
- package/src/__tests__/twilio-media-websocket.test.ts +0 -1
- package/src/__tests__/twilio-relay-websocket.test.ts +0 -1
- package/src/__tests__/twilio-webhooks.test.ts +220 -3
- package/src/__tests__/upstream-transport.test.ts +0 -36
- package/src/__tests__/whatsapp-download.test.ts +0 -1
- package/src/__tests__/whatsapp-webhook.test.ts +0 -1
- package/src/auth/guardian-refresh.ts +4 -18
- package/src/auth/ipc-route-policy.ts +217 -0
- package/src/backup/backup-key.ts +138 -0
- package/src/backup/backup-routes.ts +159 -0
- package/src/backup/backup-worker.ts +374 -0
- package/src/backup/list-snapshots.ts +97 -0
- package/src/backup/local-writer.ts +87 -0
- package/src/backup/offsite-writer.ts +182 -0
- package/src/backup/paths.ts +123 -0
- package/src/backup/stream-crypt.ts +258 -0
- package/src/chrome-extension-origins.ts +28 -0
- package/src/cli/enable-proxy.ts +0 -1
- package/src/config-file-cache.ts +3 -19
- package/src/config-file-utils.ts +124 -0
- package/src/config-file-watcher.ts +57 -25
- package/src/config.ts +4 -7
- package/src/db/connection.ts +65 -3
- package/src/db/contact-store.ts +30 -1
- package/src/db/data-migrations/index.ts +2 -0
- package/src/db/data-migrations/m0003-recover-backup-key.ts +71 -0
- package/src/db/schema.ts +92 -0
- package/src/db/slack-store.ts +144 -11
- package/src/feature-flag-registry.json +40 -152
- package/src/handlers/handle-inbound.ts +123 -0
- package/src/http/middleware/auth.ts +44 -1
- package/src/http/middleware/cors.ts +84 -0
- package/src/http/middleware/rate-limit.ts +6 -8
- package/src/http/routes/auto-approve-thresholds.ts +17 -1
- package/src/http/routes/brain-graph-proxy.ts +1 -1
- package/src/http/routes/channel-readiness-proxy.ts +2 -2
- package/src/http/routes/channel-verification-session-proxy.ts +19 -37
- package/src/http/routes/contact-prompt.ts +149 -0
- package/src/http/routes/contacts-control-plane-proxy.ts +2 -2
- package/src/http/routes/email-webhook.test.ts +0 -1
- package/src/http/routes/ipc-runtime-proxy.test.ts +197 -1
- package/src/http/routes/ipc-runtime-proxy.ts +95 -0
- package/src/http/routes/log-export.test.ts +0 -1
- package/src/http/routes/log-tail.test.ts +336 -0
- package/src/http/routes/log-tail.ts +87 -0
- package/src/http/routes/migration-proxy.ts +1 -2
- package/src/http/routes/oauth-apps-proxy.ts +2 -2
- package/src/http/routes/oauth-providers-proxy.ts +2 -2
- package/src/http/routes/pair.ts +322 -0
- package/src/http/routes/privacy-config.ts +65 -79
- package/src/http/routes/runtime-health-proxy.ts +2 -2
- package/src/http/routes/runtime-proxy.ts +3 -1
- package/src/http/routes/slack-control-plane-proxy.ts +3 -20
- package/src/http/routes/stt-stream-websocket.ts +2 -3
- package/src/http/routes/telegram-control-plane-proxy.ts +2 -2
- package/src/http/routes/telegram-webhook.test.ts +0 -1
- package/src/http/routes/telegram-webhook.ts +6 -0
- package/src/http/routes/trust-rules.suggest.test.ts +25 -0
- package/src/http/routes/trust-rules.ts +7 -0
- package/src/http/routes/twilio-control-plane-proxy.ts +2 -2
- package/src/http/routes/twilio-media-websocket.ts +5 -5
- package/src/http/routes/twilio-voice-verify-callback.ts +310 -0
- package/src/http/routes/twilio-voice-webhook.test.ts +65 -1
- package/src/http/routes/twilio-voice-webhook.ts +45 -1
- package/src/http/routes/whatsapp-webhook.test.ts +0 -1
- package/src/index.ts +357 -278
- package/src/ipc/assistant-client.ts +8 -4
- package/src/ipc/contact-handlers.ts +88 -3
- package/src/ipc/threshold-handlers.ts +2 -0
- package/src/post-assistant-ready.ts +5 -3
- package/src/risk/bash-risk-classifier.test.ts +35 -27
- package/src/risk/bash-risk-classifier.ts +44 -14
- package/src/risk/command-registry/commands/assistant.ts +8 -19
- package/src/risk/command-registry.test.ts +0 -15
- package/src/risk/risk-classifier-parity.test.ts +1 -3
- package/src/runtime/client.ts +58 -3
- package/src/schema.ts +277 -104
- package/src/slack/normalize.test.ts +98 -0
- package/src/slack/normalize.ts +107 -32
- package/src/slack/slack-web.ts +213 -0
- package/src/slack/socket-mode.ts +701 -39
- package/src/telegram/send.test.ts +0 -1
- package/src/twilio/validate-webhook.ts +53 -14
- package/src/twilio/webhook-sync-trigger.ts +58 -0
- package/src/twilio/webhook-sync.test.ts +286 -0
- package/src/twilio/webhook-sync.ts +84 -0
- package/src/util/is-loopback-address.ts +27 -0
- package/src/velay/bridge-utils.ts +228 -0
- package/src/velay/client.test.ts +939 -0
- package/src/velay/client.ts +555 -0
- package/src/velay/http-bridge.test.ts +217 -0
- package/src/velay/http-bridge.ts +83 -0
- package/src/velay/protocol.ts +178 -0
- package/src/velay/test-fake-websocket.ts +69 -0
- package/src/velay/websocket-bridge.test.ts +367 -0
- package/src/velay/websocket-bridge.ts +324 -0
- package/src/verification/binding-helpers.ts +107 -0
- package/src/verification/code-parsing.ts +44 -0
- package/src/verification/contact-helpers.ts +342 -0
- package/src/verification/identity-match.ts +68 -0
- package/src/verification/identity.ts +61 -0
- package/src/verification/rate-limit-helpers.ts +205 -0
- package/src/verification/reply-delivery.ts +109 -0
- package/src/verification/session-helpers.ts +164 -0
- package/src/verification/text-verification.ts +372 -0
- package/src/version.ts +35 -0
- package/src/voice/verification.ts +456 -0
- package/src/webhook-pipeline.ts +4 -0
- package/src/__tests__/browser-relay-websocket.test.ts +0 -698
- package/src/__tests__/telegram-only-default.test.ts +0 -133
- package/src/auth/capability-tokens.ts +0 -248
- package/src/http/routes/browser-extension-pair.ts +0 -455
- package/src/http/routes/browser-relay-websocket.ts +0 -381
- package/src/http/routes/config-file-utils.ts +0 -73
- package/src/ipc/capability-token-handlers.ts +0 -30
- package/src/pairing/approved-devices-store.ts +0 -110
- package/src/pairing/pairing-routes.ts +0 -379
- package/src/pairing/pairing-store.ts +0 -218
|
@@ -2,8 +2,12 @@ import { describe, test, expect } from "bun:test";
|
|
|
2
2
|
import {
|
|
3
3
|
stripBotMention,
|
|
4
4
|
normalizeSlackAppMention,
|
|
5
|
+
normalizeSlackChannelMessage,
|
|
6
|
+
normalizeSlackDirectMessage,
|
|
5
7
|
normalizeSlackMessageEdit,
|
|
6
8
|
type SlackAppMentionEvent,
|
|
9
|
+
type SlackChannelMessageEvent,
|
|
10
|
+
type SlackDirectMessageEvent,
|
|
7
11
|
type SlackMessageChangedEvent,
|
|
8
12
|
} from "../slack/normalize.js";
|
|
9
13
|
import type { GatewayConfig } from "../config.js";
|
|
@@ -26,7 +30,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
|
|
|
26
30
|
routingEntries: [],
|
|
27
31
|
runtimeInitialBackoffMs: 500,
|
|
28
32
|
runtimeMaxRetries: 2,
|
|
29
|
-
runtimeProxyEnabled: false,
|
|
30
33
|
runtimeProxyRequireAuth: false,
|
|
31
34
|
runtimeTimeoutMs: 30000,
|
|
32
35
|
shutdownDrainMs: 5000,
|
|
@@ -54,8 +57,16 @@ describe("stripBotMention", () => {
|
|
|
54
57
|
expect(stripBotMention("<@U123BOT> hello world")).toBe("hello world");
|
|
55
58
|
});
|
|
56
59
|
|
|
57
|
-
test("strips
|
|
58
|
-
expect(
|
|
60
|
+
test("strips repeated leading bot mentions while preserving a following human mention", () => {
|
|
61
|
+
expect(
|
|
62
|
+
stripBotMention("<@U123BOT> <@U123BOT> <@U456OTHER> hello", "U123BOT"),
|
|
63
|
+
).toBe("<@U456OTHER> hello");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("fallback strips only the first leading mention", () => {
|
|
67
|
+
expect(stripBotMention("<@U123BOT> <@U456OTHER> hello")).toBe(
|
|
68
|
+
"<@U456OTHER> hello",
|
|
69
|
+
);
|
|
59
70
|
});
|
|
60
71
|
|
|
61
72
|
test("falls back to original text when stripping produces empty string", () => {
|
|
@@ -133,7 +144,37 @@ describe("normalizeSlackAppMention", () => {
|
|
|
133
144
|
const result = await normalizeSlackAppMention(event, "evt-006", config);
|
|
134
145
|
|
|
135
146
|
expect(result).not.toBeNull();
|
|
136
|
-
expect(result!.event.message.content).toBe("
|
|
147
|
+
expect(result!.event.message.content).toBe("@unknown-user");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("renders a human mention after stripping the app mention", async () => {
|
|
151
|
+
const config = makeConfig();
|
|
152
|
+
const event = makeEvent({ text: "<@UBOT> <@ULEO> can you check?" });
|
|
153
|
+
const result = await normalizeSlackAppMention(
|
|
154
|
+
event,
|
|
155
|
+
"evt-mention-label",
|
|
156
|
+
config,
|
|
157
|
+
"UBOT",
|
|
158
|
+
undefined,
|
|
159
|
+
{ userLabels: { ULEO: "leo" } },
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(result).not.toBeNull();
|
|
163
|
+
expect(result!.event.message.content).toBe("@leo can you check?");
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("renders unresolved user mentions with the unknown-user fallback", async () => {
|
|
167
|
+
const config = makeConfig();
|
|
168
|
+
const event = makeEvent({ text: "<@UBOT> <@UUNKNOWN> can you check?" });
|
|
169
|
+
const result = await normalizeSlackAppMention(
|
|
170
|
+
event,
|
|
171
|
+
"evt-unknown-mention",
|
|
172
|
+
config,
|
|
173
|
+
"UBOT",
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(result).not.toBeNull();
|
|
177
|
+
expect(result!.event.message.content).toBe("@unknown-user can you check?");
|
|
137
178
|
});
|
|
138
179
|
|
|
139
180
|
test("thread_ts is preserved in return value", async () => {
|
|
@@ -205,6 +246,119 @@ describe("normalizeSlackAppMention", () => {
|
|
|
205
246
|
});
|
|
206
247
|
});
|
|
207
248
|
|
|
249
|
+
function makeDirectMessageEvent(
|
|
250
|
+
overrides: Partial<SlackDirectMessageEvent> = {},
|
|
251
|
+
): SlackDirectMessageEvent {
|
|
252
|
+
return {
|
|
253
|
+
type: "message",
|
|
254
|
+
user: "U_USER123",
|
|
255
|
+
text: "hello world",
|
|
256
|
+
ts: "1700000000.000100",
|
|
257
|
+
channel: "D_DIRECT1",
|
|
258
|
+
channel_type: "im",
|
|
259
|
+
...overrides,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function makeChannelMessageEvent(
|
|
264
|
+
overrides: Partial<SlackChannelMessageEvent> = {},
|
|
265
|
+
): SlackChannelMessageEvent {
|
|
266
|
+
return {
|
|
267
|
+
type: "message",
|
|
268
|
+
user: "U_USER123",
|
|
269
|
+
text: "hello world",
|
|
270
|
+
ts: "1700000000.000100",
|
|
271
|
+
channel: "C_CHANNEL1",
|
|
272
|
+
channel_type: "channel",
|
|
273
|
+
...overrides,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
describe("Slack inbound mention rendering", () => {
|
|
278
|
+
test("direct messages render mentions without stripping the bot mention", () => {
|
|
279
|
+
const config = makeConfig();
|
|
280
|
+
const event = makeDirectMessageEvent({
|
|
281
|
+
text: "<@UBOT> <@ULEO> hello",
|
|
282
|
+
});
|
|
283
|
+
const result = normalizeSlackDirectMessage(
|
|
284
|
+
event,
|
|
285
|
+
"evt-dm-render",
|
|
286
|
+
config,
|
|
287
|
+
"UBOT",
|
|
288
|
+
undefined,
|
|
289
|
+
{ userLabels: { UBOT: "assistant", ULEO: "leo" } },
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
expect(result).not.toBeNull();
|
|
293
|
+
expect(result!.event.message.content).toBe("@assistant @leo hello");
|
|
294
|
+
expect(result!.event.actor.actorExternalId).toBe("U_USER123");
|
|
295
|
+
expect(result!.event.message.conversationExternalId).toBe("D_DIRECT1");
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("channel messages strip only the configured bot mention and render remaining mentions", () => {
|
|
299
|
+
const config = makeConfig();
|
|
300
|
+
const event = makeChannelMessageEvent({
|
|
301
|
+
text: "<@UBOT> <@ULEO> hello",
|
|
302
|
+
});
|
|
303
|
+
const result = normalizeSlackChannelMessage(
|
|
304
|
+
event,
|
|
305
|
+
"evt-channel-render",
|
|
306
|
+
config,
|
|
307
|
+
"UBOT",
|
|
308
|
+
undefined,
|
|
309
|
+
{ userLabels: { ULEO: "leo" } },
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
expect(result).not.toBeNull();
|
|
313
|
+
expect(result!.event.message.content).toBe("@leo hello");
|
|
314
|
+
expect(result!.event.actor.actorExternalId).toBe("U_USER123");
|
|
315
|
+
expect(result!.event.message.conversationExternalId).toBe("C_CHANNEL1");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("channel messages without bot user ID strip only the first leading mention fallback", () => {
|
|
319
|
+
const config = makeConfig();
|
|
320
|
+
const event = makeChannelMessageEvent({
|
|
321
|
+
text: "<@UBOT> <@ULEO> hello",
|
|
322
|
+
});
|
|
323
|
+
const result = normalizeSlackChannelMessage(
|
|
324
|
+
event,
|
|
325
|
+
"evt-channel-fallback-render",
|
|
326
|
+
config,
|
|
327
|
+
undefined,
|
|
328
|
+
undefined,
|
|
329
|
+
{ userLabels: { ULEO: "leo" } },
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
expect(result).not.toBeNull();
|
|
333
|
+
expect(result!.event.message.content).toBe("@leo hello");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("message edits render mentions and preserve edit metadata", () => {
|
|
337
|
+
const config = makeConfig();
|
|
338
|
+
const event = makeMessageChangedEvent({
|
|
339
|
+
message: {
|
|
340
|
+
user: "U_USER123",
|
|
341
|
+
text: "<@UBOT> <@ULEO> edited",
|
|
342
|
+
ts: "1700000000.000100",
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
const result = normalizeSlackMessageEdit(
|
|
346
|
+
event,
|
|
347
|
+
"evt-edit-render",
|
|
348
|
+
config,
|
|
349
|
+
"UBOT",
|
|
350
|
+
{ userLabels: { ULEO: "leo" } },
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
expect(result).not.toBeNull();
|
|
354
|
+
expect(result!.event.message.content).toBe("@leo edited");
|
|
355
|
+
expect(result!.event.message.isEdit).toBe(true);
|
|
356
|
+
expect(result!.event.message.externalMessageId).toBe("evt-edit-render");
|
|
357
|
+
expect(result!.event.source.messageId).toBe("1700000000.000100");
|
|
358
|
+
expect(result!.event.actor.actorExternalId).toBe("U_USER123");
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
208
362
|
function makeMessageChangedEvent(
|
|
209
363
|
overrides: Partial<SlackMessageChangedEvent> = {},
|
|
210
364
|
): SlackMessageChangedEvent {
|
|
@@ -23,7 +23,6 @@ function makeConfig(overrides: Partial<GatewayConfig> = {}): GatewayConfig {
|
|
|
23
23
|
routingEntries: [],
|
|
24
24
|
runtimeInitialBackoffMs: 500,
|
|
25
25
|
runtimeMaxRetries: 2,
|
|
26
|
-
runtimeProxyEnabled: false,
|
|
27
26
|
runtimeProxyRequireAuth: false,
|
|
28
27
|
runtimeTimeoutMs: 30000,
|
|
29
28
|
shutdownDrainMs: 5000,
|