@vellumai/assistant 0.4.2 → 0.4.4
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/.env.example +3 -0
- package/ARCHITECTURE.md +124 -10
- package/README.md +43 -35
- package/docs/trusted-contact-access.md +20 -0
- package/package.json +1 -1
- package/scripts/ipc/generate-swift.ts +1 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +58 -120
- package/src/__tests__/access-request-decision.test.ts +0 -1
- package/src/__tests__/actor-token-service.test.ts +1099 -0
- package/src/__tests__/agent-loop.test.ts +51 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -5
- package/src/__tests__/assistant-id-boundary-guard.test.ts +415 -0
- package/src/__tests__/call-controller.test.ts +49 -0
- package/src/__tests__/call-pointer-message-composer.test.ts +171 -0
- package/src/__tests__/call-pointer-messages.test.ts +93 -3
- package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +42 -0
- package/src/__tests__/call-routes-http.test.ts +0 -25
- package/src/__tests__/callback-handoff-copy.test.ts +186 -0
- package/src/__tests__/channel-approval-routes.test.ts +133 -12
- package/src/__tests__/channel-guardian.test.ts +0 -86
- package/src/__tests__/channel-readiness-service.test.ts +10 -16
- package/src/__tests__/checker.test.ts +33 -12
- package/src/__tests__/config-schema.test.ts +6 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +410 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +256 -0
- package/src/__tests__/conversation-routes.test.ts +12 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/daemon-server-session-init.test.ts +4 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +39 -13
- package/src/__tests__/guardian-dispatch.test.ts +8 -0
- package/src/__tests__/guardian-outbound-http.test.ts +4 -5
- package/src/__tests__/guardian-question-mode.test.ts +200 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +178 -0
- package/src/__tests__/guardian-routing-state.test.ts +525 -0
- package/src/__tests__/handle-user-message-secret-resume.test.ts +2 -0
- package/src/__tests__/handlers-telegram-config.test.ts +0 -83
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +55 -0
- package/src/__tests__/headless-browser-navigate.test.ts +2 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +55 -0
- package/src/__tests__/ipc-snapshot.test.ts +18 -51
- package/src/__tests__/non-member-access-request.test.ts +159 -9
- package/src/__tests__/notification-decision-fallback.test.ts +129 -4
- package/src/__tests__/notification-decision-strategy.test.ts +106 -2
- package/src/__tests__/notification-guardian-path.test.ts +3 -0
- package/src/__tests__/recording-intent-handler.test.ts +1 -0
- package/src/__tests__/relay-server.test.ts +1475 -33
- package/src/__tests__/send-endpoint-busy.test.ts +5 -0
- package/src/__tests__/session-agent-loop.test.ts +1 -0
- package/src/__tests__/session-confirmation-signals.test.ts +523 -0
- package/src/__tests__/session-init.benchmark.test.ts +0 -2
- package/src/__tests__/session-runtime-assembly.test.ts +4 -1
- package/src/__tests__/session-surfaces-task-progress.test.ts +44 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +81 -2
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -1
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +21 -2
- package/src/__tests__/tool-grant-request-escalation.test.ts +333 -27
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +678 -0
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1064 -0
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +11 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/trusted-contact-verification.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +2 -13
- package/src/__tests__/twilio-routes.test.ts +4 -3
- package/src/__tests__/update-bulletin.test.ts +0 -1
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-decision-primitive.ts +12 -3
- package/src/approvals/guardian-request-resolvers.ts +169 -11
- package/src/calls/call-constants.ts +29 -0
- package/src/calls/call-controller.ts +11 -3
- package/src/calls/call-domain.ts +33 -11
- package/src/calls/call-pointer-message-composer.ts +154 -0
- package/src/calls/call-pointer-messages.ts +106 -27
- package/src/calls/guardian-dispatch.ts +4 -2
- package/src/calls/relay-server.ts +921 -112
- package/src/calls/twilio-config.ts +4 -11
- package/src/calls/twilio-routes.ts +4 -6
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-session-bridge.ts +4 -3
- package/src/cli/core-commands.ts +7 -4
- package/src/cli.ts +5 -4
- package/src/config/bundled-skills/agentmail/SKILL.md +4 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +309 -10
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -1
- package/src/config/bundled-skills/email-setup/SKILL.md +1 -1
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +105 -81
- package/src/config/bundled-skills/messaging/SKILL.md +61 -12
- package/src/config/bundled-skills/messaging/TOOLS.json +58 -0
- package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +6 -1
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +35 -0
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +52 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +30 -39
- package/src/config/bundled-skills/twitter/SKILL.md +3 -3
- package/src/config/bundled-skills/vercel-token-setup/SKILL.md +215 -0
- package/src/config/calls-schema.ts +36 -0
- package/src/config/env.ts +22 -0
- package/src/config/feature-flag-registry.json +8 -8
- package/src/config/schema.ts +2 -2
- package/src/config/skills.ts +11 -0
- package/src/config/system-prompt.ts +11 -1
- package/src/config/templates/SOUL.md +2 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +71 -82
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +8 -1
- package/src/config/vellum-skills/twilio-setup/SKILL.md +88 -73
- package/src/daemon/call-pointer-generators.ts +59 -0
- package/src/daemon/computer-use-session.ts +2 -5
- package/src/daemon/handlers/apps.ts +76 -20
- package/src/daemon/handlers/config-channels.ts +9 -61
- package/src/daemon/handlers/config-inbox.ts +11 -3
- package/src/daemon/handlers/config-ingress.ts +28 -3
- package/src/daemon/handlers/config-telegram.ts +12 -0
- package/src/daemon/handlers/config.ts +2 -6
- package/src/daemon/handlers/index.ts +2 -1
- package/src/daemon/handlers/pairing.ts +2 -0
- package/src/daemon/handlers/publish.ts +11 -46
- package/src/daemon/handlers/sessions.ts +59 -5
- package/src/daemon/handlers/shared.ts +17 -2
- package/src/daemon/ipc-contract/apps.ts +1 -0
- package/src/daemon/ipc-contract/inbox.ts +4 -0
- package/src/daemon/ipc-contract/integrations.ts +1 -97
- package/src/daemon/ipc-contract/messages.ts +47 -1
- package/src/daemon/ipc-contract/notifications.ts +11 -0
- package/src/daemon/ipc-contract-inventory.json +2 -4
- package/src/daemon/lifecycle.ts +17 -0
- package/src/daemon/server.ts +16 -2
- package/src/daemon/session-agent-loop-handlers.ts +20 -0
- package/src/daemon/session-agent-loop.ts +24 -12
- package/src/daemon/session-lifecycle.ts +1 -1
- package/src/daemon/session-process.ts +11 -1
- package/src/daemon/session-runtime-assembly.ts +6 -1
- package/src/daemon/session-surfaces.ts +32 -3
- package/src/daemon/session.ts +88 -1
- package/src/daemon/tool-side-effects.ts +22 -0
- package/src/home-base/prebuilt/brain-graph.html +1483 -0
- package/src/home-base/prebuilt/index.html +40 -0
- package/src/inbound/platform-callback-registration.ts +157 -0
- package/src/memory/canonical-guardian-store.ts +1 -1
- package/src/memory/conversation-crud.ts +2 -1
- package/src/memory/conversation-title-service.ts +16 -2
- package/src/memory/db-init.ts +8 -0
- package/src/memory/delivery-crud.ts +2 -1
- package/src/memory/guardian-action-store.ts +2 -1
- package/src/memory/guardian-approvals.ts +3 -2
- package/src/memory/ingress-invite-store.ts +12 -2
- package/src/memory/ingress-member-store.ts +4 -3
- package/src/memory/migrations/038-actor-token-records.ts +39 -0
- package/src/memory/migrations/124-voice-invite-display-metadata.ts +14 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema.ts +26 -5
- package/src/messaging/provider-types.ts +24 -0
- package/src/messaging/provider.ts +7 -0
- package/src/messaging/providers/gmail/adapter.ts +127 -0
- package/src/messaging/providers/sms/adapter.ts +40 -37
- package/src/notifications/adapters/macos.ts +45 -2
- package/src/notifications/broadcaster.ts +16 -0
- package/src/notifications/copy-composer.ts +50 -2
- package/src/notifications/decision-engine.ts +22 -9
- package/src/notifications/destination-resolver.ts +16 -2
- package/src/notifications/emit-signal.ts +18 -9
- package/src/notifications/guardian-question-mode.ts +419 -0
- package/src/notifications/signal.ts +14 -3
- package/src/permissions/checker.ts +13 -1
- package/src/permissions/prompter.ts +14 -0
- package/src/providers/anthropic/client.ts +20 -0
- package/src/providers/provider-send-message.ts +15 -3
- package/src/runtime/access-request-helper.ts +82 -4
- package/src/runtime/actor-token-service.ts +234 -0
- package/src/runtime/actor-token-store.ts +236 -0
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-scope.ts +10 -0
- package/src/runtime/channel-approvals.ts +5 -3
- package/src/runtime/channel-readiness-service.ts +23 -64
- package/src/runtime/channel-readiness-types.ts +3 -4
- package/src/runtime/channel-retry-sweep.ts +4 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +197 -0
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-context-resolver.ts +82 -0
- package/src/runtime/guardian-outbound-actions.ts +5 -7
- package/src/runtime/guardian-reply-router.ts +67 -30
- package/src/runtime/guardian-vellum-migration.ts +57 -0
- package/src/runtime/http-server.ts +75 -31
- package/src/runtime/http-types.ts +13 -0
- package/src/runtime/ingress-service.ts +14 -0
- package/src/runtime/invite-redemption-service.ts +10 -1
- package/src/runtime/local-actor-identity.ts +76 -0
- package/src/runtime/middleware/actor-token.ts +271 -0
- package/src/runtime/middleware/twilio-validation.ts +2 -4
- package/src/runtime/routes/approval-routes.ts +82 -7
- package/src/runtime/routes/brain-graph-routes.ts +222 -0
- package/src/runtime/routes/call-routes.ts +2 -1
- package/src/runtime/routes/channel-readiness-routes.ts +71 -0
- package/src/runtime/routes/channel-route-shared.ts +3 -3
- package/src/runtime/routes/conversation-attention-routes.ts +2 -1
- package/src/runtime/routes/conversation-routes.ts +142 -53
- package/src/runtime/routes/events-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +45 -3
- package/src/runtime/routes/guardian-approval-interception.ts +29 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +145 -0
- package/src/runtime/routes/inbound-conversation.ts +4 -3
- package/src/runtime/routes/inbound-message-handler.ts +147 -5
- package/src/runtime/routes/ingress-routes.ts +2 -0
- package/src/runtime/routes/integration-routes.ts +7 -15
- package/src/runtime/routes/pairing-routes.ts +163 -0
- package/src/runtime/routes/twilio-routes.ts +934 -0
- package/src/runtime/tool-grant-request-helper.ts +3 -1
- package/src/security/oauth2.ts +27 -2
- package/src/security/token-manager.ts +46 -10
- package/src/tools/browser/browser-execution.ts +4 -3
- package/src/tools/browser/browser-handoff.ts +10 -18
- package/src/tools/browser/browser-manager.ts +80 -25
- package/src/tools/browser/browser-screencast.ts +35 -119
- package/src/tools/calls/call-start.ts +2 -1
- package/src/tools/permission-checker.ts +15 -4
- package/src/tools/terminal/parser.ts +12 -0
- package/src/tools/tool-approval-handler.ts +244 -19
- package/src/workspace/git-service.ts +19 -0
- package/src/__tests__/handlers-twilio-config.test.ts +0 -1928
- package/src/daemon/handlers/config-twilio.ts +0 -1082
|
@@ -17,7 +17,15 @@ Twilio credentials and phone number configuration are shared between voice calls
|
|
|
17
17
|
|
|
18
18
|
The twilio-setup skill handles credential storage, phone number provisioning/assignment, and public ingress setup. Once complete, return here to enable the calls feature and start making calls.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Check if Twilio is already configured:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
TOKEN=$(cat ~/.vellum/http-token)
|
|
24
|
+
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
25
|
+
-H "Authorization: Bearer $TOKEN"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If `hasCredentials` is `true` and `phoneNumber` is set, skip directly to **Step 5: Enable Calls** below.
|
|
21
29
|
|
|
22
30
|
## Overview
|
|
23
31
|
|
|
@@ -58,65 +66,48 @@ The user's assistant gets its own personal phone number through Twilio. All impl
|
|
|
58
66
|
First, check whether Twilio is already configured:
|
|
59
67
|
|
|
60
68
|
```bash
|
|
61
|
-
vellum
|
|
69
|
+
TOKEN=$(cat ~/.vellum/http-token)
|
|
70
|
+
curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/config" \
|
|
71
|
+
-H "Authorization: Bearer $TOKEN"
|
|
62
72
|
```
|
|
63
73
|
|
|
64
|
-
Also check
|
|
74
|
+
Also check calls feature status:
|
|
65
75
|
|
|
66
76
|
```bash
|
|
67
|
-
|
|
77
|
+
vellum config get calls.enabled
|
|
68
78
|
```
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
If all three credentials exist and `calls.enabled` is `true`, skip to the **Making Calls** section. If credentials are partially configured, skip to whichever step is still needed.
|
|
80
|
+
If the config response shows `hasCredentials: true` and `phoneNumber` is set, and `calls.enabled` is `true`, skip to the **Making Calls** section. If credentials are partially configured, skip to whichever step is still needed.
|
|
73
81
|
|
|
74
82
|
## Step 2: Create a Twilio Account
|
|
75
83
|
|
|
76
84
|
If the user doesn't have a Twilio account yet, guide them through setup:
|
|
77
85
|
|
|
78
86
|
1. Tell the user: **"You'll need a Twilio account to make phone calls. Sign up at https://www.twilio.com/try-twilio — it's free to start and includes trial credit."**
|
|
79
|
-
2. Once they have an account, they need
|
|
87
|
+
2. Once they have an account, they need two pieces of information:
|
|
80
88
|
- **Account SID** — found on the Twilio Console dashboard at https://console.twilio.com
|
|
81
89
|
- **Auth Token** — found on the same dashboard (click "Show" to reveal it)
|
|
82
|
-
- **Phone Number** — a Twilio phone number capable of making voice calls
|
|
83
|
-
|
|
84
|
-
### Getting a Twilio Phone Number
|
|
85
|
-
|
|
86
|
-
If the user doesn't have a Twilio phone number yet:
|
|
87
|
-
|
|
88
|
-
1. Direct them to https://console.twilio.com/us1/develop/phone-numbers/manage/incoming
|
|
89
|
-
2. Click **"Buy a Number"**
|
|
90
|
-
3. Select a number with **Voice** capability enabled
|
|
91
|
-
4. For trial accounts, Twilio provides one free number automatically — check "Active Numbers" first
|
|
92
90
|
|
|
93
|
-
Tell the user: **"
|
|
91
|
+
Tell the user: **"The assistant will get its own personal phone number through Twilio — the number that shows up on caller ID when calls are placed."**
|
|
94
92
|
|
|
95
93
|
## Step 3: Store Twilio Credentials
|
|
96
94
|
|
|
97
|
-
|
|
95
|
+
**IMPORTANT — Secure credential collection only:** Never use credentials pasted in plaintext chat. Always collect credentials through the secure credential prompt flow:
|
|
98
96
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
credential_store action=store service=twilio field=account_sid value=<their_account_sid>
|
|
102
|
-
```
|
|
97
|
+
- Call `credential_store` with `action: "prompt"`, `service: "twilio"`, `field: "account_sid"`, `label: "Twilio Account SID"`, `description: "Enter your Account SID from the Twilio Console dashboard"`, and `placeholder: "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"`.
|
|
98
|
+
- Call `credential_store` with `action: "prompt"`, `service: "twilio"`, `field: "auth_token"`, `label: "Twilio Auth Token"`, `description: "Enter your Auth Token from the Twilio Console dashboard"`, and `placeholder: "your_auth_token"`.
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
```
|
|
106
|
-
credential_store action=store service=twilio field=auth_token value=<their_auth_token>
|
|
107
|
-
```
|
|
100
|
+
After both credentials are collected, send them to the gateway:
|
|
108
101
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
credential_store action=list
|
|
102
|
+
```bash
|
|
103
|
+
TOKEN=$(cat ~/.vellum/http-token)
|
|
104
|
+
curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/twilio/credentials" \
|
|
105
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
106
|
+
-H "Content-Type: application/json" \
|
|
107
|
+
-d '{"accountSid":"<value from credential_store for twilio/account_sid>","authToken":"<value from credential_store for twilio/auth_token>"}'
|
|
117
108
|
```
|
|
118
109
|
|
|
119
|
-
|
|
110
|
+
The endpoint validates the credentials against the Twilio API before storing them. If credentials are invalid, ask the user to re-enter via the secure prompt.
|
|
120
111
|
|
|
121
112
|
**Important:** Credentials are stored in the OS keychain (macOS Keychain / Linux secret-service) or encrypted at rest. They are never logged or exposed in plaintext.
|
|
122
113
|
|
|
@@ -163,7 +154,7 @@ vellum config get calls.enabled
|
|
|
163
154
|
|
|
164
155
|
Before making real calls, offer a quick verification:
|
|
165
156
|
|
|
166
|
-
1. Confirm credentials are stored:
|
|
157
|
+
1. Confirm credentials are stored: check the Twilio config endpoint for `hasCredentials: true` and `phoneNumber`
|
|
167
158
|
2. Confirm ingress is running: `ingress.publicBaseUrl` must be set and the tunnel active
|
|
168
159
|
3. Confirm calls are enabled: `calls.enabled` must be `true`
|
|
169
160
|
|
|
@@ -605,7 +596,7 @@ The following behavioral changes were introduced with the cross-channel guardian
|
|
|
605
596
|
## Troubleshooting
|
|
606
597
|
|
|
607
598
|
### "Twilio credentials not configured"
|
|
608
|
-
Run Step 3 to store your Account SID
|
|
599
|
+
Run Step 3 to store your Account SID and Auth Token via the secure credential prompt flow, or load the `twilio-setup` skill.
|
|
609
600
|
|
|
610
601
|
### "Calls feature is disabled"
|
|
611
602
|
Run `vellum config set calls.enabled true`.
|
|
@@ -17,7 +17,7 @@ OAuth uses the official X API v2. It is the most reliable connection method and
|
|
|
17
17
|
|
|
18
18
|
- Supports: **post** and **reply**
|
|
19
19
|
- Read-only operations (timeline, search, home, bookmarks, notifications, likes, followers, following, media) always use the browser path directly, regardless of the strategy setting.
|
|
20
|
-
- Setup: Collect the OAuth Client ID (and optional Client Secret) from the user in chat using `credential_store` with `action: "prompt"` (canonical field names: `client_id`, `client_secret`), then initiate the `twitter_auth_start`
|
|
20
|
+
- Setup: Collect the OAuth Client ID (and optional Client Secret) from the user in chat using `credential_store` with `action: "prompt"` (canonical field names: `client_id`, `client_secret`), then initiate the `twitter_auth_start` flow. See the **First-Use Decision Flow** for the full sequence.
|
|
21
21
|
- Set the strategy: `vellum x strategy set oauth`
|
|
22
22
|
|
|
23
23
|
### Browser session (no developer credentials needed)
|
|
@@ -56,7 +56,7 @@ When the user triggers a Twitter operation and no strategy has been configured y
|
|
|
56
56
|
|
|
57
57
|
### OAuth Setup Sequence
|
|
58
58
|
|
|
59
|
-
When the user chooses OAuth, collect their X developer credentials conversationally using the secure UI. The OAuth flow delegates to the generic connect orchestrator
|
|
59
|
+
When the user chooses OAuth, collect their X developer credentials conversationally using the secure UI. The OAuth flow delegates to the generic connect orchestrator, which resolves the Twitter provider profile, computes scopes via policy, opens the X authorization page in the user's browser, verifies the user's identity, and stores tokens. The orchestrator also manages stale refresh-token cleanup and enforces integration-mode guards.
|
|
60
60
|
|
|
61
61
|
1. **Collect the Client ID securely:**
|
|
62
62
|
Call `credential_store` with `action: "prompt"`, `service: "integration:twitter"`, `field: "client_id"`, `label: "X (Twitter) OAuth Client ID"`, `description: "Enter the Client ID from your X Developer App"`, and `placeholder: "your-client-id"`.
|
|
@@ -65,7 +65,7 @@ When the user chooses OAuth, collect their X developer credentials conversationa
|
|
|
65
65
|
Ask the user if their X app uses a confidential client (has a Client Secret). If yes, call `credential_store` with `action: "prompt"`, `service: "integration:twitter"`, `field: "client_secret"`, `label: "X (Twitter) OAuth Client Secret"`, `description: "Enter the Client Secret from your X Developer App (leave blank if using a public client)"`, and `placeholder: "your-client-secret"`.
|
|
66
66
|
|
|
67
67
|
3. **Initiate the OAuth flow:**
|
|
68
|
-
|
|
68
|
+
Trigger the Twitter auth start flow. The connect orchestrator resolves the Twitter provider profile, computes scopes via policy, opens the X authorization page in the user's browser, verifies the user's identity, and stores tokens. Wait for the auth result.
|
|
69
69
|
|
|
70
70
|
4. **Confirm success:**
|
|
71
71
|
Tell the user: "Great, your X account is connected! You can always update these credentials from the Settings page."
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Vercel Token Setup"
|
|
3
|
+
description: "Set up a Vercel API token for publishing apps using browser automation"
|
|
4
|
+
includes: ["browser"]
|
|
5
|
+
credential-setup-for: "vercel:api_token"
|
|
6
|
+
metadata: {"vellum": {"emoji": "▲"}}
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
You are helping your user set up a Vercel API token so they can publish apps to the web.
|
|
10
|
+
|
|
11
|
+
## Client Check
|
|
12
|
+
|
|
13
|
+
Determine whether the user has browser automation available (macOS desktop app) or is on a non-interactive channel (Telegram, SMS, etc.).
|
|
14
|
+
|
|
15
|
+
- **macOS desktop app**: Follow the **Automated Setup** path below.
|
|
16
|
+
- **Telegram or other channel** (no browser automation): Follow the **Manual Setup for Channels** path below.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Path A: Manual Setup for Channels (Telegram, SMS, etc.)
|
|
21
|
+
|
|
22
|
+
When the user is on Telegram or any non-macOS client, walk them through a text-based setup. No browser automation is used — the user follows links and performs each action manually.
|
|
23
|
+
|
|
24
|
+
### Channel Step 1: Confirm and Explain
|
|
25
|
+
|
|
26
|
+
Tell the user:
|
|
27
|
+
|
|
28
|
+
> **Setting up Vercel API Token**
|
|
29
|
+
>
|
|
30
|
+
> Since I can't automate the browser from here, I'll walk you through each step with direct links. You'll need:
|
|
31
|
+
> 1. A Vercel account (free tier works)
|
|
32
|
+
> 2. About 2 minutes
|
|
33
|
+
>
|
|
34
|
+
> Ready to start?
|
|
35
|
+
|
|
36
|
+
If the user declines, acknowledge and stop.
|
|
37
|
+
|
|
38
|
+
### Channel Step 2: Create the Token
|
|
39
|
+
|
|
40
|
+
Tell the user:
|
|
41
|
+
|
|
42
|
+
> **Step 1: Create an API token**
|
|
43
|
+
>
|
|
44
|
+
> Open this link to go to your Vercel tokens page:
|
|
45
|
+
> https://vercel.com/account/tokens
|
|
46
|
+
>
|
|
47
|
+
> 1. Click **"Create"** (or **"Create Token"**)
|
|
48
|
+
> 2. Set the token name to **"Vellum Assistant"**
|
|
49
|
+
> 3. Select scope: **"Full Account"**
|
|
50
|
+
> 4. Set expiration to the longest option available (or **"No Expiration"** if offered)
|
|
51
|
+
> 5. Click **"Create Token"**
|
|
52
|
+
>
|
|
53
|
+
> A token value will appear — **copy it now**, as it's only shown once.
|
|
54
|
+
|
|
55
|
+
### Channel Step 3: Store the Token
|
|
56
|
+
|
|
57
|
+
Tell the user:
|
|
58
|
+
|
|
59
|
+
> **Step 2: Send me the token**
|
|
60
|
+
>
|
|
61
|
+
> Please paste the token value into the secure prompt below.
|
|
62
|
+
|
|
63
|
+
Present the secure prompt:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
credential_store prompt:
|
|
67
|
+
service: "vercel"
|
|
68
|
+
field: "api_token"
|
|
69
|
+
label: "Vercel API Token"
|
|
70
|
+
description: "Paste the API token you just created on vercel.com"
|
|
71
|
+
placeholder: "Enter your Vercel API token"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Wait for the user to complete the prompt. Once received, store it:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
credential_store store:
|
|
78
|
+
service: "vercel"
|
|
79
|
+
field: "api_token"
|
|
80
|
+
value: "<the token the user provided>"
|
|
81
|
+
allowedTools: ["publish_page", "unpublish_page"]
|
|
82
|
+
allowedDomains: ["api.vercel.com"]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Channel Step 4: Done!
|
|
86
|
+
|
|
87
|
+
> **Vercel is connected!** You can now publish apps to the web. Try clicking Publish on any app you've built.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
# Path B: Automated Setup (macOS Desktop App)
|
|
92
|
+
|
|
93
|
+
You will automate Vercel token creation via the browser while the user watches. The user's only manual action is signing in to Vercel (if needed) and one copy-paste for the token value.
|
|
94
|
+
|
|
95
|
+
## Browser Interaction Principles
|
|
96
|
+
|
|
97
|
+
Vercel's UI may change over time. Do NOT memorize or depend on specific element IDs, CSS selectors, or DOM structures. Instead:
|
|
98
|
+
|
|
99
|
+
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.
|
|
100
|
+
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.
|
|
101
|
+
3. **Verify after every action.** After clicking, typing, or navigating, take a new screenshot to confirm the action succeeded.
|
|
102
|
+
4. **Never assume DOM structure.** Use the snapshot to identify what's on the page and interact accordingly.
|
|
103
|
+
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.
|
|
104
|
+
|
|
105
|
+
## Anti-Loop Guardrails
|
|
106
|
+
|
|
107
|
+
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:
|
|
108
|
+
|
|
109
|
+
1. **Stop trying.** Do not continue retrying the same approach.
|
|
110
|
+
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.
|
|
111
|
+
3. **Resume automation** at the next step once the user confirms the manual step is done.
|
|
112
|
+
|
|
113
|
+
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.
|
|
114
|
+
|
|
115
|
+
## Things That Do Not Work — Do Not Attempt
|
|
116
|
+
|
|
117
|
+
These actions are technically impossible in the browser automation environment:
|
|
118
|
+
|
|
119
|
+
- **Downloading files.** `browser_click` on a Download button does not save files to disk.
|
|
120
|
+
- **Reading the token value from a screenshot.** The token 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.
|
|
121
|
+
- **Clipboard operations.** You cannot copy/paste via browser automation.
|
|
122
|
+
|
|
123
|
+
## Step 1: Single Upfront Confirmation
|
|
124
|
+
|
|
125
|
+
Tell the user:
|
|
126
|
+
|
|
127
|
+
> **Setting up your Vercel API token so we can publish your app...**
|
|
128
|
+
>
|
|
129
|
+
> Here's what will happen:
|
|
130
|
+
> 1. **A browser opens** to your Vercel account settings
|
|
131
|
+
> 2. **You sign in** (if not already signed in)
|
|
132
|
+
> 3. **I create the token** — you just watch
|
|
133
|
+
> 4. **One quick copy-paste** — I'll ask you to copy the token value into a secure prompt
|
|
134
|
+
>
|
|
135
|
+
> Takes about a minute. Ready?
|
|
136
|
+
|
|
137
|
+
If the user declines, acknowledge and stop. No further confirmations are needed after this point.
|
|
138
|
+
|
|
139
|
+
## Step 2: Open Vercel and Sign In
|
|
140
|
+
|
|
141
|
+
**Goal:** The user is signed in and the Vercel tokens page is loaded.
|
|
142
|
+
|
|
143
|
+
Navigate to `https://vercel.com/account/tokens`.
|
|
144
|
+
|
|
145
|
+
Take a screenshot and snapshot to check the page state:
|
|
146
|
+
- **Sign-in page:** Tell the user: "Please sign in to your Vercel account in the browser." Then auto-detect sign-in completion by polling screenshots every 5-10 seconds. Check if the current URL has moved away from the login/sign-in page to the tokens page. Do NOT ask the user to "let me know when you're done" — detect it automatically. Once sign-in is detected, tell the user: "Signed in! Creating your API token now..."
|
|
147
|
+
- **Already signed in:** Tell the user: "Already signed in — creating your API token now..." and continue immediately.
|
|
148
|
+
|
|
149
|
+
**Verify:** URL contains `vercel.com/account/tokens` and no sign-in overlay is visible.
|
|
150
|
+
|
|
151
|
+
## Step 3: Create Token
|
|
152
|
+
|
|
153
|
+
**Goal:** A new API token named "Vellum Assistant" is created.
|
|
154
|
+
|
|
155
|
+
Take a screenshot and snapshot. Find and click the button to create a new token (typically labeled "Create" or "Create Token").
|
|
156
|
+
|
|
157
|
+
On the creation form:
|
|
158
|
+
- Token name: **"Vellum Assistant"**
|
|
159
|
+
- Scope: Select **"Full Account"** (or the broadest scope available)
|
|
160
|
+
- Expiration: Select the longest option available, or **"No Expiration"** if offered
|
|
161
|
+
- Click create/submit
|
|
162
|
+
|
|
163
|
+
**Verify:** Take a screenshot. A dialog or section should now display the newly created token value.
|
|
164
|
+
|
|
165
|
+
## Step 4: Capture Token via Secure Prompt
|
|
166
|
+
|
|
167
|
+
**Goal:** The token value is securely captured and stored.
|
|
168
|
+
|
|
169
|
+
### CRITICAL — Token Capture Protocol
|
|
170
|
+
|
|
171
|
+
After token creation, Vercel shows the token value **once**. You MUST follow this exact sequence — **no improvisation**:
|
|
172
|
+
|
|
173
|
+
1. Tell the user: "Your token has been created! Please copy the token value shown on screen and paste it into the secure prompt below."
|
|
174
|
+
2. **IMMEDIATELY** present a `credential_store prompt` for the token. This is your ONLY next action.
|
|
175
|
+
3. Wait for the user to paste the token.
|
|
176
|
+
|
|
177
|
+
**Absolute prohibitions during this step:**
|
|
178
|
+
- Do NOT try to read the token value from the screenshot. It must come from the user via secure prompt to ensure accuracy.
|
|
179
|
+
- Do NOT navigate away from the page until the user has pasted the token.
|
|
180
|
+
- Do NOT click any download or copy buttons.
|
|
181
|
+
|
|
182
|
+
Present the secure prompt:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
credential_store prompt:
|
|
186
|
+
service: "vercel"
|
|
187
|
+
field: "api_token"
|
|
188
|
+
label: "Vercel API Token"
|
|
189
|
+
description: "Copy the token value shown on the Vercel page and paste it here."
|
|
190
|
+
placeholder: "Enter your Vercel API token"
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Wait for the user to complete the prompt. Once received, store it:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
credential_store store:
|
|
197
|
+
service: "vercel"
|
|
198
|
+
field: "api_token"
|
|
199
|
+
value: "<the token the user provided>"
|
|
200
|
+
allowedTools: ["publish_page", "unpublish_page"]
|
|
201
|
+
allowedDomains: ["api.vercel.com"]
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Verify:** `credential_store list` shows `api_token` for `vercel`.
|
|
205
|
+
|
|
206
|
+
## Step 5: Done!
|
|
207
|
+
|
|
208
|
+
"**Vercel is connected!** Your API token is set up and ready to go. You can now publish apps to the web."
|
|
209
|
+
|
|
210
|
+
## Error Handling
|
|
211
|
+
|
|
212
|
+
- **Page load failures:** Retry navigation once. If it still fails, tell the user and ask them to check their internet connection.
|
|
213
|
+
- **Element not found:** Take a fresh screenshot to re-assess. The Vercel UI may have changed. Describe what you see and try alternative approaches. If stuck after 2 attempts, ask the user for guidance.
|
|
214
|
+
- **Token already exists with same name:** This is fine — Vercel allows multiple tokens with the same name. Proceed with creation.
|
|
215
|
+
- **Any unexpected state:** Take a `browser_screenshot`, describe what you see, and ask the user for guidance.
|
|
@@ -125,6 +125,42 @@ export const CallsConfigSchema = z.object({
|
|
|
125
125
|
.positive('calls.userConsultTimeoutSeconds must be a positive integer')
|
|
126
126
|
.max(2_147_483, 'calls.userConsultTimeoutSeconds must be at most 2147483 (setTimeout-safe limit)')
|
|
127
127
|
.default(120),
|
|
128
|
+
ttsPlaybackDelayMs: z
|
|
129
|
+
.number({ error: 'calls.ttsPlaybackDelayMs must be a number' })
|
|
130
|
+
.int('calls.ttsPlaybackDelayMs must be an integer')
|
|
131
|
+
.min(0, 'calls.ttsPlaybackDelayMs must be >= 0')
|
|
132
|
+
.max(10_000, 'calls.ttsPlaybackDelayMs must be at most 10000')
|
|
133
|
+
.default(3000),
|
|
134
|
+
accessRequestPollIntervalMs: z
|
|
135
|
+
.number({ error: 'calls.accessRequestPollIntervalMs must be a number' })
|
|
136
|
+
.int('calls.accessRequestPollIntervalMs must be an integer')
|
|
137
|
+
.min(50, 'calls.accessRequestPollIntervalMs must be >= 50')
|
|
138
|
+
.max(10_000, 'calls.accessRequestPollIntervalMs must be at most 10000')
|
|
139
|
+
.default(500),
|
|
140
|
+
guardianWaitUpdateInitialIntervalMs: z
|
|
141
|
+
.number({ error: 'calls.guardianWaitUpdateInitialIntervalMs must be a number' })
|
|
142
|
+
.int('calls.guardianWaitUpdateInitialIntervalMs must be an integer')
|
|
143
|
+
.min(1000, 'calls.guardianWaitUpdateInitialIntervalMs must be >= 1000')
|
|
144
|
+
.max(60_000, 'calls.guardianWaitUpdateInitialIntervalMs must be at most 60000')
|
|
145
|
+
.default(5000),
|
|
146
|
+
guardianWaitUpdateInitialWindowMs: z
|
|
147
|
+
.number({ error: 'calls.guardianWaitUpdateInitialWindowMs must be a number' })
|
|
148
|
+
.int('calls.guardianWaitUpdateInitialWindowMs must be an integer')
|
|
149
|
+
.min(1000, 'calls.guardianWaitUpdateInitialWindowMs must be >= 1000')
|
|
150
|
+
.max(60_000, 'calls.guardianWaitUpdateInitialWindowMs must be at most 60000')
|
|
151
|
+
.default(30_000),
|
|
152
|
+
guardianWaitUpdateSteadyMinIntervalMs: z
|
|
153
|
+
.number({ error: 'calls.guardianWaitUpdateSteadyMinIntervalMs must be a number' })
|
|
154
|
+
.int('calls.guardianWaitUpdateSteadyMinIntervalMs must be an integer')
|
|
155
|
+
.min(1000, 'calls.guardianWaitUpdateSteadyMinIntervalMs must be >= 1000')
|
|
156
|
+
.max(60_000, 'calls.guardianWaitUpdateSteadyMinIntervalMs must be at most 60000')
|
|
157
|
+
.default(7000),
|
|
158
|
+
guardianWaitUpdateSteadyMaxIntervalMs: z
|
|
159
|
+
.number({ error: 'calls.guardianWaitUpdateSteadyMaxIntervalMs must be a number' })
|
|
160
|
+
.int('calls.guardianWaitUpdateSteadyMaxIntervalMs must be an integer')
|
|
161
|
+
.min(1000, 'calls.guardianWaitUpdateSteadyMaxIntervalMs must be >= 1000')
|
|
162
|
+
.max(60_000, 'calls.guardianWaitUpdateSteadyMaxIntervalMs must be at most 60000')
|
|
163
|
+
.default(10_000),
|
|
128
164
|
disclosure: CallsDisclosureConfigSchema.default(CallsDisclosureConfigSchema.parse({})),
|
|
129
165
|
safety: CallsSafetyConfigSchema.default(CallsSafetyConfigSchema.parse({})),
|
|
130
166
|
voice: CallsVoiceConfigSchema.default(CallsVoiceConfigSchema.parse({})),
|
package/src/config/env.ts
CHANGED
|
@@ -162,6 +162,28 @@ export function getOllamaBaseUrlEnv(): string | undefined {
|
|
|
162
162
|
return str('OLLAMA_BASE_URL');
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// ── Platform ─────────────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
export function getPlatformBaseUrl(): string {
|
|
168
|
+
return str('PLATFORM_BASE_URL') ?? '';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* PLATFORM_ASSISTANT_ID — UUID of this assistant on the platform.
|
|
173
|
+
* Required for registering callback routes when containerized.
|
|
174
|
+
*/
|
|
175
|
+
export function getPlatformAssistantId(): string {
|
|
176
|
+
return str('PLATFORM_ASSISTANT_ID') ?? '';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* PLATFORM_INTERNAL_API_KEY — static internal gateway key for authenticating
|
|
181
|
+
* with the platform's internal gateway callback route registration endpoint.
|
|
182
|
+
*/
|
|
183
|
+
export function getPlatformInternalApiKey(): string {
|
|
184
|
+
return str('PLATFORM_INTERNAL_API_KEY') ?? '';
|
|
185
|
+
}
|
|
186
|
+
|
|
165
187
|
// ── Startup validation ──────────────────────────────────────────────────────
|
|
166
188
|
|
|
167
189
|
/**
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"description": "Enable X (Twitter) skill section in the system prompt",
|
|
42
42
|
"defaultEnabled": true
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
"id": "messaging",
|
|
46
|
+
"scope": "assistant",
|
|
47
|
+
"key": "feature_flags.messaging.enabled",
|
|
48
|
+
"label": "Messaging",
|
|
49
|
+
"description": "Enable messaging skill section in the system prompt",
|
|
50
|
+
"defaultEnabled": true
|
|
51
|
+
},
|
|
44
52
|
{
|
|
45
53
|
"id": "collect-usage-data",
|
|
46
54
|
"scope": "assistant",
|
|
@@ -49,14 +57,6 @@
|
|
|
49
57
|
"description": "Send crash reports and error diagnostics to help improve the app",
|
|
50
58
|
"defaultEnabled": true
|
|
51
59
|
},
|
|
52
|
-
{
|
|
53
|
-
"id": "voice-invite-redemption",
|
|
54
|
-
"scope": "assistant",
|
|
55
|
-
"key": "feature_flags.voice-invite-redemption.enabled",
|
|
56
|
-
"label": "Voice Invite Redemption",
|
|
57
|
-
"description": "Enable voice invite code redemption for inbound callers with active voice invites",
|
|
58
|
-
"defaultEnabled": false
|
|
59
|
-
},
|
|
60
60
|
{
|
|
61
61
|
"id": "user-hosted-enabled",
|
|
62
62
|
"scope": "macos",
|
package/src/config/schema.ts
CHANGED
|
@@ -204,8 +204,8 @@ export const AssistantConfigSchema = z.object({
|
|
|
204
204
|
maxToolUseTurns: z
|
|
205
205
|
.number({ error: 'maxToolUseTurns must be a number' })
|
|
206
206
|
.int('maxToolUseTurns must be an integer')
|
|
207
|
-
.
|
|
208
|
-
.default(
|
|
207
|
+
.nonnegative('maxToolUseTurns must be a non-negative integer')
|
|
208
|
+
.default(0),
|
|
209
209
|
thinking: ThinkingConfigSchema.default(ThinkingConfigSchema.parse({})),
|
|
210
210
|
contextWindow: ContextWindowConfigSchema.default(ContextWindowConfigSchema.parse({})),
|
|
211
211
|
memory: MemoryConfigSchema.default(MemoryConfigSchema.parse({})),
|
package/src/config/skills.ts
CHANGED
|
@@ -65,6 +65,8 @@ export interface SkillSummary {
|
|
|
65
65
|
toolManifest?: SkillToolManifestMeta;
|
|
66
66
|
/** IDs of child skills that this skill includes (metadata-only, not auto-activated). */
|
|
67
67
|
includes?: string[];
|
|
68
|
+
/** Declares which credential this skill sets up (e.g. "vercel:api_token"). */
|
|
69
|
+
credentialSetupFor?: string;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
export interface SkillDefinition extends SkillSummary {
|
|
@@ -251,6 +253,7 @@ interface ParsedFrontmatter {
|
|
|
251
253
|
disableModelInvocation: boolean;
|
|
252
254
|
metadata?: VellumMetadata;
|
|
253
255
|
includes?: string[];
|
|
256
|
+
credentialSetupFor?: string;
|
|
254
257
|
}
|
|
255
258
|
|
|
256
259
|
function parseIncludes(raw: string | undefined, skillFilePath: string): string[] | undefined {
|
|
@@ -338,6 +341,7 @@ function parseFrontmatter(content: string, skillFilePath: string): ParsedFrontma
|
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
const includes = parseIncludes(fields.includes, skillFilePath);
|
|
344
|
+
const credentialSetupFor = fields['credential-setup-for']?.trim() || undefined;
|
|
341
345
|
|
|
342
346
|
return {
|
|
343
347
|
name,
|
|
@@ -348,6 +352,7 @@ function parseFrontmatter(content: string, skillFilePath: string): ParsedFrontma
|
|
|
348
352
|
disableModelInvocation,
|
|
349
353
|
metadata,
|
|
350
354
|
includes,
|
|
355
|
+
credentialSetupFor,
|
|
351
356
|
};
|
|
352
357
|
}
|
|
353
358
|
|
|
@@ -471,6 +476,7 @@ function readSkillFromDirectory(directoryPath: string, skillsDir: string, source
|
|
|
471
476
|
metadata: parsed.metadata,
|
|
472
477
|
toolManifest: detectToolManifest(directoryPath),
|
|
473
478
|
includes: parsed.includes,
|
|
479
|
+
credentialSetupFor: parsed.credentialSetupFor,
|
|
474
480
|
};
|
|
475
481
|
} catch (err) {
|
|
476
482
|
log.warn({ err, skillFilePath }, 'Failed to read skill file');
|
|
@@ -512,6 +518,7 @@ function readBundledSkillFromDirectory(directoryPath: string): SkillDefinition |
|
|
|
512
518
|
metadata: parsed.metadata,
|
|
513
519
|
toolManifest: detectToolManifest(directoryPath),
|
|
514
520
|
includes: parsed.includes,
|
|
521
|
+
credentialSetupFor: parsed.credentialSetupFor,
|
|
515
522
|
};
|
|
516
523
|
} catch (err) {
|
|
517
524
|
log.warn({ err, skillFilePath }, 'Failed to read bundled skill file');
|
|
@@ -566,6 +573,7 @@ function loadBundledSkills(): SkillSummary[] {
|
|
|
566
573
|
metadata: skill.metadata,
|
|
567
574
|
toolManifest: skill.toolManifest,
|
|
568
575
|
includes: skill.includes,
|
|
576
|
+
credentialSetupFor: skill.credentialSetupFor,
|
|
569
577
|
});
|
|
570
578
|
}
|
|
571
579
|
|
|
@@ -686,6 +694,7 @@ function skillSummaryFromDefinition(skill: SkillDefinition, source: SkillSource)
|
|
|
686
694
|
metadata: skill.metadata,
|
|
687
695
|
toolManifest: skill.toolManifest,
|
|
688
696
|
includes: skill.includes,
|
|
697
|
+
credentialSetupFor: skill.credentialSetupFor,
|
|
689
698
|
};
|
|
690
699
|
}
|
|
691
700
|
|
|
@@ -731,6 +740,7 @@ export function loadSkillCatalog(workspaceSkillsDir?: string, extraDirs?: string
|
|
|
731
740
|
metadata: parsed.metadata,
|
|
732
741
|
toolManifest: detectToolManifest(directory),
|
|
733
742
|
includes: parsed.includes,
|
|
743
|
+
credentialSetupFor: parsed.credentialSetupFor,
|
|
734
744
|
});
|
|
735
745
|
} catch (err) {
|
|
736
746
|
log.warn({ err, directory }, 'Failed to read skill from extraDirs');
|
|
@@ -813,6 +823,7 @@ export function loadSkillCatalog(workspaceSkillsDir?: string, extraDirs?: string
|
|
|
813
823
|
metadata: parsed.metadata,
|
|
814
824
|
toolManifest: detectToolManifest(directory),
|
|
815
825
|
includes: parsed.includes,
|
|
826
|
+
credentialSetupFor: parsed.credentialSetupFor,
|
|
816
827
|
};
|
|
817
828
|
|
|
818
829
|
if (seenIds.has(id)) {
|
|
@@ -811,6 +811,14 @@ function buildDynamicSkillWorkflowSection(config: import('./schema.js').Assistan
|
|
|
811
811
|
);
|
|
812
812
|
}
|
|
813
813
|
|
|
814
|
+
if (isAssistantFeatureFlagEnabled('feature_flags.messaging.enabled', config)) {
|
|
815
|
+
lines.push(
|
|
816
|
+
'',
|
|
817
|
+
'### Messaging Skill',
|
|
818
|
+
'When the user asks about email, messaging, inbox management, or wants to read/send/search messages on any platform (Gmail, Slack, Telegram, SMS), load the "messaging" skill using `skill_load`. The messaging skill handles connection setup, credential flows, and all messaging operations — do not improvise setup instructions from general knowledge.',
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
814
822
|
return lines.join('\n');
|
|
815
823
|
}
|
|
816
824
|
|
|
@@ -839,13 +847,15 @@ function formatSkillsCatalog(skills: SkillSummary[]): string {
|
|
|
839
847
|
const nameAttr = escapeXml(skill.name);
|
|
840
848
|
const descAttr = escapeXml(skill.description);
|
|
841
849
|
const locAttr = escapeXml(skill.directoryPath);
|
|
842
|
-
|
|
850
|
+
const credAttr = skill.credentialSetupFor ? ` credential-setup-for="${escapeXml(skill.credentialSetupFor)}"` : '';
|
|
851
|
+
lines.push(`<skill id="${idAttr}" name="${nameAttr}" description="${descAttr}" location="${locAttr}"${credAttr} />`);
|
|
843
852
|
}
|
|
844
853
|
lines.push('</available_skills>');
|
|
845
854
|
|
|
846
855
|
return [
|
|
847
856
|
'## Available Skills',
|
|
848
857
|
'The following skills are available. Before executing one, call the `skill_load` tool with its `id` to load the full instructions.',
|
|
858
|
+
'When a credential is missing, check if any skill declares `credential-setup-for` matching that service — if so, load that skill.',
|
|
849
859
|
'',
|
|
850
860
|
lines.join('\n'),
|
|
851
861
|
].join('\n');
|
|
@@ -16,6 +16,8 @@ Never remove or weaken safety boundaries, tool-use permission rules, or the Boun
|
|
|
16
16
|
|
|
17
17
|
**Be resourceful before asking.** Try to figure it out. Read the file. Check the context. Search for it. Then ask if you're stuck. Come back with answers, not questions.
|
|
18
18
|
|
|
19
|
+
**Know your own capabilities.** Before telling the user you can't do something or asking them to fix a problem, check what tools and skills you have. If a connection is broken, try to fix it. If a service needs setup, offer to do it. Escalate only after you've tried.
|
|
20
|
+
|
|
19
21
|
**Have opinions.** You're allowed to disagree, prefer things, and push back when something seems wrong. An assistant with no perspective is just a search engine.
|
|
20
22
|
|
|
21
23
|
**Earn trust through competence.** You have access to your user's machine, files, and tools. Don't make them regret it. Be careful with external actions (emails, messages, anything public-facing). Be bold with internal ones (reading, organizing, building).
|