@vellumai/assistant 0.4.35 → 0.4.37
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 +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -3,7 +3,7 @@ name: "Google OAuth Setup"
|
|
|
3
3
|
description: "Set up Google Cloud OAuth credentials for Gmail and Calendar"
|
|
4
4
|
user-invocable: true
|
|
5
5
|
credential-setup-for: "gmail"
|
|
6
|
-
includes: ["public-ingress"]
|
|
6
|
+
includes: ["public-ingress", "browser"]
|
|
7
7
|
metadata: { "vellum": { "emoji": "\ud83d\udd11" } }
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -88,6 +88,7 @@ Tell the user:
|
|
|
88
88
|
> - `https://www.googleapis.com/auth/calendar.readonly`
|
|
89
89
|
> - `https://www.googleapis.com/auth/calendar.events`
|
|
90
90
|
> - `https://www.googleapis.com/auth/userinfo.email`
|
|
91
|
+
> - `https://www.googleapis.com/auth/contacts.readonly`
|
|
91
92
|
> - Click **Update**, then **Save and Continue**
|
|
92
93
|
> 5. On the Test users page, add **your email**, click **Save and Continue**
|
|
93
94
|
> 6. On the Summary page, click **Back to Dashboard**
|
|
@@ -198,7 +199,42 @@ After the user authorizes (they'll come back and say so, or you can suggest they
|
|
|
198
199
|
|
|
199
200
|
**IMPORTANT: Always use `host_bash` (not `bash`) for all commands in this path.** The `gcloud` and `gws` CLIs need host access for Homebrew/npm installation, browser-based authentication, and interactive terminal prompts — none of which are available inside the sandbox.
|
|
200
201
|
|
|
201
|
-
You will set up Google Cloud OAuth credentials using the `gcloud` and `gws` command-line tools
|
|
202
|
+
You will set up Google Cloud OAuth credentials using the `gcloud` and `gws` command-line tools plus browser automation. The user signs in once via the browser, the CLI handles project setup, and the browser automates credential creation — the user only needs to copy-paste the Client Secret.
|
|
203
|
+
|
|
204
|
+
## Browser Interaction Principles
|
|
205
|
+
|
|
206
|
+
Google Cloud Console's UI may change over time. Do NOT memorize or depend on specific element IDs, CSS selectors, or DOM structures. Instead:
|
|
207
|
+
|
|
208
|
+
1. **Screenshot first, act second.** Before every interaction, take a `browser_screenshot` to see the current visual state. Use `browser_snapshot` to find interactive elements.
|
|
209
|
+
2. **Adapt to what you see.** If a button's label or position differs from what you expect, use the screenshot to find the correct element.
|
|
210
|
+
3. **Verify after every action.** After clicking, typing, or navigating, take a new screenshot to confirm the action succeeded.
|
|
211
|
+
4. **Never assume DOM structure.** Use the snapshot to identify what's on the page and interact accordingly.
|
|
212
|
+
5. **When stuck, screenshot and describe.** If you cannot find an expected element after 2 attempts, take a screenshot, describe what you see to the user, and ask for guidance.
|
|
213
|
+
|
|
214
|
+
## Anti-Loop Guardrails
|
|
215
|
+
|
|
216
|
+
Each step has a **retry budget of 3 attempts**. An attempt is one try at the step's primary action (e.g., clicking a button, filling a form). If a step fails after 3 attempts:
|
|
217
|
+
|
|
218
|
+
1. **Stop trying.** Do not continue retrying the same approach.
|
|
219
|
+
2. **Fall back to manual.** Tell the user what you were trying to do and ask them to complete that step manually in the browser. Give them the direct URL and clear text instructions.
|
|
220
|
+
3. **Resume automation** at the next step once the user confirms the manual step is done.
|
|
221
|
+
|
|
222
|
+
If **two or more steps** require manual fallback, abandon the automated flow entirely and switch to giving the user the remaining steps as clear text instructions with links.
|
|
223
|
+
|
|
224
|
+
## Things That Do Not Work — Do Not Attempt
|
|
225
|
+
|
|
226
|
+
These actions are technically impossible in the browser automation environment:
|
|
227
|
+
|
|
228
|
+
- **Downloading files.** `browser_click` on a Download button does not save files to disk. Do NOT click "Download JSON" in the OAuth client creation dialog.
|
|
229
|
+
- **Reading the Client Secret from a screenshot.** The secret IS visible in the creation dialog, but you MUST NOT attempt to read it from a screenshot — it is too easy to misread characters, and the value must be exact. Always use the `credential_store prompt` approach to let the user copy-paste it accurately.
|
|
230
|
+
- **Clipboard operations.** You cannot copy/paste via browser automation.
|
|
231
|
+
|
|
232
|
+
## Error Handling
|
|
233
|
+
|
|
234
|
+
- **Page load failures:** Retry navigation once. If it still fails, tell the user and ask them to check their internet connection.
|
|
235
|
+
- **Element not found:** Take a fresh screenshot to re-assess. The GCP Console UI may have changed. Describe what you see and try alternative approaches. If stuck after 2 attempts, ask the user for guidance.
|
|
236
|
+
- **OAuth client already exists with same name:** This is fine — GCP allows multiple clients with the same name. Proceed with creation.
|
|
237
|
+
- **Any unexpected state:** Take a `browser_screenshot`, describe what you see, and ask the user for guidance.
|
|
202
238
|
|
|
203
239
|
## CLI Step 1: Confirm
|
|
204
240
|
|
|
@@ -210,9 +246,10 @@ Use `ui_show` with `surface_type: "confirmation"`:
|
|
|
210
246
|
>
|
|
211
247
|
> 1. **Install CLI tools** (`gcloud` and `gws`) if not already installed
|
|
212
248
|
> 2. **You sign in** to your Google account once via the browser
|
|
213
|
-
> 3. **CLI automates everything** — project creation, APIs, consent screen
|
|
214
|
-
> 4. **
|
|
215
|
-
> 5. **
|
|
249
|
+
> 3. **CLI automates everything** — project creation, APIs, and consent screen
|
|
250
|
+
> 4. **I create OAuth credentials** in the browser — you just watch
|
|
251
|
+
> 5. **One quick copy-paste** — you copy the Client Secret into a secure prompt
|
|
252
|
+
> 6. **You authorize Vellum** with one click
|
|
216
253
|
>
|
|
217
254
|
> Takes about a minute after first-time setup. Ready?
|
|
218
255
|
|
|
@@ -274,6 +311,8 @@ If the user is already authenticated (`gcloud auth list` shows an active account
|
|
|
274
311
|
|
|
275
312
|
Tell the user: "Setting up your Google Cloud project, APIs, and credentials..."
|
|
276
313
|
|
|
314
|
+
### Primary: `gws auth setup`
|
|
315
|
+
|
|
277
316
|
```bash
|
|
278
317
|
gws auth setup
|
|
279
318
|
```
|
|
@@ -286,7 +325,52 @@ This command automates:
|
|
|
286
325
|
|
|
287
326
|
Wait for the command to complete. It may have interactive prompts — let them run in the terminal and the user can respond if needed.
|
|
288
327
|
|
|
289
|
-
|
|
328
|
+
If `gws auth setup` **succeeds**, note the **project ID** from the output and continue to **CLI Step 5**.
|
|
329
|
+
|
|
330
|
+
### Fallback: `gcloud` CLI + browser automation
|
|
331
|
+
|
|
332
|
+
If `gws auth setup` **fails** (common with personal Google accounts due to workspace-admin scope errors), use `gcloud` CLI and browser automation instead.
|
|
333
|
+
|
|
334
|
+
**Step 4f-1: Create GCP project**
|
|
335
|
+
|
|
336
|
+
Generate a unique suffix (4-6 random alphanumeric characters):
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
gcloud projects create vellum-assistant-SUFFIX --name="Vellum Assistant"
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
If the project already exists, use it. Note the **project ID**.
|
|
343
|
+
|
|
344
|
+
**Step 4f-2: Enable APIs**
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
gcloud services enable gmail.googleapis.com --project=PROJECT_ID
|
|
348
|
+
gcloud services enable calendar-json.googleapis.com --project=PROJECT_ID
|
|
349
|
+
gcloud services enable people.googleapis.com --project=PROJECT_ID
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
**Step 4f-3: Configure OAuth consent screen via browser**
|
|
353
|
+
|
|
354
|
+
Navigate to `https://console.cloud.google.com/apis/credentials/consent?project=PROJECT_ID`.
|
|
355
|
+
|
|
356
|
+
Take a screenshot and snapshot, then:
|
|
357
|
+
|
|
358
|
+
1. Select **"External"** user type, click **Create**
|
|
359
|
+
2. Fill in the app registration form:
|
|
360
|
+
- App name: **"Vellum Assistant"**
|
|
361
|
+
- User support email: select the authenticated email from the dropdown
|
|
362
|
+
- Developer contact email: type the same email
|
|
363
|
+
- Click **Save and Continue**
|
|
364
|
+
3. On the Scopes page, click **Save and Continue** (scopes are not needed for test-mode apps)
|
|
365
|
+
4. On the Test users page:
|
|
366
|
+
- Click **+ Add Users**
|
|
367
|
+
- Enter the authenticated email address
|
|
368
|
+
- Click **Add**, then **Save and Continue**
|
|
369
|
+
5. On the Summary page, click **Back to Dashboard**
|
|
370
|
+
|
|
371
|
+
**Verify:** Take a screenshot. The consent screen should show "Testing" publishing status.
|
|
372
|
+
|
|
373
|
+
After the fallback completes, skip CLI Step 5 (APIs already enabled above) and CLI Step 5c (test user already added above) — continue directly to **CLI Step 6**.
|
|
290
374
|
|
|
291
375
|
## CLI Step 5: Enable Additional APIs
|
|
292
376
|
|
|
@@ -299,56 +383,73 @@ gcloud services enable people.googleapis.com --project=PROJECT_ID
|
|
|
299
383
|
|
|
300
384
|
If either command reports the API is already enabled, that's fine — continue.
|
|
301
385
|
|
|
302
|
-
## CLI Step
|
|
386
|
+
## CLI Step 5c: Add Test User
|
|
303
387
|
|
|
304
|
-
`gws auth setup`
|
|
388
|
+
`gws auth setup` does not add test users to the consent screen, and there is no CLI/API for it. Use browser automation to add the authenticated email as a test user.
|
|
305
389
|
|
|
306
|
-
|
|
307
|
-
>
|
|
308
|
-
> Open: `https://console.cloud.google.com/apis/credentials/consent/edit?project=PROJECT_ID`
|
|
309
|
-
>
|
|
310
|
-
> 1. Click through to the **Scopes** page (click **Save and Continue** on the first page)
|
|
311
|
-
> 2. Click **Add or Remove Scopes** and ensure all of these are present:
|
|
312
|
-
> - `https://www.googleapis.com/auth/gmail.readonly`
|
|
313
|
-
> - `https://www.googleapis.com/auth/gmail.modify`
|
|
314
|
-
> - `https://www.googleapis.com/auth/gmail.send`
|
|
315
|
-
> - `https://www.googleapis.com/auth/calendar.readonly`
|
|
316
|
-
> - `https://www.googleapis.com/auth/calendar.events`
|
|
317
|
-
> - `https://www.googleapis.com/auth/userinfo.email`
|
|
318
|
-
> - `https://www.googleapis.com/auth/contacts.readonly`
|
|
319
|
-
> 3. Click **Update**, then **Save and Continue** through the remaining pages
|
|
390
|
+
Navigate to `https://console.cloud.google.com/apis/credentials/consent?project=PROJECT_ID`.
|
|
320
391
|
|
|
321
|
-
|
|
392
|
+
Take a screenshot and snapshot. Find the **Test users** section (you may need to click **Edit App** or navigate to the consent screen edit flow):
|
|
322
393
|
|
|
323
|
-
|
|
394
|
+
1. Find and click the option to add test users (e.g., **+ Add Users** button)
|
|
395
|
+
2. Enter the authenticated email address (from `gcloud auth list`)
|
|
396
|
+
3. Click **Add** or **Save**
|
|
324
397
|
|
|
325
|
-
|
|
398
|
+
**Verify:** Take a screenshot confirming the email appears in the test users list.
|
|
326
399
|
|
|
327
|
-
|
|
400
|
+
If the user is already listed as a test user, skip this step.
|
|
328
401
|
|
|
329
|
-
|
|
402
|
+
## CLI Step 6: Create OAuth Credentials via Browser
|
|
403
|
+
|
|
404
|
+
**Goal:** Create a Desktop OAuth client in GCP Console and capture both credentials.
|
|
405
|
+
|
|
406
|
+
Navigate to `https://console.cloud.google.com/apis/credentials?project=PROJECT_ID` (substitute the actual project ID from step 4).
|
|
407
|
+
|
|
408
|
+
Take a screenshot and snapshot to check the page state:
|
|
409
|
+
|
|
410
|
+
- **Sign-in page:** Tell the user: "Please sign in to your Google account in the browser." Then auto-detect sign-in completion by polling screenshots every 5-10 seconds. Once signed in, continue.
|
|
411
|
+
- **Already signed in / Credentials page loaded:** Continue immediately.
|
|
412
|
+
|
|
413
|
+
### Step 6a: Create the OAuth Client
|
|
414
|
+
|
|
415
|
+
1. Take a screenshot and snapshot. Find and click **+ Create Credentials**, then select **OAuth client ID**.
|
|
416
|
+
2. On the creation form:
|
|
417
|
+
- Application type: Select **"Desktop app"**
|
|
418
|
+
- Name: **"Vellum Assistant"**
|
|
419
|
+
- Click **Create**
|
|
420
|
+
3. **Verify:** Take a screenshot. A dialog should appear showing both the **Client ID** and **Client Secret**.
|
|
421
|
+
|
|
422
|
+
### Step 6b: Extract Client ID
|
|
423
|
+
|
|
424
|
+
Use `browser_extract` to read the Client ID from the creation dialog. It looks like `123456789-xxxxx.apps.googleusercontent.com`.
|
|
425
|
+
|
|
426
|
+
Store it immediately:
|
|
330
427
|
|
|
331
428
|
```
|
|
332
|
-
credential_store
|
|
429
|
+
credential_store store:
|
|
333
430
|
service: "integration:gmail"
|
|
334
431
|
field: "client_id"
|
|
335
|
-
|
|
336
|
-
description: "Copy the Client ID from the setup output or GCP Console. It looks like 123456789-xxxxx.apps.googleusercontent.com"
|
|
337
|
-
placeholder: "xxxxx.apps.googleusercontent.com"
|
|
432
|
+
value: "<the extracted Client ID>"
|
|
338
433
|
```
|
|
339
434
|
|
|
340
|
-
|
|
435
|
+
### Step 6c: Capture Client Secret via Secure Prompt
|
|
436
|
+
|
|
437
|
+
The Client Secret is visible in the same dialog. Do NOT attempt to read it from the screenshot — use a secure prompt so the user copies it accurately.
|
|
438
|
+
|
|
439
|
+
Tell the user: "Your OAuth credentials have been created! Please copy the **Client Secret** shown in the dialog and paste it into the secure prompt below."
|
|
341
440
|
|
|
342
441
|
```
|
|
343
442
|
credential_store prompt:
|
|
344
443
|
service: "integration:gmail"
|
|
345
444
|
field: "client_secret"
|
|
346
445
|
label: "Google OAuth Client Secret"
|
|
347
|
-
description: "Copy the Client Secret from the
|
|
446
|
+
description: "Copy the Client Secret from the dialog on screen. It starts with GOCSPX-"
|
|
348
447
|
placeholder: "GOCSPX-..."
|
|
349
448
|
```
|
|
350
449
|
|
|
351
|
-
Wait for
|
|
450
|
+
**CRITICAL — do NOT dismiss the creation dialog before the user has copied the secret.** Wait for the secure prompt to be completed before clicking OK or navigating away.
|
|
451
|
+
|
|
452
|
+
After the secret is stored, you may close the dialog.
|
|
352
453
|
|
|
353
454
|
## CLI Step 7: Authorize
|
|
354
455
|
|
|
@@ -11,6 +11,7 @@ import type { MessagingProvider } from "../../../../messaging/provider.js";
|
|
|
11
11
|
import {
|
|
12
12
|
getConnectedProviders,
|
|
13
13
|
getMessagingProvider,
|
|
14
|
+
isPlatformEnabled,
|
|
14
15
|
} from "../../../../messaging/registry.js";
|
|
15
16
|
import { withValidToken } from "../../../../security/token-manager.js";
|
|
16
17
|
import type { ToolExecutionResult } from "../../../../tools/types.js";
|
|
@@ -32,7 +33,9 @@ export function err(message: string): ToolExecutionResult {
|
|
|
32
33
|
export function resolveProvider(platformInput?: string): MessagingProvider {
|
|
33
34
|
if (platformInput) return getMessagingProvider(platformInput);
|
|
34
35
|
|
|
35
|
-
const connected = getConnectedProviders()
|
|
36
|
+
const connected = getConnectedProviders().filter((p) =>
|
|
37
|
+
isPlatformEnabled(p.id),
|
|
38
|
+
);
|
|
36
39
|
if (connected.length === 1) return connected[0];
|
|
37
40
|
if (connected.length === 0) {
|
|
38
41
|
throw new Error(
|
|
@@ -17,10 +17,17 @@ vellum integrations twilio config --json
|
|
|
17
17
|
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
|
|
18
18
|
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" -H "Content-Type: application/json" \
|
|
19
19
|
-d '{"accountSid":"ACxxx","authToken":"xxx"}'
|
|
20
|
-
# 3.
|
|
21
|
-
|
|
20
|
+
# 3. Get credential ID and Account SID for proxied calls
|
|
21
|
+
credential_store action=list # → note credential_id for twilio/account_sid
|
|
22
|
+
vellum integrations twilio config --json | jq -r '.accountSid'
|
|
23
|
+
# 4. Search and provision via Twilio API (proxy injects auth automatically)
|
|
24
|
+
# bash network_mode=proxied credential_ids=["<cred_id>"]
|
|
25
|
+
curl -s "https://api.twilio.com/2010-04-01/Accounts/<SID>/AvailablePhoneNumbers/US/Local.json?SmsEnabled=true&VoiceEnabled=true"
|
|
26
|
+
curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/<SID>/IncomingPhoneNumbers.json" -d "PhoneNumber=+1xxx"
|
|
27
|
+
# 5. Assign locally (saves to config + sets up webhooks)
|
|
28
|
+
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/assign" \
|
|
22
29
|
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" -H "Content-Type: application/json" \
|
|
23
|
-
-d '{"
|
|
30
|
+
-d '{"phoneNumber":"+1xxx"}'
|
|
24
31
|
```
|
|
25
32
|
|
|
26
33
|
For voice call setup after Twilio is configured, use `phone-calls` + `call_start`.
|
|
@@ -30,11 +37,11 @@ For voice call setup after Twilio is configured, use `phone-calls` + `call_start
|
|
|
30
37
|
This skill manages the full Twilio lifecycle:
|
|
31
38
|
|
|
32
39
|
- **Credential storage** — Account SID and Auth Token
|
|
33
|
-
- **
|
|
40
|
+
- **Direct Twilio API access** — Search and purchase numbers via proxied calls to the Twilio REST API (the proxy injects authentication automatically)
|
|
34
41
|
- **Phone number assignment** — Assign an existing Twilio number to the assistant
|
|
35
42
|
- **Status checking** — Verify credentials and assigned number
|
|
36
43
|
|
|
37
|
-
|
|
44
|
+
Number search and purchase use proxied calls to the Twilio REST API (`bash` with `network_mode: "proxied"`). Local bookkeeping (assign, webhook sync) uses gateway control-plane endpoints. Status/list retrieval uses `vellum integrations ...` CLI reads.
|
|
38
45
|
|
|
39
46
|
### Multi-Assistant Setups
|
|
40
47
|
|
|
@@ -49,7 +56,7 @@ In a multi-assistant environment (multiple assistants sharing the same runtime),
|
|
|
49
56
|
|
|
50
57
|
- `GET /v1/integrations/twilio/config` — Returns the phone number assigned to the specified assistant.
|
|
51
58
|
- `POST /v1/integrations/twilio/numbers/assign` — Assigns a phone number to a specific assistant via the per-assistant mapping.
|
|
52
|
-
- `POST /v1/integrations/twilio/numbers/provision` —
|
|
59
|
+
- `POST /v1/integrations/twilio/numbers/provision` — Legacy convenience endpoint that provisions a new number and assigns it to the specified assistant. The main flow now uses proxied Twilio API calls (search + purchase) followed by the assign endpoint.
|
|
53
60
|
- `GET /v1/integrations/twilio/numbers` — Lists all phone numbers on the shared Twilio account (uses global credentials).
|
|
54
61
|
|
|
55
62
|
Include `assistantId` as a query parameter in assistant-scoped requests whenever:
|
|
@@ -110,19 +117,61 @@ The assistant needs a phone number to make calls and send SMS. There are two pat
|
|
|
110
117
|
|
|
111
118
|
If the user wants to buy a new number through Twilio:
|
|
112
119
|
|
|
120
|
+
**3a. Get the credential ID and Account SID:**
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
credential_store action=list
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Find the entry with `service: "twilio"` and `field: "account_sid"`. Note its `credential_id`.
|
|
127
|
+
|
|
128
|
+
Then retrieve the Account SID (needed for Twilio URL paths):
|
|
129
|
+
|
|
113
130
|
```bash
|
|
114
|
-
|
|
131
|
+
vellum integrations twilio config --json | jq -r '.accountSid'
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**3b. Search for available numbers (proxied Twilio API):**
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
bash:
|
|
138
|
+
network_mode: proxied
|
|
139
|
+
credential_ids: ["<credential_id from 3a>"]
|
|
140
|
+
command: |
|
|
141
|
+
curl -s "https://api.twilio.com/2010-04-01/Accounts/<ACCOUNT_SID>/AvailablePhoneNumbers/US/Local.json?SmsEnabled=true&VoiceEnabled=true&AreaCode=415"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- `AreaCode` is optional — ask the user if they have a preferred area code
|
|
145
|
+
- Replace `US` with a different ISO 3166-1 alpha-2 country code if the user wants a non-US number
|
|
146
|
+
- The proxy automatically injects `Authorization: Basic <credentials>` — do not include an Authorization header
|
|
147
|
+
|
|
148
|
+
The response contains an `available_phone_numbers` array. Present the first few options to the user with their `phone_number` and `friendly_name`.
|
|
149
|
+
|
|
150
|
+
**3c. Purchase the chosen number (proxied Twilio API):**
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
bash:
|
|
154
|
+
network_mode: proxied
|
|
155
|
+
credential_ids: ["<credential_id from 3a>"]
|
|
156
|
+
command: |
|
|
157
|
+
curl -s -X POST "https://api.twilio.com/2010-04-01/Accounts/<ACCOUNT_SID>/IncomingPhoneNumbers.json" \
|
|
158
|
+
-d "PhoneNumber=+14155551234"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The response includes the purchased number's `phone_number` and `sid`.
|
|
162
|
+
|
|
163
|
+
**3d. Assign locally (saves to config + sets up webhooks):**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/numbers/assign" \
|
|
115
167
|
-H "Authorization: Bearer $GATEWAY_AUTH_TOKEN" \
|
|
116
168
|
-H "Content-Type: application/json" \
|
|
117
|
-
-d '{"
|
|
169
|
+
-d '{"phoneNumber":"+14155551234"}'
|
|
118
170
|
```
|
|
119
171
|
|
|
120
|
-
|
|
121
|
-
- `country` defaults to `"US"` — ask if they want a different country (ISO 3166-1 alpha-2)
|
|
172
|
+
This persists the number to secure storage and config, and configures Twilio webhooks (voice, status callback, SMS) if a public ingress URL is available. The response includes the new `phoneNumber`. No separate assign call is needed.
|
|
122
173
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
**Webhook auto-configuration:** When `ingress.publicBaseUrl` is configured, the endpoint automatically sets the following webhooks on the Twilio phone number:
|
|
174
|
+
**Webhook auto-configuration:** When `ingress.publicBaseUrl` is configured, the assign endpoint automatically sets the following webhooks on the Twilio phone number:
|
|
126
175
|
|
|
127
176
|
- Voice webhook: `{publicBaseUrl}/webhooks/twilio/voice`
|
|
128
177
|
- Voice status callback: `{publicBaseUrl}/webhooks/twilio/status`
|
|
@@ -134,13 +183,17 @@ If ingress is not yet configured, webhook setup is skipped gracefully — the nu
|
|
|
134
183
|
|
|
135
184
|
### Option B: Assign an Existing Number
|
|
136
185
|
|
|
137
|
-
If the user already has a Twilio phone number, first list available numbers:
|
|
186
|
+
If the user already has a Twilio phone number, first get the credential ID and Account SID (same as Option A, step 3a), then list available numbers:
|
|
138
187
|
|
|
139
|
-
```
|
|
140
|
-
|
|
188
|
+
```
|
|
189
|
+
bash:
|
|
190
|
+
network_mode: proxied
|
|
191
|
+
credential_ids: ["<credential_id>"]
|
|
192
|
+
command: |
|
|
193
|
+
curl -s "https://api.twilio.com/2010-04-01/Accounts/<ACCOUNT_SID>/IncomingPhoneNumbers.json"
|
|
141
194
|
```
|
|
142
195
|
|
|
143
|
-
The response includes
|
|
196
|
+
The response includes an `incoming_phone_numbers` array with each number's `phone_number`, `friendly_name`, and `capabilities`. Present these to the user and let them choose.
|
|
144
197
|
|
|
145
198
|
Then assign the chosen number:
|
|
146
199
|
|
|
@@ -20,6 +20,7 @@ import * as appFileEdit from "./bundled-skills/app-builder/tools/app-file-edit.j
|
|
|
20
20
|
import * as appFileList from "./bundled-skills/app-builder/tools/app-file-list.js";
|
|
21
21
|
import * as appFileRead from "./bundled-skills/app-builder/tools/app-file-read.js";
|
|
22
22
|
import * as appFileWrite from "./bundled-skills/app-builder/tools/app-file-write.js";
|
|
23
|
+
import * as appGenerateIcon from "./bundled-skills/app-builder/tools/app-generate-icon.js";
|
|
23
24
|
import * as appList from "./bundled-skills/app-builder/tools/app-list.js";
|
|
24
25
|
import * as appQuery from "./bundled-skills/app-builder/tools/app-query.js";
|
|
25
26
|
import * as appUpdate from "./bundled-skills/app-builder/tools/app-update.js";
|
|
@@ -190,6 +191,7 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
|
|
|
190
191
|
["app-builder:tools/app-file-read.ts", appFileRead],
|
|
191
192
|
["app-builder:tools/app-file-edit.ts", appFileEdit],
|
|
192
193
|
["app-builder:tools/app-file-write.ts", appFileWrite],
|
|
194
|
+
["app-builder:tools/app-generate-icon.ts", appGenerateIcon],
|
|
193
195
|
|
|
194
196
|
// browser
|
|
195
197
|
["browser:tools/browser-navigate.ts", browserNavigate],
|
|
@@ -252,6 +252,12 @@ export const SmsConfigSchema = z.object({
|
|
|
252
252
|
.optional(),
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
export const WhatsAppConfigSchema = z.object({
|
|
256
|
+
phoneNumber: z
|
|
257
|
+
.string({ error: "whatsapp.phoneNumber must be a string" })
|
|
258
|
+
.default(""),
|
|
259
|
+
});
|
|
260
|
+
|
|
255
261
|
export const IngressWebhookConfigSchema = z.object({
|
|
256
262
|
secret: z
|
|
257
263
|
.string({ error: "ingress.webhook.secret must be a string" })
|
|
@@ -392,6 +398,7 @@ export type ContextOverflowRecoveryConfig = z.infer<
|
|
|
392
398
|
export type ContextWindowConfig = z.infer<typeof ContextWindowConfigSchema>;
|
|
393
399
|
export type ModelPricingOverride = z.infer<typeof ModelPricingOverrideSchema>;
|
|
394
400
|
export type SmsConfig = z.infer<typeof SmsConfigSchema>;
|
|
401
|
+
export type WhatsAppConfig = z.infer<typeof WhatsAppConfigSchema>;
|
|
395
402
|
export type IngressWebhookConfig = z.infer<typeof IngressWebhookConfigSchema>;
|
|
396
403
|
export type IngressRateLimitConfig = z.infer<
|
|
397
404
|
typeof IngressRateLimitConfigSchema
|
|
@@ -47,6 +47,22 @@
|
|
|
47
47
|
"key": "feature_flags.messaging.enabled",
|
|
48
48
|
"label": "Messaging",
|
|
49
49
|
"description": "Enable messaging skill section in the system prompt",
|
|
50
|
+
"defaultEnabled": true
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "messaging-gmail",
|
|
54
|
+
"scope": "assistant",
|
|
55
|
+
"key": "feature_flags.messaging.gmail.enabled",
|
|
56
|
+
"label": "Messaging: Gmail",
|
|
57
|
+
"description": "Allow messaging tools to operate on the Gmail platform",
|
|
58
|
+
"defaultEnabled": false
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "messaging-telegram",
|
|
62
|
+
"scope": "assistant",
|
|
63
|
+
"key": "feature_flags.messaging.telegram.enabled",
|
|
64
|
+
"label": "Messaging: Telegram",
|
|
65
|
+
"description": "Allow messaging tools to operate on the Telegram platform",
|
|
50
66
|
"defaultEnabled": false
|
|
51
67
|
},
|
|
52
68
|
{
|
package/src/config/loader.ts
CHANGED
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
getWorkspaceConfigPath,
|
|
20
20
|
migrateToDataLayout,
|
|
21
21
|
migrateToWorkspaceLayout,
|
|
22
|
+
readLockfile,
|
|
23
|
+
writeLockfile,
|
|
22
24
|
} from "../util/platform.js";
|
|
23
25
|
import { AssistantConfigSchema } from "./schema.js";
|
|
24
26
|
import type { AssistantConfig } from "./types.js";
|
|
@@ -422,6 +424,30 @@ export function saveRawConfig(config: Record<string, unknown>): void {
|
|
|
422
424
|
cached = null; // invalidate cache
|
|
423
425
|
}
|
|
424
426
|
|
|
427
|
+
/**
|
|
428
|
+
* Sync client-relevant config values (e.g. platform.baseUrl) to the lockfile
|
|
429
|
+
* so external tools (e.g. vel) can discover them without importing the full
|
|
430
|
+
* config schema. Mirrors the behaviour of `syncConfigToLockfile` in the
|
|
431
|
+
* lightweight CLI (`cli/src/lib/assistant-config.ts`).
|
|
432
|
+
*/
|
|
433
|
+
export function syncConfigToLockfile(): void {
|
|
434
|
+
const configPath = getWorkspaceConfigPath();
|
|
435
|
+
if (!existsSync(configPath)) return;
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8")) as Record<
|
|
439
|
+
string,
|
|
440
|
+
unknown
|
|
441
|
+
>;
|
|
442
|
+
const platform = raw.platform as Record<string, unknown> | undefined;
|
|
443
|
+
const data = readLockfile() ?? {};
|
|
444
|
+
data.platformBaseUrl = (platform?.baseUrl as string) || undefined;
|
|
445
|
+
writeLockfile(data);
|
|
446
|
+
} catch {
|
|
447
|
+
// Config file unreadable — skip sync
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
425
451
|
export function getNestedValue(
|
|
426
452
|
obj: Record<string, unknown>,
|
|
427
453
|
path: string,
|
package/src/config/schema.ts
CHANGED
|
@@ -49,6 +49,7 @@ export type {
|
|
|
49
49
|
ThinkingConfig,
|
|
50
50
|
TimeoutConfig,
|
|
51
51
|
UiConfig,
|
|
52
|
+
WhatsAppConfig,
|
|
52
53
|
} from "./core-schema.js";
|
|
53
54
|
export {
|
|
54
55
|
AuditLogConfigSchema,
|
|
@@ -69,6 +70,7 @@ export {
|
|
|
69
70
|
ThinkingConfigSchema,
|
|
70
71
|
TimeoutConfigSchema,
|
|
71
72
|
UiConfigSchema,
|
|
73
|
+
WhatsAppConfigSchema,
|
|
72
74
|
} from "./core-schema.js";
|
|
73
75
|
export type { ElevenLabsConfig } from "./elevenlabs-schema.js";
|
|
74
76
|
export {
|
|
@@ -161,6 +163,7 @@ import {
|
|
|
161
163
|
ThinkingConfigSchema,
|
|
162
164
|
TimeoutConfigSchema,
|
|
163
165
|
UiConfigSchema,
|
|
166
|
+
WhatsAppConfigSchema,
|
|
164
167
|
} from "./core-schema.js";
|
|
165
168
|
import { ElevenLabsConfigSchema } from "./elevenlabs-schema.js";
|
|
166
169
|
import { McpConfigSchema } from "./mcp-schema.js";
|
|
@@ -261,6 +264,7 @@ export const AssistantConfigSchema = z
|
|
|
261
264
|
ElevenLabsConfigSchema.parse({}),
|
|
262
265
|
),
|
|
263
266
|
sms: SmsConfigSchema.default(SmsConfigSchema.parse({})),
|
|
267
|
+
whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
|
|
264
268
|
ingress: IngressConfigSchema,
|
|
265
269
|
platform: PlatformConfigSchema.default(PlatformConfigSchema.parse({})),
|
|
266
270
|
daemon: DaemonConfigSchema.default(DaemonConfigSchema.parse({})),
|
|
@@ -33,19 +33,6 @@ export function skillFlagKey(skillId: string): string {
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* @deprecated Use `isAssistantFeatureFlagEnabled` from `./assistant-feature-flags.js` instead.
|
|
38
|
-
*
|
|
39
|
-
* Thin backward-compatible wrapper that delegates to the canonical resolver.
|
|
40
|
-
* Kept to avoid breaking existing call sites during migration.
|
|
41
|
-
*/
|
|
42
|
-
export function isSkillFeatureEnabled(
|
|
43
|
-
skillId: string,
|
|
44
|
-
config: AssistantConfig,
|
|
45
|
-
): boolean {
|
|
46
|
-
return isAssistantFeatureFlagEnabled(skillFlagKey(skillId), config);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
36
|
export function resolveSkillStates(
|
|
50
37
|
catalog: SkillSummary[],
|
|
51
38
|
config: AssistantConfig,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { CLI_HELP_REFERENCE } from "../cli/reference.js";
|
|
4
5
|
import { listCredentialMetadata } from "../tools/credentials/metadata-store.js";
|
|
5
6
|
import { resolveBundledDir } from "../util/bundled-asset.js";
|
|
6
7
|
import { getLogger } from "../util/logger.js";
|
|
@@ -20,6 +21,13 @@ const log = getLogger("system-prompt");
|
|
|
20
21
|
|
|
21
22
|
const PROMPT_FILES = ["SOUL.md", "IDENTITY.md", "USER.md"] as const;
|
|
22
23
|
|
|
24
|
+
let cachedCliHelp: string | undefined;
|
|
25
|
+
|
|
26
|
+
/** @internal Reset the CLI help cache — exposed for testing only. */
|
|
27
|
+
export function _resetCliHelpCache(): void {
|
|
28
|
+
cachedCliHelp = undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
/**
|
|
24
32
|
* Copy template prompt files into the data directory if they don't already exist.
|
|
25
33
|
* Called once during daemon startup so users always have discoverable files to edit.
|
|
@@ -149,6 +157,7 @@ export function buildSystemPrompt(): string {
|
|
|
149
157
|
}
|
|
150
158
|
if (getIsContainerized()) parts.push(buildContainerizedSection());
|
|
151
159
|
parts.push(buildConfigSection());
|
|
160
|
+
parts.push(buildCliReferenceSection());
|
|
152
161
|
parts.push(buildPostToolResponseSection());
|
|
153
162
|
parts.push(buildExternalCommsIdentitySection());
|
|
154
163
|
parts.push(buildChannelAwarenessSection());
|
|
@@ -778,6 +787,24 @@ function buildConfigSection(): string {
|
|
|
778
787
|
].join("\n");
|
|
779
788
|
}
|
|
780
789
|
|
|
790
|
+
export function buildCliReferenceSection(): string {
|
|
791
|
+
if (cachedCliHelp === undefined) {
|
|
792
|
+
cachedCliHelp = CLI_HELP_REFERENCE.trim();
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return [
|
|
796
|
+
"## Assistant CLI",
|
|
797
|
+
"",
|
|
798
|
+
"The `assistant` CLI is installed on the user's machine and available via `bash`.",
|
|
799
|
+
"",
|
|
800
|
+
"```",
|
|
801
|
+
cachedCliHelp,
|
|
802
|
+
"```",
|
|
803
|
+
"",
|
|
804
|
+
"Run `assistant <command> --help` for detailed help on any subcommand.",
|
|
805
|
+
].join("\n");
|
|
806
|
+
}
|
|
807
|
+
|
|
781
808
|
/**
|
|
782
809
|
* Strip lines starting with `_` (comment convention for prompt .md files)
|
|
783
810
|
* and collapse any resulting consecutive blank lines.
|
|
@@ -623,6 +623,31 @@ export function mergeContacts(
|
|
|
623
623
|
return getContactInternal(keepId)!;
|
|
624
624
|
}
|
|
625
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Delete a contact by ID. Guardians cannot be deleted as a safety guard.
|
|
628
|
+
* Associated contactChannels and assistantContactMetadata rows are
|
|
629
|
+
* cascade-deleted by the DB schema's onDelete constraints.
|
|
630
|
+
*/
|
|
631
|
+
export function deleteContact(
|
|
632
|
+
contactId: string,
|
|
633
|
+
): "ok" | "not_found" | "is_guardian" {
|
|
634
|
+
const db = getDb();
|
|
635
|
+
|
|
636
|
+
const contact = db
|
|
637
|
+
.select()
|
|
638
|
+
.from(contacts)
|
|
639
|
+
.where(eq(contacts.id, contactId))
|
|
640
|
+
.get();
|
|
641
|
+
|
|
642
|
+
if (!contact) return "not_found";
|
|
643
|
+
if (contact.role === "guardian") return "is_guardian";
|
|
644
|
+
|
|
645
|
+
db.delete(contacts).where(eq(contacts.id, contactId)).run();
|
|
646
|
+
|
|
647
|
+
emitContactChange();
|
|
648
|
+
return "ok";
|
|
649
|
+
}
|
|
650
|
+
|
|
626
651
|
/**
|
|
627
652
|
* Find a contact by a specific channel address. Returns null if not found.
|
|
628
653
|
*/
|
|
@@ -103,7 +103,7 @@ export class ComputerUseSession {
|
|
|
103
103
|
resolve: (result: ToolExecutionResult) => void;
|
|
104
104
|
}
|
|
105
105
|
>();
|
|
106
|
-
|
|
106
|
+
/** @internal */ surfaceState = new Map<
|
|
107
107
|
string,
|
|
108
108
|
{ surfaceType: SurfaceType; data: SurfaceData; title?: string }
|
|
109
109
|
>();
|