@vellumai/vellum-gateway 0.7.3 → 0.8.1

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 (63) hide show
  1. package/AGENTS.md +10 -0
  2. package/Dockerfile +4 -2
  3. package/bun.lock +8 -1
  4. package/knip.json +1 -0
  5. package/package.json +2 -1
  6. package/src/__tests__/contact-prompt-submit.test.ts +5 -5
  7. package/src/__tests__/contact-store-mark-channel-verified.test.ts +177 -0
  8. package/src/__tests__/contacts-control-plane-proxy.test.ts +297 -6
  9. package/src/__tests__/contacts-control-plane-route-match.test.ts +16 -0
  10. package/src/__tests__/edge-auth.test.ts +253 -0
  11. package/src/__tests__/edge-guardian-auth.test.ts +198 -0
  12. package/src/__tests__/ipc-route-policy-coverage.test.ts +297 -0
  13. package/src/__tests__/ipc-route-policy.test.ts +43 -0
  14. package/src/__tests__/ipc-server-watchdog.test.ts +189 -0
  15. package/src/__tests__/live-voice-websocket.test.ts +1 -1
  16. package/src/__tests__/slack-normalize.test.ts +132 -0
  17. package/src/auth/guardian-bootstrap.ts +3 -1
  18. package/src/auth/ipc-route-policy.ts +34 -0
  19. package/src/db/assistant-db-proxy.ts +76 -7
  20. package/src/db/contact-store.ts +767 -1
  21. package/src/db/schema.ts +29 -0
  22. package/src/feature-flag-registry.json +17 -17
  23. package/src/handlers/handle-inbound.ts +9 -23
  24. package/src/http/middleware/auth.ts +193 -40
  25. package/src/http/router.ts +26 -4
  26. package/src/http/routes/channel-verification-session-proxy.ts +53 -6
  27. package/src/http/routes/contact-prompt.ts +44 -15
  28. package/src/http/routes/contacts-control-plane-proxy.ts +329 -2
  29. package/src/http/routes/contacts-control-plane-route-match.ts +12 -0
  30. package/src/http/routes/ipc-runtime-proxy.test.ts +38 -43
  31. package/src/http/routes/ipc-runtime-proxy.ts +2 -2
  32. package/src/http/routes/log-export.test.ts +1 -0
  33. package/src/http/routes/log-export.ts +9 -2
  34. package/src/http/routes/log-tail.test.ts +10 -9
  35. package/src/http/routes/log-tail.ts +5 -2
  36. package/src/http/routes/pair.ts +8 -0
  37. package/src/http/routes/twilio-voice-webhook.ts +11 -2
  38. package/src/index.ts +98 -13
  39. package/src/ipc/assistant-client.test.ts +67 -15
  40. package/src/ipc/assistant-client.ts +12 -118
  41. package/src/ipc/risk-classification-handlers.test.ts +76 -0
  42. package/src/ipc/risk-classification-handlers.ts +20 -9
  43. package/src/ipc/server.ts +113 -46
  44. package/src/logger.ts +71 -17
  45. package/src/post-assistant-ready.ts +9 -3
  46. package/src/risk/bash-risk-classifier.test.ts +106 -0
  47. package/src/risk/bash-risk-classifier.ts +19 -15
  48. package/src/risk/command-registry/commands/assistant.ts +75 -26
  49. package/src/risk/command-registry.test.ts +3 -1
  50. package/src/risk/shell-parser.test.ts +159 -0
  51. package/src/risk/shell-parser.ts +150 -19
  52. package/src/runtime/client.ts +0 -11
  53. package/src/schema.ts +32 -0
  54. package/src/slack/normalize.test.ts +5 -0
  55. package/src/slack/normalize.ts +7 -0
  56. package/src/velay/bridge-utils.ts +10 -0
  57. package/src/velay/client.test.ts +156 -0
  58. package/src/velay/client.ts +83 -0
  59. package/src/velay/http-bridge.test.ts +29 -0
  60. package/src/velay/http-bridge.ts +7 -0
  61. package/src/velay/protocol.ts +12 -1
  62. package/src/verification/outbound-voice-verification-sync.ts +171 -0
  63. package/src/verification/voice-approval-sync.ts +107 -0
