@vellumai/assistant 0.4.29 → 0.4.30
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/ARCHITECTURE.md +39 -37
- package/README.md +5 -6
- package/docs/runbook-trusted-contacts.md +79 -43
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -3
- package/scripts/test.sh +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +4 -37
- package/src/__tests__/actor-token-service.test.ts +4 -3
- package/src/__tests__/app-executors.test.ts +7 -17
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -10
- package/src/__tests__/browser-skill-endstate.test.ts +10 -1
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +44 -44
- package/src/__tests__/channel-approval.test.ts +8 -0
- package/src/__tests__/channel-approvals.test.ts +39 -1
- package/src/__tests__/channel-guardian.test.ts +15 -5
- package/src/__tests__/channel-reply-delivery.test.ts +31 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +4 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +9 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +2 -2
- package/src/__tests__/guardian-grant-minting.test.ts +6 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +34 -11
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +4 -6
- package/src/__tests__/inbound-invite-redemption.test.ts +1 -1
- package/src/__tests__/integrations-cli.test.ts +3 -27
- package/src/__tests__/intent-routing.test.ts +3 -0
- package/src/__tests__/invite-redemption-service.test.ts +1 -1
- package/src/__tests__/{ingress-routes-http.test.ts → invite-routes-http.test.ts} +40 -320
- package/src/__tests__/ipc-snapshot.test.ts +4 -31
- package/src/__tests__/nl-approval-parser.test.ts +305 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +34 -0
- package/src/__tests__/provider-error-scenarios.test.ts +68 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/retry-after-extraction.test.ts +111 -0
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +127 -0
- package/src/__tests__/session-media-retry.test.ts +147 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +9 -5
- package/src/__tests__/skill-feature-flags.test.ts +18 -12
- package/src/__tests__/skill-load-feature-flag.test.ts +4 -3
- package/src/__tests__/slack-block-formatting.test.ts +100 -0
- package/src/__tests__/slack-inbound-verification.test.ts +346 -0
- package/src/__tests__/slack-reaction-approvals.test.ts +77 -0
- package/src/__tests__/slack-skill.test.ts +3 -2
- package/src/__tests__/starter-task-flow.test.ts +0 -1
- package/src/__tests__/trusted-contact-verification.test.ts +3 -1
- package/src/__tests__/voice-invite-redemption.test.ts +1 -1
- package/src/amazon/client.ts +7 -24
- package/src/calls/relay-server.ts +39 -11
- package/src/channels/config.ts +1 -1
- package/src/cli/integrations.ts +10 -66
- package/src/config/bundled-skills/app-builder/SKILL.md +193 -1500
- package/src/config/bundled-skills/app-builder/TOOLS.json +70 -18
- package/src/config/bundled-skills/browser/TOOLS.json +59 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +4 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +50 -2
- package/src/config/bundled-skills/contacts/SKILL.md +42 -35
- package/src/config/bundled-skills/contacts/TOOLS.json +22 -2
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +38 -58
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +11 -31
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +19 -37
- package/src/config/bundled-skills/document/TOOLS.json +8 -0
- package/src/config/bundled-skills/email-setup/SKILL.md +10 -7
- package/src/config/bundled-skills/followups/TOOLS.json +12 -0
- package/src/config/bundled-skills/google-calendar/TOOLS.json +124 -26
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +54 -21
- package/src/config/bundled-skills/image-studio/TOOLS.json +12 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +14 -8
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +13 -3
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -1
- package/src/config/bundled-skills/media-processing/TOOLS.json +28 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +26 -6
- package/src/config/bundled-skills/messaging/TOOLS.json +228 -182
- package/src/config/bundled-skills/notifications/SKILL.md +3 -2
- package/src/config/bundled-skills/notifications/TOOLS.json +7 -13
- package/src/config/bundled-skills/phone-calls/TOOLS.json +13 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +16 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +15 -2
- package/src/config/bundled-skills/schedule/SKILL.md +33 -15
- package/src/config/bundled-skills/schedule/TOOLS.json +17 -1
- package/src/config/bundled-skills/slack/SKILL.md +30 -1
- package/src/config/bundled-skills/slack/TOOLS.json +89 -2
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +146 -0
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +120 -0
- package/src/config/bundled-skills/slack-app-setup/SKILL.md +200 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +22 -2
- package/src/config/bundled-skills/tasks/TOOLS.json +86 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +4 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +20 -0
- package/src/config/bundled-skills/weather/TOOLS.json +4 -0
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/channel-permission-profiles.ts +155 -0
- package/src/config/env.ts +4 -1
- package/src/contacts/contact-store.ts +195 -4
- package/src/contacts/types.ts +26 -0
- package/src/daemon/assistant-attachments.ts +23 -3
- package/src/daemon/guardian-verification-intent.ts +7 -4
- package/src/daemon/handlers/apps.ts +1 -2
- package/src/daemon/handlers/config-inbox.ts +16 -134
- package/src/daemon/handlers/guardian-actions.ts +20 -87
- package/src/daemon/handlers/sessions.ts +0 -1
- package/src/daemon/ipc-contract/apps.ts +0 -1
- package/src/daemon/ipc-contract/inbox.ts +7 -66
- package/src/daemon/ipc-contract/sessions.ts +1 -0
- package/src/daemon/ipc-contract/surfaces.ts +0 -1
- package/src/daemon/ipc-contract-inventory.json +2 -4
- package/src/daemon/lifecycle.ts +14 -2
- package/src/daemon/session-agent-loop-handlers.ts +9 -0
- package/src/daemon/session-agent-loop.ts +1 -0
- package/src/daemon/session-attachments.ts +5 -1
- package/src/daemon/session-error.ts +18 -0
- package/src/daemon/session-lifecycle.ts +4 -5
- package/src/daemon/session-media-retry.ts +15 -1
- package/src/daemon/session-surfaces.ts +0 -1
- package/src/daemon/session-tool-setup.ts +7 -4
- package/src/events/domain-events.ts +2 -1
- package/src/home-base/prebuilt/seed.ts +0 -1
- package/src/influencer/client.ts +7 -24
- package/src/media/gemini-image-service.ts +48 -3
- package/src/memory/app-store.ts +0 -4
- package/src/memory/conversation-attention-store.ts +3 -1
- package/src/memory/db-init.ts +4 -0
- package/src/memory/migrations/133-assistant-contact-metadata.ts +21 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema.ts +12 -0
- package/src/memory/slack-thread-store.ts +187 -0
- package/src/messaging/providers/slack/client.ts +84 -26
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/adapters/slack.ts +90 -0
- package/src/notifications/destination-resolver.ts +42 -1
- package/src/notifications/emit-signal.ts +17 -1
- package/src/oauth/provider-profiles.ts +22 -0
- package/src/providers/anthropic/client.ts +3 -0
- package/src/providers/openai/client.ts +3 -0
- package/src/providers/retry.ts +9 -1
- package/src/runtime/actor-trust-resolver.ts +8 -0
- package/src/runtime/auth/require-bound-guardian.ts +44 -0
- package/src/runtime/auth/route-policy.ts +4 -8
- package/src/runtime/channel-approval-types.ts +18 -0
- package/src/runtime/channel-approvals.ts +8 -0
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +62 -3
- package/src/runtime/gateway-client.ts +36 -2
- package/src/runtime/gateway-internal-client.ts +86 -0
- package/src/runtime/guardian-action-service.ts +127 -0
- package/src/runtime/guardian-verification-templates.ts +16 -1
- package/src/runtime/http-server.ts +20 -49
- package/src/runtime/invite-redemption-service.ts +1 -1
- package/src/runtime/{ingress-service.ts → invite-service.ts} +5 -157
- package/src/runtime/nl-approval-parser.ts +138 -0
- package/src/runtime/routes/approval-routes.ts +1 -40
- package/src/runtime/routes/channel-route-shared.ts +35 -1
- package/src/runtime/routes/contact-routes.ts +196 -28
- package/src/runtime/routes/guardian-action-routes.ts +19 -111
- package/src/runtime/routes/guardian-approval-interception.ts +76 -0
- package/src/runtime/routes/inbound-message-handler.ts +40 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +222 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +108 -0
- package/src/runtime/routes/{ingress-routes.ts → invite-routes.ts} +10 -110
- package/src/runtime/slack-block-formatting.ts +176 -0
- package/src/schedule/scheduler.ts +11 -2
- package/src/tools/apps/executors.ts +16 -15
- package/src/tools/calls/call-end.ts +1 -1
- package/src/tools/computer-use/definitions.ts +16 -0
- package/src/tools/credentials/vault.ts +86 -2
- package/src/tools/network/script-proxy/session-manager.ts +28 -3
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/terminal/shell.ts +15 -5
- package/src/tools/tool-approval-handler.ts +48 -4
- package/src/tools/types.ts +38 -1
- package/src/util/errors.ts +5 -1
- package/src/util/retry.ts +21 -0
- package/src/watcher/providers/slack.ts +33 -3
- /package/src/memory/{ingress-invite-store.ts → invite-store.ts} +0 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -433,31 +433,33 @@ External users who are not the guardian can gain access to the assistant through
|
|
|
433
433
|
|
|
434
434
|
**Notification signals:** The flow emits signals at each lifecycle transition via `emitNotificationSignal()`:
|
|
435
435
|
|
|
436
|
-
- `ingress.access_request` —
|
|
436
|
+
- `ingress.access_request` — unknown contact denied, guardian notified
|
|
437
437
|
- `ingress.trusted_contact.guardian_decision` — guardian approved or denied
|
|
438
438
|
- `ingress.trusted_contact.verification_sent` — code created and delivered
|
|
439
|
-
- `ingress.trusted_contact.activated` — requester verified,
|
|
439
|
+
- `ingress.trusted_contact.activated` — requester verified, contact active
|
|
440
440
|
- `ingress.trusted_contact.denied` — guardian explicitly denied
|
|
441
441
|
|
|
442
442
|
**HTTP API (for management):**
|
|
443
443
|
|
|
444
|
-
| Endpoint
|
|
445
|
-
|
|
|
446
|
-
| `/v1/
|
|
447
|
-
| `/v1/
|
|
448
|
-
| `/v1/
|
|
449
|
-
| `/v1/
|
|
444
|
+
| Endpoint | Method | Description |
|
|
445
|
+
| --------------------------- | ------ | ---------------------------------------------------------------- |
|
|
446
|
+
| `/v1/contacts` | GET | List contacts (filterable by role, search by query/channel/etc.) |
|
|
447
|
+
| `/v1/contacts` | POST | Create or update a contact |
|
|
448
|
+
| `/v1/contacts/:id` | GET | Get a contact by ID |
|
|
449
|
+
| `/v1/contacts/merge` | POST | Merge two contacts |
|
|
450
|
+
| `/v1/contacts/channels/:id` | PATCH | Update a contact channel's status/policy |
|
|
450
451
|
|
|
451
452
|
**Key source files:**
|
|
452
453
|
|
|
453
454
|
| File | Purpose |
|
|
454
455
|
| ------------------------------------------------------ | ----------------------------------------------------------------------------- |
|
|
455
|
-
| `src/runtime/routes/inbound-message-handler.ts` | Ingress ACL,
|
|
456
|
+
| `src/runtime/routes/inbound-message-handler.ts` | Ingress ACL, unknown-contact rejection, verification code interception |
|
|
456
457
|
| `src/runtime/routes/access-request-decision.ts` | Guardian decision → verification session creation |
|
|
457
458
|
| `src/runtime/routes/guardian-approval-interception.ts` | Routes guardian decisions (button + conversational) to access request handler |
|
|
458
459
|
| `src/runtime/channel-guardian-service.ts` | Verification challenge lifecycle, identity binding, rate limiting |
|
|
459
|
-
| `src/runtime/routes/
|
|
460
|
-
| `src/runtime/
|
|
460
|
+
| `src/runtime/routes/contact-routes.ts` | HTTP API handlers for contact and channel management |
|
|
461
|
+
| `src/runtime/routes/invite-routes.ts` | HTTP API handlers for invite management |
|
|
462
|
+
| `src/runtime/invite-service.ts` | Business logic for invite operations |
|
|
461
463
|
| `src/contacts/contact-store.ts` | Contact read queries — lookup, search, list, and channel operations |
|
|
462
464
|
| `src/memory/channel-guardian-store.ts` | Approval request and verification challenge persistence |
|
|
463
465
|
| `src/config/bundled-skills/contacts/SKILL.md` | Unified skill for contact management, access control, and invite links |
|
|
@@ -482,7 +484,7 @@ A complementary access-granting flow where the guardian proactively creates a sh
|
|
|
482
484
|
├─────────────────────────────────────────────────────────────┤
|
|
483
485
|
│ Core Redemption Engine (invite-redemption-service.ts) │
|
|
484
486
|
│ Channel-agnostic token validation, expiry, use-count, │
|
|
485
|
-
│ channel-match enforcement,
|
|
487
|
+
│ channel-match enforcement, contact activation/reactivation │
|
|
486
488
|
│ Returns: InviteRedemptionOutcome (discriminated union) │
|
|
487
489
|
│ Reply templates: invite-redemption-templates.ts │
|
|
488
490
|
└─────────────────────────────────────────────────────────────┘
|
|
@@ -496,12 +498,12 @@ A complementary access-granting flow where the guardian proactively creates a sh
|
|
|
496
498
|
4. Guardian shares the link with the invitee out-of-band.
|
|
497
499
|
5. Invitee clicks the link, opening Telegram which sends `/start iv_<token>` to the bot.
|
|
498
500
|
6. The gateway forwards the message to `/channels/inbound`. The inbound handler calls `getTransport('telegram').extractInboundToken()` to parse the `iv_` token.
|
|
499
|
-
7. The token is redeemed via `invite-redemption-service.ts`, which validates,
|
|
501
|
+
7. The token is redeemed via `invite-redemption-service.ts`, which validates, activates the contact, and returns a `redeemed` outcome.
|
|
500
502
|
8. A deterministic welcome message is delivered to the invitee (bypasses the LLM pipeline).
|
|
501
503
|
|
|
502
504
|
**Token prefix convention:** The `iv_` prefix distinguishes invite tokens from `gv_` (guardian verification) tokens. Both use the same Telegram `/start` deep-link mechanism but are routed to different handlers.
|
|
503
505
|
|
|
504
|
-
**Inbound intercept points:** Invite token extraction runs early in the inbound handler, before ACL denial, so valid invites short-circuit the
|
|
506
|
+
**Inbound intercept points:** Invite token extraction runs early in the inbound handler, before ACL denial, so valid invites short-circuit the contact check. Two intercept branches handle: (a) unknown contacts — the invite creates their first contact record; (b) inactive contacts (revoked/pending) — the invite reactivates them.
|
|
505
507
|
|
|
506
508
|
**Channel adapter status:**
|
|
507
509
|
|
|
@@ -518,8 +520,8 @@ Voice invites use a short numeric code (4-10 digits, default 6) instead of a URL
|
|
|
518
520
|
|
|
519
521
|
**Creation flow:**
|
|
520
522
|
|
|
521
|
-
1. Guardian creates a voice invite via `POST /v1/
|
|
522
|
-
2. `
|
|
523
|
+
1. Guardian creates a voice invite via `POST /v1/contacts/invites` with `sourceChannel: "voice"` and `expectedExternalUserId` (E.164 phone).
|
|
524
|
+
2. `invite-service.ts` generates a cryptographically random numeric code (`generateVoiceCode`), hashes it with SHA-256 (`hashVoiceCode`), and stores only the hash.
|
|
523
525
|
3. The one-time plaintext `voiceCode` is returned in the creation response. The raw token is NOT returned for voice invites — redemption uses the identity-bound code flow exclusively.
|
|
524
526
|
4. Guardian communicates the code to the invitee out-of-band.
|
|
525
527
|
|
|
@@ -528,7 +530,7 @@ Voice invites use a short numeric code (4-10 digits, default 6) instead of a URL
|
|
|
528
530
|
1. Unknown caller dials in. `relay-server.ts` resolves trust via `resolveActorTrust`. Caller is `unknown`, no pending guardian challenge.
|
|
529
531
|
2. The relay checks `findActiveVoiceInvites` for invites bound to the caller's phone number.
|
|
530
532
|
3. If active, non-expired invites exist, the relay enters the `invite_redemption_pending` state (reuses the `verification_pending` connection state) and prompts the caller with personalized copy: `Welcome <friend-name>. Please enter the 6-digit code that <guardian-name> provided you to verify your identity.`
|
|
531
|
-
4. `redeemVoiceInviteCode` validates: identity match, code hash match, expiry, use count. On success,
|
|
533
|
+
4. `redeemVoiceInviteCode` validates: identity match, code hash match, expiry, use count. On success, the contact is activated and the call transitions to the normal call flow.
|
|
532
534
|
5. On invalid/expired code, the caller hears deterministic failure copy: `Sorry, the code you provided is incorrect or has since expired. Please ask <guardian-name> for a new code. Goodbye.` and the call ends immediately.
|
|
533
535
|
|
|
534
536
|
**Security invariants:**
|
|
@@ -536,24 +538,24 @@ Voice invites use a short numeric code (4-10 digits, default 6) instead of a URL
|
|
|
536
538
|
- The plaintext voice code is returned exactly once at creation time and never stored.
|
|
537
539
|
- Voice invites are identity-bound: `expectedExternalUserId` must match the caller's E.164 number. An attacker with the code but the wrong phone number cannot redeem.
|
|
538
540
|
- Failure responses are intentionally generic (`invalid_or_expired`) to prevent oracle attacks.
|
|
539
|
-
- Blocked
|
|
541
|
+
- Blocked contacts cannot bypass the guardian's explicit block via invite redemption.
|
|
540
542
|
|
|
541
543
|
**Key source files:**
|
|
542
544
|
|
|
543
|
-
| File | Purpose
|
|
544
|
-
| --------------------------------------------------- |
|
|
545
|
-
| `src/runtime/invite-redemption-service.ts` | Core redemption engine — token validation, voice code redemption,
|
|
546
|
-
| `src/runtime/invite-redemption-templates.ts` | Deterministic reply templates for each redemption outcome
|
|
547
|
-
| `src/runtime/channel-invite-transport.ts` | Transport adapter registry with `buildShareableInvite` / `extractInboundToken` interface
|
|
548
|
-
| `src/runtime/channel-invite-transports/telegram.ts` | Telegram adapter — `t.me/<bot>?start=iv_<token>` deep links, `/start iv_<token>` extraction
|
|
549
|
-
| `src/runtime/channel-invite-transports/voice.ts` | Voice transport adapter — code-based redemption metadata
|
|
550
|
-
| `src/daemon/guardian-invite-intent.ts` | Intent detection — routes create/list/revoke requests into the contacts skill
|
|
551
|
-
| `src/runtime/
|
|
552
|
-
| `src/runtime/routes/
|
|
553
|
-
| `src/runtime/routes/inbound-message-handler.ts` | Invite token intercept in the inbound flow (
|
|
554
|
-
| `src/calls/relay-server.ts` | Voice relay state machine — `invite_redemption_pending` subflow (always-on canonical behavior)
|
|
555
|
-
| `src/util/voice-code.ts` | Cryptographic voice code generation and SHA-256 hashing
|
|
556
|
-
| `src/memory/
|
|
545
|
+
| File | Purpose |
|
|
546
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
547
|
+
| `src/runtime/invite-redemption-service.ts` | Core redemption engine — token validation, voice code redemption, contact activation, discriminated-union outcomes |
|
|
548
|
+
| `src/runtime/invite-redemption-templates.ts` | Deterministic reply templates for each redemption outcome |
|
|
549
|
+
| `src/runtime/channel-invite-transport.ts` | Transport adapter registry with `buildShareableInvite` / `extractInboundToken` interface |
|
|
550
|
+
| `src/runtime/channel-invite-transports/telegram.ts` | Telegram adapter — `t.me/<bot>?start=iv_<token>` deep links, `/start iv_<token>` extraction |
|
|
551
|
+
| `src/runtime/channel-invite-transports/voice.ts` | Voice transport adapter — code-based redemption metadata |
|
|
552
|
+
| `src/daemon/guardian-invite-intent.ts` | Intent detection — routes create/list/revoke requests into the contacts skill |
|
|
553
|
+
| `src/runtime/invite-service.ts` | Shared business logic for invite operations (used by both HTTP routes and IPC) |
|
|
554
|
+
| `src/runtime/routes/invite-routes.ts` | HTTP API handlers for invite management including voice invite creation and redemption |
|
|
555
|
+
| `src/runtime/routes/inbound-message-handler.ts` | Invite token intercept in the inbound flow (unknown-contact and inactive-contact branches) |
|
|
556
|
+
| `src/calls/relay-server.ts` | Voice relay state machine — `invite_redemption_pending` subflow (always-on canonical behavior) |
|
|
557
|
+
| `src/util/voice-code.ts` | Cryptographic voice code generation and SHA-256 hashing |
|
|
558
|
+
| `src/memory/invite-store.ts` | Invite persistence including `findActiveVoiceInvites` for identity-bound lookup |
|
|
557
559
|
|
|
558
560
|
### Voice Inbound Security Model (Canonical)
|
|
559
561
|
|
|
@@ -595,7 +597,7 @@ When no invite exists and no pending guardian challenge is active, the relay ent
|
|
|
595
597
|
2. On name capture, `notifyGuardianOfAccessRequest` creates a canonical guardian request (`kind: 'access_request'`) and notifies the guardian via the notification pipeline.
|
|
596
598
|
3. The relay transitions to `awaiting_guardian_decision` and plays hold music/messaging while polling the canonical request status.
|
|
597
599
|
4. The guardian approves or denies via any channel (Telegram, SMS, desktop). All decisions route through `applyCanonicalGuardianDecision`, which dispatches to the `access_request` resolver in `guardian-request-resolvers.ts`.
|
|
598
|
-
5. On approval: the resolver directly activates the caller as a trusted contact (
|
|
600
|
+
5. On approval: the resolver directly activates the caller as a trusted contact (sets channel `status: 'active'`, `policy: 'allow'`), the poll detects the approved status, the relay transitions to the normal call flow with the caller's guardian context updated.
|
|
599
601
|
6. On denial or timeout: the caller hears a denial message and the call ends.
|
|
600
602
|
|
|
601
603
|
**Path 3: Inbound guardian verification (pending challenge)**
|
|
@@ -888,7 +890,7 @@ graph LR
|
|
|
888
890
|
C12["tool_permission_simulate<br/>toolName, input, workingDir?,<br/>isInteractive?, forcePromptSideEffects?,<br/>executionTarget?"]
|
|
889
891
|
C13["conversation_search<br/>query, limit?,<br/>maxMessagesPerConversation?"]
|
|
890
892
|
C14["ingress_invite<br/>create / list / revoke / redeem"]
|
|
891
|
-
C15["
|
|
893
|
+
C15["contacts<br/>list / get / update_channel"]
|
|
892
894
|
end
|
|
893
895
|
|
|
894
896
|
SOCKET["Unix Socket<br/>~/.vellum/vellum.sock<br/>───────────────<br/>Newline-delimited JSON<br/>Max 96MB per message<br/>Ping/pong every 30s<br/>Auto-reconnect<br/>1s → 30s backoff"]
|
|
@@ -920,7 +922,7 @@ graph LR
|
|
|
920
922
|
S22["tool_permission_simulate_response<br/>decision, riskLevel, reason?,<br/>promptPayload?, matchedRuleId?"]
|
|
921
923
|
S23["conversation_search_response<br/>query, results[]: conversationId,<br/>title, updatedAt, matchingMessages[]"]
|
|
922
924
|
S24["ingress_invite_response<br/>invite / invites"]
|
|
923
|
-
S25["
|
|
925
|
+
S25["contacts_response<br/>contact / contacts"]
|
|
924
926
|
end
|
|
925
927
|
|
|
926
928
|
C0 --> SOCKET
|
|
@@ -2204,8 +2206,8 @@ Connected channels are resolved at signal emission time: vellum is always includ
|
|
|
2204
2206
|
| Guardian bindings | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; revoked bindings retained |
|
|
2205
2207
|
| Guardian verification challenges | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; consumed/expired challenges retained |
|
|
2206
2208
|
| Guardian approval requests | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; decision outcome retained |
|
|
2207
|
-
|
|
|
2208
|
-
|
|
|
2209
|
+
| Contact invites | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; token hash stored, raw token never persisted |
|
|
2210
|
+
| Contacts & channels | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; revoked/blocked contacts retained |
|
|
2209
2211
|
| Notification events | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; deduplicated by dedupeKey |
|
|
2210
2212
|
| Notification decisions | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; FK to notification_events |
|
|
2211
2213
|
| Notification deliveries | `~/.vellum/workspace/data/db/assistant.db` | SQLite | Drizzle ORM | Permanent; FK to notification_decisions |
|
package/README.md
CHANGED
|
@@ -337,7 +337,7 @@ Guardian verification and ingress contact management are complementary but indep
|
|
|
337
337
|
| `src/runtime/trust-context-resolver.ts` | Actor role classification: guardian / non-guardian / unverified_channel |
|
|
338
338
|
| `src/runtime/routes/inbound-message-handler.ts` | Ingress ACL enforcement, verification-code intercept, escalation creation |
|
|
339
339
|
| `src/contacts/contact-store.ts` | Contact + channel CRUD: `findContactChannel`, `upsertContact`, `updateChannelStatus`, `searchContacts` |
|
|
340
|
-
| `src/memory/
|
|
340
|
+
| `src/memory/invite-store.ts` | Invite lifecycle: `createInvite`, `redeemInvite` (atomically creates member record) |
|
|
341
341
|
| `src/memory/channel-guardian-store.ts` | Persistence for guardian bindings, verification challenges, and approval requests |
|
|
342
342
|
| `src/runtime/guardian-outbound-actions.ts` | Shared business logic for outbound verification (start/resend/cancel) |
|
|
343
343
|
| `src/runtime/routes/integration-routes.ts` | HTTP route handlers for outbound guardian verification endpoints |
|
|
@@ -432,7 +432,7 @@ Redemption auto-creates a **member** record with an access policy:
|
|
|
432
432
|
- **`deny`** — Messages are rejected with a refusal notice.
|
|
433
433
|
- **`escalate`** — Messages are held for guardian (owner) approval before processing.
|
|
434
434
|
|
|
435
|
-
Non-members (senders with no invite redemption) are denied by default.
|
|
435
|
+
Non-members (senders with no invite redemption) are denied by default. Contacts can be listed, updated, revoked, or blocked via the HTTP API (`/v1/contacts` and `/v1/contacts/channels`).
|
|
436
436
|
|
|
437
437
|
### Escalation Flow
|
|
438
438
|
|
|
@@ -447,15 +447,14 @@ If no guardian binding exists, escalation fails closed — the message is denied
|
|
|
447
447
|
| Message Type | Actions | Description |
|
|
448
448
|
| ---------------- | ---------------------------- | ------------------------------------------------------------------------ |
|
|
449
449
|
| `ingress_invite` | create, list, revoke, redeem | Manage invite tokens (SHA-256 hashed, raw token returned once on create) |
|
|
450
|
-
| `ingress_member` | list, upsert, revoke, block | Manage member records and access policies |
|
|
451
450
|
|
|
452
451
|
### Key Modules
|
|
453
452
|
|
|
454
453
|
| File | Purpose |
|
|
455
454
|
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
456
|
-
| `src/memory/
|
|
455
|
+
| `src/memory/invite-store.ts` | CRUD for invite tokens with SHA-256 hashing and expiry |
|
|
457
456
|
| `src/contacts/contact-store.ts` | Contact + channel CRUD with policy enforcement |
|
|
458
|
-
| `src/daemon/handlers/config-inbox.ts` | IPC handlers for
|
|
457
|
+
| `src/daemon/handlers/config-inbox.ts` | IPC handlers for invite contract |
|
|
459
458
|
| `src/daemon/ipc-contract/inbox.ts` | TypeScript type definitions for ingress IPC messages |
|
|
460
459
|
| `src/runtime/routes/channel-routes.ts` | ACL enforcement point — member lookup, policy check, escalation creation |
|
|
461
460
|
| `src/runtime/invite-redemption-service.ts` | Core redemption engine — token validation, member creation, discriminated-union outcomes |
|
|
@@ -463,7 +462,7 @@ If no guardian binding exists, escalation fails closed — the message is denied
|
|
|
463
462
|
| `src/runtime/channel-invite-transport.ts` | Transport adapter registry — `buildShareableInvite` / `extractInboundToken` per channel |
|
|
464
463
|
| `src/runtime/channel-invite-transports/telegram.ts` | Telegram adapter — builds `t.me/<bot>?start=iv_<token>` deep links, extracts `iv_` tokens from `/start` commands |
|
|
465
464
|
| `src/daemon/guardian-invite-intent.ts` | Intent detection — routes guardian invite management requests into the `contacts` skill |
|
|
466
|
-
| `src/runtime/
|
|
465
|
+
| `src/runtime/invite-service.ts` | Shared business logic for invite and contact operations (HTTP + IPC) |
|
|
467
466
|
|
|
468
467
|
## Database
|
|
469
468
|
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# Trusted Contacts — Operator Runbook
|
|
2
2
|
|
|
3
|
-
Operational procedures for inspecting, managing, and debugging the trusted contact access flow.
|
|
3
|
+
Operational procedures for inspecting, managing, and debugging the trusted contact access flow. HTTP commands use the assistant runtime API (default `http://localhost:7821`) with bearer authentication.
|
|
4
|
+
|
|
5
|
+
> **Note:** The `/v1/contacts` endpoints are served by the assistant runtime, not
|
|
6
|
+
> the gateway. If you prefer to route through the gateway (`localhost:7830`), set
|
|
7
|
+
> `GATEWAY_RUNTIME_PROXY_ENABLED=true` in the gateway environment — the proxy is
|
|
8
|
+
> disabled by default and these routes will 404 without it.
|
|
4
9
|
|
|
5
10
|
## Prerequisites
|
|
6
11
|
|
|
7
12
|
```bash
|
|
8
|
-
# Base URL (adjust if using a non-default port)
|
|
9
|
-
BASE=http://localhost:
|
|
13
|
+
# Base URL — assistant runtime (adjust if using a non-default port)
|
|
14
|
+
BASE=http://localhost:7821
|
|
10
15
|
|
|
11
16
|
# Bearer token: if running via the assistant's shell tools, $GATEWAY_AUTH_TOKEN
|
|
12
17
|
# is injected automatically. For manual operator use, mint a token via the CLI
|
|
@@ -14,51 +19,74 @@ BASE=http://localhost:7830
|
|
|
14
19
|
TOKEN=$GATEWAY_AUTH_TOKEN
|
|
15
20
|
```
|
|
16
21
|
|
|
17
|
-
## 1. Inspect Trusted Contacts
|
|
22
|
+
## 1. Inspect Trusted Contacts
|
|
18
23
|
|
|
19
24
|
### List all active trusted contacts
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
|
-
curl -s "$BASE/v1/
|
|
27
|
+
curl -s "$BASE/v1/contacts?role=contact" \
|
|
23
28
|
-H "Authorization: Bearer $TOKEN" | jq
|
|
24
29
|
```
|
|
25
30
|
|
|
26
|
-
### Filter by channel
|
|
31
|
+
### Filter by channel type
|
|
27
32
|
|
|
28
33
|
```bash
|
|
29
34
|
# Telegram contacts only
|
|
30
|
-
curl -s "$BASE/v1/
|
|
35
|
+
curl -s "$BASE/v1/contacts?channelType=telegram" \
|
|
31
36
|
-H "Authorization: Bearer $TOKEN" | jq
|
|
32
37
|
|
|
33
38
|
# SMS contacts only
|
|
34
|
-
curl -s "$BASE/v1/
|
|
39
|
+
curl -s "$BASE/v1/contacts?channelType=sms" \
|
|
35
40
|
-H "Authorization: Bearer $TOKEN" | jq
|
|
36
41
|
```
|
|
37
42
|
|
|
38
|
-
### List all
|
|
43
|
+
### List all contacts (including revoked and blocked)
|
|
39
44
|
|
|
40
45
|
```bash
|
|
41
|
-
curl -s "$BASE/v1/
|
|
46
|
+
curl -s "$BASE/v1/contacts" \
|
|
42
47
|
-H "Authorization: Bearer $TOKEN" | jq
|
|
43
48
|
```
|
|
44
49
|
|
|
50
|
+
### Via CLI
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
vellum contacts list --role contact
|
|
54
|
+
```
|
|
55
|
+
|
|
45
56
|
Response shape:
|
|
46
57
|
|
|
47
58
|
```json
|
|
48
59
|
{
|
|
49
60
|
"ok": true,
|
|
50
|
-
"
|
|
61
|
+
"contacts": [
|
|
51
62
|
{
|
|
52
63
|
"id": "uuid",
|
|
53
|
-
"sourceChannel": "telegram",
|
|
54
|
-
"externalUserId": "123456789",
|
|
55
|
-
"externalChatId": "123456789",
|
|
56
64
|
"displayName": "Alice",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
65
|
+
"relationship": "friend",
|
|
66
|
+
"importance": 0.5,
|
|
67
|
+
"responseExpectation": null,
|
|
68
|
+
"preferredTone": null,
|
|
69
|
+
"lastInteraction": 1700000000000,
|
|
70
|
+
"interactionCount": 12,
|
|
71
|
+
"createdAt": 1699000000000,
|
|
72
|
+
"updatedAt": 1700000000000,
|
|
73
|
+
"role": "contact",
|
|
74
|
+
"channels": [
|
|
75
|
+
{
|
|
76
|
+
"id": "channel-uuid",
|
|
77
|
+
"contactId": "uuid",
|
|
78
|
+
"type": "telegram",
|
|
79
|
+
"address": "alice_handle",
|
|
80
|
+
"isPrimary": true,
|
|
81
|
+
"externalUserId": "123456789",
|
|
82
|
+
"externalChatId": "123456789",
|
|
83
|
+
"status": "active",
|
|
84
|
+
"policy": "allow",
|
|
85
|
+
"verifiedAt": 1699500000000,
|
|
86
|
+
"lastSeenAt": 1700000000000,
|
|
87
|
+
"createdAt": 1699000000000
|
|
88
|
+
}
|
|
89
|
+
]
|
|
62
90
|
}
|
|
63
91
|
]
|
|
64
92
|
}
|
|
@@ -109,29 +137,29 @@ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
|
|
|
109
137
|
|
|
110
138
|
### Via HTTP API
|
|
111
139
|
|
|
112
|
-
First, find the
|
|
140
|
+
First, find the contact and its channel ID from the list endpoint, then revoke the channel:
|
|
113
141
|
|
|
114
142
|
```bash
|
|
115
|
-
# Find the
|
|
116
|
-
|
|
117
|
-
-H "Authorization: Bearer $TOKEN" | jq -r '.
|
|
143
|
+
# Find the contact's channel ID
|
|
144
|
+
CHANNEL_ID=$(curl -s "$BASE/v1/contacts?channelType=telegram" \
|
|
145
|
+
-H "Authorization: Bearer $TOKEN" | jq -r '.contacts[] | select(.channels[] | select(.externalUserId == "TARGET_USER_ID")) | .channels[] | select(.externalUserId == "TARGET_USER_ID") | .id')
|
|
118
146
|
|
|
119
147
|
# Revoke with reason
|
|
120
|
-
curl -s -X
|
|
148
|
+
curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
|
|
121
149
|
-H "Authorization: Bearer $TOKEN" \
|
|
122
150
|
-H "Content-Type: application/json" \
|
|
123
|
-
-d '{"reason": "Revoked by operator"}' | jq
|
|
151
|
+
-d '{"status": "revoked", "reason": "Revoked by operator"}' | jq
|
|
124
152
|
```
|
|
125
153
|
|
|
126
|
-
### Block a
|
|
154
|
+
### Block a contact channel (stronger than revoke)
|
|
127
155
|
|
|
128
|
-
Blocking prevents the
|
|
156
|
+
Blocking prevents the contact from re-entering the flow without explicit unblocking.
|
|
129
157
|
|
|
130
158
|
```bash
|
|
131
|
-
curl -s -X
|
|
159
|
+
curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
|
|
132
160
|
-H "Authorization: Bearer $TOKEN" \
|
|
133
161
|
-H "Content-Type: application/json" \
|
|
134
|
-
-d '{"reason": "Blocked by operator"}' | jq
|
|
162
|
+
-d '{"status": "blocked", "reason": "Blocked by operator"}' | jq
|
|
135
163
|
```
|
|
136
164
|
|
|
137
165
|
### Via SQLite (emergency)
|
|
@@ -229,35 +257,43 @@ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
|
|
|
229
257
|
|
|
230
258
|
## 7. Manually Add a Trusted Contact (Bypass Verification)
|
|
231
259
|
|
|
232
|
-
If the verification flow cannot be completed, an operator can directly create an active
|
|
260
|
+
If the verification flow cannot be completed, an operator can directly create an active contact:
|
|
233
261
|
|
|
234
262
|
```bash
|
|
235
|
-
curl -s -X POST "$BASE/v1/
|
|
263
|
+
curl -s -X POST "$BASE/v1/contacts" \
|
|
236
264
|
-H "Authorization: Bearer $TOKEN" \
|
|
237
265
|
-H "Content-Type: application/json" \
|
|
238
266
|
-d '{
|
|
239
|
-
"sourceChannel": "telegram",
|
|
240
|
-
"externalUserId": "123456789",
|
|
241
|
-
"externalChatId": "123456789",
|
|
242
267
|
"displayName": "Alice",
|
|
243
|
-
"
|
|
244
|
-
"
|
|
268
|
+
"role": "contact",
|
|
269
|
+
"channels": [{
|
|
270
|
+
"type": "telegram",
|
|
271
|
+
"address": "alice_handle",
|
|
272
|
+
"externalUserId": "123456789",
|
|
273
|
+
"externalChatId": "123456789",
|
|
274
|
+
"status": "active",
|
|
275
|
+
"policy": "allow"
|
|
276
|
+
}]
|
|
245
277
|
}' | jq
|
|
246
278
|
```
|
|
247
279
|
|
|
248
|
-
For SMS contacts, use the E.164 phone number as the external user/chat ID:
|
|
280
|
+
For SMS contacts, use the E.164 phone number as the address and external user/chat ID:
|
|
249
281
|
|
|
250
282
|
```bash
|
|
251
|
-
curl -s -X POST "$BASE/v1/
|
|
283
|
+
curl -s -X POST "$BASE/v1/contacts" \
|
|
252
284
|
-H "Authorization: Bearer $TOKEN" \
|
|
253
285
|
-H "Content-Type: application/json" \
|
|
254
286
|
-d '{
|
|
255
|
-
"sourceChannel": "sms",
|
|
256
|
-
"externalUserId": "+15551234567",
|
|
257
|
-
"externalChatId": "+15551234567",
|
|
258
287
|
"displayName": "Bob",
|
|
259
|
-
"
|
|
260
|
-
"
|
|
288
|
+
"role": "contact",
|
|
289
|
+
"channels": [{
|
|
290
|
+
"type": "sms",
|
|
291
|
+
"address": "+15551234567",
|
|
292
|
+
"externalUserId": "+15551234567",
|
|
293
|
+
"externalChatId": "+15551234567",
|
|
294
|
+
"status": "active",
|
|
295
|
+
"policy": "allow"
|
|
296
|
+
}]
|
|
261
297
|
}' | jq
|
|
262
298
|
```
|
|
263
299
|
|
package/package.json
CHANGED
|
@@ -49,9 +49,8 @@ const SWIFT_OMIT_ALLOWLIST = new Set<string>([
|
|
|
49
49
|
"heartbeat_alert",
|
|
50
50
|
// Guardian verification — daemon-internal for Telegram channel setup
|
|
51
51
|
"guardian_verification_response",
|
|
52
|
-
//
|
|
53
|
-
"
|
|
54
|
-
"ingress_member_response",
|
|
52
|
+
// Contacts invite management — not yet consumed by the macOS client
|
|
53
|
+
"contacts_invite_response",
|
|
55
54
|
// Inbox escalation — not yet consumed by the macOS client
|
|
56
55
|
"assistant_inbox_escalation_response",
|
|
57
56
|
// Work item messages — not yet consumed by the macOS client
|
package/scripts/test.sh
CHANGED
|
@@ -166,7 +166,7 @@ printf '%s\n' "${test_files[@]}" | xargs -P "${WORKERS}" -I {} bash -c '
|
|
|
166
166
|
if grep -q "^(fail)" "${out_file}" 2>/dev/null; then
|
|
167
167
|
echo "${test_file}" >> "${results_dir}/failures"
|
|
168
168
|
echo " ✗ ${base} (killed after ${per_test_timeout}s — tests failed and process hung)"
|
|
169
|
-
elif grep -qE "^Ran [0-9]+ tests across" "${out_file}" 2>/dev/null; then
|
|
169
|
+
elif grep -qE "^Ran [0-9]+ tests? across" "${out_file}" 2>/dev/null; then
|
|
170
170
|
echo " ⚠ ${base} (tests passed but process hung after ${per_test_timeout}s — likely open handles)"
|
|
171
171
|
else
|
|
172
172
|
echo "${test_file}" >> "${results_dir}/failures"
|
|
@@ -967,28 +967,14 @@ exports[`IPC message snapshots ClientMessage types dictation_request serializes
|
|
|
967
967
|
}
|
|
968
968
|
`;
|
|
969
969
|
|
|
970
|
-
exports[`IPC message snapshots ClientMessage types
|
|
970
|
+
exports[`IPC message snapshots ClientMessage types contacts_invite serializes to expected JSON 1`] = `
|
|
971
971
|
{
|
|
972
972
|
"action": "create",
|
|
973
973
|
"expiresInMs": 86400000,
|
|
974
974
|
"maxUses": 5,
|
|
975
975
|
"note": "Test invite",
|
|
976
976
|
"sourceChannel": "telegram",
|
|
977
|
-
"type": "
|
|
978
|
-
}
|
|
979
|
-
`;
|
|
980
|
-
|
|
981
|
-
exports[`IPC message snapshots ClientMessage types ingress_member serializes to expected JSON 1`] = `
|
|
982
|
-
{
|
|
983
|
-
"action": "upsert",
|
|
984
|
-
"displayName": "Test User",
|
|
985
|
-
"externalChatId": "chat-456",
|
|
986
|
-
"externalUserId": "user-123",
|
|
987
|
-
"policy": "allow",
|
|
988
|
-
"sourceChannel": "telegram",
|
|
989
|
-
"status": "active",
|
|
990
|
-
"type": "ingress_member",
|
|
991
|
-
"username": "testuser",
|
|
977
|
+
"type": "contacts_invite",
|
|
992
978
|
}
|
|
993
979
|
`;
|
|
994
980
|
|
|
@@ -2844,7 +2830,7 @@ exports[`IPC message snapshots ServerMessage types dictation_response serializes
|
|
|
2844
2830
|
}
|
|
2845
2831
|
`;
|
|
2846
2832
|
|
|
2847
|
-
exports[`IPC message snapshots ServerMessage types
|
|
2833
|
+
exports[`IPC message snapshots ServerMessage types contacts_invite_response serializes to expected JSON 1`] = `
|
|
2848
2834
|
{
|
|
2849
2835
|
"invite": {
|
|
2850
2836
|
"createdAt": 1700000000,
|
|
@@ -2859,26 +2845,7 @@ exports[`IPC message snapshots ServerMessage types ingress_invite_response seria
|
|
|
2859
2845
|
"useCount": 0,
|
|
2860
2846
|
},
|
|
2861
2847
|
"success": true,
|
|
2862
|
-
"type": "
|
|
2863
|
-
}
|
|
2864
|
-
`;
|
|
2865
|
-
|
|
2866
|
-
exports[`IPC message snapshots ServerMessage types ingress_member_response serializes to expected JSON 1`] = `
|
|
2867
|
-
{
|
|
2868
|
-
"member": {
|
|
2869
|
-
"createdAt": 1700000000,
|
|
2870
|
-
"displayName": "Test User",
|
|
2871
|
-
"externalChatId": "chat-456",
|
|
2872
|
-
"externalUserId": "user-123",
|
|
2873
|
-
"id": "mem-001",
|
|
2874
|
-
"lastSeenAt": 1700000000,
|
|
2875
|
-
"policy": "allow",
|
|
2876
|
-
"sourceChannel": "telegram",
|
|
2877
|
-
"status": "active",
|
|
2878
|
-
"username": "testuser",
|
|
2879
|
-
},
|
|
2880
|
-
"success": true,
|
|
2881
|
-
"type": "ingress_member_response",
|
|
2848
|
+
"type": "contacts_invite_response",
|
|
2882
2849
|
}
|
|
2883
2850
|
`;
|
|
2884
2851
|
|
|
@@ -449,14 +449,15 @@ describe("resolveLocalIpcAuthContext", () => {
|
|
|
449
449
|
);
|
|
450
450
|
});
|
|
451
451
|
|
|
452
|
-
test("actorPrincipalId is
|
|
452
|
+
test("actorPrincipalId is auto-created via self-heal when no vellum binding exists", () => {
|
|
453
453
|
// Reset DB to ensure no binding
|
|
454
454
|
resetDb();
|
|
455
455
|
initializeDb();
|
|
456
456
|
|
|
457
457
|
const ctx = resolveLocalIpcAuthContext("session-123");
|
|
458
|
-
//
|
|
459
|
-
expect(ctx.actorPrincipalId).
|
|
458
|
+
// Self-heal creates a vellum guardian binding automatically
|
|
459
|
+
expect(ctx.actorPrincipalId).toBeDefined();
|
|
460
|
+
expect(ctx.actorPrincipalId).toMatch(/^vellum-principal-/);
|
|
460
461
|
});
|
|
461
462
|
|
|
462
463
|
test("sessionId matches the provided argument", () => {
|
|
@@ -180,28 +180,18 @@ describe("executeAppCreate", () => {
|
|
|
180
180
|
expect(capturedPages).toEqual({ "settings.html": "<div/>" });
|
|
181
181
|
});
|
|
182
182
|
|
|
183
|
-
test(
|
|
184
|
-
let
|
|
183
|
+
test("defaults html to minimal scaffold when omitted", async () => {
|
|
184
|
+
let capturedHtml: string | undefined;
|
|
185
185
|
const store = makeMockStore({
|
|
186
186
|
createApp: (params) => {
|
|
187
|
-
|
|
187
|
+
capturedHtml = params.htmlDefinition;
|
|
188
188
|
return makeApp({ name: params.name });
|
|
189
189
|
},
|
|
190
190
|
});
|
|
191
|
-
await executeAppCreate({ name: "
|
|
192
|
-
expect(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
test('defaults appType to "app" when type is not "site"', async () => {
|
|
196
|
-
let capturedType: "app" | "site" | undefined;
|
|
197
|
-
const store = makeMockStore({
|
|
198
|
-
createApp: (params) => {
|
|
199
|
-
capturedType = params.appType;
|
|
200
|
-
return makeApp({ name: params.name });
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
await executeAppCreate({ name: "App", html: "<p/>" }, store);
|
|
204
|
-
expect(capturedType).toBe("app");
|
|
191
|
+
await executeAppCreate({ name: "No HTML" }, store);
|
|
192
|
+
expect(capturedHtml).toBe(
|
|
193
|
+
"<!DOCTYPE html><html><head></head><body></body></html>",
|
|
194
|
+
);
|
|
205
195
|
});
|
|
206
196
|
});
|
|
207
197
|
|