@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
|
@@ -193,36 +193,36 @@ After the user authorizes (they'll come back and say so, or you can suggest they
|
|
|
193
193
|
|
|
194
194
|
# Path B: Automated Setup (macOS Desktop App)
|
|
195
195
|
|
|
196
|
-
You will automate the entire GCP setup via the browser while the user watches
|
|
196
|
+
You will automate the entire GCP setup via the browser while the user watches in the Chrome window on the side. The user's only manual actions are: signing in to their Google account, and copy-pasting credentials from the Chrome window into secure prompts.
|
|
197
197
|
|
|
198
198
|
## Browser Interaction Principles
|
|
199
199
|
|
|
200
200
|
Google Cloud Console's UI changes frequently. Do NOT memorize or depend on specific element IDs, CSS selectors, or DOM structures. Instead:
|
|
201
201
|
|
|
202
|
-
1. **
|
|
203
|
-
2. **Adapt to what you see.** If
|
|
204
|
-
3. **Verify after every action.** After clicking, typing, or navigating, take a new
|
|
205
|
-
4. **Never assume DOM structure.** Dropdowns may be `<select>`, `<mat-select>`, `<div role="listbox">`, or something else entirely. Use the snapshot to identify
|
|
206
|
-
5. **When stuck
|
|
202
|
+
1. **Snapshot first, act second.** Before every interaction, use `browser_snapshot` to discover interactive elements and their IDs. This is your primary navigation tool — it gives you the accessibility tree with clickable/typeable element IDs. Use `browser_screenshot` for visual context when the snapshot alone isn't enough.
|
|
203
|
+
2. **Adapt to what you see.** If an element's label or position differs from what you expect, use the snapshot to find the correct element. GCP may rename buttons, reorganize menus, or change form layouts at any time.
|
|
204
|
+
3. **Verify after every action.** After clicking, typing, or navigating, take a new snapshot to confirm the action succeeded. If it didn't, try an alternative interaction (e.g., if a dropdown didn't open on click, try pressing Space or Enter on the element).
|
|
205
|
+
4. **Never assume DOM structure.** Dropdowns may be `<select>`, `<mat-select>`, `<div role="listbox">`, or something else entirely. Use the snapshot to identify element types and interact accordingly.
|
|
206
|
+
5. **When stuck after 2 attempts, describe and ask.** Take a screenshot, describe what you see to the user, and ask for guidance.
|
|
207
207
|
|
|
208
208
|
## Anti-Loop Guardrails
|
|
209
209
|
|
|
210
210
|
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:
|
|
211
211
|
|
|
212
212
|
1. **Stop trying.** Do not continue retrying the same approach.
|
|
213
|
-
2. **Fall back to manual.** Tell the user what you were trying to do and ask them to complete that step manually in the
|
|
213
|
+
2. **Fall back to manual.** Tell the user what you were trying to do and ask them to complete that step manually in the Chrome window (which they can see on the side). Give them the direct URL and clear text instructions.
|
|
214
214
|
3. **Resume automation** at the next step once the user confirms the manual step is done.
|
|
215
215
|
|
|
216
|
-
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 — using
|
|
216
|
+
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 — using "Desktop app" as the OAuth application type.
|
|
217
217
|
|
|
218
218
|
## Things That Do Not Work — Do Not Attempt
|
|
219
219
|
|
|
220
220
|
These actions are technically impossible in the browser automation environment. Attempting them wastes time and leads to loops:
|
|
221
221
|
|
|
222
|
-
- **Downloading files.** `browser_click` on a Download button does not save files to disk.
|
|
223
|
-
- **
|
|
224
|
-
- **Clipboard operations.** You cannot copy/paste via browser automation.
|
|
222
|
+
- **Downloading files.** `browser_click` on a Download button does not save files to disk. There is NO JSON file to find at `~/Downloads` or anywhere else. Never click Download buttons.
|
|
223
|
+
- **Clipboard operations.** You cannot copy/paste via browser automation. The user must manually copy values from the Chrome window.
|
|
225
224
|
- **Deleting and recreating OAuth clients** to get a fresh secret — this orphans the stored client_id and causes `invalid_client` errors.
|
|
225
|
+
- **Navigating away from the credential dialog** before both credentials are stored — you will lose the Client Secret display and cannot get it back without creating a new client.
|
|
226
226
|
|
|
227
227
|
## Step 1: Single Upfront Confirmation
|
|
228
228
|
|
|
@@ -231,10 +231,11 @@ Use `ui_show` with `surface_type: "confirmation"` and this message:
|
|
|
231
231
|
> **Set up Google Cloud for Gmail & Calendar**
|
|
232
232
|
>
|
|
233
233
|
> Here's what will happen:
|
|
234
|
-
> 1. **A browser opens** — you
|
|
235
|
-
> 2. **
|
|
236
|
-
> 3. **
|
|
237
|
-
> 4. **
|
|
234
|
+
> 1. **A browser opens on the side** — you'll be able to watch everything I do
|
|
235
|
+
> 2. **You sign in** to your Google account in the browser
|
|
236
|
+
> 3. **I automate everything** — project creation, APIs, OAuth config, credentials
|
|
237
|
+
> 4. **One copy-paste** — I'll ask you to copy the Client Secret from the browser into a secure prompt
|
|
238
|
+
> 5. **You authorize Vellum** with one click
|
|
238
239
|
>
|
|
239
240
|
> The whole thing takes 2-3 minutes. Ready?
|
|
240
241
|
|
|
@@ -246,24 +247,32 @@ If the user declines, acknowledge and stop. No further confirmations are needed
|
|
|
246
247
|
|
|
247
248
|
Navigate to `https://console.cloud.google.com/`.
|
|
248
249
|
|
|
249
|
-
Take a screenshot
|
|
250
|
-
|
|
250
|
+
Take a screenshot to check the page state:
|
|
251
|
+
|
|
252
|
+
- **Sign-in page:** Tell the user: "Please sign in to your Google account in the Chrome window on the right side of your screen." Then auto-detect sign-in completion by polling with `browser_screenshot` every 5-10 seconds — check if the URL has moved away from `accounts.google.com` to `console.cloud.google.com`. 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! Starting the automated setup now..."
|
|
251
253
|
- **Already signed in:** Tell the user: "Already signed in — starting setup now..." and continue immediately.
|
|
252
254
|
- **CAPTCHA:** The browser automation's built-in handoff will handle this. If it persists, tell the user: "There's a CAPTCHA in the browser — please complete it and I'll continue automatically."
|
|
253
255
|
|
|
254
|
-
**
|
|
256
|
+
**What you should see when done:** URL contains `console.cloud.google.com` and no sign-in overlay is visible.
|
|
255
257
|
|
|
256
258
|
## Step 3: Create or Select a Project
|
|
257
259
|
|
|
258
260
|
**Goal:** A GCP project named "Vellum Assistant" exists and is selected.
|
|
259
261
|
|
|
260
|
-
Tell the user: "Creating Google Cloud project
|
|
262
|
+
Tell the user: "Creating Google Cloud project..."
|
|
263
|
+
|
|
264
|
+
Navigate to `https://console.cloud.google.com/projectcreate`.
|
|
265
|
+
|
|
266
|
+
Take a `browser_snapshot`. Find the project name input field (look for an element with label containing "Project name" or a text input near the top of the form). Type "Vellum Assistant" into it.
|
|
261
267
|
|
|
262
|
-
|
|
268
|
+
Look for a "Create" button in the snapshot and click it. Wait 10-15 seconds for project creation — take a screenshot to check for:
|
|
269
|
+
- **Success message** or redirect to the new project dashboard — note the project ID from the URL or page content.
|
|
270
|
+
- **"Project name already in use" error** — that's fine. Navigate to `https://console.cloud.google.com/cloud-resource-manager` to find and select the existing "Vellum Assistant" project. Use `browser_extract` to read the project ID from the page.
|
|
271
|
+
- **Organization restriction or quota error** — tell the user what happened and ask them to resolve it.
|
|
263
272
|
|
|
264
|
-
|
|
273
|
+
**What you should see when done:** The project selector in the top bar shows the project name, and you have the project ID (something like `vellum-assistant-12345`).
|
|
265
274
|
|
|
266
|
-
|
|
275
|
+
Tell the user: "Project created!"
|
|
267
276
|
|
|
268
277
|
## Step 4: Enable Gmail and Calendar APIs
|
|
269
278
|
|
|
@@ -275,98 +284,111 @@ Navigate to each API's library page and enable it if not already enabled:
|
|
|
275
284
|
1. `https://console.cloud.google.com/apis/library/gmail.googleapis.com?project=PROJECT_ID`
|
|
276
285
|
2. `https://console.cloud.google.com/apis/library/calendar-json.googleapis.com?project=PROJECT_ID`
|
|
277
286
|
|
|
278
|
-
For each page: take a
|
|
287
|
+
For each page: take a `browser_snapshot`. Look for:
|
|
288
|
+
- **"Enable" button** — click it, wait a few seconds, take another snapshot to confirm.
|
|
289
|
+
- **"Manage" button or "API enabled" text** — the API is already enabled. Skip it.
|
|
279
290
|
|
|
280
|
-
**
|
|
291
|
+
**What you should see when done:** Both API pages show "Manage" or "API enabled" status.
|
|
292
|
+
|
|
293
|
+
Tell the user: "APIs enabled!"
|
|
281
294
|
|
|
282
295
|
## Step 5: Configure OAuth Consent Screen
|
|
283
296
|
|
|
284
297
|
**Goal:** An OAuth consent screen is configured with External user type, the required scopes, and the user added as a test user.
|
|
285
298
|
|
|
286
|
-
Tell the user: "
|
|
299
|
+
Tell the user: "Setting up OAuth consent screen — this is the longest step but it's fully automated..."
|
|
287
300
|
|
|
288
301
|
Navigate to `https://console.cloud.google.com/apis/credentials/consent?project=PROJECT_ID`.
|
|
289
302
|
|
|
290
|
-
Take a
|
|
303
|
+
Take a `browser_snapshot` and `browser_screenshot`. Check the page state:
|
|
291
304
|
|
|
292
|
-
|
|
305
|
+
### If the consent screen is already configured
|
|
293
306
|
|
|
294
|
-
|
|
295
|
-
- Select "External" user type if prompted
|
|
296
|
-
- App name: "Vellum Assistant"
|
|
297
|
-
- User support email: select the user's email (this may be a dropdown or text input — adapt to what you see)
|
|
298
|
-
- Developer contact email: enter the user's email
|
|
299
|
-
- Submit / Save and Continue
|
|
307
|
+
You'll see a dashboard showing the app name ("Vellum Assistant" or similar) with an "Edit App" button. **Skip to Step 6.**
|
|
300
308
|
|
|
301
|
-
|
|
302
|
-
- Add these scopes:
|
|
303
|
-
- `https://www.googleapis.com/auth/gmail.readonly`
|
|
304
|
-
- `https://www.googleapis.com/auth/gmail.modify`
|
|
305
|
-
- `https://www.googleapis.com/auth/gmail.send`
|
|
306
|
-
- `https://www.googleapis.com/auth/calendar.readonly`
|
|
307
|
-
- `https://www.googleapis.com/auth/calendar.events`
|
|
308
|
-
- `https://www.googleapis.com/auth/userinfo.email`
|
|
309
|
-
- Save and Continue
|
|
309
|
+
### If you see a user type selection (External / Internal)
|
|
310
310
|
|
|
311
|
-
**
|
|
312
|
-
- Add the user's email as a test user
|
|
313
|
-
- Save and Continue
|
|
311
|
+
Select **"External"** and click **Create** or **Get Started**.
|
|
314
312
|
|
|
315
|
-
|
|
316
|
-
- Return to dashboard
|
|
313
|
+
### Consent screen form (wizard or single-page)
|
|
317
314
|
|
|
318
|
-
|
|
315
|
+
Google Cloud uses either a multi-page wizard or a single-page form. Adapt to what you see:
|
|
319
316
|
|
|
320
|
-
|
|
317
|
+
**App information section:**
|
|
318
|
+
- **App name**: Type "Vellum Assistant" in the app name field.
|
|
319
|
+
- **User support email**: This is typically a dropdown showing the signed-in user's email. Use `browser_snapshot` to find a `<select>` or clickable dropdown element near "User support email". Select the user's email.
|
|
320
|
+
- **Developer contact email**: Type the user's email into this field. (Use the same email visible in the support email dropdown if you can read it, or use `browser_extract` to find the email shown on the page.)
|
|
321
|
+
- Click **Save and Continue** if on a multi-page wizard.
|
|
321
322
|
|
|
322
|
-
**
|
|
323
|
+
**Scopes section:**
|
|
324
|
+
- Click **"Add or Remove Scopes"** (or similar button).
|
|
325
|
+
- In the scope picker dialog, look for a text input labeled **"Manually add scopes"** or **"Filter"** at the bottom or top of the dialog.
|
|
326
|
+
- Paste all 6 scopes at once as a comma-separated string into that input:
|
|
327
|
+
```
|
|
328
|
+
https://www.googleapis.com/auth/gmail.readonly,https://www.googleapis.com/auth/gmail.modify,https://www.googleapis.com/auth/gmail.send,https://www.googleapis.com/auth/calendar.readonly,https://www.googleapis.com/auth/calendar.events,https://www.googleapis.com/auth/userinfo.email
|
|
329
|
+
```
|
|
330
|
+
- Click **"Add to Table"** or **"Update"** to confirm the scopes.
|
|
331
|
+
- If no manual input is available, you'll need to search for and check each scope individually using the scope tree. Search for each scope URL in the filter box and check its checkbox.
|
|
332
|
+
- Click **Save and Continue** (or **Update** then **Save and Continue**).
|
|
323
333
|
|
|
324
|
-
|
|
334
|
+
**Test users section:**
|
|
335
|
+
- Click **"Add Users"** or similar.
|
|
336
|
+
- Enter the user's email address.
|
|
337
|
+
- Click **Add** then **Save and Continue**.
|
|
325
338
|
|
|
326
|
-
|
|
339
|
+
**Summary section:**
|
|
340
|
+
- Click **"Back to Dashboard"** or **"Submit"**.
|
|
327
341
|
|
|
328
|
-
|
|
329
|
-
2. Store the Client ID via `credential_store store`.
|
|
330
|
-
3. **IMMEDIATELY** present a `credential_store prompt` for the Client Secret. This is your ONLY next action after storing the Client ID. Do not attempt anything else.
|
|
331
|
-
4. Wait for the user to paste the secret.
|
|
342
|
+
**What you should see when done:** A consent screen dashboard showing "Vellum Assistant" as the app name.
|
|
332
343
|
|
|
333
|
-
|
|
334
|
-
- Do NOT click the Download button. There is no JSON file. Downloads do not work.
|
|
335
|
-
- Do NOT try to read the Client Secret from the screenshot. It is visible on screen but must come from the user via secure prompt to ensure accuracy.
|
|
336
|
-
- Do NOT navigate away from the dialog, close it, or interact with any other element until the user has pasted the secret.
|
|
337
|
-
- Do NOT mention JSON files, downloads, or `~/Downloads` to the user — none of these exist.
|
|
344
|
+
Tell the user: "Consent screen configured!"
|
|
338
345
|
|
|
339
|
-
|
|
346
|
+
## Step 6: Create OAuth Credentials and Capture Them
|
|
347
|
+
|
|
348
|
+
**Goal:** A "Desktop app" OAuth client exists, and both its Client ID and Client Secret are stored in the vault.
|
|
340
349
|
|
|
341
350
|
Tell the user: "Creating OAuth credentials..."
|
|
342
351
|
|
|
352
|
+
### 6a: Create the credential
|
|
353
|
+
|
|
343
354
|
Navigate to `https://console.cloud.google.com/apis/credentials?project=PROJECT_ID`.
|
|
344
355
|
|
|
345
|
-
|
|
356
|
+
Take a `browser_snapshot`. Find and click a button labeled **"Create Credentials"** or **"+ Create Credentials"**. A dropdown menu should appear — take another snapshot and click **"OAuth client ID"**.
|
|
346
357
|
|
|
347
|
-
On the creation form:
|
|
348
|
-
- Application type
|
|
349
|
-
- Name
|
|
350
|
-
- Do NOT add any redirect URIs
|
|
358
|
+
On the creation form (take a snapshot to see the fields):
|
|
359
|
+
- **Application type**: Find the dropdown and select **"Desktop app"**. This may be a `<select>` element or a custom dropdown — use the snapshot to identify it. You might need to click the dropdown first, then take another snapshot to see the options, then click "Desktop app".
|
|
360
|
+
- **Name**: Type "Vellum Assistant" in the name field.
|
|
361
|
+
- Do NOT add any redirect URIs — the desktop app flow doesn't need them.
|
|
351
362
|
|
|
352
|
-
|
|
363
|
+
Click **"Create"** to submit the form.
|
|
353
364
|
|
|
354
|
-
### 6b:
|
|
365
|
+
### 6b: Capture credentials from the dialog
|
|
355
366
|
|
|
356
|
-
After creation, a dialog will display the
|
|
367
|
+
After creation, a dialog will display the **Client ID** and **Client Secret**. This is the critical step.
|
|
357
368
|
|
|
358
|
-
**First**, read the **Client ID**
|
|
369
|
+
**First**, try to auto-read the **Client ID** using `browser_extract`. The Client ID matches the pattern `*.apps.googleusercontent.com`. Search the extracted text for this pattern. If found, store it:
|
|
359
370
|
|
|
360
371
|
```
|
|
361
372
|
credential_store store:
|
|
362
373
|
service: "integration:gmail"
|
|
363
374
|
field: "client_id"
|
|
364
|
-
value: "<the Client ID
|
|
375
|
+
value: "<the Client ID extracted from the page>"
|
|
365
376
|
```
|
|
366
377
|
|
|
367
|
-
|
|
378
|
+
If `browser_extract` fails to find the Client ID, prompt the user instead:
|
|
368
379
|
|
|
369
|
-
|
|
380
|
+
```
|
|
381
|
+
credential_store prompt:
|
|
382
|
+
service: "integration:gmail"
|
|
383
|
+
field: "client_id"
|
|
384
|
+
label: "Google OAuth Client ID"
|
|
385
|
+
description: "Copy the Client ID from the dialog in the Chrome window and paste it here. It looks like 123456789-xxxxx.apps.googleusercontent.com"
|
|
386
|
+
placeholder: "xxxxx.apps.googleusercontent.com"
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
**Then** — whether the Client ID was auto-read or prompted — tell the user:
|
|
390
|
+
|
|
391
|
+
> "Got the Client ID! Now I need the Client Secret. You can see it in the dialog in the Chrome window — it starts with `GOCSPX-`. Please copy it and paste it into the secure prompt below."
|
|
370
392
|
|
|
371
393
|
And present the secure prompt:
|
|
372
394
|
|
|
@@ -379,17 +401,19 @@ credential_store prompt:
|
|
|
379
401
|
placeholder: "GOCSPX-..."
|
|
380
402
|
```
|
|
381
403
|
|
|
382
|
-
Wait for the user to complete the prompt. Do not take any other
|
|
404
|
+
Wait for the user to complete the prompt. **Do not take any other browser actions until the user has pasted the secret.** The dialog must stay open so they can see and copy the value.
|
|
383
405
|
|
|
384
|
-
If the user has trouble locating the secret, take a `browser_screenshot` and
|
|
406
|
+
If the user has trouble locating the secret, take a `browser_screenshot` and describe where the secret field is on the screen — but do NOT attempt to read the secret value yourself. It must come from the user for accuracy.
|
|
385
407
|
|
|
386
|
-
**
|
|
408
|
+
**What you should see when done:** `credential_store list` shows both `client_id` and `client_secret` for `integration:gmail`.
|
|
409
|
+
|
|
410
|
+
Tell the user: "Credentials stored securely!"
|
|
387
411
|
|
|
388
412
|
## Step 7: OAuth2 Authorization
|
|
389
413
|
|
|
390
414
|
**Goal:** The user authorizes Vellum to access their Gmail and Calendar via OAuth.
|
|
391
415
|
|
|
392
|
-
Tell the user: "Opening Google
|
|
416
|
+
Tell the user: "Opening Google authorization — just click 'Allow' on the consent page."
|
|
393
417
|
|
|
394
418
|
Use `credential_store` with:
|
|
395
419
|
|
|
@@ -400,19 +424,19 @@ service: "integration:gmail"
|
|
|
400
424
|
|
|
401
425
|
This auto-reads client_id and client_secret from the secure store and auto-fills auth_url, token_url, scopes, and extra_params from well-known config.
|
|
402
426
|
|
|
403
|
-
**If the user sees a "This app isn't verified" warning:** Tell them this is normal for apps in testing mode. Click
|
|
427
|
+
**If the user sees a "This app isn't verified" warning:** Tell them: "You'll see an 'app isn't verified' warning — this is normal for personal apps in testing mode. Click **Advanced**, then **Go to Vellum Assistant (unsafe)** to proceed."
|
|
404
428
|
|
|
405
429
|
**Verify:** The `oauth2_connect` call returns a success message with the connected account email.
|
|
406
430
|
|
|
407
431
|
## Step 8: Done!
|
|
408
432
|
|
|
409
|
-
"**Gmail and Calendar are connected!** You can now read, search, and send emails, plus view and manage your calendar. Try asking me to check your inbox or show your upcoming events!"
|
|
433
|
+
Tell the user: "**Gmail and Calendar are connected!** You can now read, search, and send emails, plus view and manage your calendar. Try asking me to check your inbox or show your upcoming events!"
|
|
410
434
|
|
|
411
435
|
## Error Handling
|
|
412
436
|
|
|
413
437
|
- **Page load failures:** Retry navigation once. If it still fails, tell the user and ask them to check their internet connection.
|
|
414
438
|
- **Permission errors in GCP:** The user may need billing enabled or organization-level permissions. Explain clearly and ask them to resolve it.
|
|
415
439
|
- **Consent screen already configured:** Don't overwrite — skip to credential creation.
|
|
416
|
-
- **Element not found:** Take a fresh
|
|
440
|
+
- **Element not found:** Take a fresh `browser_snapshot` to re-assess. The GCP UI may have changed. Describe what you see and try alternative approaches. If stuck after 2 attempts, ask the user for guidance — they can see the Chrome window too.
|
|
417
441
|
- **OAuth flow timeout or failure:** Offer to retry. The credentials are already stored, so reconnecting only requires re-running the authorization flow.
|
|
418
442
|
- **Any unexpected state:** Take a `browser_screenshot`, describe what you see, and ask the user for guidance.
|
|
@@ -7,6 +7,21 @@ metadata: {"vellum": {"emoji": "💬"}}
|
|
|
7
7
|
|
|
8
8
|
You are a unified messaging assistant with access to multiple platforms (Slack, Gmail, Telegram, and more). Use the messaging tools to help users read, search, organize, draft, and send messages across all connected platforms.
|
|
9
9
|
|
|
10
|
+
## Email Routing Priority
|
|
11
|
+
|
|
12
|
+
When the user mentions "email" — sending, reading, checking, decluttering, drafting, or anything else — **always default to the user's own email (Gmail)** unless they explicitly ask about the assistant's own email address (e.g., "set up your email", "send from your address", "check your inbox"). The vast majority of email requests are about the user's Gmail, not the assistant's AgentMail address.
|
|
13
|
+
|
|
14
|
+
Do not offer AgentMail as an option or mention it unless the user specifically asks. If Gmail is not connected, guide them through Gmail setup — do not suggest AgentMail as an alternative.
|
|
15
|
+
|
|
16
|
+
## Communication Style
|
|
17
|
+
|
|
18
|
+
- **Be action-oriented.** When the user asks to do something ("declutter", "check my email"), start doing it immediately. Don't ask for permission to read their inbox — that's obviously what they want.
|
|
19
|
+
- **Keep it human.** Never mention OAuth, tokens, APIs, sandboxes, credential proxies, or other technical internals. If something isn't working, say "Gmail needs to be reconnected" — not "the OAuth2 access token for integration:gmail has expired."
|
|
20
|
+
- **Show progress.** When running a tool that scans many emails, tell the user what you're doing: "Scanning your inbox for clutter..." Don't go silent.
|
|
21
|
+
- **Be brief and warm.** One or two sentences per update is plenty. Don't over-explain what you're about to do — just do it and narrate lightly.
|
|
22
|
+
|
|
23
|
+
When a platform is connected (auth test succeeds), always use the messaging API tools for that platform. Never fall back to browser automation, shell commands (bash, curl), or any other approach for operations that messaging tools can handle. The messaging tools handle authentication internally — never try to access tokens or call APIs directly. Browser automation is only appropriate for initial credential setup (OAuth consent screens), not for day-to-day messaging operations.
|
|
24
|
+
|
|
10
25
|
## Connection Setup
|
|
11
26
|
|
|
12
27
|
Before using any messaging tool, verify that the platform is connected by calling `messaging_auth_test` with the appropriate `platform` parameter. If the call fails with a token/authorization error, follow the steps below.
|
|
@@ -15,6 +30,14 @@ Before using any messaging tool, verify that the platform is connected by callin
|
|
|
15
30
|
|
|
16
31
|
Gmail, Slack, and Telegram setup all require a publicly reachable URL for OAuth callbacks or webhook delivery. The **public-ingress** skill handles ngrok tunnel setup and persists the URL as `ingress.publicBaseUrl`. Each setup skill below declares `public-ingress` as a dependency and will prompt you to run it if `ingress.publicBaseUrl` is not configured.
|
|
17
32
|
|
|
33
|
+
### Email Connection Flow
|
|
34
|
+
|
|
35
|
+
When the user asks to "connect my email", "set up email", "manage my email", or similar — and has not named a specific provider:
|
|
36
|
+
|
|
37
|
+
1. **Discover what's connected.** Call `messaging_auth_test` for `gmail` (and any other email-capable platforms). If one succeeds, tell the user it's already connected and proceed with their request.
|
|
38
|
+
2. **If nothing is connected**, ask which provider they use — but keep it brief and conversational (e.g., "Which email do you use — Gmail, Outlook, etc.?"), not a numbered list of options with descriptions.
|
|
39
|
+
3. **Once the provider is known, act immediately.** Don't present setup options or explain OAuth. If it's Gmail, follow the Gmail section below. For any other provider, let the user know that only Gmail is fully supported right now, and offer to set up Gmail instead.
|
|
40
|
+
|
|
18
41
|
### Gmail
|
|
19
42
|
1. **Try connecting directly first.** Call `credential_store` with `action: "oauth2_connect"` and `service: "gmail"`. The tool auto-fills Google's OAuth endpoints and looks up any previously stored client credentials — so this single call may be all that's needed.
|
|
20
43
|
2. **If it fails because no client_id is found:** The user needs to create Google Cloud OAuth credentials first. Install and load the **google-oauth-setup** skill (which depends on **public-ingress** for the redirect URI):
|
|
@@ -70,12 +93,21 @@ If the user asks to verify their guardian identity for any channel (SMS, voice,
|
|
|
70
93
|
|
|
71
94
|
The guardian-verify-setup skill handles the full outbound verification flow for all supported channels. It collects the user's destination (phone number or Telegram chat ID/handle), initiates an outbound verification session, and guides the user through entering or replying with the verification code. This is the single source of truth for guardian verification setup -- do not duplicate the verification flow inline.
|
|
72
95
|
|
|
96
|
+
## Error Recovery
|
|
97
|
+
|
|
98
|
+
When a messaging tool fails with a token or authorization error:
|
|
99
|
+
|
|
100
|
+
1. **Try to reconnect silently.** Call `credential_store` with `action: "oauth2_connect"` and the appropriate `service`. This often resolves expired tokens automatically.
|
|
101
|
+
2. **If reconnection fails, go straight to setup.** Don't present options, ask which route the user prefers, or explain what went wrong technically. Just tell the user briefly (e.g., "Gmail needs to be reconnected — let me set that up") and immediately follow the connection setup flow for that platform (e.g., install and load **google-oauth-setup** for Gmail). The user came to you to get something done, not to troubleshoot OAuth — make it seamless.
|
|
102
|
+
3. **Never try alternative approaches.** Don't use bash, curl, browser automation, or any workaround. If the messaging tools can't do it, the reconnection flow is the answer.
|
|
103
|
+
4. **Never expose error details.** The user doesn't need to see error messages about tokens, OAuth, or API failures. Translate errors into plain language.
|
|
104
|
+
|
|
73
105
|
## Platform Selection
|
|
74
106
|
|
|
75
107
|
- If the user specifies a platform (e.g., "check my Slack"), pass it as the `platform` parameter.
|
|
76
108
|
- If only one platform is connected, it is auto-selected.
|
|
77
109
|
- If multiple platforms are connected and the user doesn't specify, ask which platform they mean — or search across all of them.
|
|
78
|
-
- **
|
|
110
|
+
- **Be action-oriented with email.** When the user says "email" and wants to *do* something (declutter, check, search, send), check what's connected first. If nothing is connected, ask which provider briefly and then go straight into setup — don't present menus, options lists, or explain the setup process. Just do it.
|
|
79
111
|
|
|
80
112
|
## Capabilities
|
|
81
113
|
|
|
@@ -217,27 +249,44 @@ Medium and high risk tools require a confidence score between 0 and 1:
|
|
|
217
249
|
|
|
218
250
|
Use `messaging_analyze_activity` to classify channels or conversations by activity level (high, medium, low, dead). Useful for decluttering — suggest leaving dead channels or archiving old emails.
|
|
219
251
|
|
|
220
|
-
##
|
|
252
|
+
## Email Decluttering
|
|
253
|
+
|
|
254
|
+
When a user asks to declutter, clean up, or organize their email — start scanning immediately. Don't ask what kind of cleanup they want or request permission to read their inbox. Go straight to scanning — but once results are ready, always show them via `ui_show` and let the user choose actions before archiving or unsubscribing.
|
|
255
|
+
|
|
256
|
+
### Provider Selection
|
|
221
257
|
|
|
222
|
-
Use `gmail_sender_digest`
|
|
258
|
+
- **Gmail connected**: Use the Gmail-specific tools (`gmail_sender_digest`, `gmail_archive_by_query`, `gmail_unsubscribe`, `gmail_filters`) — they have richer features like unsubscribe support and filter creation.
|
|
259
|
+
- **Non-Gmail email connected**: Use the generic tools (`messaging_sender_digest`, `messaging_archive_by_sender`) — they work with any provider that supports these operations. Skip unsubscribe and filter offers since they are Gmail-specific.
|
|
260
|
+
- **Nothing connected**: Ask which email provider they use. If it's Gmail, go straight into the Gmail connection flow. For other providers, let the user know only Gmail is supported right now and offer to set up Gmail instead. Don't present a menu of options or explain what OAuth is.
|
|
223
261
|
|
|
224
262
|
### Workflow
|
|
225
263
|
|
|
226
|
-
1. **Scan**: Call `gmail_sender_digest` (
|
|
264
|
+
1. **Scan**: Call `gmail_sender_digest` (or `messaging_sender_digest` for non-Gmail). Default query targets promotions from the last 90 days.
|
|
227
265
|
2. **Present**: Show results as a `ui_show` table with `selectionMode: "multiple"`:
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
|
|
231
|
-
-
|
|
232
|
-
-
|
|
233
|
-
-
|
|
234
|
-
|
|
266
|
+
- **Gmail columns (exactly 3)**: Sender, Emails Found, Unsub?
|
|
267
|
+
- **Non-Gmail columns (exactly 2)**: Sender, Emails Found (omit the Unsub? column — unsubscribe is not available)
|
|
268
|
+
- **Pre-select all rows** (`selected: true`) — users deselect what they want to keep
|
|
269
|
+
- **Caption**: "Showing emails from last 90 days in Promotions" (or adjusted to match the query used)
|
|
270
|
+
- **Gmail action buttons (exactly 2)**: "Archive & Unsubscribe" (primary), "Archive Only" (secondary). **NEVER offer Delete, Trash, or any destructive action.**
|
|
271
|
+
- **Non-Gmail action button (exactly 1)**: "Archive Selected" (primary). Do not offer an unsubscribe button — it is Gmail-specific. **NEVER offer Delete, Trash, or any destructive action.**
|
|
272
|
+
3. **Live progress**: After the user clicks an action button:
|
|
273
|
+
- **Dismiss the table immediately** with `ui_dismiss` — it collapses to a completion chip
|
|
274
|
+
- **Show a `task_progress` card** with one step per selected sender (e.g., "Archiving TechCrunch (247 emails)"). Update each step from `in_progress` → `completed` as each sender finishes.
|
|
275
|
+
- When all senders are processed, set the progress card's `status: "completed"`.
|
|
276
|
+
4. **Act on selection**: For each selected sender:
|
|
277
|
+
- Use `gmail_archive_by_query` (or `messaging_archive_by_sender` for non-Gmail) with the sender's `search_query` — this archives all matching messages in one call, regardless of volume
|
|
278
|
+
- If Gmail and the action is "Archive & Unsubscribe" and `has_unsubscribe` is true, call `gmail_unsubscribe` with the sender's `newest_message_id`
|
|
279
|
+
5. **Accurate summary**: Use the **actual counts returned by the archive tool**, not the scan counts from the digest. The scan is a sample; the archive is comprehensive. Format: "Cleaned up [total_archived] emails from [sender_count] senders." For Gmail, append: "Unsubscribed from [unsub_count]."
|
|
280
|
+
6. **Ongoing protection offer (Gmail only)**: After reporting results, offer auto-archive filters:
|
|
281
|
+
- "Want me to set up auto-archive filters so future emails from these senders skip your inbox?"
|
|
282
|
+
- If yes, call `gmail_filters` with `action: "create"` for each sender with `from` set to the sender's email and `remove_label_ids: ["INBOX"]`.
|
|
283
|
+
- Then offer a recurring declutter schedule: "Want me to scan for new clutter monthly?" If yes, use `schedule_create` to set up a monthly declutter check.
|
|
235
284
|
|
|
236
285
|
### Edge Cases
|
|
237
286
|
|
|
238
287
|
- **Zero results**: Tell the user "No newsletter emails found" and suggest broadening the query (e.g. removing the category filter or extending the date range)
|
|
239
288
|
- **Unsubscribe failures**: Report per-sender success/failure; the existing `gmail_unsubscribe` tool handles edge cases
|
|
240
|
-
- **Large sender counts**: The `has_more` flag indicates a sender had more messages than collected —
|
|
289
|
+
- **Large sender counts**: The `has_more` flag indicates a sender had more messages than collected — `gmail_archive_by_query` handles this automatically via its own pagination
|
|
241
290
|
|
|
242
291
|
## Batch Operations
|
|
243
292
|
|
|
@@ -1017,6 +1017,64 @@
|
|
|
1017
1017
|
"executor": "tools/gmail-sender-digest.ts",
|
|
1018
1018
|
"execution_target": "host"
|
|
1019
1019
|
},
|
|
1020
|
+
{
|
|
1021
|
+
"name": "messaging_sender_digest",
|
|
1022
|
+
"description": "Scan connected email platform and group messages by sender to identify high-volume senders (e.g. newsletters). Works with any email provider that supports sender digest. Returns top senders sorted by message count with metadata for bulk cleanup.",
|
|
1023
|
+
"category": "messaging",
|
|
1024
|
+
"risk": "low",
|
|
1025
|
+
"input_schema": {
|
|
1026
|
+
"type": "object",
|
|
1027
|
+
"properties": {
|
|
1028
|
+
"platform": {
|
|
1029
|
+
"type": "string",
|
|
1030
|
+
"description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected."
|
|
1031
|
+
},
|
|
1032
|
+
"query": {
|
|
1033
|
+
"type": "string",
|
|
1034
|
+
"description": "Search query (default 'category:promotions newer_than:90d')"
|
|
1035
|
+
},
|
|
1036
|
+
"max_messages": {
|
|
1037
|
+
"type": "number",
|
|
1038
|
+
"description": "Maximum messages to scan (default 500, cap 2000)"
|
|
1039
|
+
},
|
|
1040
|
+
"max_senders": {
|
|
1041
|
+
"type": "number",
|
|
1042
|
+
"description": "Maximum senders to return (default 30)"
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
},
|
|
1046
|
+
"executor": "tools/messaging-sender-digest.ts",
|
|
1047
|
+
"execution_target": "host"
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
"name": "messaging_archive_by_sender",
|
|
1051
|
+
"description": "Archive all messages matching a search query on the connected email platform. Paginates through all results and archives in bulk. Works with any email provider that supports archive by query. Include a confidence score (0-1).",
|
|
1052
|
+
"category": "messaging",
|
|
1053
|
+
"risk": "medium",
|
|
1054
|
+
"input_schema": {
|
|
1055
|
+
"type": "object",
|
|
1056
|
+
"properties": {
|
|
1057
|
+
"platform": {
|
|
1058
|
+
"type": "string",
|
|
1059
|
+
"description": "Platform (e.g. \"gmail\"). Auto-detected if only one is connected."
|
|
1060
|
+
},
|
|
1061
|
+
"query": {
|
|
1062
|
+
"type": "string",
|
|
1063
|
+
"description": "Search query (e.g. \"from:marketing@example.com category:promotions newer_than:90d\")"
|
|
1064
|
+
},
|
|
1065
|
+
"confidence": {
|
|
1066
|
+
"type": "number",
|
|
1067
|
+
"description": "Confidence score (0-1) for this action"
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
"required": [
|
|
1071
|
+
"query",
|
|
1072
|
+
"confidence"
|
|
1073
|
+
]
|
|
1074
|
+
},
|
|
1075
|
+
"executor": "tools/messaging-archive-by-sender.ts",
|
|
1076
|
+
"execution_target": "host"
|
|
1077
|
+
},
|
|
1020
1078
|
{
|
|
1021
1079
|
"name": "gmail_outreach_scan",
|
|
1022
1080
|
"description": "Scan Gmail for cold outreach emails (sales, recruiting, marketing) using LLM classification. Returns top outreach senders with suggested cleanup actions. Read-only \u2014 use gmail_batch_archive and gmail_filters for cleanup.",
|
|
@@ -161,7 +161,12 @@ export async function run(input: Record<string, unknown>, _context: ToolContext)
|
|
|
161
161
|
sample_subjects: s.sampleSubjects,
|
|
162
162
|
}));
|
|
163
163
|
|
|
164
|
-
return ok(JSON.stringify({
|
|
164
|
+
return ok(JSON.stringify({
|
|
165
|
+
senders: result,
|
|
166
|
+
total_scanned: allMessageIds.length,
|
|
167
|
+
query_used: query,
|
|
168
|
+
note: `message_count reflects emails found per sender within the ${allMessageIds.length} messages scanned. The archive tool may find additional messages beyond this sample.`,
|
|
169
|
+
}));
|
|
165
170
|
});
|
|
166
171
|
} catch (e) {
|
|
167
172
|
return err(e instanceof Error ? e.message : String(e));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
|
|
2
|
+
import { err, ok, resolveProvider, withProviderToken } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export async function run(input: Record<string, unknown>, _context: ToolContext): Promise<ToolExecutionResult> {
|
|
5
|
+
const platform = input.platform as string | undefined;
|
|
6
|
+
const query = input.query as string;
|
|
7
|
+
|
|
8
|
+
if (!query) {
|
|
9
|
+
return err('query is required.');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const provider = resolveProvider(platform);
|
|
14
|
+
|
|
15
|
+
if (!provider.archiveByQuery) {
|
|
16
|
+
return err(`The ${provider.displayName} provider does not support archive by query.`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return withProviderToken(provider, async (token) => {
|
|
20
|
+
const result = await provider.archiveByQuery!(token, query);
|
|
21
|
+
|
|
22
|
+
if (result.archived === 0) {
|
|
23
|
+
return ok('No messages matched the query. Nothing archived.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const summary = `Archived ${result.archived} message(s) matching query: ${query}`;
|
|
27
|
+
if (result.truncated) {
|
|
28
|
+
return ok(`${summary}\n\nNote: this operation was capped at 5000 messages. Additional messages matching the query may remain in the inbox. Run the command again to archive more.`);
|
|
29
|
+
}
|
|
30
|
+
return ok(summary);
|
|
31
|
+
});
|
|
32
|
+
} catch (e) {
|
|
33
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ToolContext, ToolExecutionResult } from '../../../../tools/types.js';
|
|
2
|
+
import { err, ok, resolveProvider, withProviderToken } from './shared.js';
|
|
3
|
+
|
|
4
|
+
export async function run(input: Record<string, unknown>, _context: ToolContext): Promise<ToolExecutionResult> {
|
|
5
|
+
const platform = input.platform as string | undefined;
|
|
6
|
+
const query = (input.query as string) ?? 'category:promotions newer_than:90d';
|
|
7
|
+
const maxMessages = input.max_messages as number | undefined;
|
|
8
|
+
const maxSenders = input.max_senders as number | undefined;
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
const provider = resolveProvider(platform);
|
|
12
|
+
|
|
13
|
+
if (!provider.senderDigest) {
|
|
14
|
+
return err(`The ${provider.displayName} provider does not support sender digest scanning.`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return withProviderToken(provider, async (token) => {
|
|
18
|
+
const result = await provider.senderDigest!(token, query, { maxMessages, maxSenders });
|
|
19
|
+
|
|
20
|
+
if (result.senders.length === 0) {
|
|
21
|
+
return ok(JSON.stringify({
|
|
22
|
+
senders: [],
|
|
23
|
+
total_scanned: result.totalScanned,
|
|
24
|
+
query_used: result.queryUsed,
|
|
25
|
+
message: 'No emails found matching the query. Try broadening the search (e.g. remove category filter or extend date range).',
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Map to snake_case output format for LLM consumption
|
|
30
|
+
const senders = result.senders.map((s) => ({
|
|
31
|
+
id: s.id,
|
|
32
|
+
display_name: s.displayName,
|
|
33
|
+
email: s.email,
|
|
34
|
+
message_count: s.messageCount,
|
|
35
|
+
has_unsubscribe: s.hasUnsubscribe,
|
|
36
|
+
newest_message_id: s.newestMessageId,
|
|
37
|
+
search_query: s.searchQuery,
|
|
38
|
+
message_ids: s.messageIds,
|
|
39
|
+
has_more: s.hasMore,
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
return ok(JSON.stringify({
|
|
43
|
+
senders,
|
|
44
|
+
total_scanned: result.totalScanned,
|
|
45
|
+
query_used: result.queryUsed,
|
|
46
|
+
note: `message_count reflects emails found per sender within the ${result.totalScanned} messages scanned. The archive tool may find additional messages beyond this sample.`,
|
|
47
|
+
}));
|
|
48
|
+
});
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return err(e instanceof Error ? e.message : String(e));
|
|
51
|
+
}
|
|
52
|
+
}
|