@@ -98,7 +98,9 @@ interface GuardianLookupRow {
98
98
  * Find the existing guardian contact for the "vellum" channel.
99
99
  * Mirrors assistant's `findGuardianForChannel("vellum")`.
100
100
  */
101
- async function findVellumGuardian(): Promise<{ principalId: string } | null> {
101
+ export async function findVellumGuardian(): Promise<{
102
+ principalId: string;
103
+ } | null> {
102
104
  const rows = await assistantDbQuery<GuardianLookupRow>(
103
105
  `SELECT c.id AS contact_id, c.principal_id
104
106
  FROM contacts c
@@ -48,12 +48,34 @@ type PolicyEntry =
48
48
  */
49
49
  const POLICY_TABLE: PolicyEntry[] = [
50
50
  // Admin / internal
51
+ //
52
+ // Every operationId whose daemon-side route policy is gateway-only
53
+ // (`allowedPrincipalTypes: ["svc_gateway"]` in
54
+ // `assistant/src/runtime/auth/route-policy.ts`) MUST have a matching
55
+ // entry here. The gateway IPC proxy default-allows operationIds with
56
+ // no policy entry, so an authenticated edge JWT could otherwise reach
57
+ // them by setting `X-Vellum-Proxy-Server: ipc`, bypassing the daemon
58
+ // HTTP router entirely.
59
+ //
60
+ // The `ipc-route-policy-coverage.test.ts` lint enforces this invariant
61
+ // by walking the daemon route source files at test time.
51
62
  ["admin_rollbackmigrations_post", ["internal.write"], ["svc_gateway"]],
63
+ ["channel_inbound", ["ingress.write"], ["svc_gateway"]],
64
+ ["emit_event", ["internal.write"], ["svc_gateway"]],
65
+ ["internal_mcp_add", ["internal.write"], ["svc_gateway"]],
52
66
  ["internal_mcp_auth_start", ["internal.write"], ["svc_gateway"]],
53
67
  ["internal_mcp_auth_status", ["internal.write"], ["svc_gateway"]],
68
+ ["internal_mcp_list", ["internal.write"], ["svc_gateway"]],
54
69
  ["internal_mcp_reload", ["internal.write"], ["svc_gateway"]],
70
+ ["internal_mcp_remove", ["internal.write"], ["svc_gateway"]],
71
+ ["internal_oauth_callback", ["internal.write"], ["svc_gateway"]],
55
72
  ["internal_oauth_connect_start", ["internal.write"], ["svc_gateway"]],
56
73
  ["internal_oauth_connect_status", ["internal.write"], ["svc_gateway"]],
74
+ ["internal_twilio_connect_action", ["internal.write"], ["svc_gateway"]],
75
+ ["internal_twilio_status", ["internal.write"], ["svc_gateway"]],
76
+ ["internal_twilio_voice_webhook", ["internal.write"], ["svc_gateway"]],
77
+ ["upgrade_broadcast", ["internal.write"], ["svc_gateway"]],
78
+ ["workspace_commit", ["internal.write"], ["svc_gateway"]],
57
79
 
58
80
  // Calls
59
81
  ["calls_answer", ["calls.write"]],
@@ -134,6 +156,18 @@ const POLICY_TABLE: PolicyEntry[] = [
134
156
  ["listMemoryItems", ["settings.read"]],
135
157
  ["updateMemoryItem", ["settings.write"]],
136
158
 
159
+ // Inference profile sessions
160
+ ["inference_profile_close", ["chat.write"]],
161
+ ["inference_profile_list", ["chat.read"]],
162
+ ["inference_profile_open", ["chat.write"]],
163
+
164
+ // Inference provider connections
165
+ ["inference_provider_connections_create", ["settings.write"]],
166
+ ["inference_provider_connections_delete", ["settings.write"]],
167
+ ["inference_provider_connections_get", ["settings.read"]],
168
+ ["inference_provider_connections_list", ["settings.read"]],
169
+ ["inference_provider_connections_update", ["settings.write"]],
170
+
137
171
  // Notification intent
138
172
  ["notificationintentresult_post", ["settings.write"]],
139
173
 
@@ -13,9 +13,12 @@
13
13
  * gateway's own database.
14
14
  */
15
15
 
16
- import { ipcCallAssistant } from "../ipc/assistant-client.js";
16
+ import {
17
+ IpcHandlerError,
18
+ ipcCallAssistant,
19
+ } from "../ipc/assistant-client.js";
17
20
 
18
- type SqliteValue = string | number | null | Uint8Array;
21
+ export type SqliteValue = string | number | null | Uint8Array;
19
22
 
20
23
  interface DbProxyResult {
21
24
  rows?: Record<string, SqliteValue>[];
@@ -28,11 +31,11 @@ async function dbProxy(
28
31
  mode: "query" | "run" | "exec",
29
32
  bind?: SqliteValue[],
30
33
  ): Promise<DbProxyResult> {
31
- const result = await ipcCallAssistant("db_proxy", { sql, mode, bind });
32
- if (result === undefined) {
33
- throw new Error("db_proxy IPC call failed — assistant may not be ready");
34
- }
35
- return result as DbProxyResult;
34
+ return (await ipcCallAssistant("db_proxy", {
35
+ sql,
36
+ mode,
37
+ bind,
38
+ })) as DbProxyResult;
36
39
  }
37
40
 
38
41
  /**
@@ -66,3 +69,69 @@ export async function assistantDbRun(
66
69
  export async function assistantDbExec(sql: string): Promise<void> {
67
70
  await dbProxy(sql, "exec");
68
71
  }
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Transaction helper
75
+ // ---------------------------------------------------------------------------
76
+
77
+ export interface AssistantDbTransactionStep {
78
+ /** The SQL write statement to execute. */
79
+ sql: string;
80
+ /** Positional bind parameters. */
81
+ bind?: SqliteValue[];
82
+ /**
83
+ * If set, abort the transaction (rollback) when this step's row-change
84
+ * count is below this threshold. Used for stale-write detection — e.g.
85
+ * "increment use_count only if status = 'active' AND use_count < max_uses",
86
+ * with `requireChanges: 1` to abort when no rows match.
87
+ */
88
+ requireChanges?: number;
89
+ }
90
+
91
+ export type AssistantDbTransactionResult =
92
+ | {
93
+ ok: true;
94
+ results: Array<{ changes: number; lastInsertRowid: number }>;
95
+ }
96
+ | {
97
+ ok: false;
98
+ reason: "require_changes_failed";
99
+ failedStep: number;
100
+ actualChanges: number;
101
+ requiredChanges: number;
102
+ };
103
+
104
+ /**
105
+ * Execute multiple write statements against the assistant's SQLite DB inside
106
+ * a single atomic transaction (BEGIN IMMEDIATE). All steps commit together;
107
+ * any throw — including a `requireChanges` constraint failure — rolls back
108
+ * the entire batch.
109
+ *
110
+ * Use this when several writes must succeed or fail as a unit (e.g. invite
111
+ * redemption: contact-channel upsert + invite-use record).
112
+ *
113
+ * Error handling:
114
+ * - `requireChanges` violations return `{ ok: false, reason: "require_changes_failed", ... }`.
115
+ * - Handler-level failures (SQL constraint errors, malformed params) throw
116
+ * `IpcHandlerError` so the underlying SQL message is preserved.
117
+ * - Transport failures (socket missing, daemon unreachable, timeout) throw
118
+ * `IpcTransportError`. Use this to distinguish retryable vs.
119
+ * non-retryable failures.
120
+ *
121
+ * Read-modify-write across steps is not supported. Use SQL-level conditions
122
+ * (WHERE clauses, ON CONFLICT) plus `requireChanges` for stale-write detection.
123
+ */
124
+ export async function assistantDbTransaction(
125
+ steps: AssistantDbTransactionStep[],
126
+ ): Promise<AssistantDbTransactionResult> {
127
+ return (await ipcCallAssistant("db_proxy_transaction", {
128
+ steps,
129
+ })) as AssistantDbTransactionResult;
130
+ }
131
+
132
+ /**
133
+ * Re-export so callers in this module's domain (gateway DB write helpers)
134
+ * can identify SQL/handler failures from the assistant DB proxy without
135
+ * importing from the IPC client directly.
136
+ */
137
+ export { IpcHandlerError };