@vellumai/assistant 0.4.3 → 0.4.5
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 +40 -3
- package/README.md +43 -35
- 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__/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 +125 -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__/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 -87
- 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 +4 -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__/guardian-actions-endpoint.test.ts +19 -14
- package/src/__tests__/guardian-dispatch.test.ts +8 -0
- package/src/__tests__/guardian-outbound-http.test.ts +4 -4
- 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__/ipc-snapshot.test.ts +18 -51
- package/src/__tests__/non-member-access-request.test.ts +131 -8
- package/src/__tests__/notification-decision-fallback.test.ts +129 -4
- package/src/__tests__/notification-decision-strategy.test.ts +62 -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 +841 -39
- 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 -1
- package/src/__tests__/session-surfaces-task-progress.test.ts +1 -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__/twilio-config.test.ts +2 -13
- package/src/agent/loop.ts +1 -1
- package/src/approvals/guardian-decision-primitive.ts +10 -2
- package/src/approvals/guardian-request-resolvers.ts +128 -9
- package/src/calls/call-constants.ts +21 -0
- package/src/calls/call-controller.ts +9 -2
- package/src/calls/call-domain.ts +28 -7
- 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 +424 -12
- package/src/calls/twilio-config.ts +4 -11
- package/src/calls/twilio-routes.ts +1 -1
- package/src/calls/types.ts +3 -1
- 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 +146 -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 +1 -0
- package/src/config/calls-schema.ts +24 -0
- package/src/config/env.ts +22 -0
- package/src/config/feature-flag-registry.json +8 -0
- 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 +10 -9
- 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 +5 -55
- package/src/daemon/handlers/config-inbox.ts +9 -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/pairing.ts +2 -0
- package/src/daemon/handlers/sessions.ts +48 -3
- package/src/daemon/handlers/shared.ts +17 -2
- package/src/daemon/ipc-contract/integrations.ts +1 -99
- 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 +14 -1
- package/src/daemon/session-agent-loop-handlers.ts +20 -0
- package/src/daemon/session-agent-loop.ts +22 -11
- package/src/daemon/session-lifecycle.ts +1 -1
- package/src/daemon/session-process.ts +11 -1
- package/src/daemon/session-runtime-assembly.ts +3 -0
- package/src/daemon/session-surfaces.ts +3 -2
- 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/db-init.ts +4 -0
- package/src/memory/migrations/038-actor-token-records.ts +39 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema.ts +16 -0
- 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 +39 -1
- package/src/notifications/decision-engine.ts +22 -9
- package/src/notifications/destination-resolver.ts +16 -2
- package/src/notifications/emit-signal.ts +16 -8
- 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 +71 -1
- package/src/runtime/actor-token-service.ts +234 -0
- package/src/runtime/actor-token-store.ts +236 -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 +0 -3
- package/src/runtime/guardian-reply-router.ts +67 -30
- package/src/runtime/guardian-vellum-migration.ts +57 -0
- package/src/runtime/http-server.ts +65 -12
- package/src/runtime/http-types.ts +13 -0
- package/src/runtime/invite-redemption-service.ts +8 -0
- package/src/runtime/local-actor-identity.ts +76 -0
- package/src/runtime/middleware/actor-token.ts +271 -0
- package/src/runtime/routes/approval-routes.ts +82 -7
- package/src/runtime/routes/brain-graph-routes.ts +222 -0
- package/src/runtime/routes/channel-readiness-routes.ts +71 -0
- package/src/runtime/routes/conversation-routes.ts +140 -52
- package/src/runtime/routes/events-routes.ts +20 -5
- 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-message-handler.ts +143 -2
- 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/permission-checker.ts +15 -4
- package/src/tools/tool-approval-handler.ts +242 -18
- package/src/__tests__/handlers-twilio-config.test.ts +0 -1928
- package/src/daemon/handlers/config-twilio.ts +0 -1082
package/.env.example
CHANGED
package/ARCHITECTURE.md
CHANGED
|
@@ -22,6 +22,43 @@ This document owns assistant-runtime architecture details. The repo-level archit
|
|
|
22
22
|
- Voice calls mirror the same prompt contract: `CallController` receives guardian context on setup and refreshes it immediately after successful voice challenge verification, so the first post-verification turn is grounded as `actor_role: guardian`.
|
|
23
23
|
- Voice-specific behavior (DTMF/speech verification flow, relay state machine) remains voice-local; only actor-role resolution is shared.
|
|
24
24
|
|
|
25
|
+
### Vellum Guardian Identity Model (Actor Tokens + Bootstrap)
|
|
26
|
+
|
|
27
|
+
The vellum channel (macOS desktop, iOS, CLI) uses an identity-bound actor token system to authenticate guardian identity on HTTP routes. This replaces the previous implicit trust model where all local connections were assumed to be the guardian.
|
|
28
|
+
|
|
29
|
+
**Identity lifecycle:**
|
|
30
|
+
|
|
31
|
+
1. **Startup migration** — On daemon start, `ensureVellumGuardianBinding()` (in `guardian-vellum-migration.ts`) backfills a `channel='vellum'` guardian binding with a stable `guardianPrincipalId` (format: `vellum-principal-<uuid>`). Existing installations get a binding with `verifiedVia: 'startup-migration'`; new installs get one via bootstrap. This migration is idempotent and preserves bindings for other channels (Telegram, SMS, etc.).
|
|
32
|
+
|
|
33
|
+
2. **Hatch bootstrap (loopback-only, macOS)** — On every launch, the macOS client calls `POST /v1/integrations/guardian/vellum/bootstrap` with `{ platform: 'macos', deviceId }`. The endpoint is loopback-only: it rejects requests with `X-Forwarded-For` and verifies the peer IP is a loopback address (`127.0.0.1`, `::1`, `::ffff:127.0.0.1`). The endpoint is idempotent: it ensures a vellum guardian principal exists, revokes any prior token for the same device binding, mints a new HMAC-SHA256 signed actor token with a 90-day TTL, stores only the SHA-256 hash, and returns `{ guardianPrincipalId, actorToken, isNew }`. The raw token is returned once and never persisted on the server. macOS re-bootstraps on each startup to refresh its token.
|
|
34
|
+
|
|
35
|
+
3. **iOS pairing** — iOS devices obtain actor tokens exclusively through the QR pairing flow (re-pairs on credential loss). When an iOS device completes pairing, the pairing response includes an `actorToken` minted against the same vellum guardian principal. The pairing handler in `pairing-routes.ts` calls `mintPairingActorToken()` which looks up the vellum binding and mints a device-specific token. iOS does not call the bootstrap endpoint.
|
|
36
|
+
|
|
37
|
+
4. **IPC identity** — Local IPC connections (Unix domain socket from the macOS native app) do not send actor tokens. Instead, the daemon assigns a deterministic local actor identity via `resolveLocalIpcGuardianContext()` in `local-actor-identity.ts`. This looks up the vellum guardian binding and routes through the same `resolveGuardianContext` trust pipeline used by HTTP channel ingress. When no vellum binding exists yet (pre-bootstrap), a fallback guardian context is returned since the local macOS user is inherently the guardian of their own machine.
|
|
38
|
+
|
|
39
|
+
**Actor token format:** `base64url(JSON claims) + '.' + base64url(HMAC-SHA256 signature)`. Claims include `assistantId`, `platform`, `deviceId`, `guardianPrincipalId`, `iat`, `exp` (default: 90 days from issuance), and `jti`.
|
|
40
|
+
|
|
41
|
+
**Hash-only storage:** Only the SHA-256 hex digest of the raw token is persisted in the `actor_token_records` table. Token verification recomputes the hash and looks it up in the store to check revocation status. Tokens are scoped to `(assistantId, guardianPrincipalId, hashedDeviceId)` with a one-active-per-device invariant.
|
|
42
|
+
|
|
43
|
+
**Signing key management:** A 32-byte random signing key is generated on first startup and persisted at `~/.vellum/protected/actor-token-signing-key` with `chmod 0o600`. The key is loaded on subsequent startups via `loadOrCreateSigningKey()`.
|
|
44
|
+
|
|
45
|
+
**Strict HTTP enforcement:** Vellum-channel HTTP routes (POST /v1/messages, POST /v1/confirm, POST /v1/guardian-actions/decision, etc.) require a valid actor token via the `X-Actor-Token` header. The middleware in `middleware/actor-token.ts` verifies the HMAC signature, checks the token is active in the store, and resolves a guardian context through the standard trust pipeline. For backward compatibility with the CLI, requests without an actor token that originate from a loopback address (no `X-Forwarded-For` header) fall back to `resolveLocalIpcGuardianContext()`. Gateway-proxied requests (which carry `X-Forwarded-For`) without an actor token are rejected.
|
|
46
|
+
|
|
47
|
+
**Notification scoping:** Guardian-sensitive notifications (e.g., approval requests, access request alerts) are annotated with `targetGuardianPrincipalId` so the notification pipeline can scope delivery to the correct guardian identity across devices.
|
|
48
|
+
|
|
49
|
+
**Key source files:**
|
|
50
|
+
|
|
51
|
+
| File | Purpose |
|
|
52
|
+
|------|---------|
|
|
53
|
+
| `src/runtime/actor-token-service.ts` | HMAC-SHA256 mint/verify, signing key management, `hashToken` |
|
|
54
|
+
| `src/runtime/actor-token-store.ts` | Hash-only persistence: create, find by hash/device binding, revoke |
|
|
55
|
+
| `src/runtime/middleware/actor-token.ts` | HTTP middleware: `verifyHttpActorToken`, `verifyHttpActorTokenWithLocalFallback`, `isActorBoundGuardian` |
|
|
56
|
+
| `src/runtime/local-actor-identity.ts` | `resolveLocalIpcGuardianContext` — deterministic IPC identity |
|
|
57
|
+
| `src/runtime/guardian-vellum-migration.ts` | `ensureVellumGuardianBinding` — startup binding backfill |
|
|
58
|
+
| `src/runtime/routes/guardian-bootstrap-routes.ts` | `POST /v1/integrations/guardian/vellum/bootstrap` handler |
|
|
59
|
+
| `src/runtime/routes/pairing-routes.ts` | `mintPairingActorToken` — actor token in pairing response |
|
|
60
|
+
| `src/memory/guardian-bindings.ts` | Guardian binding persistence (shared across all channels) |
|
|
61
|
+
|
|
25
62
|
### Channel-Agnostic Scoped Approval Grants
|
|
26
63
|
|
|
27
64
|
Scoped approval grants allow a guardian's approval decision on one channel (e.g., Telegram) to authorize a tool execution on a different channel (e.g., voice). Two scope modes exist: `request_id` (bound to a specific pending request) and `tool_signature` (bound to `toolName` + canonical `inputDigest`). Grants are one-time-use, exact-match, fail-closed, and TTL-bound. Full architecture details (lifecycle flow, security invariants, key files) live in [`docs/architecture/security.md`](docs/architecture/security.md#channel-agnostic-scoped-approval-grants).
|
|
@@ -210,7 +247,7 @@ The SMS channel provides text-only messaging via Twilio, sharing the same teleph
|
|
|
210
247
|
2. The gateway authenticates the request via bearer token (same fail-closed model as `/deliver/telegram`).
|
|
211
248
|
3. The gateway sends the SMS via the Twilio Messages API using the configured `TWILIO_PHONE_NUMBER` as the `From` number.
|
|
212
249
|
|
|
213
|
-
**Setup**: Twilio credentials (Account SID, Auth Token) and phone number are managed via
|
|
250
|
+
**Setup**: Twilio credentials (Account SID, Auth Token) and phone number are managed via HTTP control-plane endpoints (`/v1/integrations/twilio/*`) exposed by the runtime and proxied by the gateway (see `src/runtime/routes/twilio-routes.ts`). A single phone number is shared across voice and SMS for each assistant. Both `provision` and `assign` endpoints auto-persist the number to config and secure storage, and auto-configure Twilio webhooks (voice URL, status callback, SMS URL) via the Twilio IncomingPhoneNumber API when a public ingress URL is available. When `assistantId` is provided, the number is persisted into the per-assistant mapping at `sms.assistantPhoneNumbers[assistantId]`, and the legacy `sms.phoneNumber` field is only set if it was previously empty/unset (acting as a fallback for single-assistant installs). This prevents multi-assistant assignments from clobbering each other's global outbound number. Without `assistantId`, the legacy field is always updated. Webhook configuration is best-effort — if ingress is not yet set up, the number is still assigned and webhooks can be configured later. Non-fatal webhook failures are surfaced as a `warning` field in the response.
|
|
214
251
|
|
|
215
252
|
**Phone Number Resolution**: At runtime, `getTwilioConfig()` resolves the phone number using this priority chain: (1) `TWILIO_PHONE_NUMBER` env var — highest priority, explicit override; (2) `sms.phoneNumber` in config — primary source of truth written by `provision_number`/`assign_number`; (3) `credential:twilio:phone_number` secure key — backward-compatible fallback. An error is thrown if no number is found after all sources are checked.
|
|
216
253
|
|
|
@@ -256,9 +293,9 @@ These can be set via environment variables or stored in the credential vault (ke
|
|
|
256
293
|
|
|
257
294
|
**Limitations (v1)**: Text-only — non-text message types are acknowledged but not forwarded; rich approval UI (inline buttons) is not supported.
|
|
258
295
|
|
|
259
|
-
**Channel Readiness**: The `
|
|
296
|
+
**Channel Readiness**: The channel readiness HTTP endpoints (`GET /v1/channels/readiness`, `POST /v1/channels/readiness/refresh`) backed by `ChannelReadinessService` in `src/runtime/channel-readiness-service.ts` provide a unified readiness subsystem for all channels. Each channel registers a `ChannelProbe` that runs synchronous local checks (credential presence, phone number, ingress config) and optional async remote checks with a 5-minute TTL cache. Built-in probes: SMS (Twilio credentials, phone number, ingress; remote checks query Twilio toll-free verification status for toll-free numbers) and Telegram (bot token, webhook secret, ingress). The GET endpoint returns cached snapshots; the refresh endpoint invalidates the cache first. Unknown channels return `unsupported_channel`. Route handlers live in `src/runtime/routes/channel-readiness-routes.ts`.
|
|
260
297
|
|
|
261
|
-
**SMS Compliance & Admin**: The
|
|
298
|
+
**SMS Compliance & Admin**: The Twilio HTTP control-plane endpoints extend beyond credential and number management with compliance and admin routes: `GET /v1/integrations/twilio/sms/compliance` detects toll-free vs local number type and fetches verification status; `POST/PATCH/DELETE /v1/integrations/twilio/sms/compliance/tollfree` manage the Twilio toll-free verification lifecycle; `POST /v1/integrations/twilio/numbers/release` removes a phone number from the Twilio account and clears all local references. All compliance actions validate required fields and Twilio enum values before calling the API.
|
|
262
299
|
|
|
263
300
|
### Slack Channel (Socket Mode)
|
|
264
301
|
|
package/README.md
CHANGED
|
@@ -201,29 +201,29 @@ The `/channels/inbound` endpoint requires a valid `X-Gateway-Origin` header to p
|
|
|
201
201
|
|
|
202
202
|
## Twilio Setup Primitive
|
|
203
203
|
|
|
204
|
-
Twilio is the shared telephony provider for both voice calls and SMS messaging. Configuration is managed through
|
|
205
|
-
|
|
206
|
-
###
|
|
207
|
-
|
|
208
|
-
The
|
|
209
|
-
|
|
210
|
-
|
|
|
211
|
-
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
218
|
-
|
|
|
219
|
-
|
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `
|
|
223
|
-
| `
|
|
224
|
-
| `
|
|
225
|
-
|
|
226
|
-
|
|
204
|
+
Twilio is the shared telephony provider for both voice calls and SMS messaging. Configuration is managed through HTTP control-plane endpoints exposed by the runtime and proxied by the gateway. For SMS-specific onboarding (including compliance verification and test sending), the `sms-setup` skill provides a guided conversational flow that layers on top of `twilio-setup`.
|
|
205
|
+
|
|
206
|
+
### Twilio HTTP Control-Plane Endpoints
|
|
207
|
+
|
|
208
|
+
The runtime exposes a RESTful HTTP API for Twilio configuration, credential management, phone number operations, and SMS compliance:
|
|
209
|
+
|
|
210
|
+
| Method | Path | Description |
|
|
211
|
+
|--------|------|-------------|
|
|
212
|
+
| GET | `/v1/integrations/twilio/config` | Returns current state: `hasCredentials` (boolean) and `phoneNumber` (if assigned) |
|
|
213
|
+
| POST | `/v1/integrations/twilio/credentials` | Validates and stores Account SID and Auth Token in secure storage (Keychain / encrypted file) |
|
|
214
|
+
| DELETE | `/v1/integrations/twilio/credentials` | Removes stored credentials. Preserves the phone number in both config and secure key so re-entering credentials resumes working without reassigning the number. |
|
|
215
|
+
| GET | `/v1/integrations/twilio/numbers` | Lists all incoming phone numbers on the Twilio account with their capabilities (voice, SMS) |
|
|
216
|
+
| POST | `/v1/integrations/twilio/numbers/provision` | Purchases a new phone number. Accepts optional `areaCode` and `country`. Auto-assigns and configures webhooks when ingress is available. |
|
|
217
|
+
| POST | `/v1/integrations/twilio/numbers/assign` | Assigns an existing Twilio phone number (E.164) and auto-configures webhooks when ingress is available |
|
|
218
|
+
| POST | `/v1/integrations/twilio/numbers/release` | Releases a phone number from the Twilio account and clears local references |
|
|
219
|
+
| GET | `/v1/integrations/twilio/sms/compliance` | Returns SMS compliance posture: number type (toll-free vs 10DLC) and toll-free verification status |
|
|
220
|
+
| POST | `/v1/integrations/twilio/sms/compliance/tollfree` | Submits a new toll-free verification request |
|
|
221
|
+
| PATCH | `/v1/integrations/twilio/sms/compliance/tollfree/:sid` | Updates an existing toll-free verification by SID |
|
|
222
|
+
| DELETE | `/v1/integrations/twilio/sms/compliance/tollfree/:sid` | Deletes a toll-free verification by SID |
|
|
223
|
+
| POST | `/v1/integrations/twilio/sms/test` | Sends a test SMS and polls for delivery status |
|
|
224
|
+
| POST | `/v1/integrations/twilio/sms/doctor` | Runs comprehensive SMS health diagnostics |
|
|
225
|
+
|
|
226
|
+
All endpoints are bearer-authenticated via the runtime HTTP token. Skills and clients should call the gateway URL (default `http://localhost:7830`) rather than the runtime port directly, as the gateway proxies all `/v1/integrations/twilio/*` routes.
|
|
227
227
|
|
|
228
228
|
### Ingress Webhook Reconciliation
|
|
229
229
|
|
|
@@ -241,9 +241,9 @@ Each assistant is assigned a single Twilio phone number that is shared between v
|
|
|
241
241
|
|
|
242
242
|
#### Assistant-Scoped Phone Numbers
|
|
243
243
|
|
|
244
|
-
When `assistantId` is provided in the
|
|
244
|
+
When `assistantId` is provided in the Twilio control-plane request, the provision and assign endpoints persist the phone number into a per-assistant mapping at `sms.assistantPhoneNumbers` (a `Record<string, string>` keyed by assistant ID). The legacy `sms.phoneNumber` field is always updated for backward compatibility.
|
|
245
245
|
|
|
246
|
-
The `
|
|
246
|
+
The config endpoint (`GET /v1/integrations/twilio/config`), when called with `assistantId`, resolves the phone number by checking `sms.assistantPhoneNumbers[assistantId]` first, falling back to `sms.phoneNumber`. This allows multiple assistants to have distinct phone numbers while preserving existing behavior for single-assistant setups.
|
|
247
247
|
|
|
248
248
|
The per-assistant mapping is propagated to the gateway via the config file watcher, enabling phone-number-based routing at the gateway boundary (see Gateway README).
|
|
249
249
|
|
|
@@ -271,6 +271,16 @@ The channel guardian service generates verification challenge instructions with
|
|
|
271
271
|
- **Rebind requirement:** Creating a new guardian challenge when a binding already exists requires `rebind: true` in the IPC request. Without it, the daemon returns `already_bound`. This prevents accidental guardian replacement.
|
|
272
272
|
- **Takeover prevention:** Verification is rejected when an active binding exists for a different external user. Same-user re-verification is allowed.
|
|
273
273
|
|
|
274
|
+
### Vellum Guardian Identity (Actor Tokens)
|
|
275
|
+
|
|
276
|
+
The vellum channel (macOS, iOS, CLI) uses HMAC-SHA256 signed actor tokens to bind guardian identity to HTTP requests. This enables identity-based authentication for the local desktop/mobile channel, paralleling how external channels (Telegram, SMS) use `externalUserId` for guardian identity.
|
|
277
|
+
|
|
278
|
+
- **Bootstrap**: After hatch, the macOS client calls `POST /v1/integrations/guardian/vellum/bootstrap` with `{ platform, deviceId }`. Returns `{ guardianPrincipalId, actorToken, isNew }`. The endpoint is idempotent -- repeated calls with the same device return the same principal but mint a fresh token (revoking the previous one).
|
|
279
|
+
- **iOS pairing**: The pairing response includes an `actorToken` automatically when a vellum guardian binding exists.
|
|
280
|
+
- **IPC fallback**: Local IPC (Unix socket) connections resolve identity server-side via `resolveLocalIpcGuardianContext()` without requiring an actor token. CLI connections that pass bearer auth but lack an actor token also use this fallback (as long as the request is direct, not proxied through the gateway).
|
|
281
|
+
- **HTTP enforcement**: Vellum HTTP routes require either an `X-Actor-Token` header or a provably-local connection (no `X-Forwarded-For` header). Gateway-proxied requests without an actor token are rejected.
|
|
282
|
+
- **Startup migration**: On daemon start, `ensureVellumGuardianBinding()` backfills a vellum guardian binding for existing installations so the identity system works without requiring a manual bootstrap step.
|
|
283
|
+
|
|
274
284
|
## Guardian Verification and Ingress ACL
|
|
275
285
|
|
|
276
286
|
This section documents the end-to-end flow from guardian verification through ingress membership enforcement, showing how the two systems work together to gate channel access.
|
|
@@ -352,18 +362,16 @@ These endpoints share the same business logic as the IPC-based verification flow
|
|
|
352
362
|
|
|
353
363
|
## Channel Readiness
|
|
354
364
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
### `channel_readiness` IPC Contract
|
|
365
|
+
Channel readiness is exposed via HTTP control-plane endpoints that provide a unified way to check whether a channel (SMS, Telegram, etc.) is fully configured and operational. Local checks (credential presence, phone number assignment, ingress config) run synchronously; optional remote checks (API reachability) run asynchronously with a 5-minute TTL cache.
|
|
358
366
|
|
|
359
|
-
|
|
360
|
-
|--------|-------------|
|
|
361
|
-
| `get` | Returns readiness snapshots for the specified channel (or all channels if omitted). Local checks always run; remote checks run only when `includeRemote=true` and cache is stale. |
|
|
362
|
-
| `refresh` | Invalidates the cache for the specified channel (or all channels), then returns fresh snapshots. |
|
|
367
|
+
### Channel Readiness HTTP Endpoints
|
|
363
368
|
|
|
364
|
-
|
|
369
|
+
| Method | Path | Description |
|
|
370
|
+
|--------|------|-------------|
|
|
371
|
+
| GET | `/v1/channels/readiness` | Returns readiness snapshots for the specified channel (query param `channel`, optional) or all channels. Local checks always run; remote checks run only when `includeRemote=true` and cache is stale. |
|
|
372
|
+
| POST | `/v1/channels/readiness/refresh` | Invalidates the cache for the specified channel (or all channels), then returns fresh snapshots. Body: `{ channel?: ChannelId, includeRemote?: boolean }` |
|
|
365
373
|
|
|
366
|
-
|
|
374
|
+
All endpoints are bearer-authenticated. Skills and clients should call the gateway URL (default `http://localhost:7830`) rather than the runtime port directly, as the gateway proxies all `/v1/channels/readiness*` routes.
|
|
367
375
|
|
|
368
376
|
### Built-in Channel Probes
|
|
369
377
|
|
|
@@ -376,7 +384,7 @@ Response type: `channel_readiness_response` with `success`, optional `snapshots`
|
|
|
376
384
|
|------|---------|
|
|
377
385
|
| `src/runtime/channel-readiness-types.ts` | Shared types: `ChannelId`, `ReadinessCheckResult`, `ChannelReadinessSnapshot`, `ChannelProbe` |
|
|
378
386
|
| `src/runtime/channel-readiness-service.ts` | Service class with probe registration, cached readiness evaluation, and built-in SMS/Telegram probes |
|
|
379
|
-
| `src/
|
|
387
|
+
| `src/runtime/routes/channel-readiness-routes.ts` | HTTP route handlers for `/v1/channels/readiness` and `/v1/channels/readiness/refresh` |
|
|
380
388
|
|
|
381
389
|
## Ingress Membership + Escalation
|
|
382
390
|
|
package/package.json
CHANGED
|
@@ -668,22 +668,6 @@ exports[`IPC message snapshots ClientMessage types telegram_config serializes to
|
|
|
668
668
|
}
|
|
669
669
|
`;
|
|
670
670
|
|
|
671
|
-
exports[`IPC message snapshots ClientMessage types twilio_config serializes to expected JSON 1`] = `
|
|
672
|
-
{
|
|
673
|
-
"action": "get",
|
|
674
|
-
"type": "twilio_config",
|
|
675
|
-
}
|
|
676
|
-
`;
|
|
677
|
-
|
|
678
|
-
exports[`IPC message snapshots ClientMessage types channel_readiness serializes to expected JSON 1`] = `
|
|
679
|
-
{
|
|
680
|
-
"action": "get",
|
|
681
|
-
"channel": "sms",
|
|
682
|
-
"includeRemote": true,
|
|
683
|
-
"type": "channel_readiness",
|
|
684
|
-
}
|
|
685
|
-
`;
|
|
686
|
-
|
|
687
671
|
exports[`IPC message snapshots ClientMessage types guardian_verification serializes to expected JSON 1`] = `
|
|
688
672
|
{
|
|
689
673
|
"action": "create_challenge",
|
|
@@ -1157,6 +1141,40 @@ exports[`IPC message snapshots ClientMessage types generate_avatar serializes to
|
|
|
1157
1141
|
}
|
|
1158
1142
|
`;
|
|
1159
1143
|
|
|
1144
|
+
exports[`IPC message snapshots ClientMessage types guardian_actions_pending_request serializes to expected JSON 1`] = `
|
|
1145
|
+
{
|
|
1146
|
+
"conversationId": "conv-guardian-001",
|
|
1147
|
+
"type": "guardian_actions_pending_request",
|
|
1148
|
+
}
|
|
1149
|
+
`;
|
|
1150
|
+
|
|
1151
|
+
exports[`IPC message snapshots ClientMessage types guardian_action_decision serializes to expected JSON 1`] = `
|
|
1152
|
+
{
|
|
1153
|
+
"action": "approve_once",
|
|
1154
|
+
"conversationId": "conv-guardian-001",
|
|
1155
|
+
"requestId": "req-guardian-001",
|
|
1156
|
+
"type": "guardian_action_decision",
|
|
1157
|
+
}
|
|
1158
|
+
`;
|
|
1159
|
+
|
|
1160
|
+
exports[`IPC message snapshots ClientMessage types reorder_threads serializes to expected JSON 1`] = `
|
|
1161
|
+
{
|
|
1162
|
+
"type": "reorder_threads",
|
|
1163
|
+
"updates": [
|
|
1164
|
+
{
|
|
1165
|
+
"displayOrder": 0,
|
|
1166
|
+
"isPinned": false,
|
|
1167
|
+
"sessionId": "sess-001",
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
"displayOrder": 1,
|
|
1171
|
+
"isPinned": true,
|
|
1172
|
+
"sessionId": "sess-002",
|
|
1173
|
+
},
|
|
1174
|
+
],
|
|
1175
|
+
}
|
|
1176
|
+
`;
|
|
1177
|
+
|
|
1160
1178
|
exports[`IPC message snapshots ServerMessage types auth_result serializes to expected JSON 1`] = `
|
|
1161
1179
|
{
|
|
1162
1180
|
"success": true,
|
|
@@ -1288,6 +1306,30 @@ exports[`IPC message snapshots ServerMessage types confirmation_request serializ
|
|
|
1288
1306
|
}
|
|
1289
1307
|
`;
|
|
1290
1308
|
|
|
1309
|
+
exports[`IPC message snapshots ServerMessage types confirmation_state_changed serializes to expected JSON 1`] = `
|
|
1310
|
+
{
|
|
1311
|
+
"causedByRequestId": "req-003",
|
|
1312
|
+
"decisionText": "approve",
|
|
1313
|
+
"requestId": "req-002",
|
|
1314
|
+
"sessionId": "sess-001",
|
|
1315
|
+
"source": "inline_nl",
|
|
1316
|
+
"state": "approved",
|
|
1317
|
+
"type": "confirmation_state_changed",
|
|
1318
|
+
}
|
|
1319
|
+
`;
|
|
1320
|
+
|
|
1321
|
+
exports[`IPC message snapshots ServerMessage types assistant_activity_state serializes to expected JSON 1`] = `
|
|
1322
|
+
{
|
|
1323
|
+
"activityVersion": 1,
|
|
1324
|
+
"anchor": "assistant_turn",
|
|
1325
|
+
"phase": "thinking",
|
|
1326
|
+
"reason": "message_dequeued",
|
|
1327
|
+
"requestId": "req-003",
|
|
1328
|
+
"sessionId": "sess-001",
|
|
1329
|
+
"type": "assistant_activity_state",
|
|
1330
|
+
}
|
|
1331
|
+
`;
|
|
1332
|
+
|
|
1291
1333
|
exports[`IPC message snapshots ServerMessage types message_complete serializes to expected JSON 1`] = `
|
|
1292
1334
|
{
|
|
1293
1335
|
"attachments": [
|
|
@@ -2269,76 +2311,6 @@ exports[`IPC message snapshots ServerMessage types telegram_config_response seri
|
|
|
2269
2311
|
}
|
|
2270
2312
|
`;
|
|
2271
2313
|
|
|
2272
|
-
exports[`IPC message snapshots ServerMessage types twilio_config_response serializes to expected JSON 1`] = `
|
|
2273
|
-
{
|
|
2274
|
-
"compliance": {
|
|
2275
|
-
"numberType": "toll_free",
|
|
2276
|
-
"verificationSid": "TF_VER_001",
|
|
2277
|
-
"verificationStatus": "TWILIO_APPROVED",
|
|
2278
|
-
},
|
|
2279
|
-
"diagnostics": {
|
|
2280
|
-
"actionItems": [],
|
|
2281
|
-
"compliance": {
|
|
2282
|
-
"detail": "Toll-free verification: TWILIO_APPROVED",
|
|
2283
|
-
"status": "TWILIO_APPROVED",
|
|
2284
|
-
},
|
|
2285
|
-
"overallStatus": "healthy",
|
|
2286
|
-
"readiness": {
|
|
2287
|
-
"issues": [],
|
|
2288
|
-
"ready": true,
|
|
2289
|
-
},
|
|
2290
|
-
},
|
|
2291
|
-
"hasCredentials": true,
|
|
2292
|
-
"phoneNumber": "+15551234567",
|
|
2293
|
-
"success": true,
|
|
2294
|
-
"testResult": {
|
|
2295
|
-
"finalStatus": "delivered",
|
|
2296
|
-
"initialStatus": "queued",
|
|
2297
|
-
"messageSid": "SM-test-001",
|
|
2298
|
-
"to": "+15559876543",
|
|
2299
|
-
},
|
|
2300
|
-
"type": "twilio_config_response",
|
|
2301
|
-
}
|
|
2302
|
-
`;
|
|
2303
|
-
|
|
2304
|
-
exports[`IPC message snapshots ServerMessage types channel_readiness_response serializes to expected JSON 1`] = `
|
|
2305
|
-
{
|
|
2306
|
-
"snapshots": [
|
|
2307
|
-
{
|
|
2308
|
-
"channel": "sms",
|
|
2309
|
-
"checkedAt": 1700000000000,
|
|
2310
|
-
"localChecks": [
|
|
2311
|
-
{
|
|
2312
|
-
"message": "Twilio credentials are not configured",
|
|
2313
|
-
"name": "twilio_credentials",
|
|
2314
|
-
"passed": false,
|
|
2315
|
-
},
|
|
2316
|
-
{
|
|
2317
|
-
"message": "Phone number is assigned",
|
|
2318
|
-
"name": "phone_number",
|
|
2319
|
-
"passed": true,
|
|
2320
|
-
},
|
|
2321
|
-
{
|
|
2322
|
-
"message": "Public ingress URL is configured",
|
|
2323
|
-
"name": "ingress",
|
|
2324
|
-
"passed": true,
|
|
2325
|
-
},
|
|
2326
|
-
],
|
|
2327
|
-
"ready": false,
|
|
2328
|
-
"reasons": [
|
|
2329
|
-
{
|
|
2330
|
-
"code": "twilio_credentials",
|
|
2331
|
-
"text": "Twilio credentials are not configured",
|
|
2332
|
-
},
|
|
2333
|
-
],
|
|
2334
|
-
"stale": false,
|
|
2335
|
-
},
|
|
2336
|
-
],
|
|
2337
|
-
"success": true,
|
|
2338
|
-
"type": "channel_readiness_response",
|
|
2339
|
-
}
|
|
2340
|
-
`;
|
|
2341
|
-
|
|
2342
2314
|
exports[`IPC message snapshots ServerMessage types guardian_verification_response serializes to expected JSON 1`] = `
|
|
2343
2315
|
{
|
|
2344
2316
|
"instruction": "Send this code to the Telegram bot",
|
|
@@ -3122,40 +3094,6 @@ exports[`IPC message snapshots ServerMessage types generate_avatar_response seri
|
|
|
3122
3094
|
}
|
|
3123
3095
|
`;
|
|
3124
3096
|
|
|
3125
|
-
exports[`IPC message snapshots ClientMessage types guardian_actions_pending_request serializes to expected JSON 1`] = `
|
|
3126
|
-
{
|
|
3127
|
-
"conversationId": "conv-guardian-001",
|
|
3128
|
-
"type": "guardian_actions_pending_request",
|
|
3129
|
-
}
|
|
3130
|
-
`;
|
|
3131
|
-
|
|
3132
|
-
exports[`IPC message snapshots ClientMessage types guardian_action_decision serializes to expected JSON 1`] = `
|
|
3133
|
-
{
|
|
3134
|
-
"action": "approve_once",
|
|
3135
|
-
"conversationId": "conv-guardian-001",
|
|
3136
|
-
"requestId": "req-guardian-001",
|
|
3137
|
-
"type": "guardian_action_decision",
|
|
3138
|
-
}
|
|
3139
|
-
`;
|
|
3140
|
-
|
|
3141
|
-
exports[`IPC message snapshots ClientMessage types reorder_threads serializes to expected JSON 1`] = `
|
|
3142
|
-
{
|
|
3143
|
-
"type": "reorder_threads",
|
|
3144
|
-
"updates": [
|
|
3145
|
-
{
|
|
3146
|
-
"displayOrder": 0,
|
|
3147
|
-
"isPinned": false,
|
|
3148
|
-
"sessionId": "sess-001",
|
|
3149
|
-
},
|
|
3150
|
-
{
|
|
3151
|
-
"displayOrder": 1,
|
|
3152
|
-
"isPinned": true,
|
|
3153
|
-
"sessionId": "sess-002",
|
|
3154
|
-
},
|
|
3155
|
-
],
|
|
3156
|
-
}
|
|
3157
|
-
`;
|
|
3158
|
-
|
|
3159
3097
|
exports[`IPC message snapshots ServerMessage types guardian_actions_pending_response serializes to expected JSON 1`] = `
|
|
3160
3098
|
{
|
|
3161
3099
|
"conversationId": "conv-guardian-001",
|