@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
package/src/schema.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import packageJson from "../package.json" with { type: "json" };
|
|
2
|
+
import {
|
|
3
|
+
TWILIO_CONNECT_ACTION_WEBHOOK_PATH,
|
|
4
|
+
TWILIO_MEDIA_STREAM_WEBHOOK_PATH,
|
|
5
|
+
TWILIO_RELAY_WEBHOOK_PATH,
|
|
6
|
+
TWILIO_STATUS_WEBHOOK_PATH,
|
|
7
|
+
TWILIO_VOICE_WEBHOOK_PATH,
|
|
8
|
+
} from "@vellumai/service-contracts/twilio-ingress";
|
|
2
9
|
|
|
3
10
|
export function buildSchema(): Record<string, unknown> {
|
|
4
11
|
return {
|
|
@@ -300,7 +307,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
300
307
|
},
|
|
301
308
|
},
|
|
302
309
|
},
|
|
303
|
-
|
|
310
|
+
[TWILIO_VOICE_WEBHOOK_PATH]: {
|
|
304
311
|
post: {
|
|
305
312
|
summary: "Twilio voice webhook",
|
|
306
313
|
description:
|
|
@@ -358,7 +365,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
358
365
|
},
|
|
359
366
|
},
|
|
360
367
|
},
|
|
361
|
-
|
|
368
|
+
[TWILIO_STATUS_WEBHOOK_PATH]: {
|
|
362
369
|
post: {
|
|
363
370
|
summary: "Twilio status webhook",
|
|
364
371
|
description:
|
|
@@ -415,7 +422,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
415
422
|
},
|
|
416
423
|
},
|
|
417
424
|
},
|
|
418
|
-
|
|
425
|
+
[TWILIO_CONNECT_ACTION_WEBHOOK_PATH]: {
|
|
419
426
|
post: {
|
|
420
427
|
summary: "Twilio connect-action webhook",
|
|
421
428
|
description:
|
|
@@ -472,6 +479,54 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
472
479
|
},
|
|
473
480
|
},
|
|
474
481
|
},
|
|
482
|
+
"/webhooks/twilio/voice-verify": {
|
|
483
|
+
post: {
|
|
484
|
+
summary: "Twilio voice verification callback",
|
|
485
|
+
description:
|
|
486
|
+
"Receives DTMF digits from Twilio <Gather> during gateway-owned voice verification. Validates the verification code, creates the guardian binding on success, and returns TwiML to either re-prompt or forward to the assistant for ConversationRelay setup.",
|
|
487
|
+
operationId: "twilioVoiceVerifyCallback",
|
|
488
|
+
security: [{ TwilioSignature: [] }],
|
|
489
|
+
parameters: [
|
|
490
|
+
{
|
|
491
|
+
name: "attempt",
|
|
492
|
+
in: "query",
|
|
493
|
+
required: false,
|
|
494
|
+
schema: { type: "integer", default: 0 },
|
|
495
|
+
description: "Zero-based attempt counter for retry tracking.",
|
|
496
|
+
},
|
|
497
|
+
],
|
|
498
|
+
requestBody: {
|
|
499
|
+
required: true,
|
|
500
|
+
content: {
|
|
501
|
+
"application/x-www-form-urlencoded": {
|
|
502
|
+
schema: {
|
|
503
|
+
type: "object",
|
|
504
|
+
additionalProperties: { type: "string" },
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
responses: {
|
|
510
|
+
"200": {
|
|
511
|
+
description:
|
|
512
|
+
"TwiML response — either a re-prompt <Gather>, a failure <Say>, or forwarded ConversationRelay setup.",
|
|
513
|
+
content: {
|
|
514
|
+
"text/xml": {
|
|
515
|
+
schema: { type: "string" },
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
"403": {
|
|
520
|
+
description: "Twilio signature validation failed",
|
|
521
|
+
content: {
|
|
522
|
+
"application/json": {
|
|
523
|
+
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
},
|
|
475
530
|
"/webhooks/whatsapp": {
|
|
476
531
|
get: {
|
|
477
532
|
summary: "WhatsApp webhook verification",
|
|
@@ -914,7 +969,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
914
969
|
},
|
|
915
970
|
},
|
|
916
971
|
},
|
|
917
|
-
|
|
972
|
+
[TWILIO_RELAY_WEBHOOK_PATH]: {
|
|
918
973
|
get: {
|
|
919
974
|
summary: "Twilio ConversationRelay WebSocket",
|
|
920
975
|
description:
|
|
@@ -954,7 +1009,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
954
1009
|
},
|
|
955
1010
|
},
|
|
956
1011
|
},
|
|
957
|
-
|
|
1012
|
+
[TWILIO_MEDIA_STREAM_WEBHOOK_PATH]: {
|
|
958
1013
|
get: {
|
|
959
1014
|
summary: "Twilio Media Stream WebSocket",
|
|
960
1015
|
description:
|
|
@@ -1195,68 +1250,6 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1195
1250
|
},
|
|
1196
1251
|
},
|
|
1197
1252
|
},
|
|
1198
|
-
"/pairing/register": {
|
|
1199
|
-
post: {
|
|
1200
|
-
summary: "Register pairing code",
|
|
1201
|
-
description:
|
|
1202
|
-
"Authenticated gateway endpoint that registers a new pairing code for device linking via the assistant runtime.",
|
|
1203
|
-
operationId: "pairingRegister",
|
|
1204
|
-
security: [{ BearerAuth: [] }],
|
|
1205
|
-
requestBody: {
|
|
1206
|
-
required: true,
|
|
1207
|
-
content: {
|
|
1208
|
-
"application/json": {
|
|
1209
|
-
schema: { type: "object", additionalProperties: true },
|
|
1210
|
-
},
|
|
1211
|
-
},
|
|
1212
|
-
},
|
|
1213
|
-
responses: {
|
|
1214
|
-
"200": { description: "Pairing code registered" },
|
|
1215
|
-
"400": { description: "Invalid request payload" },
|
|
1216
|
-
"401": {
|
|
1217
|
-
description: "Unauthorized — missing or invalid bearer token",
|
|
1218
|
-
},
|
|
1219
|
-
"502": { description: "Failed to reach assistant runtime" },
|
|
1220
|
-
},
|
|
1221
|
-
},
|
|
1222
|
-
},
|
|
1223
|
-
"/pairing/request": {
|
|
1224
|
-
post: {
|
|
1225
|
-
summary: "Request pairing",
|
|
1226
|
-
description:
|
|
1227
|
-
"Initiates a pairing request using a pairing code. Auth failures are tracked for rate limiting.",
|
|
1228
|
-
operationId: "pairingRequest",
|
|
1229
|
-
requestBody: {
|
|
1230
|
-
required: true,
|
|
1231
|
-
content: {
|
|
1232
|
-
"application/json": {
|
|
1233
|
-
schema: { type: "object", additionalProperties: true },
|
|
1234
|
-
},
|
|
1235
|
-
},
|
|
1236
|
-
},
|
|
1237
|
-
responses: {
|
|
1238
|
-
"200": { description: "Pairing request accepted" },
|
|
1239
|
-
"400": { description: "Invalid request payload" },
|
|
1240
|
-
"401": { description: "Unauthorized" },
|
|
1241
|
-
"403": { description: "Pairing code expired or invalid" },
|
|
1242
|
-
"502": { description: "Failed to reach assistant runtime" },
|
|
1243
|
-
},
|
|
1244
|
-
},
|
|
1245
|
-
},
|
|
1246
|
-
"/pairing/status": {
|
|
1247
|
-
get: {
|
|
1248
|
-
summary: "Check pairing status",
|
|
1249
|
-
description:
|
|
1250
|
-
"Checks the current status of a pairing request. Auth failures are tracked for rate limiting.",
|
|
1251
|
-
operationId: "pairingStatus",
|
|
1252
|
-
responses: {
|
|
1253
|
-
"200": { description: "Pairing status returned" },
|
|
1254
|
-
"401": { description: "Unauthorized" },
|
|
1255
|
-
"403": { description: "Pairing session not found" },
|
|
1256
|
-
"502": { description: "Failed to reach assistant runtime" },
|
|
1257
|
-
},
|
|
1258
|
-
},
|
|
1259
|
-
},
|
|
1260
1253
|
"/v1/integrations/telegram/config": {
|
|
1261
1254
|
get: {
|
|
1262
1255
|
summary: "Get Telegram integration config",
|
|
@@ -1500,6 +1493,32 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
1500
1493
|
},
|
|
1501
1494
|
},
|
|
1502
1495
|
},
|
|
1496
|
+
"/v1/contacts/prompt/submit": {
|
|
1497
|
+
post: {
|
|
1498
|
+
summary: "Submit a contact address in response to a prompt",
|
|
1499
|
+
description:
|
|
1500
|
+
"Authenticated gateway endpoint that accepts a contact address submitted by the user in response to a contacts/prompt IPC request. Writes the contact, notifies the daemon to unblock the waiting CLI call.",
|
|
1501
|
+
operationId: "contactsPromptSubmitPost",
|
|
1502
|
+
security: [{ BearerAuth: [] }],
|
|
1503
|
+
requestBody: {
|
|
1504
|
+
required: true,
|
|
1505
|
+
content: {
|
|
1506
|
+
"application/json": {
|
|
1507
|
+
schema: { type: "object", additionalProperties: true },
|
|
1508
|
+
},
|
|
1509
|
+
},
|
|
1510
|
+
},
|
|
1511
|
+
responses: {
|
|
1512
|
+
"200": { description: "Contact created and prompt resolved" },
|
|
1513
|
+
"400": { description: "Invalid request payload" },
|
|
1514
|
+
"401": {
|
|
1515
|
+
description: "Unauthorized — missing or invalid bearer token",
|
|
1516
|
+
},
|
|
1517
|
+
"409": { description: "Channel already exists for this contact" },
|
|
1518
|
+
"503": { description: "Bearer token not configured" },
|
|
1519
|
+
},
|
|
1520
|
+
},
|
|
1521
|
+
},
|
|
1503
1522
|
"/v1/contact-channels/{contactChannelId}": {
|
|
1504
1523
|
patch: {
|
|
1505
1524
|
summary: "Update a contact channel",
|
|
@@ -2153,33 +2172,6 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2153
2172
|
},
|
|
2154
2173
|
},
|
|
2155
2174
|
},
|
|
2156
|
-
"/v1/integrations/slack/channel/oauth-install": {
|
|
2157
|
-
post: {
|
|
2158
|
-
summary: "Slack OAuth install",
|
|
2159
|
-
description:
|
|
2160
|
-
"Scope-protected gateway endpoint that initiates the Slack OAuth loopback flow to capture bot and user tokens. This endpoint blocks while the user completes the Slack consent screen (up to 6 minutes). Requires a bearer token with `settings.write` scope.",
|
|
2161
|
-
operationId: "slackChannelOAuthInstallPost",
|
|
2162
|
-
security: [{ BearerAuth: [] }],
|
|
2163
|
-
requestBody: {
|
|
2164
|
-
required: false,
|
|
2165
|
-
content: {
|
|
2166
|
-
"application/json": {
|
|
2167
|
-
schema: { type: "object", additionalProperties: true },
|
|
2168
|
-
},
|
|
2169
|
-
},
|
|
2170
|
-
},
|
|
2171
|
-
responses: {
|
|
2172
|
-
"200": { description: "OAuth install completed successfully" },
|
|
2173
|
-
"400": { description: "Invalid request payload" },
|
|
2174
|
-
"401": {
|
|
2175
|
-
description: "Unauthorized — missing or invalid bearer token",
|
|
2176
|
-
},
|
|
2177
|
-
"403": { description: "Insufficient scope" },
|
|
2178
|
-
"502": { description: "Failed to reach assistant runtime" },
|
|
2179
|
-
"504": { description: "Assistant runtime request timed out" },
|
|
2180
|
-
},
|
|
2181
|
-
},
|
|
2182
|
-
},
|
|
2183
2175
|
"/v1/oauth/providers": {
|
|
2184
2176
|
get: {
|
|
2185
2177
|
summary: "List OAuth providers",
|
|
@@ -2907,6 +2899,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2907
2899
|
properties: {
|
|
2908
2900
|
interactive: { type: "string" },
|
|
2909
2901
|
autonomous: { type: "string" },
|
|
2902
|
+
headless: { type: "string" },
|
|
2910
2903
|
},
|
|
2911
2904
|
},
|
|
2912
2905
|
},
|
|
@@ -2928,6 +2921,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2928
2921
|
properties: {
|
|
2929
2922
|
interactive: { type: "string" },
|
|
2930
2923
|
autonomous: { type: "string" },
|
|
2924
|
+
headless: { type: "string" },
|
|
2931
2925
|
},
|
|
2932
2926
|
},
|
|
2933
2927
|
},
|
|
@@ -2943,6 +2937,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2943
2937
|
properties: {
|
|
2944
2938
|
interactive: { type: "string" },
|
|
2945
2939
|
autonomous: { type: "string" },
|
|
2940
|
+
headless: { type: "string" },
|
|
2946
2941
|
},
|
|
2947
2942
|
},
|
|
2948
2943
|
},
|
|
@@ -2976,6 +2971,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
2976
2971
|
properties: {
|
|
2977
2972
|
interactive: { type: "string" },
|
|
2978
2973
|
autonomous: { type: "string" },
|
|
2974
|
+
headless: { type: "string" },
|
|
2979
2975
|
},
|
|
2980
2976
|
},
|
|
2981
2977
|
},
|
|
@@ -3005,6 +3001,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3005
3001
|
properties: {
|
|
3006
3002
|
interactive: { type: "string" },
|
|
3007
3003
|
autonomous: { type: "string" },
|
|
3004
|
+
headless: { type: "string" },
|
|
3008
3005
|
},
|
|
3009
3006
|
},
|
|
3010
3007
|
},
|
|
@@ -3020,6 +3017,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3020
3017
|
properties: {
|
|
3021
3018
|
interactive: { type: "string" },
|
|
3022
3019
|
autonomous: { type: "string" },
|
|
3020
|
+
headless: { type: "string" },
|
|
3023
3021
|
},
|
|
3024
3022
|
},
|
|
3025
3023
|
},
|
|
@@ -3283,6 +3281,71 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3283
3281
|
},
|
|
3284
3282
|
},
|
|
3285
3283
|
},
|
|
3284
|
+
"/v1/logs/tail": {
|
|
3285
|
+
get: {
|
|
3286
|
+
summary: "Tail gateway log entries",
|
|
3287
|
+
description:
|
|
3288
|
+
"Returns the last N structured log entries from the gateway's pino log files, " +
|
|
3289
|
+
"with optional filtering by minimum level and module name.",
|
|
3290
|
+
operationId: "gatewayLogsTail",
|
|
3291
|
+
security: [{ BearerAuth: [] }],
|
|
3292
|
+
parameters: [
|
|
3293
|
+
{
|
|
3294
|
+
name: "n",
|
|
3295
|
+
in: "query",
|
|
3296
|
+
required: false,
|
|
3297
|
+
schema: { type: "integer", minimum: 1, maximum: 1000, default: 10 },
|
|
3298
|
+
description: "Number of log entries to return (1–1000, default: 10)",
|
|
3299
|
+
},
|
|
3300
|
+
{
|
|
3301
|
+
name: "level",
|
|
3302
|
+
in: "query",
|
|
3303
|
+
required: false,
|
|
3304
|
+
schema: {
|
|
3305
|
+
type: "string",
|
|
3306
|
+
enum: ["trace", "debug", "info", "warn", "error", "fatal"],
|
|
3307
|
+
default: "info",
|
|
3308
|
+
},
|
|
3309
|
+
description: "Minimum pino level name (default: info)",
|
|
3310
|
+
},
|
|
3311
|
+
{
|
|
3312
|
+
name: "module",
|
|
3313
|
+
in: "query",
|
|
3314
|
+
required: false,
|
|
3315
|
+
schema: { type: "string" },
|
|
3316
|
+
description: "Filter to exact pino module name",
|
|
3317
|
+
},
|
|
3318
|
+
],
|
|
3319
|
+
responses: {
|
|
3320
|
+
"200": {
|
|
3321
|
+
description: "Log entries and truncation flag",
|
|
3322
|
+
content: {
|
|
3323
|
+
"application/json": {
|
|
3324
|
+
schema: {
|
|
3325
|
+
type: "object",
|
|
3326
|
+
required: ["lines", "truncated"],
|
|
3327
|
+
properties: {
|
|
3328
|
+
lines: {
|
|
3329
|
+
type: "array",
|
|
3330
|
+
items: { type: "object" },
|
|
3331
|
+
description: "Matching log entries in chronological order",
|
|
3332
|
+
},
|
|
3333
|
+
truncated: {
|
|
3334
|
+
type: "boolean",
|
|
3335
|
+
description: "True if earlier matching entries exist beyond n",
|
|
3336
|
+
},
|
|
3337
|
+
},
|
|
3338
|
+
},
|
|
3339
|
+
},
|
|
3340
|
+
},
|
|
3341
|
+
},
|
|
3342
|
+
"400": { description: "Invalid level parameter" },
|
|
3343
|
+
"401": {
|
|
3344
|
+
description: "Unauthorized — missing or invalid edge JWT",
|
|
3345
|
+
},
|
|
3346
|
+
},
|
|
3347
|
+
},
|
|
3348
|
+
},
|
|
3286
3349
|
"/v1/trust-rules": {
|
|
3287
3350
|
get: {
|
|
3288
3351
|
summary: "List trust rules",
|
|
@@ -3716,7 +3779,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3716
3779
|
post: {
|
|
3717
3780
|
summary: "Kick off an async export-to-GCS job",
|
|
3718
3781
|
description:
|
|
3719
|
-
"Transparent proxy to the assistant daemon's `POST /v1/migrations/export-to-gcs` endpoint. The daemon schedules a background export job that streams the workspace backup to the supplied signed GCS upload URL and returns `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly (not via the runtime-proxy catch-all)
|
|
3782
|
+
"Transparent proxy to the assistant daemon's `POST /v1/migrations/export-to-gcs` endpoint. The daemon schedules a background export job that streams the workspace backup to the supplied signed GCS upload URL and returns `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly (not via the runtime-proxy catch-all) for dedicated auth and timeout handling.",
|
|
3720
3783
|
operationId: "migrationExportToGcs",
|
|
3721
3784
|
security: [{ BearerAuth: [] }],
|
|
3722
3785
|
requestBody: {
|
|
@@ -3755,7 +3818,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3755
3818
|
post: {
|
|
3756
3819
|
summary: "Kick off an async import-from-GCS job",
|
|
3757
3820
|
description:
|
|
3758
|
-
"Transparent proxy to the assistant daemon's `POST /v1/migrations/import-from-gcs` endpoint. The daemon schedules a background import job that fetches a `.vbundle` archive at the supplied `bundle_url` and streams it through the importer, returning `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly (not via the runtime-proxy catch-all)
|
|
3821
|
+
"Transparent proxy to the assistant daemon's `POST /v1/migrations/import-from-gcs` endpoint. The daemon schedules a background import job that fetches a `.vbundle` archive at the supplied `bundle_url` and streams it through the importer, returning `202 Accepted` with a `job_id`. Callers poll `GET /v1/migrations/jobs/{jobId}` for progress. Registered explicitly (not via the runtime-proxy catch-all) for dedicated auth and timeout handling.",
|
|
3759
3822
|
operationId: "migrationImportFromGcs",
|
|
3760
3823
|
security: [{ BearerAuth: [] }],
|
|
3761
3824
|
requestBody: {
|
|
@@ -3805,7 +3868,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3805
3868
|
get: {
|
|
3806
3869
|
summary: "Poll unified migration job status",
|
|
3807
3870
|
description:
|
|
3808
|
-
"Transparent proxy to the assistant daemon's `GET /v1/migrations/jobs/{job_id}` endpoint. Returns the current status of an async export-to-GCS or import-from-GCS job tracked by the daemon's migration job registry. Registered explicitly (not via the runtime-proxy catch-all)
|
|
3871
|
+
"Transparent proxy to the assistant daemon's `GET /v1/migrations/jobs/{job_id}` endpoint. Returns the current status of an async export-to-GCS or import-from-GCS job tracked by the daemon's migration job registry. Registered explicitly (not via the runtime-proxy catch-all) for dedicated auth and timeout handling.",
|
|
3809
3872
|
operationId: "migrationJobStatus",
|
|
3810
3873
|
security: [{ BearerAuth: [] }],
|
|
3811
3874
|
parameters: [
|
|
@@ -3923,8 +3986,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3923
3986
|
properties: {
|
|
3924
3987
|
type: {
|
|
3925
3988
|
type: "string",
|
|
3926
|
-
description:
|
|
3927
|
-
"Email provider type (e.g. resend, mailgun)",
|
|
3989
|
+
description: "Email provider type (e.g. resend, mailgun)",
|
|
3928
3990
|
},
|
|
3929
3991
|
guardian_email: {
|
|
3930
3992
|
type: "string",
|
|
@@ -3938,7 +4000,8 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3938
4000
|
},
|
|
3939
4001
|
responses: {
|
|
3940
4002
|
"200": {
|
|
3941
|
-
description:
|
|
4003
|
+
description:
|
|
4004
|
+
"Guardian email channel verified and binding created",
|
|
3942
4005
|
content: {
|
|
3943
4006
|
"application/json": {
|
|
3944
4007
|
schema: {
|
|
@@ -3994,11 +4057,111 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
3994
4057
|
},
|
|
3995
4058
|
},
|
|
3996
4059
|
},
|
|
4060
|
+
"/v1/backups": {
|
|
4061
|
+
get: {
|
|
4062
|
+
summary: "List backup snapshots",
|
|
4063
|
+
description:
|
|
4064
|
+
"Lists local and offsite backup snapshots. The gateway owns the backup encryption key and performs all encrypt/decrypt operations.",
|
|
4065
|
+
operationId: "backupsList",
|
|
4066
|
+
security: [{ BearerAuth: [] }],
|
|
4067
|
+
responses: {
|
|
4068
|
+
"200": {
|
|
4069
|
+
description: "Backup snapshots listed",
|
|
4070
|
+
content: {
|
|
4071
|
+
"application/json": {
|
|
4072
|
+
schema: {
|
|
4073
|
+
type: "object",
|
|
4074
|
+
required: ["local", "offsite"],
|
|
4075
|
+
properties: {
|
|
4076
|
+
local: {
|
|
4077
|
+
type: "object",
|
|
4078
|
+
required: ["directory", "snapshots"],
|
|
4079
|
+
properties: {
|
|
4080
|
+
directory: { type: "string" },
|
|
4081
|
+
snapshots: {
|
|
4082
|
+
type: "array",
|
|
4083
|
+
items: {
|
|
4084
|
+
$ref: "#/components/schemas/BackupSnapshot",
|
|
4085
|
+
},
|
|
4086
|
+
},
|
|
4087
|
+
},
|
|
4088
|
+
},
|
|
4089
|
+
offsite: {
|
|
4090
|
+
type: "array",
|
|
4091
|
+
items: {
|
|
4092
|
+
type: "object",
|
|
4093
|
+
required: ["directory", "encrypted", "snapshots"],
|
|
4094
|
+
properties: {
|
|
4095
|
+
directory: { type: "string" },
|
|
4096
|
+
encrypted: { type: "boolean" },
|
|
4097
|
+
snapshots: {
|
|
4098
|
+
type: "array",
|
|
4099
|
+
items: {
|
|
4100
|
+
$ref: "#/components/schemas/BackupSnapshot",
|
|
4101
|
+
},
|
|
4102
|
+
},
|
|
4103
|
+
},
|
|
4104
|
+
},
|
|
4105
|
+
},
|
|
4106
|
+
},
|
|
4107
|
+
},
|
|
4108
|
+
},
|
|
4109
|
+
},
|
|
4110
|
+
},
|
|
4111
|
+
"401": {
|
|
4112
|
+
description: "Unauthorized — missing or invalid bearer token",
|
|
4113
|
+
},
|
|
4114
|
+
"403": { description: "Insufficient scope" },
|
|
4115
|
+
"500": { description: "Internal server error" },
|
|
4116
|
+
},
|
|
4117
|
+
},
|
|
4118
|
+
},
|
|
4119
|
+
"/v1/backups/create": {
|
|
4120
|
+
post: {
|
|
4121
|
+
summary: "Create backup snapshot",
|
|
4122
|
+
description:
|
|
4123
|
+
"Triggers a manual backup snapshot. The gateway exports a plaintext vbundle from the daemon, writes it locally, and encrypts + mirrors to offsite destinations.",
|
|
4124
|
+
operationId: "backupsCreate",
|
|
4125
|
+
security: [{ BearerAuth: [] }],
|
|
4126
|
+
responses: {
|
|
4127
|
+
"200": {
|
|
4128
|
+
description: "Backup snapshot created",
|
|
4129
|
+
content: {
|
|
4130
|
+
"application/json": {
|
|
4131
|
+
schema: {
|
|
4132
|
+
type: "object",
|
|
4133
|
+
required: ["success", "local", "offsite", "duration_ms"],
|
|
4134
|
+
properties: {
|
|
4135
|
+
success: { type: "boolean" },
|
|
4136
|
+
local: {
|
|
4137
|
+
$ref: "#/components/schemas/BackupSnapshot",
|
|
4138
|
+
},
|
|
4139
|
+
offsite: {
|
|
4140
|
+
type: "array",
|
|
4141
|
+
items: { type: "object", additionalProperties: true },
|
|
4142
|
+
},
|
|
4143
|
+
duration_ms: { type: "number" },
|
|
4144
|
+
},
|
|
4145
|
+
},
|
|
4146
|
+
},
|
|
4147
|
+
},
|
|
4148
|
+
},
|
|
4149
|
+
"401": {
|
|
4150
|
+
description: "Unauthorized — missing or invalid bearer token",
|
|
4151
|
+
},
|
|
4152
|
+
"403": { description: "Insufficient scope" },
|
|
4153
|
+
"409": {
|
|
4154
|
+
description: "A backup snapshot is already in progress",
|
|
4155
|
+
},
|
|
4156
|
+
"500": { description: "Internal server error" },
|
|
4157
|
+
},
|
|
4158
|
+
},
|
|
4159
|
+
},
|
|
3997
4160
|
"/{path}": {
|
|
3998
4161
|
get: {
|
|
3999
4162
|
summary: "Runtime proxy",
|
|
4000
4163
|
description:
|
|
4001
|
-
"Reverse-proxies requests to the assistant runtime
|
|
4164
|
+
"Reverse-proxies requests to the assistant runtime. Supports all HTTP methods.",
|
|
4002
4165
|
operationId: "runtimeProxyGet",
|
|
4003
4166
|
security: [{ BearerAuth: [] }],
|
|
4004
4167
|
parameters: [
|
|
@@ -4023,7 +4186,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4023
4186
|
},
|
|
4024
4187
|
},
|
|
4025
4188
|
"404": {
|
|
4026
|
-
description: "
|
|
4189
|
+
description: "Route not found on upstream runtime",
|
|
4027
4190
|
content: {
|
|
4028
4191
|
"application/json": {
|
|
4029
4192
|
schema: { $ref: "#/components/schemas/ErrorResponse" },
|
|
@@ -4059,8 +4222,7 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4059
4222
|
},
|
|
4060
4223
|
post: {
|
|
4061
4224
|
summary: "Runtime proxy",
|
|
4062
|
-
description:
|
|
4063
|
-
"Reverse-proxies requests to the assistant runtime when gateway.runtimeProxyEnabled is true in workspace config.",
|
|
4225
|
+
description: "Reverse-proxies requests to the assistant runtime.",
|
|
4064
4226
|
operationId: "runtimeProxyPost",
|
|
4065
4227
|
security: [{ BearerAuth: [] }],
|
|
4066
4228
|
parameters: [
|
|
@@ -4114,6 +4276,17 @@ export function buildSchema(): Record<string, unknown> {
|
|
|
4114
4276
|
},
|
|
4115
4277
|
components: {
|
|
4116
4278
|
schemas: {
|
|
4279
|
+
BackupSnapshot: {
|
|
4280
|
+
type: "object",
|
|
4281
|
+
required: ["path", "filename", "created_at", "size_bytes", "encrypted"],
|
|
4282
|
+
properties: {
|
|
4283
|
+
path: { type: "string" },
|
|
4284
|
+
filename: { type: "string" },
|
|
4285
|
+
created_at: { type: "string", format: "date-time" },
|
|
4286
|
+
size_bytes: { type: "integer" },
|
|
4287
|
+
encrypted: { type: "boolean" },
|
|
4288
|
+
},
|
|
4289
|
+
},
|
|
4117
4290
|
HealthResponse: {
|
|
4118
4291
|
type: "object",
|
|
4119
4292
|
required: ["status"],
|
|
@@ -492,7 +492,9 @@ describe("DM threading", () => {
|
|
|
492
492
|
const result = normalizeSlackDirectMessage(event, "evt-dm-1", config);
|
|
493
493
|
|
|
494
494
|
expect(result).not.toBeNull();
|
|
495
|
+
expect(result!.event.source.chatType).toBe("im");
|
|
495
496
|
expect(result!.threadTs).toBeUndefined();
|
|
497
|
+
expect(result!.event.source.threadId).toBeUndefined();
|
|
496
498
|
});
|
|
497
499
|
|
|
498
500
|
it("threaded DM preserves threadTs", () => {
|
|
@@ -501,7 +503,9 @@ describe("DM threading", () => {
|
|
|
501
503
|
const result = normalizeSlackDirectMessage(event, "evt-dm-2", config);
|
|
502
504
|
|
|
503
505
|
expect(result).not.toBeNull();
|
|
506
|
+
expect(result!.event.source.chatType).toBe("im");
|
|
504
507
|
expect(result!.threadTs).toBe("1700000000.000050");
|
|
508
|
+
expect(result!.event.source.threadId).toBe("1700000000.000050");
|
|
505
509
|
});
|
|
506
510
|
});
|
|
507
511
|
|
|
@@ -724,6 +728,39 @@ describe("attachment extraction in normalize functions", () => {
|
|
|
724
728
|
expect(result!.slackFiles!.get("F030")!.id).toBe("F030");
|
|
725
729
|
});
|
|
726
730
|
|
|
731
|
+
it("normalizes DM file_share mentions without stripping the bot mention", () => {
|
|
732
|
+
const config = makeConfig();
|
|
733
|
+
const event = makeDmEvent({
|
|
734
|
+
subtype: "file_share",
|
|
735
|
+
text: "<@UBOT> <@ULEO> shared this",
|
|
736
|
+
files: [
|
|
737
|
+
makeSlackFile({
|
|
738
|
+
id: "F032",
|
|
739
|
+
mimetype: "image/png",
|
|
740
|
+
name: "mention.png",
|
|
741
|
+
}),
|
|
742
|
+
],
|
|
743
|
+
});
|
|
744
|
+
const result = normalizeSlackDirectMessage(
|
|
745
|
+
event,
|
|
746
|
+
"evt-fs-mentions-dm",
|
|
747
|
+
config,
|
|
748
|
+
"UBOT",
|
|
749
|
+
undefined,
|
|
750
|
+
{ userLabels: { UBOT: "assistant", ULEO: "leo" } },
|
|
751
|
+
);
|
|
752
|
+
|
|
753
|
+
expect(result).not.toBeNull();
|
|
754
|
+
expect(result!.event.message.content).toBe(
|
|
755
|
+
"@assistant @leo shared this",
|
|
756
|
+
);
|
|
757
|
+
expect(result!.event.message.content).not.toContain("ULEO");
|
|
758
|
+
expect(result!.event.message.attachments).toHaveLength(1);
|
|
759
|
+
expect(result!.event.message.attachments![0].fileId).toBe("F032");
|
|
760
|
+
expect(result!.slackFiles).toBeDefined();
|
|
761
|
+
expect(result!.slackFiles!.get("F032")!.id).toBe("F032");
|
|
762
|
+
});
|
|
763
|
+
|
|
727
764
|
it("normalizes DM with file_share subtype without files", () => {
|
|
728
765
|
const config = makeConfig();
|
|
729
766
|
const event = makeDmEvent({ subtype: "file_share" });
|
|
@@ -765,6 +802,43 @@ describe("attachment extraction in normalize functions", () => {
|
|
|
765
802
|
expect(result!.slackFiles).toBeDefined();
|
|
766
803
|
});
|
|
767
804
|
|
|
805
|
+
it("normalizes channel file_share mentions after stripping only the bot mention", () => {
|
|
806
|
+
const config = makeConfig();
|
|
807
|
+
const event = makeChannelEvent({
|
|
808
|
+
subtype: "file_share",
|
|
809
|
+
text: "<@UBOT> <@ULEO> shared this",
|
|
810
|
+
files: [
|
|
811
|
+
makeSlackFile({
|
|
812
|
+
id: "F033",
|
|
813
|
+
mimetype: "application/pdf",
|
|
814
|
+
name: "mention.pdf",
|
|
815
|
+
}),
|
|
816
|
+
],
|
|
817
|
+
});
|
|
818
|
+
const result = normalizeSlackChannelMessage(
|
|
819
|
+
event,
|
|
820
|
+
"evt-fs-mentions-channel",
|
|
821
|
+
config,
|
|
822
|
+
"UBOT",
|
|
823
|
+
undefined,
|
|
824
|
+
{ userLabels: { ULEO: "leo" } },
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
expect(result).not.toBeNull();
|
|
828
|
+
expect(result!.event.message.content).toBe("@leo shared this");
|
|
829
|
+
expect(result!.event.message.content).not.toContain("ULEO");
|
|
830
|
+
expect(result!.event.message.attachments).toHaveLength(1);
|
|
831
|
+
expect(result!.event.message.attachments![0]).toEqual({
|
|
832
|
+
type: "document",
|
|
833
|
+
fileId: "F033",
|
|
834
|
+
fileName: "mention.pdf",
|
|
835
|
+
mimeType: "application/pdf",
|
|
836
|
+
fileSize: 12345,
|
|
837
|
+
});
|
|
838
|
+
expect(result!.slackFiles).toBeDefined();
|
|
839
|
+
expect(result!.slackFiles!.get("F033")!.id).toBe("F033");
|
|
840
|
+
});
|
|
841
|
+
|
|
768
842
|
it("normalizes channel message with file_share subtype without files", () => {
|
|
769
843
|
const config = makeConfig();
|
|
770
844
|
const event = makeChannelEvent({ subtype: "file_share" });
|
|
@@ -1157,6 +1231,30 @@ describe("normalizeSlackMessageDelete", () => {
|
|
|
1157
1231
|
// DMs should not be tagged as channel chat even when inferred.
|
|
1158
1232
|
expect(result!.event.source.chatType).toBeUndefined();
|
|
1159
1233
|
});
|
|
1234
|
+
|
|
1235
|
+
it("returns null when previous_message author is the bot itself", () => {
|
|
1236
|
+
// Slack echoes self-deletes back via Socket Mode. Without this filter,
|
|
1237
|
+
// the bot's user ID flows through as the actor and triggers a spurious
|
|
1238
|
+
// ingress access-request notification to the guardian.
|
|
1239
|
+
const config = makeConfig();
|
|
1240
|
+
const event = makeMessageDeletedEvent({
|
|
1241
|
+
channel: "D789",
|
|
1242
|
+
channel_type: "im",
|
|
1243
|
+
previous_message: {
|
|
1244
|
+
user: "UBOT",
|
|
1245
|
+
text: "deleted bot message",
|
|
1246
|
+
ts: "1700000000.000100",
|
|
1247
|
+
},
|
|
1248
|
+
});
|
|
1249
|
+
const result = normalizeSlackMessageDelete(
|
|
1250
|
+
event,
|
|
1251
|
+
"evt-del-self",
|
|
1252
|
+
config,
|
|
1253
|
+
"UBOT",
|
|
1254
|
+
);
|
|
1255
|
+
|
|
1256
|
+
expect(result).toBeNull();
|
|
1257
|
+
});
|
|
1160
1258
|
});
|
|
1161
1259
|
|
|
1162
1260
|
// --- source.threadId propagation ---
|