@vellumai/assistant 0.4.18 → 0.4.20
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/docs/runbook-trusted-contacts.md +5 -3
- package/package.json +1 -1
- package/src/__tests__/channel-approvals.test.ts +7 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/daemon-server-session-init.test.ts +2 -0
- package/src/__tests__/gmail-integration.test.ts +13 -4
- package/src/__tests__/handle-user-message-secret-resume.test.ts +7 -1
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -0
- package/src/__tests__/ingress-reconcile.test.ts +13 -5
- package/src/__tests__/mcp-cli.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +9 -1
- package/src/__tests__/send-endpoint-busy.test.ts +8 -2
- package/src/__tests__/sms-messaging-provider.test.ts +4 -0
- package/src/__tests__/system-prompt.test.ts +18 -2
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/agent/loop.ts +324 -163
- package/src/cli/mcp.ts +81 -28
- package/src/config/bundled-skills/app-builder/SKILL.md +7 -5
- package/src/config/bundled-skills/app-builder/TOOLS.json +2 -2
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +6 -11
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -2
- package/src/config/bundled-skills/sms-setup/SKILL.md +8 -16
- package/src/config/bundled-skills/telegram-setup/SKILL.md +3 -3
- package/src/config/bundled-skills/trusted-contacts/SKILL.md +13 -25
- package/src/config/bundled-skills/twilio-setup/SKILL.md +13 -23
- package/src/config/system-prompt.ts +574 -518
- package/src/daemon/session-surfaces.ts +28 -0
- package/src/daemon/session.ts +255 -191
- package/src/daemon/tool-side-effects.ts +3 -13
- package/src/mcp/client.ts +2 -7
- package/src/security/secure-keys.ts +43 -3
- package/src/tools/apps/definitions.ts +5 -0
- package/src/tools/apps/executors.ts +18 -22
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/__tests__/response-tier.test.ts +0 -195
- package/src/daemon/response-tier.ts +0 -250
package/src/cli/mcp.ts
CHANGED
|
@@ -15,7 +15,7 @@ const log = getCliLogger('cli');
|
|
|
15
15
|
export const HEALTH_CHECK_TIMEOUT_MS = 10_000;
|
|
16
16
|
|
|
17
17
|
export async function checkServerHealth(serverId: string, config: McpServerConfig, timeoutMs = HEALTH_CHECK_TIMEOUT_MS): Promise<string> {
|
|
18
|
-
const client = new McpClient(serverId
|
|
18
|
+
const client = new McpClient(serverId);
|
|
19
19
|
try {
|
|
20
20
|
await Promise.race([
|
|
21
21
|
client.connect(config.transport),
|
|
@@ -73,40 +73,93 @@ export function registerMcpCommand(program: Command): void {
|
|
|
73
73
|
|
|
74
74
|
log.info(`${entries.length} MCP server(s) configured:\n`);
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
for (const [id, cfg] of entries) {
|
|
78
|
-
if (!cfg || typeof cfg !== 'object') {
|
|
79
|
-
log.info(` ${id} (invalid config — skipped)\n`);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
const enabled = cfg.enabled !== false;
|
|
83
|
-
const transport = cfg.transport;
|
|
84
|
-
const risk = cfg.defaultRiskLevel ?? 'high';
|
|
76
|
+
const isTTY = process.stdout.isTTY;
|
|
85
77
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
78
|
+
if (isTTY) {
|
|
79
|
+
// TTY path: print placeholders, run health checks in parallel, update in-place with ANSI codes
|
|
80
|
+
let lineCount = 0;
|
|
81
|
+
const healthChecks: { id: string; cfg: McpServerConfig; statusLine: number }[] = [];
|
|
82
|
+
|
|
83
|
+
for (const [id, cfg] of entries) {
|
|
84
|
+
if (!cfg || typeof cfg !== 'object') {
|
|
85
|
+
log.info(` ${id} (invalid config — skipped)\n`);
|
|
86
|
+
lineCount += 2;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const enabled = cfg.enabled !== false;
|
|
90
|
+
const transport = cfg.transport;
|
|
91
|
+
const risk = cfg.defaultRiskLevel ?? 'high';
|
|
92
|
+
const statusText = !enabled ? '✗ disabled' : '⏳ Checking...';
|
|
93
|
+
|
|
94
|
+
log.info(` ${id}`);
|
|
95
|
+
lineCount++;
|
|
96
|
+
const statusLine = lineCount;
|
|
97
|
+
log.info(` Status: ${statusText}`);
|
|
98
|
+
lineCount++;
|
|
99
|
+
log.info(` Transport: ${transport?.type ?? 'unknown'}`);
|
|
100
|
+
lineCount++;
|
|
101
|
+
if (transport?.type === 'stdio') {
|
|
102
|
+
log.info(` Command: ${transport.command} ${(transport.args ?? []).join(' ')}`);
|
|
103
|
+
lineCount++;
|
|
104
|
+
} else if (transport && 'url' in transport) {
|
|
105
|
+
log.info(` URL: ${transport.url}`);
|
|
106
|
+
lineCount++;
|
|
107
|
+
}
|
|
108
|
+
log.info(` Risk: ${risk}`);
|
|
109
|
+
lineCount++;
|
|
110
|
+
if (cfg.allowedTools) { log.info(` Allowed: ${cfg.allowedTools.join(', ')}`); lineCount++; }
|
|
111
|
+
if (cfg.blockedTools) { log.info(` Blocked: ${cfg.blockedTools.join(', ')}`); lineCount++; }
|
|
112
|
+
log.info('');
|
|
113
|
+
lineCount++;
|
|
114
|
+
|
|
115
|
+
if (enabled) {
|
|
116
|
+
healthChecks.push({ id, cfg, statusLine });
|
|
117
|
+
}
|
|
92
118
|
}
|
|
93
119
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
120
|
+
if (healthChecks.length === 0) return;
|
|
121
|
+
|
|
122
|
+
// Run health checks in parallel, update status lines in-place with ANSI codes
|
|
123
|
+
await Promise.all(healthChecks.map(async ({ id, cfg, statusLine }) => {
|
|
124
|
+
const health = await checkServerHealth(id, cfg);
|
|
125
|
+
const up = lineCount - statusLine;
|
|
126
|
+
process.stdout.write(`\x1b[${up}A\r\x1b[2K Status: ${health}\x1b[${up}B\r`);
|
|
127
|
+
}));
|
|
128
|
+
} else {
|
|
129
|
+
// Non-TTY path: run health checks sequentially, print final status directly (no ANSI codes)
|
|
130
|
+
for (const [id, cfg] of entries) {
|
|
131
|
+
if (!cfg || typeof cfg !== 'object') {
|
|
132
|
+
log.info(` ${id} (invalid config — skipped)\n`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const enabled = cfg.enabled !== false;
|
|
136
|
+
const transport = cfg.transport;
|
|
137
|
+
const risk = cfg.defaultRiskLevel ?? 'high';
|
|
138
|
+
|
|
139
|
+
let statusText: string;
|
|
140
|
+
if (!enabled) {
|
|
141
|
+
statusText = '✗ disabled';
|
|
142
|
+
} else {
|
|
143
|
+
statusText = await checkServerHealth(id, cfg);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
log.info(` ${id}`);
|
|
147
|
+
log.info(` Status: ${statusText}`);
|
|
148
|
+
log.info(` Transport: ${transport?.type ?? 'unknown'}`);
|
|
149
|
+
if (transport?.type === 'stdio') {
|
|
150
|
+
log.info(` Command: ${transport.command} ${(transport.args ?? []).join(' ')}`);
|
|
151
|
+
} else if (transport && 'url' in transport) {
|
|
152
|
+
log.info(` URL: ${transport.url}`);
|
|
153
|
+
}
|
|
154
|
+
log.info(` Risk: ${risk}`);
|
|
155
|
+
if (cfg.allowedTools) { log.info(` Allowed: ${cfg.allowedTools.join(', ')}`); }
|
|
156
|
+
if (cfg.blockedTools) { log.info(` Blocked: ${cfg.blockedTools.join(', ')}`); }
|
|
157
|
+
log.info('');
|
|
101
158
|
}
|
|
102
|
-
log.info(` Risk: ${risk}`);
|
|
103
|
-
if (cfg.allowedTools) log.info(` Allowed: ${cfg.allowedTools.join(', ')}`);
|
|
104
|
-
if (cfg.blockedTools) log.info(` Blocked: ${cfg.blockedTools.join(', ')}`);
|
|
105
|
-
log.info('');
|
|
106
159
|
}
|
|
107
160
|
|
|
108
161
|
// Health checks may leave MCP transports alive — force exit
|
|
109
|
-
|
|
162
|
+
process.exit(0);
|
|
110
163
|
});
|
|
111
164
|
|
|
112
165
|
mcp
|
|
@@ -1323,14 +1323,16 @@ Call `app_create` with:
|
|
|
1323
1323
|
- `description`: One-sentence summary
|
|
1324
1324
|
- `schema_json`: JSON schema as string
|
|
1325
1325
|
- `html`: Complete HTML document as string
|
|
1326
|
-
- `auto_open`: (optional, defaults to `true`)
|
|
1327
|
-
- `preview`:
|
|
1326
|
+
- `auto_open`: (optional, defaults to `true`) Shows an inline preview card in chat after creation
|
|
1327
|
+
- `preview`: Always include this — see below
|
|
1328
1328
|
|
|
1329
|
-
Since `auto_open` defaults to `true`,
|
|
1329
|
+
Since `auto_open` defaults to `true`, an inline preview card is shown in chat after creation. The app is NOT opened in a workspace panel automatically — users open it explicitly if desired by clicking 'Open App' on the inline card. The app appears in Things (sidebar) immediately after creation via the `app_files_changed` broadcast.
|
|
1330
1330
|
|
|
1331
1331
|
#### Preview metadata
|
|
1332
1332
|
|
|
1333
|
-
|
|
1333
|
+
Always include preview metadata in `app_create` calls. The app shows as an inline card in chat first — no workspace opens automatically. Users can click 'Open App' on the inline card to open the workspace.
|
|
1334
|
+
|
|
1335
|
+
Both `ui_show` and `app_create` support a `preview` object for an inline chat preview card. Always include it so the user sees a compact summary of what was built.
|
|
1334
1336
|
|
|
1335
1337
|
**With `ui_show`:**
|
|
1336
1338
|
```json
|
|
@@ -1387,7 +1389,7 @@ Both `ui_show` and `app_create` support a `preview` object for an inline chat pr
|
|
|
1387
1389
|
}
|
|
1388
1390
|
```
|
|
1389
1391
|
|
|
1390
|
-
Preview fields: `title` (required), `subtitle`, `description`, `icon`, `metrics` (up to 3 key-value pills). The `icon` field accepts an emoji or an image URL. **Prefer an image URL whenever you have a relevant one** — logos, favicons, product images, headshots, flags, album art, or any image you encountered during research. The preview card renders image URLs as a thumbnail automatically. Fall back to emoji only when there is no natural image. When `app_create` is called with `auto_open: true` (the default), the preview is
|
|
1392
|
+
Preview fields: `title` (required), `subtitle`, `description`, `icon`, `metrics` (up to 3 key-value pills). The `icon` field accepts an emoji or an image URL. **Prefer an image URL whenever you have a relevant one** — logos, favicons, product images, headshots, flags, album art, or any image you encountered during research. The preview card renders image URLs as a thumbnail automatically. Fall back to emoji only when there is no natural image. When `app_create` is called with `auto_open: true` (the default), the inline preview card is shown in chat — the app is NOT automatically opened in a workspace panel.
|
|
1391
1393
|
|
|
1392
1394
|
### 6. Handle Iteration
|
|
1393
1395
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"auto_open": {
|
|
39
39
|
"type": "boolean",
|
|
40
|
-
"description": "
|
|
40
|
+
"description": "When true (default), an inline preview card is shown in chat after creation. The app is NOT automatically opened in a workspace panel — users can open it explicitly via the 'Open App' button on the inline card."
|
|
41
41
|
},
|
|
42
42
|
"set_as_home_base": {
|
|
43
43
|
"type": "boolean",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"preview": {
|
|
47
47
|
"type": "object",
|
|
48
|
-
"description": "
|
|
48
|
+
"description": "Inline preview card shown in chat after creation. Always include this so users see a compact summary of what was built. Required fields: title.",
|
|
49
49
|
"properties": {
|
|
50
50
|
"title": { "type": "string", "description": "Preview card title" },
|
|
51
51
|
"subtitle": { "type": "string", "description": "Optional subtitle" },
|
|
@@ -11,7 +11,7 @@ You are helping your user set up guardian verification for a messaging channel (
|
|
|
11
11
|
|
|
12
12
|
- Use the injected `INTERNAL_GATEWAY_BASE_URL` for gateway API calls.
|
|
13
13
|
- Never call the daemon runtime port directly; always call the gateway URL.
|
|
14
|
-
- The bearer token is
|
|
14
|
+
- The bearer token is available as the `$GATEWAY_AUTH_TOKEN` environment variable.
|
|
15
15
|
- Run shell commands for this skill with `host_bash` (not sandbox `bash`) so host auth/token and gateway routing are reliable.
|
|
16
16
|
- Keep narration minimal: execute required calls first, then provide a concise status update. Do not narrate internal install/check/load chatter unless something fails.
|
|
17
17
|
|
|
@@ -39,10 +39,9 @@ Based on the chosen channel, ask for the required destination:
|
|
|
39
39
|
Execute the outbound start request:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
43
42
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/outbound/start" \
|
|
44
43
|
-H "Content-Type: application/json" \
|
|
45
|
-
-H "Authorization: Bearer $
|
|
44
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
46
45
|
-d '{"channel": "<channel>", "destination": "<destination>"}'
|
|
47
46
|
```
|
|
48
47
|
|
|
@@ -77,10 +76,9 @@ Handle each error code:
|
|
|
77
76
|
If the user says they did not receive the code or asks to resend:
|
|
78
77
|
|
|
79
78
|
```bash
|
|
80
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
81
79
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/outbound/resend" \
|
|
82
80
|
-H "Content-Type: application/json" \
|
|
83
|
-
-H "Authorization: Bearer $
|
|
81
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
84
82
|
-d '{"channel": "<channel>"}'
|
|
85
83
|
```
|
|
86
84
|
|
|
@@ -107,10 +105,9 @@ Handle each error code from the resend endpoint:
|
|
|
107
105
|
If the user wants to cancel the verification:
|
|
108
106
|
|
|
109
107
|
```bash
|
|
110
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
111
108
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/outbound/cancel" \
|
|
112
109
|
-H "Content-Type: application/json" \
|
|
113
|
-
-H "Authorization: Bearer $
|
|
110
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
114
111
|
-d '{"channel": "<channel>"}'
|
|
115
112
|
```
|
|
116
113
|
|
|
@@ -126,9 +123,8 @@ For **voice** verification only: after telling the user their code and instructi
|
|
|
126
123
|
2. Check the binding status:
|
|
127
124
|
|
|
128
125
|
```bash
|
|
129
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
130
126
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=voice" \
|
|
131
|
-
-H "Authorization: Bearer $
|
|
127
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
132
128
|
```
|
|
133
129
|
|
|
134
130
|
3. If the response shows `bound: true`: immediately send a proactive success message in the current chat — "Voice verification complete! Your phone number is now the trusted guardian." Stop polling.
|
|
@@ -153,9 +149,8 @@ When in a **rebind flow** (i.e., the `start_outbound` request included `"rebind"
|
|
|
153
149
|
After the user reports entering the code, verify the binding was created:
|
|
154
150
|
|
|
155
151
|
```bash
|
|
156
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
157
152
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=<channel>" \
|
|
158
|
-
-H "Authorization: Bearer $
|
|
153
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
159
154
|
```
|
|
160
155
|
|
|
161
156
|
If the response shows the guardian is bound, confirm success: "Guardian verified! Your [channel] identity is now the trusted guardian."
|
|
@@ -49,9 +49,8 @@ The user's assistant gets its own personal phone number through Twilio. All impl
|
|
|
49
49
|
Check whether Twilio credentials, phone number, and public ingress are already configured:
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
53
52
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
54
|
-
-H "Authorization: Bearer $
|
|
53
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
55
54
|
```
|
|
56
55
|
|
|
57
56
|
```bash
|
|
@@ -12,9 +12,8 @@ You are helping your user set up SMS messaging. This skill orchestrates Twilio s
|
|
|
12
12
|
First, check the current SMS channel readiness state via the gateway:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
16
15
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
17
|
-
-H "Authorization: Bearer $
|
|
16
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
18
17
|
```
|
|
19
18
|
|
|
20
19
|
Inspect the response for `hasCredentials` and `phoneNumber`.
|
|
@@ -35,9 +34,8 @@ Tell the user: _"SMS needs Twilio configured first. I've loaded the Twilio setup
|
|
|
35
34
|
After twilio-setup completes, re-check readiness by calling the config endpoint again:
|
|
36
35
|
|
|
37
36
|
```bash
|
|
38
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
39
37
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
40
|
-
-H "Authorization: Bearer $
|
|
38
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
41
39
|
```
|
|
42
40
|
|
|
43
41
|
If baseline is still not ready, report the specific failures and ask the user to address them before continuing.
|
|
@@ -47,9 +45,8 @@ If baseline is still not ready, report the specific failures and ask the user to
|
|
|
47
45
|
Once baseline is ready, check SMS compliance status including remote (Twilio API) checks:
|
|
48
46
|
|
|
49
47
|
```bash
|
|
50
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
51
48
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance" \
|
|
52
|
-
-H "Authorization: Bearer $
|
|
49
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
53
50
|
```
|
|
54
51
|
|
|
55
52
|
Examine the compliance results:
|
|
@@ -90,9 +87,8 @@ The `tollfreePhoneNumberSid` is returned by the compliance status response in th
|
|
|
90
87
|
**Step 3c: Submit verification:**
|
|
91
88
|
|
|
92
89
|
```bash
|
|
93
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
94
90
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance/tollfree" \
|
|
95
|
-
-H "Authorization: Bearer $
|
|
91
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
96
92
|
-H "Content-Type: application/json" \
|
|
97
93
|
-d '{
|
|
98
94
|
"tollfreePhoneNumberSid": "<compliance.tollfreePhoneNumberSid from Step 3a>",
|
|
@@ -118,9 +114,8 @@ The endpoint validates all fields before submitting to Twilio and returns clear
|
|
|
118
114
|
**Step 3d: Update a rejected verification** (if `editAllowed` is true):
|
|
119
115
|
|
|
120
116
|
```bash
|
|
121
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
122
117
|
curl -s -X PATCH "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance/tollfree/<verificationSid>" \
|
|
123
|
-
-H "Authorization: Bearer $
|
|
118
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
124
119
|
-H "Content-Type: application/json" \
|
|
125
120
|
-d '{
|
|
126
121
|
"businessName": "updated value",
|
|
@@ -133,9 +128,8 @@ Only include fields that need to change. The endpoint checks edit eligibility an
|
|
|
133
128
|
**Step 3e: Delete and resubmit** (if editing is not allowed):
|
|
134
129
|
|
|
135
130
|
```bash
|
|
136
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
137
131
|
curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/compliance/tollfree/<verificationSid>" \
|
|
138
|
-
-H "Authorization: Bearer $
|
|
132
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
139
133
|
```
|
|
140
134
|
|
|
141
135
|
After deletion, return to Step 3b to collect information and resubmit. Warn the user that deleting resets their position in the review queue.
|
|
@@ -183,9 +177,8 @@ Tell the user: _"Let's send a test SMS to verify everything works. What phone nu
|
|
|
183
177
|
After the user provides a number, send a test message via the gateway:
|
|
184
178
|
|
|
185
179
|
```bash
|
|
186
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
187
180
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/test" \
|
|
188
|
-
-H "Authorization: Bearer $
|
|
181
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
189
182
|
-H "Content-Type: application/json" \
|
|
190
183
|
-d '{"phoneNumber":"<recipient phone number>","text":"Test SMS from your Vellum assistant."}'
|
|
191
184
|
```
|
|
@@ -200,9 +193,8 @@ Report the result honestly:
|
|
|
200
193
|
If the test fails or the user reports SMS issues, run the SMS doctor:
|
|
201
194
|
|
|
202
195
|
```bash
|
|
203
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
204
196
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/sms/doctor" \
|
|
205
|
-
-H "Authorization: Bearer $
|
|
197
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
206
198
|
```
|
|
207
199
|
|
|
208
200
|
This runs a comprehensive health diagnostic, checking channel readiness, compliance/toll-free verification status, and the last test result. Report the diagnostics and actionable items to the user.
|
|
@@ -39,7 +39,7 @@ After the token is collected, call the composite setup endpoint which validates
|
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
41
|
curl -sf -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/telegram/setup" \
|
|
42
|
-
-H "Authorization: Bearer $
|
|
42
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
43
43
|
-H "Content-Type: application/json" \
|
|
44
44
|
-d '{}'
|
|
45
45
|
```
|
|
@@ -101,7 +101,7 @@ Before reporting success, confirm the guardian binding was actually created. Che
|
|
|
101
101
|
|
|
102
102
|
```bash
|
|
103
103
|
curl -sf "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=telegram" \
|
|
104
|
-
-H "Authorization: Bearer $
|
|
104
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
If the binding is absent and the user said they completed the verification:
|
|
@@ -120,7 +120,7 @@ Summarize what was done:
|
|
|
120
120
|
- Guardian identity: {verified | not configured}
|
|
121
121
|
- Guardian verification status: {verified via outbound flow | skipped}
|
|
122
122
|
- Routing configuration validated
|
|
123
|
-
- To re-check guardian status later, use: `curl -sf "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=telegram" -H "Authorization: Bearer $
|
|
123
|
+
- To re-check guardian status later, use: `curl -sf "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=telegram" -H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"`
|
|
124
124
|
|
|
125
125
|
The gateway automatically detects credentials from the vault, reconciles the Telegram webhook registration, and begins accepting Telegram webhooks shortly. In single-assistant mode, routing is automatically configured — no manual environment variable configuration or webhook registration is needed. If the webhook secret changes later, the gateway's credential watcher will automatically re-register the webhook. If the ingress URL changes (e.g., tunnel restart), the assistant daemon triggers an immediate internal reconcile so the webhook re-registers automatically without a gateway restart.
|
|
126
126
|
|
|
@@ -11,7 +11,7 @@ You are helping your user manage trusted contacts and invite links for the Vellu
|
|
|
11
11
|
|
|
12
12
|
- Use the injected `INTERNAL_GATEWAY_BASE_URL` for gateway API calls.
|
|
13
13
|
- Use gateway control-plane routes only: this skill calls `/v1/ingress/*` and `/v1/integrations/telegram/config` on the gateway, never the daemon runtime port directly.
|
|
14
|
-
- The bearer token is
|
|
14
|
+
- The bearer token is available as the `$GATEWAY_AUTH_TOKEN` environment variable.
|
|
15
15
|
|
|
16
16
|
## Concepts
|
|
17
17
|
|
|
@@ -29,9 +29,8 @@ You are helping your user manage trusted contacts and invite links for the Vellu
|
|
|
29
29
|
Use this to show the user who currently has access, or to look up a specific contact.
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
33
32
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/members" \
|
|
34
|
-
-H "Authorization: Bearer $
|
|
33
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
35
34
|
```
|
|
36
35
|
|
|
37
36
|
Optional query parameters for filtering:
|
|
@@ -42,7 +41,7 @@ Optional query parameters for filtering:
|
|
|
42
41
|
Example with filters:
|
|
43
42
|
```bash
|
|
44
43
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/members?sourceChannel=telegram&status=active" \
|
|
45
|
-
-H "Authorization: Bearer $
|
|
44
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
46
45
|
```
|
|
47
46
|
|
|
48
47
|
The response contains `{ ok: true, members: [...] }` where each member has:
|
|
@@ -65,10 +64,9 @@ Use this when the user wants to grant someone access to message the assistant. *
|
|
|
65
64
|
Ask the user: *"I'll add [name/identifier] on [channel] as an allowed contact. Should I proceed?"*
|
|
66
65
|
|
|
67
66
|
```bash
|
|
68
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
69
67
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/members" \
|
|
70
68
|
-H "Content-Type: application/json" \
|
|
71
|
-
-H "Authorization: Bearer $
|
|
69
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
72
70
|
-d '{
|
|
73
71
|
"sourceChannel": "<channel>",
|
|
74
72
|
"externalUserId": "<user_id>",
|
|
@@ -97,9 +95,8 @@ Ask the user: *"I'll revoke access for [name/identifier]. They will no longer be
|
|
|
97
95
|
First, list members to find the member's `id`, then revoke:
|
|
98
96
|
|
|
99
97
|
```bash
|
|
100
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
101
98
|
curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/members/<member_id>" \
|
|
102
|
-
-H "Authorization: Bearer $
|
|
99
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
103
100
|
-H "Content-Type: application/json" \
|
|
104
101
|
-d '{"reason": "<optional reason>"}'
|
|
105
102
|
```
|
|
@@ -113,10 +110,9 @@ Use this when the user wants to explicitly block someone. Blocking is stronger t
|
|
|
113
110
|
Ask the user: *"I'll block [name/identifier]. They will be permanently denied from messaging the assistant. Should I proceed?"*
|
|
114
111
|
|
|
115
112
|
```bash
|
|
116
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
117
113
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/members/<member_id>/block" \
|
|
118
114
|
-H "Content-Type: application/json" \
|
|
119
|
-
-H "Authorization: Bearer $
|
|
115
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
120
116
|
-d '{"reason": "<optional reason>"}'
|
|
121
117
|
```
|
|
122
118
|
|
|
@@ -127,11 +123,9 @@ Use this when the guardian wants to invite someone to message the assistant on T
|
|
|
127
123
|
**Important**: The shell snippet below emits a `<vellum-sensitive-output>` directive containing the raw invite token. The tool executor automatically strips this directive and replaces the raw token with a placeholder so the LLM never sees it. The placeholder is resolved back to the real token in the final assistant reply.
|
|
128
124
|
|
|
129
125
|
```bash
|
|
130
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
131
|
-
|
|
132
126
|
INVITE_JSON=$(curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites" \
|
|
133
127
|
-H "Content-Type: application/json" \
|
|
134
|
-
-H "Authorization: Bearer $
|
|
128
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
135
129
|
-d '{
|
|
136
130
|
"sourceChannel": "telegram",
|
|
137
131
|
"maxUses": 1,
|
|
@@ -160,7 +154,7 @@ fi
|
|
|
160
154
|
# Prefer backend-provided canonical link when available.
|
|
161
155
|
if [ -z "$INVITE_URL" ]; then
|
|
162
156
|
BOT_CONFIG_JSON=$(curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/telegram/config" \
|
|
163
|
-
-H "Authorization: Bearer $
|
|
157
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN")
|
|
164
158
|
BOT_USERNAME=$(printf '%s' "$BOT_CONFIG_JSON" | tr -d '\n' | sed -n 's/.*"botUsername"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
|
|
165
159
|
if [ -z "$BOT_USERNAME" ]; then
|
|
166
160
|
echo "error:no_share_url_or_bot_username"
|
|
@@ -201,9 +195,8 @@ If the Telegram bot username is not available (integration not set up), tell the
|
|
|
201
195
|
Use this to show the guardian their active (and optionally all) invite links.
|
|
202
196
|
|
|
203
197
|
```bash
|
|
204
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
205
198
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites?sourceChannel=telegram" \
|
|
206
|
-
-H "Authorization: Bearer $
|
|
199
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
207
200
|
```
|
|
208
201
|
|
|
209
202
|
Optional query parameters:
|
|
@@ -232,9 +225,8 @@ Ask the user: *"I'll revoke the invite link [note or ID]. It will no longer be u
|
|
|
232
225
|
First, list invites to find the invite's `id`, then revoke:
|
|
233
226
|
|
|
234
227
|
```bash
|
|
235
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
236
228
|
curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites/<invite_id>" \
|
|
237
|
-
-H "Authorization: Bearer $
|
|
229
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
238
230
|
```
|
|
239
231
|
|
|
240
232
|
Replace `<invite_id>` with the invite's `id` from the list response.
|
|
@@ -246,11 +238,9 @@ Use this when the guardian wants to authorize a specific phone number to call th
|
|
|
246
238
|
**Important**: The response includes a `voiceCode` field that is only returned at creation time and cannot be retrieved later. Extract and present it clearly.
|
|
247
239
|
|
|
248
240
|
```bash
|
|
249
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
250
|
-
|
|
251
241
|
INVITE_JSON=$(curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites" \
|
|
252
242
|
-H "Content-Type: application/json" \
|
|
253
|
-
-H "Authorization: Bearer $
|
|
243
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
254
244
|
-d '{
|
|
255
245
|
"sourceChannel": "voice",
|
|
256
246
|
"expectedExternalUserId": "<phone_number_E164>",
|
|
@@ -303,9 +293,8 @@ If the user provides a phone number without the `+` country code prefix, ask the
|
|
|
303
293
|
Use this to show the guardian their active voice invites.
|
|
304
294
|
|
|
305
295
|
```bash
|
|
306
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
307
296
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites?sourceChannel=voice" \
|
|
308
|
-
-H "Authorization: Bearer $
|
|
297
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
309
298
|
```
|
|
310
299
|
|
|
311
300
|
Optional query parameters:
|
|
@@ -327,9 +316,8 @@ Ask the user: *"I'll revoke the voice invite for [phone number or note]. The cod
|
|
|
327
316
|
First, list voice invites to find the invite's `id`, then revoke:
|
|
328
317
|
|
|
329
318
|
```bash
|
|
330
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
331
319
|
curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites/<invite_id>" \
|
|
332
|
-
-H "Authorization: Bearer $
|
|
320
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
333
321
|
```
|
|
334
322
|
|
|
335
323
|
Replace `<invite_id>` with the invite's `id` from the list response. The same revoke endpoint is used for both Telegram and voice invites.
|
|
@@ -11,17 +11,16 @@ You are helping your user configure Twilio for voice calls and SMS messaging. Tw
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
15
14
|
# 1. Check current status
|
|
16
15
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
17
|
-
-H "Authorization: Bearer $
|
|
16
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
18
17
|
# 2. Store credentials (after collecting via credential_store prompt)
|
|
19
18
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
|
|
20
|
-
-H "Authorization: Bearer $
|
|
19
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" -H "Content-Type: application/json" \
|
|
21
20
|
-d '{"accountSid":"ACxxx","authToken":"xxx"}'
|
|
22
21
|
# 3. Provision or assign a number
|
|
23
22
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/provision" \
|
|
24
|
-
-H "Authorization: Bearer $
|
|
23
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" -H "Content-Type: application/json" \
|
|
25
24
|
-d '{"country":"US","areaCode":"415"}'
|
|
26
25
|
```
|
|
27
26
|
|
|
@@ -67,9 +66,8 @@ All HTTP examples below include the optional `assistantId` query parameter in as
|
|
|
67
66
|
First, check whether Twilio is already configured:
|
|
68
67
|
|
|
69
68
|
```bash
|
|
70
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
71
69
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
72
|
-
-H "Authorization: Bearer $
|
|
70
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
73
71
|
```
|
|
74
72
|
|
|
75
73
|
The response includes:
|
|
@@ -96,9 +94,8 @@ If credentials are not yet stored, guide the user through Twilio account setup:
|
|
|
96
94
|
After both credentials are collected, retrieve them from secure storage and send them to the gateway:
|
|
97
95
|
|
|
98
96
|
```bash
|
|
99
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
100
97
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
|
|
101
|
-
-H "Authorization: Bearer $
|
|
98
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
102
99
|
-H "Content-Type: application/json" \
|
|
103
100
|
-d '{"accountSid":"<value from credential_store for twilio/account_sid>","authToken":"<value from credential_store for twilio/auth_token>"}'
|
|
104
101
|
```
|
|
@@ -116,9 +113,8 @@ The assistant needs a phone number to make calls and send SMS. There are two pat
|
|
|
116
113
|
If the user wants to buy a new number through Twilio:
|
|
117
114
|
|
|
118
115
|
```bash
|
|
119
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
120
116
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/provision" \
|
|
121
|
-
-H "Authorization: Bearer $
|
|
117
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
122
118
|
-H "Content-Type: application/json" \
|
|
123
119
|
-d '{"country":"US","areaCode":"415"}'
|
|
124
120
|
```
|
|
@@ -143,9 +139,8 @@ If ingress is not yet configured, webhook setup is skipped gracefully — the nu
|
|
|
143
139
|
If the user already has a Twilio phone number, first list available numbers:
|
|
144
140
|
|
|
145
141
|
```bash
|
|
146
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
147
142
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers" \
|
|
148
|
-
-H "Authorization: Bearer $
|
|
143
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
149
144
|
```
|
|
150
145
|
|
|
151
146
|
The response includes a `numbers` array with each number's `phoneNumber`, `friendlyName`, and `capabilities` (voice, SMS). Present these to the user and let them choose.
|
|
@@ -153,9 +148,8 @@ The response includes a `numbers` array with each number's `phoneNumber`, `frien
|
|
|
153
148
|
Then assign the chosen number:
|
|
154
149
|
|
|
155
150
|
```bash
|
|
156
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
157
151
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/assign" \
|
|
158
|
-
-H "Authorization: Bearer $
|
|
152
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
159
153
|
-H "Content-Type: application/json" \
|
|
160
154
|
-d '{"phoneNumber":"+14155551234"}'
|
|
161
155
|
```
|
|
@@ -173,9 +167,8 @@ credential_store action=store service=twilio field=phone_number value=+141555512
|
|
|
173
167
|
Then assign it through the gateway:
|
|
174
168
|
|
|
175
169
|
```bash
|
|
176
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
177
170
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/assign" \
|
|
178
|
-
-H "Authorization: Bearer $
|
|
171
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
179
172
|
-H "Content-Type: application/json" \
|
|
180
173
|
-d '{"phoneNumber":"+14155551234"}'
|
|
181
174
|
```
|
|
@@ -211,9 +204,8 @@ Webhook URLs are automatically configured on the Twilio phone number when provis
|
|
|
211
204
|
After configuration, verify by checking the config endpoint again.
|
|
212
205
|
|
|
213
206
|
```bash
|
|
214
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
215
207
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
216
|
-
-H "Authorization: Bearer $
|
|
208
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
217
209
|
```
|
|
218
210
|
|
|
219
211
|
Confirm:
|
|
@@ -251,13 +243,12 @@ After the guardian-verify-setup skill completes verification for a channel, load
|
|
|
251
243
|
To re-check guardian status later, query the channel(s) that were verified:
|
|
252
244
|
|
|
253
245
|
```bash
|
|
254
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
255
246
|
# Check SMS guardian status
|
|
256
247
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=sms" \
|
|
257
|
-
-H "Authorization: Bearer $
|
|
248
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
258
249
|
# Check voice guardian status
|
|
259
250
|
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/guardian/status?channel=voice" \
|
|
260
|
-
-H "Authorization: Bearer $
|
|
251
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
261
252
|
```
|
|
262
253
|
|
|
263
254
|
Check the status for whichever channel(s) the user actually verified (SMS, voice, or both). Report the guardian verification result per channel: **"Guardian identity — SMS: {verified | not configured}, Voice: {verified | not configured}."**
|
|
@@ -280,9 +271,8 @@ SMS is available automatically once Twilio is configured — no additional featu
|
|
|
280
271
|
If the user wants to disconnect Twilio:
|
|
281
272
|
|
|
282
273
|
```bash
|
|
283
|
-
TOKEN=$(cat ~/.vellum/http-token)
|
|
284
274
|
curl -s -X DELETE "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
|
|
285
|
-
-H "Authorization: Bearer $
|
|
275
|
+
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN"
|
|
286
276
|
```
|
|
287
277
|
|
|
288
278
|
This removes the stored Account SID and Auth Token. Phone number assignments are preserved. Voice calls and SMS will stop working until credentials are reconfigured.
|