@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.
Files changed (162) hide show
  1. package/AGENTS.md +4 -0
  2. package/ARCHITECTURE.md +67 -25
  3. package/Dockerfile +2 -0
  4. package/README.md +50 -13
  5. package/bun.lock +16 -2
  6. package/knip.json +3 -1
  7. package/package.json +3 -1
  8. package/src/__tests__/auto-approve-thresholds.test.ts +49 -22
  9. package/src/__tests__/channel-verification-session-proxy.test.ts +0 -1
  10. package/src/__tests__/config-file-watcher.test.ts +181 -0
  11. package/src/__tests__/config.test.ts +0 -1
  12. package/src/__tests__/contacts-control-plane-proxy.test.ts +0 -1
  13. package/src/__tests__/credential-watcher-managed-bootstrap.test.ts +10 -2
  14. package/src/__tests__/credential-watcher.test.ts +30 -2
  15. package/src/__tests__/db-connection-isolation.test.ts +157 -0
  16. package/src/__tests__/fake-assistant-ipc.ts +39 -0
  17. package/src/__tests__/feature-flags-route.test.ts +8 -8
  18. package/src/__tests__/guardian-init-lockfile.test.ts +30 -4
  19. package/src/__tests__/ipc-feature-flag-routes.test.ts +1 -1
  20. package/src/__tests__/live-voice-websocket.test.ts +0 -1
  21. package/src/__tests__/load-guards.test.ts +0 -1
  22. package/src/__tests__/migration-teleport-gcs-proxy.test.ts +0 -1
  23. package/src/__tests__/oauth-callback.test.ts +0 -1
  24. package/src/__tests__/pair-origin-allowlist.test.ts +155 -0
  25. package/src/__tests__/rate-limit-loopback.test.ts +1 -1
  26. package/src/__tests__/remote-feature-flag-sync.test.ts +47 -7
  27. package/src/__tests__/resolve-assistant.test.ts +0 -1
  28. package/src/__tests__/route-schema-guard.test.ts +42 -6
  29. package/src/__tests__/runtime-client.test.ts +0 -1
  30. package/src/__tests__/runtime-health-proxy.test.ts +0 -1
  31. package/src/__tests__/runtime-proxy-auth.test.ts +0 -1
  32. package/src/__tests__/runtime-proxy.test.ts +0 -1
  33. package/src/__tests__/slack-control-plane-proxy.test.ts +0 -1
  34. package/src/__tests__/slack-display-name.test.ts +66 -1
  35. package/src/__tests__/slack-normalize.test.ts +158 -4
  36. package/src/__tests__/slack-reaction-normalize.test.ts +0 -1
  37. package/src/__tests__/slack-socket-mode-catchup.test.ts +857 -0
  38. package/src/__tests__/slack-socket-mode-scopes.test.ts +52 -0
  39. package/src/__tests__/slack-socket-mode-thread-tracking.test.ts +654 -0
  40. package/src/__tests__/stt-stream-websocket.test.ts +0 -1
  41. package/src/__tests__/telegram-control-plane-proxy.test.ts +0 -1
  42. package/src/__tests__/telegram-send-attachments.test.ts +0 -1
  43. package/src/__tests__/telegram-webhook-handler.test.ts +0 -1
  44. package/src/__tests__/text-verification-helpers.test.ts +136 -0
  45. package/src/__tests__/twilio-media-websocket.test.ts +0 -1
  46. package/src/__tests__/twilio-relay-websocket.test.ts +0 -1
  47. package/src/__tests__/twilio-webhooks.test.ts +220 -3
  48. package/src/__tests__/upstream-transport.test.ts +0 -36
  49. package/src/__tests__/whatsapp-download.test.ts +0 -1
  50. package/src/__tests__/whatsapp-webhook.test.ts +0 -1
  51. package/src/auth/guardian-refresh.ts +4 -18
  52. package/src/auth/ipc-route-policy.ts +217 -0
  53. package/src/backup/backup-key.ts +138 -0
  54. package/src/backup/backup-routes.ts +159 -0
  55. package/src/backup/backup-worker.ts +374 -0
  56. package/src/backup/list-snapshots.ts +97 -0
  57. package/src/backup/local-writer.ts +87 -0
  58. package/src/backup/offsite-writer.ts +182 -0
  59. package/src/backup/paths.ts +123 -0
  60. package/src/backup/stream-crypt.ts +258 -0
  61. package/src/chrome-extension-origins.ts +28 -0
  62. package/src/cli/enable-proxy.ts +0 -1
  63. package/src/config-file-cache.ts +3 -19
  64. package/src/config-file-utils.ts +124 -0
  65. package/src/config-file-watcher.ts +57 -25
  66. package/src/config.ts +4 -7
  67. package/src/db/connection.ts +65 -3
  68. package/src/db/contact-store.ts +30 -1
  69. package/src/db/data-migrations/index.ts +2 -0
  70. package/src/db/data-migrations/m0003-recover-backup-key.ts +71 -0
  71. package/src/db/schema.ts +92 -0
  72. package/src/db/slack-store.ts +144 -11
  73. package/src/feature-flag-registry.json +40 -152
  74. package/src/handlers/handle-inbound.ts +123 -0
  75. package/src/http/middleware/auth.ts +44 -1
  76. package/src/http/middleware/cors.ts +84 -0
  77. package/src/http/middleware/rate-limit.ts +6 -8
  78. package/src/http/routes/auto-approve-thresholds.ts +17 -1
  79. package/src/http/routes/brain-graph-proxy.ts +1 -1
  80. package/src/http/routes/channel-readiness-proxy.ts +2 -2
  81. package/src/http/routes/channel-verification-session-proxy.ts +19 -37
  82. package/src/http/routes/contact-prompt.ts +149 -0
  83. package/src/http/routes/contacts-control-plane-proxy.ts +2 -2
  84. package/src/http/routes/email-webhook.test.ts +0 -1
  85. package/src/http/routes/ipc-runtime-proxy.test.ts +197 -1
  86. package/src/http/routes/ipc-runtime-proxy.ts +95 -0
  87. package/src/http/routes/log-export.test.ts +0 -1
  88. package/src/http/routes/log-tail.test.ts +336 -0
  89. package/src/http/routes/log-tail.ts +87 -0
  90. package/src/http/routes/migration-proxy.ts +1 -2
  91. package/src/http/routes/oauth-apps-proxy.ts +2 -2
  92. package/src/http/routes/oauth-providers-proxy.ts +2 -2
  93. package/src/http/routes/pair.ts +322 -0
  94. package/src/http/routes/privacy-config.ts +65 -79
  95. package/src/http/routes/runtime-health-proxy.ts +2 -2
  96. package/src/http/routes/runtime-proxy.ts +3 -1
  97. package/src/http/routes/slack-control-plane-proxy.ts +3 -20
  98. package/src/http/routes/stt-stream-websocket.ts +2 -3
  99. package/src/http/routes/telegram-control-plane-proxy.ts +2 -2
  100. package/src/http/routes/telegram-webhook.test.ts +0 -1
  101. package/src/http/routes/telegram-webhook.ts +6 -0
  102. package/src/http/routes/trust-rules.suggest.test.ts +25 -0
  103. package/src/http/routes/trust-rules.ts +7 -0
  104. package/src/http/routes/twilio-control-plane-proxy.ts +2 -2
  105. package/src/http/routes/twilio-media-websocket.ts +5 -5
  106. package/src/http/routes/twilio-voice-verify-callback.ts +310 -0
  107. package/src/http/routes/twilio-voice-webhook.test.ts +65 -1
  108. package/src/http/routes/twilio-voice-webhook.ts +45 -1
  109. package/src/http/routes/whatsapp-webhook.test.ts +0 -1
  110. package/src/index.ts +357 -278
  111. package/src/ipc/assistant-client.ts +8 -4
  112. package/src/ipc/contact-handlers.ts +88 -3
  113. package/src/ipc/threshold-handlers.ts +2 -0
  114. package/src/post-assistant-ready.ts +5 -3
  115. package/src/risk/bash-risk-classifier.test.ts +35 -27
  116. package/src/risk/bash-risk-classifier.ts +44 -14
  117. package/src/risk/command-registry/commands/assistant.ts +8 -19
  118. package/src/risk/command-registry.test.ts +0 -15
  119. package/src/risk/risk-classifier-parity.test.ts +1 -3
  120. package/src/runtime/client.ts +58 -3
  121. package/src/schema.ts +277 -104
  122. package/src/slack/normalize.test.ts +98 -0
  123. package/src/slack/normalize.ts +107 -32
  124. package/src/slack/slack-web.ts +213 -0
  125. package/src/slack/socket-mode.ts +701 -39
  126. package/src/telegram/send.test.ts +0 -1
  127. package/src/twilio/validate-webhook.ts +53 -14
  128. package/src/twilio/webhook-sync-trigger.ts +58 -0
  129. package/src/twilio/webhook-sync.test.ts +286 -0
  130. package/src/twilio/webhook-sync.ts +84 -0
  131. package/src/util/is-loopback-address.ts +27 -0
  132. package/src/velay/bridge-utils.ts +228 -0
  133. package/src/velay/client.test.ts +939 -0
  134. package/src/velay/client.ts +555 -0
  135. package/src/velay/http-bridge.test.ts +217 -0
  136. package/src/velay/http-bridge.ts +83 -0
  137. package/src/velay/protocol.ts +178 -0
  138. package/src/velay/test-fake-websocket.ts +69 -0
  139. package/src/velay/websocket-bridge.test.ts +367 -0
  140. package/src/velay/websocket-bridge.ts +324 -0
  141. package/src/verification/binding-helpers.ts +107 -0
  142. package/src/verification/code-parsing.ts +44 -0
  143. package/src/verification/contact-helpers.ts +342 -0
  144. package/src/verification/identity-match.ts +68 -0
  145. package/src/verification/identity.ts +61 -0
  146. package/src/verification/rate-limit-helpers.ts +205 -0
  147. package/src/verification/reply-delivery.ts +109 -0
  148. package/src/verification/session-helpers.ts +164 -0
  149. package/src/verification/text-verification.ts +372 -0
  150. package/src/version.ts +35 -0
  151. package/src/voice/verification.ts +456 -0
  152. package/src/webhook-pipeline.ts +4 -0
  153. package/src/__tests__/browser-relay-websocket.test.ts +0 -698
  154. package/src/__tests__/telegram-only-default.test.ts +0 -133
  155. package/src/auth/capability-tokens.ts +0 -248
  156. package/src/http/routes/browser-extension-pair.ts +0 -455
  157. package/src/http/routes/browser-relay-websocket.ts +0 -381
  158. package/src/http/routes/config-file-utils.ts +0 -73
  159. package/src/ipc/capability-token-handlers.ts +0 -30
  160. package/src/pairing/approved-devices-store.ts +0 -110
  161. package/src/pairing/pairing-routes.ts +0 -379
  162. package/src/pairing/pairing-store.ts +0 -218
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Gateway-owned text-channel verification intercept.
3
+ *
4
+ * Called from handleInbound before forwardToRuntime. When a message is a
5
+ * bare verification code AND there is a pending/active session for this
6
+ * channel, the gateway handles the entire flow:
7
+ *
8
+ * 1. Parse code from message content
9
+ * 2. Check rate limits
10
+ * 3. Hash + find matching session
11
+ * 4. Verify identity binding (outbound sessions)
12
+ * 5. Consume session (dual-write, atomic status guard)
13
+ * 6. Apply side effects (guardian binding OR trusted contact upsert)
14
+ * 7. Deliver deterministic reply
15
+ *
16
+ * The assistant NEVER sees verification code messages. Both success and
17
+ * failure are short-circuited at the gateway.
18
+ */
19
+
20
+ import { createGuardianBinding } from "../auth/guardian-bootstrap.js";
21
+ import { getLogger } from "../logger.js";
22
+
23
+ import {
24
+ getExistingGuardianBinding,
25
+ resolveCanonicalPrincipal,
26
+ revokeExistingChannelGuardian,
27
+ } from "./binding-helpers.js";
28
+ import { parseVerificationCode, hashVerificationSecret } from "./code-parsing.js";
29
+ import {
30
+ findContactChannelByExternalUserId,
31
+ upsertVerifiedContactChannel,
32
+ } from "./contact-helpers.js";
33
+ import { canonicalizeInboundIdentity } from "./identity.js";
34
+ import { checkIdentityMatch } from "./identity-match.js";
35
+ import {
36
+ isRateLimited,
37
+ recordInvalidAttempt,
38
+ resetRateLimit,
39
+ } from "./rate-limit-helpers.js";
40
+ import {
41
+ composeVerificationFailureReply,
42
+ composeVerificationSuccessReply,
43
+ deliverVerificationReply,
44
+ } from "./reply-delivery.js";
45
+ import {
46
+ consumeSession,
47
+ findSessionByHash,
48
+ hasPendingOrActiveSession,
49
+ } from "./session-helpers.js";
50
+
51
+ const log = getLogger("text-verification");
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // Types
55
+ // ---------------------------------------------------------------------------
56
+
57
+ export interface TextVerificationInterceptParams {
58
+ sourceChannel: string;
59
+ messageContent: string;
60
+ actorExternalUserId: string;
61
+ actorChatId: string;
62
+ actorDisplayName?: string;
63
+ actorUsername?: string;
64
+ replyCallbackUrl?: string;
65
+ assistantId?: string;
66
+ }
67
+
68
+ export type TextVerificationResult =
69
+ | { intercepted: false }
70
+ | {
71
+ intercepted: true;
72
+ outcome: "verified" | "failed";
73
+ trustClass: "guardian" | "trusted_contact";
74
+ };
75
+
76
+ // ---------------------------------------------------------------------------
77
+ // Main intercept
78
+ // ---------------------------------------------------------------------------
79
+
80
+ export async function tryTextVerificationIntercept(
81
+ params: TextVerificationInterceptParams,
82
+ ): Promise<TextVerificationResult> {
83
+ const {
84
+ sourceChannel,
85
+ messageContent,
86
+ actorExternalUserId,
87
+ actorChatId,
88
+ actorDisplayName,
89
+ actorUsername,
90
+ replyCallbackUrl,
91
+ assistantId,
92
+ } = params;
93
+
94
+ // 1. Parse — only bare 6-digit numeric or 64-char hex codes are intercepted
95
+ const code = parseVerificationCode(messageContent);
96
+ if (code === undefined) {
97
+ return { intercepted: false };
98
+ }
99
+
100
+ // 2. Fast guard — is there any pending session for this channel?
101
+ const hasSessions = await hasPendingOrActiveSession(sourceChannel);
102
+ if (!hasSessions) {
103
+ return { intercepted: false };
104
+ }
105
+
106
+ const canonicalUserId =
107
+ canonicalizeInboundIdentity(sourceChannel, actorExternalUserId) ??
108
+ actorExternalUserId;
109
+
110
+ // 3. Rate limit check
111
+ if (isRateLimited(sourceChannel, canonicalUserId, actorChatId)) {
112
+ log.info(
113
+ { sourceChannel, actorExternalUserId: canonicalUserId },
114
+ "Verification attempt rate-limited",
115
+ );
116
+ await replyWithFailure(
117
+ replyCallbackUrl,
118
+ actorChatId,
119
+ assistantId,
120
+ "The verification code is invalid or has expired.",
121
+ );
122
+ return {
123
+ intercepted: true,
124
+ outcome: "failed",
125
+ trustClass: "guardian",
126
+ };
127
+ }
128
+
129
+ // 4. Hash + find session
130
+ const challengeHash = hashVerificationSecret(code);
131
+ const session = await findSessionByHash(sourceChannel, challengeHash);
132
+
133
+ if (!session) {
134
+ await recordInvalidAttempt(sourceChannel, canonicalUserId, actorChatId);
135
+ log.info(
136
+ { sourceChannel, actorExternalUserId: canonicalUserId },
137
+ "Verification code did not match any pending session",
138
+ );
139
+ await replyWithFailure(
140
+ replyCallbackUrl,
141
+ actorChatId,
142
+ assistantId,
143
+ "The verification code is invalid or has expired.",
144
+ );
145
+ return {
146
+ intercepted: true,
147
+ outcome: "failed",
148
+ trustClass: "guardian",
149
+ };
150
+ }
151
+
152
+ // 5. Identity binding check (outbound sessions)
153
+ if (!checkIdentityMatch(session, canonicalUserId, actorChatId)) {
154
+ await recordInvalidAttempt(sourceChannel, canonicalUserId, actorChatId);
155
+ log.info(
156
+ { sourceChannel, sessionId: session.id },
157
+ "Verification identity mismatch (anti-oracle: same error as invalid code)",
158
+ );
159
+ await replyWithFailure(
160
+ replyCallbackUrl,
161
+ actorChatId,
162
+ assistantId,
163
+ "The verification code is invalid or has expired.",
164
+ );
165
+ return {
166
+ intercepted: true,
167
+ outcome: "failed",
168
+ trustClass: session.verificationPurpose === "trusted_contact"
169
+ ? "trusted_contact"
170
+ : "guardian",
171
+ };
172
+ }
173
+
174
+ // 6. Consume session (atomic — only the first consumer wins)
175
+ const consumed = await consumeSession(
176
+ session.id,
177
+ canonicalUserId,
178
+ actorChatId,
179
+ );
180
+ if (!consumed) {
181
+ log.warn(
182
+ { sessionId: session.id },
183
+ "Session already consumed by concurrent request",
184
+ );
185
+ await replyWithFailure(
186
+ replyCallbackUrl,
187
+ actorChatId,
188
+ assistantId,
189
+ "The verification code is invalid or has expired.",
190
+ );
191
+ return {
192
+ intercepted: true,
193
+ outcome: "failed",
194
+ trustClass: session.verificationPurpose === "trusted_contact"
195
+ ? "trusted_contact"
196
+ : "guardian",
197
+ };
198
+ }
199
+
200
+ // Reset rate limits on success
201
+ await resetRateLimit(sourceChannel, canonicalUserId, actorChatId);
202
+
203
+ const trustClass: "guardian" | "trusted_contact" =
204
+ session.verificationPurpose === "trusted_contact"
205
+ ? "trusted_contact"
206
+ : "guardian";
207
+
208
+ // 7. Apply side effects
209
+ if (trustClass === "guardian") {
210
+ await applyGuardianSideEffects({
211
+ sourceChannel,
212
+ canonicalUserId,
213
+ actorChatId,
214
+ actorDisplayName,
215
+ actorUsername,
216
+ });
217
+ } else {
218
+ await applyTrustedContactSideEffects({
219
+ sourceChannel,
220
+ canonicalUserId,
221
+ actorChatId,
222
+ actorDisplayName,
223
+ actorUsername,
224
+ });
225
+ }
226
+
227
+ // 8. Deliver success reply
228
+ if (replyCallbackUrl) {
229
+ const replyText = composeVerificationSuccessReply(trustClass);
230
+ await deliverVerificationReply({
231
+ replyCallbackUrl,
232
+ chatId: actorChatId,
233
+ text: replyText,
234
+ assistantId,
235
+ });
236
+ }
237
+
238
+ log.info(
239
+ {
240
+ sourceChannel,
241
+ actorExternalUserId: canonicalUserId,
242
+ trustClass,
243
+ sessionId: session.id,
244
+ },
245
+ "Text verification succeeded",
246
+ );
247
+
248
+ return { intercepted: true, outcome: "verified", trustClass };
249
+ }
250
+
251
+ // ---------------------------------------------------------------------------
252
+ // Side effects
253
+ // ---------------------------------------------------------------------------
254
+
255
+ async function applyGuardianSideEffects(params: {
256
+ sourceChannel: string;
257
+ canonicalUserId: string;
258
+ actorChatId: string;
259
+ actorDisplayName?: string;
260
+ actorUsername?: string;
261
+ }): Promise<void> {
262
+ const {
263
+ sourceChannel,
264
+ canonicalUserId,
265
+ actorChatId,
266
+ actorDisplayName,
267
+ actorUsername,
268
+ } = params;
269
+
270
+ // Check for binding conflict — another user already holds guardian
271
+ const existing = await getExistingGuardianBinding(sourceChannel);
272
+ if (existing?.externalUserId && existing.externalUserId !== canonicalUserId) {
273
+ log.warn(
274
+ {
275
+ sourceChannel,
276
+ existingGuardian: existing.externalUserId,
277
+ newActor: canonicalUserId,
278
+ },
279
+ "Guardian binding conflict: another user already holds this channel",
280
+ );
281
+ // Still upsert the contact channel so the sender is a known contact,
282
+ // but skip guardian binding creation.
283
+ await upsertVerifiedContactChannel({
284
+ sourceChannel,
285
+ externalUserId: canonicalUserId,
286
+ externalChatId: actorChatId,
287
+ displayName: actorDisplayName,
288
+ username: actorUsername,
289
+ });
290
+ return;
291
+ }
292
+
293
+ // Revoke existing binding (same-user re-verification)
294
+ await revokeExistingChannelGuardian(sourceChannel);
295
+
296
+ // Resolve canonical principal — unify all channel bindings
297
+ const canonicalPrincipal = await resolveCanonicalPrincipal(canonicalUserId);
298
+
299
+ // Determine display name — preserve existing if user is re-verifying
300
+ const existingContact = await findContactChannelByExternalUserId(
301
+ sourceChannel,
302
+ canonicalUserId,
303
+ );
304
+ const displayName =
305
+ existingContact?.displayName?.trim().length
306
+ ? existingContact.displayName
307
+ : actorDisplayName ?? actorUsername ?? canonicalUserId;
308
+
309
+ // Create guardian binding (dual-writes to both DBs)
310
+ await createGuardianBinding({
311
+ channel: sourceChannel,
312
+ externalUserId: canonicalUserId,
313
+ deliveryChatId: actorChatId,
314
+ guardianPrincipalId: canonicalPrincipal,
315
+ displayName,
316
+ verifiedVia: "challenge",
317
+ });
318
+ }
319
+
320
+ async function applyTrustedContactSideEffects(params: {
321
+ sourceChannel: string;
322
+ canonicalUserId: string;
323
+ actorChatId: string;
324
+ actorDisplayName?: string;
325
+ actorUsername?: string;
326
+ }): Promise<void> {
327
+ const {
328
+ sourceChannel,
329
+ canonicalUserId,
330
+ actorChatId,
331
+ actorDisplayName,
332
+ actorUsername,
333
+ } = params;
334
+
335
+ // Preserve existing display name if available
336
+ const existingContact = await findContactChannelByExternalUserId(
337
+ sourceChannel,
338
+ canonicalUserId,
339
+ );
340
+ const displayName =
341
+ existingContact?.displayName?.trim().length
342
+ ? existingContact.displayName
343
+ : actorDisplayName ?? actorUsername ?? canonicalUserId;
344
+
345
+ await upsertVerifiedContactChannel({
346
+ sourceChannel,
347
+ externalUserId: canonicalUserId,
348
+ externalChatId: actorChatId,
349
+ displayName,
350
+ username: actorUsername,
351
+ });
352
+ }
353
+
354
+ // ---------------------------------------------------------------------------
355
+ // Reply helpers
356
+ // ---------------------------------------------------------------------------
357
+
358
+ async function replyWithFailure(
359
+ replyCallbackUrl: string | undefined,
360
+ chatId: string,
361
+ assistantId: string | undefined,
362
+ reason: string,
363
+ ): Promise<void> {
364
+ if (!replyCallbackUrl) return;
365
+ const text = composeVerificationFailureReply(reason);
366
+ await deliverVerificationReply({
367
+ replyCallbackUrl,
368
+ chatId,
369
+ text,
370
+ assistantId,
371
+ });
372
+ }
package/src/version.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ const DEV_VERSION_SENTINEL = "0.0.0-dev";
5
+
6
+ function readPackageVersion(): string | undefined {
7
+ try {
8
+ const pkgPath = join(
9
+ import.meta.dirname ?? __dirname,
10
+ "..",
11
+ "package.json",
12
+ );
13
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
14
+ if (pkg.version && typeof pkg.version === "string") return pkg.version;
15
+ } catch {
16
+ // package.json missing or unreadable
17
+ }
18
+ return undefined;
19
+ }
20
+
21
+ function resolveVersion(): string {
22
+ const envVersion = process.env.APP_VERSION;
23
+ if (envVersion && envVersion !== DEV_VERSION_SENTINEL) return envVersion;
24
+ return readPackageVersion() ?? DEV_VERSION_SENTINEL;
25
+ }
26
+
27
+ export const APP_VERSION: string = resolveVersion();
28
+
29
+ /**
30
+ * Header name for the assistant version returned in every gateway response.
31
+ * Allows the platform to trace which build handled a request.
32
+ */
33
+ export const VERSION_HEADER_NAME = "X-Vellum-Assistant-Version";
34
+
35
+ export const VERSION_HEADER_VALUE: string = APP_VERSION;