@vellumai/assistant 0.4.2 → 0.4.3
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 +84 -7
- package/docs/trusted-contact-access.md +20 -0
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +0 -1
- package/src/__tests__/assistant-id-boundary-guard.test.ts +290 -0
- package/src/__tests__/call-routes-http.test.ts +0 -25
- package/src/__tests__/channel-guardian.test.ts +6 -5
- package/src/__tests__/config-schema.test.ts +2 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +21 -0
- package/src/__tests__/guardian-outbound-http.test.ts +0 -1
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/ingress-routes-http.test.ts +55 -0
- package/src/__tests__/non-member-access-request.test.ts +28 -1
- package/src/__tests__/notification-decision-strategy.test.ts +44 -0
- package/src/__tests__/relay-server.test.ts +644 -4
- package/src/__tests__/session-init.benchmark.test.ts +0 -1
- package/src/__tests__/session-runtime-assembly.test.ts +4 -1
- package/src/__tests__/session-surfaces-task-progress.test.ts +43 -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-routes.test.ts +4 -3
- package/src/__tests__/update-bulletin.test.ts +0 -1
- package/src/approvals/guardian-decision-primitive.ts +2 -1
- package/src/approvals/guardian-request-resolvers.ts +42 -3
- package/src/calls/call-constants.ts +8 -0
- package/src/calls/call-controller.ts +2 -1
- package/src/calls/call-domain.ts +5 -4
- package/src/calls/relay-server.ts +513 -116
- package/src/calls/twilio-routes.ts +3 -5
- package/src/calls/types.ts +1 -1
- package/src/calls/voice-session-bridge.ts +4 -3
- package/src/cli/core-commands.ts +7 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +164 -1
- package/src/config/bundled-skills/vercel-token-setup/SKILL.md +214 -0
- package/src/config/calls-schema.ts +12 -0
- package/src/config/feature-flag-registry.json +0 -8
- package/src/config/vellum-skills/trusted-contacts/SKILL.md +8 -2
- package/src/daemon/handlers/config-channels.ts +5 -7
- package/src/daemon/handlers/config-inbox.ts +2 -0
- package/src/daemon/handlers/index.ts +2 -1
- package/src/daemon/handlers/publish.ts +11 -46
- package/src/daemon/handlers/sessions.ts +11 -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 +3 -1
- package/src/daemon/server.ts +2 -1
- package/src/daemon/session-agent-loop.ts +2 -1
- package/src/daemon/session-runtime-assembly.ts +3 -1
- package/src/daemon/session-surfaces.ts +29 -1
- package/src/memory/conversation-crud.ts +2 -1
- package/src/memory/conversation-title-service.ts +16 -2
- package/src/memory/db-init.ts +4 -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/124-voice-invite-display-metadata.ts +14 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema.ts +10 -5
- package/src/notifications/copy-composer.ts +11 -1
- package/src/notifications/emit-signal.ts +2 -1
- package/src/runtime/access-request-helper.ts +11 -3
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-scope.ts +10 -0
- package/src/runtime/guardian-outbound-actions.ts +5 -4
- package/src/runtime/http-server.ts +11 -20
- package/src/runtime/ingress-service.ts +14 -0
- package/src/runtime/invite-redemption-service.ts +2 -1
- package/src/runtime/middleware/twilio-validation.ts +2 -4
- package/src/runtime/routes/call-routes.ts +2 -1
- 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 +2 -1
- package/src/runtime/routes/events-routes.ts +2 -3
- package/src/runtime/routes/inbound-conversation.ts +4 -3
- package/src/runtime/routes/inbound-message-handler.ts +4 -3
- package/src/runtime/routes/ingress-routes.ts +2 -0
- package/src/tools/calls/call-start.ts +2 -1
- package/src/tools/terminal/parser.ts +12 -0
- package/src/tools/tool-approval-handler.ts +2 -1
- package/src/workspace/git-service.ts +19 -0
|
@@ -147,10 +147,9 @@ function mapTwilioStatus(twilioStatus: string): CallStatus | null {
|
|
|
147
147
|
* Supports two modes:
|
|
148
148
|
* - **Outbound** (callSessionId present in query): uses the existing session
|
|
149
149
|
* - **Inbound** (callSessionId absent): creates or reuses a session keyed
|
|
150
|
-
* by the Twilio CallSid.
|
|
151
|
-
* by the gateway from the "To" phone number.
|
|
150
|
+
* by the Twilio CallSid. Uses daemon internal scope for assistant identity.
|
|
152
151
|
*/
|
|
153
|
-
export async function handleVoiceWebhook(req: Request
|
|
152
|
+
export async function handleVoiceWebhook(req: Request): Promise<Response> {
|
|
154
153
|
const url = new URL(req.url);
|
|
155
154
|
const callSessionId = url.searchParams.get('callSessionId');
|
|
156
155
|
|
|
@@ -167,13 +166,12 @@ export async function handleVoiceWebhook(req: Request, forwardedAssistantId?: st
|
|
|
167
166
|
return new Response('Missing CallSid', { status: 400 });
|
|
168
167
|
}
|
|
169
168
|
|
|
170
|
-
log.info({ callSid, from: callerFrom, to: callerTo
|
|
169
|
+
log.info({ callSid, from: callerFrom, to: callerTo }, 'Inbound voice webhook — creating/reusing session');
|
|
171
170
|
|
|
172
171
|
const { session } = createInboundVoiceSession({
|
|
173
172
|
callSid,
|
|
174
173
|
fromNumber: callerFrom,
|
|
175
174
|
toNumber: callerTo,
|
|
176
|
-
assistantId: forwardedAssistantId,
|
|
177
175
|
});
|
|
178
176
|
|
|
179
177
|
return buildVoiceWebhookTwiml(session.id, session.assistantId ?? undefined, session.task, session.guardianVerificationSessionId);
|
package/src/calls/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type CallStatus = 'initiated' | 'ringing' | 'in_progress' | 'waiting_on_user' | 'completed' | 'failed' | 'cancelled';
|
|
2
|
-
export type CallEventType = 'call_started' | 'call_connected' | 'caller_spoke' | 'assistant_spoke' | 'user_question_asked' | 'user_answered' | 'user_instruction_relayed' | 'call_ended' | 'call_failed' | 'callee_verification_started' | 'callee_verification_succeeded' | 'callee_verification_failed' | 'guardian_voice_verification_started' | 'guardian_voice_verification_succeeded' | 'guardian_voice_verification_failed' | 'outbound_guardian_voice_verification_started' | 'outbound_guardian_voice_verification_succeeded' | 'outbound_guardian_voice_verification_failed' | 'guardian_consultation_timed_out' | 'guardian_unavailable_skipped' | 'guardian_consult_deferred' | 'guardian_consult_coalesced' | 'inbound_acl_denied' | 'invite_redemption_started' | 'invite_redemption_succeeded' | 'invite_redemption_failed';
|
|
2
|
+
export type CallEventType = 'call_started' | 'call_connected' | 'caller_spoke' | 'assistant_spoke' | 'user_question_asked' | 'user_answered' | 'user_instruction_relayed' | 'call_ended' | 'call_failed' | 'callee_verification_started' | 'callee_verification_succeeded' | 'callee_verification_failed' | 'guardian_voice_verification_started' | 'guardian_voice_verification_succeeded' | 'guardian_voice_verification_failed' | 'outbound_guardian_voice_verification_started' | 'outbound_guardian_voice_verification_succeeded' | 'outbound_guardian_voice_verification_failed' | 'guardian_consultation_timed_out' | 'guardian_unavailable_skipped' | 'guardian_consult_deferred' | 'guardian_consult_coalesced' | 'inbound_acl_denied' | 'inbound_acl_name_capture_started' | 'inbound_acl_name_captured' | 'inbound_acl_name_capture_timeout' | 'inbound_acl_access_approved' | 'inbound_acl_access_denied' | 'inbound_acl_access_timeout' | 'invite_redemption_started' | 'invite_redemption_succeeded' | 'invite_redemption_failed';
|
|
3
3
|
export type PendingQuestionStatus = 'pending' | 'answered' | 'expired' | 'cancelled';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -19,6 +19,7 @@ import type { GuardianRuntimeContext } from '../daemon/session-runtime-assembly.
|
|
|
19
19
|
import { resolveChannelCapabilities } from '../daemon/session-runtime-assembly.js';
|
|
20
20
|
import { buildAssistantEvent } from '../runtime/assistant-event.js';
|
|
21
21
|
import { assistantEventHub } from '../runtime/assistant-event-hub.js';
|
|
22
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from '../runtime/assistant-scope.js';
|
|
22
23
|
import { checkIngressForSecrets } from '../security/secret-ingress.js';
|
|
23
24
|
import { computeToolApprovalDigest } from '../security/tool-approval-digest.js';
|
|
24
25
|
import { IngressBlockedError } from '../util/errors.js';
|
|
@@ -306,7 +307,7 @@ export async function startVoiceTurn(opts: VoiceTurnOptions): Promise<VoiceTurnH
|
|
|
306
307
|
...session.memoryPolicy,
|
|
307
308
|
strictSideEffects,
|
|
308
309
|
};
|
|
309
|
-
session.setAssistantId(opts.assistantId ??
|
|
310
|
+
session.setAssistantId(opts.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID);
|
|
310
311
|
session.callSessionId = opts.callSessionId;
|
|
311
312
|
session.setGuardianContext(opts.guardianContext ?? null);
|
|
312
313
|
session.setCommandIntent(null);
|
|
@@ -330,7 +331,7 @@ export async function startVoiceTurn(opts: VoiceTurnOptions): Promise<VoiceTurnH
|
|
|
330
331
|
? (msg as { sessionId: string }).sessionId
|
|
331
332
|
: undefined;
|
|
332
333
|
const resolvedSessionId = msgSessionId ?? opts.conversationId;
|
|
333
|
-
const event = buildAssistantEvent(
|
|
334
|
+
const event = buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, msg, resolvedSessionId);
|
|
334
335
|
hubChain = (async () => {
|
|
335
336
|
await hubChain;
|
|
336
337
|
try {
|
|
@@ -364,7 +365,7 @@ export async function startVoiceTurn(opts: VoiceTurnOptions): Promise<VoiceTurnH
|
|
|
364
365
|
toolName: msg.toolName,
|
|
365
366
|
inputDigest,
|
|
366
367
|
consumingRequestId: msg.requestId,
|
|
367
|
-
assistantId: opts.assistantId ??
|
|
368
|
+
assistantId: opts.assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
|
|
368
369
|
executionChannel: 'voice',
|
|
369
370
|
conversationId: opts.conversationId,
|
|
370
371
|
callSessionId: opts.callSessionId,
|
package/src/cli/core-commands.ts
CHANGED
|
@@ -107,8 +107,9 @@ export function registerDaemonCommand(program: Command): void {
|
|
|
107
107
|
export function registerDevCommand(program: Command): void {
|
|
108
108
|
program
|
|
109
109
|
.command('dev')
|
|
110
|
-
.description('Run the daemon in dev mode
|
|
111
|
-
.
|
|
110
|
+
.description('Run the daemon in dev mode')
|
|
111
|
+
.option('--watch', 'Auto-restart on source file changes (disruptive during Claude Code sessions)')
|
|
112
|
+
.action(async (opts: { watch?: boolean }) => {
|
|
112
113
|
let status = await getDaemonStatus();
|
|
113
114
|
if (status.running) {
|
|
114
115
|
log.info('Stopping existing daemon...');
|
|
@@ -161,10 +162,12 @@ export function registerDevCommand(program: Command): void {
|
|
|
161
162
|
|
|
162
163
|
const mainPath = `${import.meta.dirname}/../daemon/main.ts`;
|
|
163
164
|
|
|
164
|
-
|
|
165
|
+
const useWatch = opts.watch === true;
|
|
166
|
+
log.info(`Starting daemon in dev mode${useWatch ? ' with file watching' : ''} (Ctrl+C to stop)`);
|
|
165
167
|
|
|
166
168
|
const repoRoot = join(import.meta.dirname, '..', '..', '..');
|
|
167
|
-
const
|
|
169
|
+
const args = useWatch ? ['--watch', 'run', mainPath] : ['run', mainPath];
|
|
170
|
+
const child = spawn('bun', args, {
|
|
168
171
|
stdio: 'inherit',
|
|
169
172
|
env: {
|
|
170
173
|
...process.env,
|
|
@@ -673,6 +673,28 @@ Wrap in `.v-metric-grid` for responsive 2-4 column layout. Always use a semantic
|
|
|
673
673
|
|
|
674
674
|
`.v-pullquote` — Blockquote with gradient accent border. `.v-comparison` — Before/after cards (3-column grid with `.before`/`.after` modifiers). `.v-page` — Centered container (max-width 600px). Use `.v-animate-in` on children for staggered fade-in. Use `.v-gradient-text` for accent-colored gradient text.
|
|
675
675
|
|
|
676
|
+
`.v-slideshow` — Presentation slide deck with transitions and navigation. Init with `vellum.widgets.slideshow()`:
|
|
677
|
+
```html
|
|
678
|
+
<div class="v-slideshow" id="deck">
|
|
679
|
+
<div class="v-slide">
|
|
680
|
+
<div class="v-slide-header">
|
|
681
|
+
<span class="v-slide-label">Overview</span>
|
|
682
|
+
</div>
|
|
683
|
+
<h1 class="v-slide-title">The city that never <span class="accent-word">sleeps</span></h1>
|
|
684
|
+
<p class="v-slide-body">Body text here...</p>
|
|
685
|
+
<div class="v-slide-stats">
|
|
686
|
+
<div class="v-slide-stat">
|
|
687
|
+
<span class="v-slide-stat-value">8.3M</span>
|
|
688
|
+
<span class="v-slide-stat-label">Residents</span>
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
691
|
+
</div>
|
|
692
|
+
<div class="v-slide"><!-- Slide 2 --></div>
|
|
693
|
+
<div class="v-slide"><!-- Slide 3 --></div>
|
|
694
|
+
</div>
|
|
695
|
+
```
|
|
696
|
+
Slide content helpers: `.v-slide-label` (section label with colored dot), `.v-slide-title` (responsive heading), `.v-slide-body` (body text, max-width 540px), `.v-slide-stats` (auto-fit grid), `.v-slide-stat` / `.v-slide-stat-value` / `.v-slide-stat-label` (big-number cards), `.v-slide-quote` / `.v-slide-quote-attribution` (blockquote), `.v-slide-list` (styled list), `.v-slide-columns` / `.v-slide-column` (2-column comparison grid).
|
|
697
|
+
|
|
676
698
|
#### Widget JavaScript utilities
|
|
677
699
|
|
|
678
700
|
Interactive utilities at `window.vellum.widgets.*`:
|
|
@@ -724,6 +746,13 @@ vellum.widgets.toast('Connection lost', 'error', 0); // Manual dismiss
|
|
|
724
746
|
vellum.widgets.countdown('timer-el', '2025-12-31T00:00:00Z', {
|
|
725
747
|
onComplete: () => console.log('Done!')
|
|
726
748
|
});
|
|
749
|
+
|
|
750
|
+
// Slideshow — presentation deck with transitions and navigation
|
|
751
|
+
vellum.widgets.slideshow('deck', {
|
|
752
|
+
transition: 'fade', showDots: true, showArrows: true,
|
|
753
|
+
showCounter: true, keyboard: true, loop: true
|
|
754
|
+
});
|
|
755
|
+
// Returns: { goTo(index), next(), prev() }
|
|
727
756
|
```
|
|
728
757
|
|
|
729
758
|
#### Composition recipes
|
|
@@ -992,9 +1021,142 @@ async function handleBulk(action) {
|
|
|
992
1021
|
}
|
|
993
1022
|
```
|
|
994
1023
|
|
|
1024
|
+
**Presentation slideshow** — multi-slide deck with 8 layout variants (title, stats, bullets, quote, comparison, visual, timeline, closing). Use the slideshow widget for presentations, pitch decks, and multi-slide educational content. The model provides slide content; the widget handles navigation, transitions, and keyboard support.
|
|
1025
|
+
|
|
1026
|
+
```html
|
|
1027
|
+
<!DOCTYPE html>
|
|
1028
|
+
<html lang="en">
|
|
1029
|
+
<head>
|
|
1030
|
+
<meta charset="UTF-8">
|
|
1031
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1032
|
+
<style>
|
|
1033
|
+
:root { --v-accent: #8B5CF6; }
|
|
1034
|
+
body { margin: 0; padding: 0; background: linear-gradient(-45deg, #0f172a, #1e1b4b, #172554); min-height: 100vh; }
|
|
1035
|
+
.v-slideshow { border-radius: 0; min-height: 100vh; }
|
|
1036
|
+
.accent-word { color: var(--v-accent); }
|
|
1037
|
+
.trust-pill { display: inline-flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: 999px; font-size: 13px; font-weight: 500; background: color-mix(in srgb, var(--v-surface) 60%, transparent); border: 1px solid var(--v-surface-border); color: var(--v-text-secondary); margin-top: var(--v-spacing-lg); }
|
|
1038
|
+
.trust-pill.accent { border-color: color-mix(in srgb, var(--v-accent) 30%, transparent); color: var(--v-accent); }
|
|
1039
|
+
.v-slide-list li { font-size: 15px; line-height: 1.7; }
|
|
1040
|
+
.v-slide-columns h3 { margin: 0 0 var(--v-spacing-sm); color: var(--v-text); font-size: var(--v-font-size-lg); }
|
|
1041
|
+
.slide-visual { position: relative; overflow: hidden; }
|
|
1042
|
+
.slide-visual-bg { position: absolute; inset: 0; background: radial-gradient(ellipse at 30% 40%, color-mix(in srgb, var(--v-accent) 15%, transparent), transparent 70%), radial-gradient(ellipse at 70% 60%, color-mix(in srgb, var(--v-forest-500, #22c55e) 10%, transparent), transparent 60%); }
|
|
1043
|
+
.slide-visual-overlay { position: relative; z-index: 1; background: color-mix(in srgb, var(--v-bg) 40%, transparent); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-radius: var(--v-radius-lg); padding: var(--v-spacing-xl); max-width: 500px; }
|
|
1044
|
+
</style>
|
|
1045
|
+
</head>
|
|
1046
|
+
<body>
|
|
1047
|
+
<div class="v-slideshow" id="deck">
|
|
1048
|
+
|
|
1049
|
+
<!-- 1. Title slide -->
|
|
1050
|
+
<div class="v-slide">
|
|
1051
|
+
<span class="v-slide-label">Introduction</span>
|
|
1052
|
+
<h1 class="v-slide-title">The city that never <span class="accent-word">sleeps</span></h1>
|
|
1053
|
+
<p class="v-slide-body">A brief subtitle or tagline here.</p>
|
|
1054
|
+
<span class="trust-pill accent">🗽 8.3 million residents</span>
|
|
1055
|
+
</div>
|
|
1056
|
+
|
|
1057
|
+
<!-- 2. Content + Stats slide -->
|
|
1058
|
+
<div class="v-slide">
|
|
1059
|
+
<span class="v-slide-label">By the Numbers</span>
|
|
1060
|
+
<h2 class="v-slide-title">Economy at a glance</h2>
|
|
1061
|
+
<p class="v-slide-body">New York generates more GDP than most countries...</p>
|
|
1062
|
+
<div class="v-slide-stats">
|
|
1063
|
+
<div class="v-slide-stat"><span class="v-slide-stat-value">$2.1T</span><span class="v-slide-stat-label">Metro GDP</span></div>
|
|
1064
|
+
<div class="v-slide-stat"><span class="v-slide-stat-value">4.7M</span><span class="v-slide-stat-label">Jobs</span></div>
|
|
1065
|
+
<div class="v-slide-stat"><span class="v-slide-stat-value">#1</span><span class="v-slide-stat-label">Financial Hub</span></div>
|
|
1066
|
+
</div>
|
|
1067
|
+
</div>
|
|
1068
|
+
|
|
1069
|
+
<!-- 3. Bullet points slide -->
|
|
1070
|
+
<div class="v-slide">
|
|
1071
|
+
<span class="v-slide-label">Culture</span>
|
|
1072
|
+
<h2 class="v-slide-title">What makes it <span class="accent-word">unique</span></h2>
|
|
1073
|
+
<ul class="v-slide-list">
|
|
1074
|
+
<li>🎭 Broadway — 41 professional theaters in the Theater District</li>
|
|
1075
|
+
<li>🏛️ 80+ world-class museums including the Met and MoMA</li>
|
|
1076
|
+
<li>🍕 Over 27,000 restaurants spanning every cuisine on earth</li>
|
|
1077
|
+
<li>🌳 843 acres of Central Park in the heart of Manhattan</li>
|
|
1078
|
+
</ul>
|
|
1079
|
+
</div>
|
|
1080
|
+
|
|
1081
|
+
<!-- 4. Quote slide -->
|
|
1082
|
+
<div class="v-slide" style="justify-content: center; align-items: center; text-align: center;">
|
|
1083
|
+
<div class="v-slide-quote" style="border-left: none; padding-left: 0;">
|
|
1084
|
+
"There is no place like New York. It is the most exciting city in the world."
|
|
1085
|
+
</div>
|
|
1086
|
+
<div class="v-slide-quote-attribution">— John Updike</div>
|
|
1087
|
+
</div>
|
|
1088
|
+
|
|
1089
|
+
<!-- 5. Comparison / Two-column slide -->
|
|
1090
|
+
<div class="v-slide">
|
|
1091
|
+
<span class="v-slide-label">Comparison</span>
|
|
1092
|
+
<h2 class="v-slide-title">Manhattan vs Brooklyn</h2>
|
|
1093
|
+
<div class="v-slide-columns">
|
|
1094
|
+
<div class="v-slide-column">
|
|
1095
|
+
<h3>🏙️ Manhattan</h3>
|
|
1096
|
+
<p class="v-slide-body" style="margin-bottom: var(--v-spacing-sm);">Financial center, dense skyscrapers, high-energy nightlife, world-famous landmarks.</p>
|
|
1097
|
+
<span class="v-slide-stat-value">1.6M</span>
|
|
1098
|
+
<span class="v-slide-stat-label">Population</span>
|
|
1099
|
+
</div>
|
|
1100
|
+
<div class="v-slide-column">
|
|
1101
|
+
<h3>🌉 Brooklyn</h3>
|
|
1102
|
+
<p class="v-slide-body" style="margin-bottom: var(--v-spacing-sm);">Creative hub, brownstone neighborhoods, artisan food scene, waterfront parks.</p>
|
|
1103
|
+
<span class="v-slide-stat-value">2.7M</span>
|
|
1104
|
+
<span class="v-slide-stat-label">Population</span>
|
|
1105
|
+
</div>
|
|
1106
|
+
</div>
|
|
1107
|
+
</div>
|
|
1108
|
+
|
|
1109
|
+
<!-- 6. Image/visual slide -->
|
|
1110
|
+
<div class="v-slide slide-visual">
|
|
1111
|
+
<div class="slide-visual-bg"></div>
|
|
1112
|
+
<div class="slide-visual-overlay">
|
|
1113
|
+
<span class="v-slide-label">Skyline</span>
|
|
1114
|
+
<h2 class="v-slide-title">An iconic <span class="accent-word">horizon</span></h2>
|
|
1115
|
+
<p class="v-slide-body">The Manhattan skyline is recognized worldwide...</p>
|
|
1116
|
+
</div>
|
|
1117
|
+
</div>
|
|
1118
|
+
|
|
1119
|
+
<!-- 7. Timeline slide -->
|
|
1120
|
+
<div class="v-slide">
|
|
1121
|
+
<span class="v-slide-label">History</span>
|
|
1122
|
+
<h2 class="v-slide-title">Key <span class="accent-word">milestones</span></h2>
|
|
1123
|
+
<div class="v-timeline" style="margin-top: var(--v-spacing-lg);">
|
|
1124
|
+
<div class="v-timeline-entry"><span class="v-timeline-time">1626</span><span class="v-timeline-title">Manhattan purchased</span></div>
|
|
1125
|
+
<div class="v-timeline-entry"><span class="v-timeline-time">1886</span><span class="v-timeline-title">Statue of Liberty dedicated</span></div>
|
|
1126
|
+
<div class="v-timeline-entry active"><span class="v-timeline-time">1931</span><span class="v-timeline-title">Empire State Building opens</span></div>
|
|
1127
|
+
</div>
|
|
1128
|
+
</div>
|
|
1129
|
+
|
|
1130
|
+
<!-- 8. Closing / CTA slide -->
|
|
1131
|
+
<div class="v-slide" style="text-align: center; align-items: center;">
|
|
1132
|
+
<h1 class="v-slide-title">The world's <span class="accent-word">capital</span></h1>
|
|
1133
|
+
<p class="v-slide-body" style="max-width: 400px;">New York isn't just a city — it's an idea that never stops evolving.</p>
|
|
1134
|
+
<div class="v-slide-stats" style="margin-top: var(--v-spacing-xxl);">
|
|
1135
|
+
<div class="v-slide-stat"><span class="v-slide-stat-value">800+</span><span class="v-slide-stat-label">Languages spoken</span></div>
|
|
1136
|
+
<div class="v-slide-stat"><span class="v-slide-stat-value">62M</span><span class="v-slide-stat-label">Annual visitors</span></div>
|
|
1137
|
+
</div>
|
|
1138
|
+
</div>
|
|
1139
|
+
|
|
1140
|
+
</div>
|
|
1141
|
+
<script>
|
|
1142
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
1143
|
+
vellum.widgets.slideshow('deck', {
|
|
1144
|
+
transition: 'fade',
|
|
1145
|
+
showDots: true,
|
|
1146
|
+
showArrows: true,
|
|
1147
|
+
showCounter: true,
|
|
1148
|
+
keyboard: true,
|
|
1149
|
+
loop: true
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
</script>
|
|
1153
|
+
</body>
|
|
1154
|
+
</html>
|
|
1155
|
+
```
|
|
1156
|
+
|
|
995
1157
|
#### When to use widgets vs custom HTML
|
|
996
1158
|
|
|
997
|
-
- **Use widgets** for standard patterns — tables, metrics, timelines, notifications
|
|
1159
|
+
- **Use widgets** for standard patterns — tables, metrics, timelines, notifications, presentations
|
|
998
1160
|
- **Use custom HTML** for novel or creative UIs — games, art tools, unique dashboards
|
|
999
1161
|
- **Mix freely** — widgets compose well together and with custom elements
|
|
1000
1162
|
- Always prioritize the ideal user experience over using the widget library
|
|
@@ -1326,6 +1488,7 @@ Before delivering any app, mentally verify these 10 items — they cover the gap
|
|
|
1326
1488
|
| `.v-pill-toggles` | Time range / filter toggle group | `.v-pill-toggle` (`.active`) — container with pill buttons |
|
|
1327
1489
|
| `.v-chip-group` | Suggestion / filter chip row | `.v-chip` (`.active`) — wrapping row of clickable pills |
|
|
1328
1490
|
| `.v-metric-card .v-metric-icon` | Emoji icon in metric cards | Place emoji `<span>` with `.v-metric-icon` inside `.v-metric-card` |
|
|
1491
|
+
| `.v-slideshow` | Presentation slide deck with transitions | `.v-slide` (`.active`), `.v-slide-label`, `.v-slide-title`, `.v-slide-body`, `.v-slide-stats`, `.v-slide-stat`, `.v-slide-quote` — init with `vellum.widgets.slideshow()` |
|
|
1329
1492
|
|
|
1330
1493
|
Every app should include: search/filter, toast notifications for all CRUD operations, `window.vellum.confirm()` for destructive actions, staggered page-load animation, card hover effects, and skeleton loading states.
|
|
1331
1494
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "Vercel Token Setup"
|
|
3
|
+
description: "Set up a Vercel API token for publishing apps using browser automation"
|
|
4
|
+
includes: ["browser"]
|
|
5
|
+
metadata: {"vellum": {"emoji": "▲"}}
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You are helping your user set up a Vercel API token so they can publish apps to the web.
|
|
9
|
+
|
|
10
|
+
## Client Check
|
|
11
|
+
|
|
12
|
+
Determine whether the user has browser automation available (macOS desktop app) or is on a non-interactive channel (Telegram, SMS, etc.).
|
|
13
|
+
|
|
14
|
+
- **macOS desktop app**: Follow the **Automated Setup** path below.
|
|
15
|
+
- **Telegram or other channel** (no browser automation): Follow the **Manual Setup for Channels** path below.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Path A: Manual Setup for Channels (Telegram, SMS, etc.)
|
|
20
|
+
|
|
21
|
+
When the user is on Telegram or any non-macOS client, walk them through a text-based setup. No browser automation is used — the user follows links and performs each action manually.
|
|
22
|
+
|
|
23
|
+
### Channel Step 1: Confirm and Explain
|
|
24
|
+
|
|
25
|
+
Tell the user:
|
|
26
|
+
|
|
27
|
+
> **Setting up Vercel API Token**
|
|
28
|
+
>
|
|
29
|
+
> Since I can't automate the browser from here, I'll walk you through each step with direct links. You'll need:
|
|
30
|
+
> 1. A Vercel account (free tier works)
|
|
31
|
+
> 2. About 2 minutes
|
|
32
|
+
>
|
|
33
|
+
> Ready to start?
|
|
34
|
+
|
|
35
|
+
If the user declines, acknowledge and stop.
|
|
36
|
+
|
|
37
|
+
### Channel Step 2: Create the Token
|
|
38
|
+
|
|
39
|
+
Tell the user:
|
|
40
|
+
|
|
41
|
+
> **Step 1: Create an API token**
|
|
42
|
+
>
|
|
43
|
+
> Open this link to go to your Vercel tokens page:
|
|
44
|
+
> https://vercel.com/account/tokens
|
|
45
|
+
>
|
|
46
|
+
> 1. Click **"Create"** (or **"Create Token"**)
|
|
47
|
+
> 2. Set the token name to **"Vellum Assistant"**
|
|
48
|
+
> 3. Select scope: **"Full Account"**
|
|
49
|
+
> 4. Set expiration to the longest option available (or **"No Expiration"** if offered)
|
|
50
|
+
> 5. Click **"Create Token"**
|
|
51
|
+
>
|
|
52
|
+
> A token value will appear — **copy it now**, as it's only shown once.
|
|
53
|
+
|
|
54
|
+
### Channel Step 3: Store the Token
|
|
55
|
+
|
|
56
|
+
Tell the user:
|
|
57
|
+
|
|
58
|
+
> **Step 2: Send me the token**
|
|
59
|
+
>
|
|
60
|
+
> Please paste the token value into the secure prompt below.
|
|
61
|
+
|
|
62
|
+
Present the secure prompt:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
credential_store prompt:
|
|
66
|
+
service: "vercel"
|
|
67
|
+
field: "api_token"
|
|
68
|
+
label: "Vercel API Token"
|
|
69
|
+
description: "Paste the API token you just created on vercel.com"
|
|
70
|
+
placeholder: "Enter your Vercel API token"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Wait for the user to complete the prompt. Once received, store it:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
credential_store store:
|
|
77
|
+
service: "vercel"
|
|
78
|
+
field: "api_token"
|
|
79
|
+
value: "<the token the user provided>"
|
|
80
|
+
allowedTools: ["publish_page", "unpublish_page"]
|
|
81
|
+
allowedDomains: ["api.vercel.com"]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Channel Step 4: Done!
|
|
85
|
+
|
|
86
|
+
> **Vercel is connected!** You can now publish apps to the web. Try clicking Publish on any app you've built.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
# Path B: Automated Setup (macOS Desktop App)
|
|
91
|
+
|
|
92
|
+
You will automate Vercel token creation via the browser while the user watches. The user's only manual action is signing in to Vercel (if needed) and one copy-paste for the token value.
|
|
93
|
+
|
|
94
|
+
## Browser Interaction Principles
|
|
95
|
+
|
|
96
|
+
Vercel's UI may change over time. Do NOT memorize or depend on specific element IDs, CSS selectors, or DOM structures. Instead:
|
|
97
|
+
|
|
98
|
+
1. **Screenshot first, act second.** Before every interaction, take a `browser_screenshot` to see the current visual state. Use `browser_snapshot` to find interactive elements.
|
|
99
|
+
2. **Adapt to what you see.** If a button's label or position differs from what you expect, use the screenshot to find the correct element.
|
|
100
|
+
3. **Verify after every action.** After clicking, typing, or navigating, take a new screenshot to confirm the action succeeded.
|
|
101
|
+
4. **Never assume DOM structure.** Use the snapshot to identify what's on the page and interact accordingly.
|
|
102
|
+
5. **When stuck, screenshot and describe.** If you cannot find an expected element after 2 attempts, take a screenshot, describe what you see to the user, and ask for guidance.
|
|
103
|
+
|
|
104
|
+
## Anti-Loop Guardrails
|
|
105
|
+
|
|
106
|
+
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:
|
|
107
|
+
|
|
108
|
+
1. **Stop trying.** Do not continue retrying the same approach.
|
|
109
|
+
2. **Fall back to manual.** Tell the user what you were trying to do and ask them to complete that step manually in the browser. Give them the direct URL and clear text instructions.
|
|
110
|
+
3. **Resume automation** at the next step once the user confirms the manual step is done.
|
|
111
|
+
|
|
112
|
+
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.
|
|
113
|
+
|
|
114
|
+
## Things That Do Not Work — Do Not Attempt
|
|
115
|
+
|
|
116
|
+
These actions are technically impossible in the browser automation environment:
|
|
117
|
+
|
|
118
|
+
- **Downloading files.** `browser_click` on a Download button does not save files to disk.
|
|
119
|
+
- **Reading the token value from a screenshot.** The token IS visible in the creation dialog, but you MUST NOT attempt to read it from a screenshot — it is too easy to misread characters, and the value must be exact. Always use the `credential_store prompt` approach to let the user copy-paste it accurately.
|
|
120
|
+
- **Clipboard operations.** You cannot copy/paste via browser automation.
|
|
121
|
+
|
|
122
|
+
## Step 1: Single Upfront Confirmation
|
|
123
|
+
|
|
124
|
+
Tell the user:
|
|
125
|
+
|
|
126
|
+
> **Setting up your Vercel API token so we can publish your app...**
|
|
127
|
+
>
|
|
128
|
+
> Here's what will happen:
|
|
129
|
+
> 1. **A browser opens** to your Vercel account settings
|
|
130
|
+
> 2. **You sign in** (if not already signed in)
|
|
131
|
+
> 3. **I create the token** — you just watch
|
|
132
|
+
> 4. **One quick copy-paste** — I'll ask you to copy the token value into a secure prompt
|
|
133
|
+
>
|
|
134
|
+
> Takes about a minute. Ready?
|
|
135
|
+
|
|
136
|
+
If the user declines, acknowledge and stop. No further confirmations are needed after this point.
|
|
137
|
+
|
|
138
|
+
## Step 2: Open Vercel and Sign In
|
|
139
|
+
|
|
140
|
+
**Goal:** The user is signed in and the Vercel tokens page is loaded.
|
|
141
|
+
|
|
142
|
+
Navigate to `https://vercel.com/account/tokens`.
|
|
143
|
+
|
|
144
|
+
Take a screenshot and snapshot to check the page state:
|
|
145
|
+
- **Sign-in page:** Tell the user: "Please sign in to your Vercel account in the browser." Then auto-detect sign-in completion by polling screenshots every 5-10 seconds. Check if the current URL has moved away from the login/sign-in page to the tokens page. Do NOT ask the user to "let me know when you're done" — detect it automatically. Once sign-in is detected, tell the user: "Signed in! Creating your API token now..."
|
|
146
|
+
- **Already signed in:** Tell the user: "Already signed in — creating your API token now..." and continue immediately.
|
|
147
|
+
|
|
148
|
+
**Verify:** URL contains `vercel.com/account/tokens` and no sign-in overlay is visible.
|
|
149
|
+
|
|
150
|
+
## Step 3: Create Token
|
|
151
|
+
|
|
152
|
+
**Goal:** A new API token named "Vellum Assistant" is created.
|
|
153
|
+
|
|
154
|
+
Take a screenshot and snapshot. Find and click the button to create a new token (typically labeled "Create" or "Create Token").
|
|
155
|
+
|
|
156
|
+
On the creation form:
|
|
157
|
+
- Token name: **"Vellum Assistant"**
|
|
158
|
+
- Scope: Select **"Full Account"** (or the broadest scope available)
|
|
159
|
+
- Expiration: Select the longest option available, or **"No Expiration"** if offered
|
|
160
|
+
- Click create/submit
|
|
161
|
+
|
|
162
|
+
**Verify:** Take a screenshot. A dialog or section should now display the newly created token value.
|
|
163
|
+
|
|
164
|
+
## Step 4: Capture Token via Secure Prompt
|
|
165
|
+
|
|
166
|
+
**Goal:** The token value is securely captured and stored.
|
|
167
|
+
|
|
168
|
+
### CRITICAL — Token Capture Protocol
|
|
169
|
+
|
|
170
|
+
After token creation, Vercel shows the token value **once**. You MUST follow this exact sequence — **no improvisation**:
|
|
171
|
+
|
|
172
|
+
1. Tell the user: "Your token has been created! Please copy the token value shown on screen and paste it into the secure prompt below."
|
|
173
|
+
2. **IMMEDIATELY** present a `credential_store prompt` for the token. This is your ONLY next action.
|
|
174
|
+
3. Wait for the user to paste the token.
|
|
175
|
+
|
|
176
|
+
**Absolute prohibitions during this step:**
|
|
177
|
+
- Do NOT try to read the token value from the screenshot. It must come from the user via secure prompt to ensure accuracy.
|
|
178
|
+
- Do NOT navigate away from the page until the user has pasted the token.
|
|
179
|
+
- Do NOT click any download or copy buttons.
|
|
180
|
+
|
|
181
|
+
Present the secure prompt:
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
credential_store prompt:
|
|
185
|
+
service: "vercel"
|
|
186
|
+
field: "api_token"
|
|
187
|
+
label: "Vercel API Token"
|
|
188
|
+
description: "Copy the token value shown on the Vercel page and paste it here."
|
|
189
|
+
placeholder: "Enter your Vercel API token"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Wait for the user to complete the prompt. Once received, store it:
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
credential_store store:
|
|
196
|
+
service: "vercel"
|
|
197
|
+
field: "api_token"
|
|
198
|
+
value: "<the token the user provided>"
|
|
199
|
+
allowedTools: ["publish_page", "unpublish_page"]
|
|
200
|
+
allowedDomains: ["api.vercel.com"]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Verify:** `credential_store list` shows `api_token` for `vercel`.
|
|
204
|
+
|
|
205
|
+
## Step 5: Done!
|
|
206
|
+
|
|
207
|
+
"**Vercel is connected!** Your API token is set up and ready to go. You can now publish apps to the web."
|
|
208
|
+
|
|
209
|
+
## Error Handling
|
|
210
|
+
|
|
211
|
+
- **Page load failures:** Retry navigation once. If it still fails, tell the user and ask them to check their internet connection.
|
|
212
|
+
- **Element not found:** Take a fresh screenshot to re-assess. The Vercel UI may have changed. Describe what you see and try alternative approaches. If stuck after 2 attempts, ask the user for guidance.
|
|
213
|
+
- **Token already exists with same name:** This is fine — Vercel allows multiple tokens with the same name. Proceed with creation.
|
|
214
|
+
- **Any unexpected state:** Take a `browser_screenshot`, describe what you see, and ask the user for guidance.
|
|
@@ -125,6 +125,18 @@ export const CallsConfigSchema = z.object({
|
|
|
125
125
|
.positive('calls.userConsultTimeoutSeconds must be a positive integer')
|
|
126
126
|
.max(2_147_483, 'calls.userConsultTimeoutSeconds must be at most 2147483 (setTimeout-safe limit)')
|
|
127
127
|
.default(120),
|
|
128
|
+
ttsPlaybackDelayMs: z
|
|
129
|
+
.number({ error: 'calls.ttsPlaybackDelayMs must be a number' })
|
|
130
|
+
.int('calls.ttsPlaybackDelayMs must be an integer')
|
|
131
|
+
.min(0, 'calls.ttsPlaybackDelayMs must be >= 0')
|
|
132
|
+
.max(10_000, 'calls.ttsPlaybackDelayMs must be at most 10000')
|
|
133
|
+
.default(3000),
|
|
134
|
+
accessRequestPollIntervalMs: z
|
|
135
|
+
.number({ error: 'calls.accessRequestPollIntervalMs must be a number' })
|
|
136
|
+
.int('calls.accessRequestPollIntervalMs must be an integer')
|
|
137
|
+
.min(50, 'calls.accessRequestPollIntervalMs must be >= 50')
|
|
138
|
+
.max(10_000, 'calls.accessRequestPollIntervalMs must be at most 10000')
|
|
139
|
+
.default(500),
|
|
128
140
|
disclosure: CallsDisclosureConfigSchema.default(CallsDisclosureConfigSchema.parse({})),
|
|
129
141
|
safety: CallsSafetyConfigSchema.default(CallsSafetyConfigSchema.parse({})),
|
|
130
142
|
voice: CallsVoiceConfigSchema.default(CallsVoiceConfigSchema.parse({})),
|
|
@@ -49,14 +49,6 @@
|
|
|
49
49
|
"description": "Send crash reports and error diagnostics to help improve the app",
|
|
50
50
|
"defaultEnabled": true
|
|
51
51
|
},
|
|
52
|
-
{
|
|
53
|
-
"id": "voice-invite-redemption",
|
|
54
|
-
"scope": "assistant",
|
|
55
|
-
"key": "feature_flags.voice-invite-redemption.enabled",
|
|
56
|
-
"label": "Voice Invite Redemption",
|
|
57
|
-
"description": "Enable voice invite code redemption for inbound callers with active voice invites",
|
|
58
|
-
"defaultEnabled": false
|
|
59
|
-
},
|
|
60
52
|
{
|
|
61
53
|
"id": "user-hosted-enabled",
|
|
62
54
|
"scope": "macos",
|
|
@@ -254,6 +254,8 @@ INVITE_JSON=$(curl -s -X POST "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites" \
|
|
|
254
254
|
-d '{
|
|
255
255
|
"sourceChannel": "voice",
|
|
256
256
|
"expectedExternalUserId": "<phone_number_E164>",
|
|
257
|
+
"friendName": "<invitee_first_name>",
|
|
258
|
+
"guardianName": "<guardian_first_name>",
|
|
257
259
|
"maxUses": 1,
|
|
258
260
|
"note": "<optional note, e.g. the person it is for>"
|
|
259
261
|
}')
|
|
@@ -263,6 +265,8 @@ printf '%s\n' "$INVITE_JSON"
|
|
|
263
265
|
Required fields:
|
|
264
266
|
- `sourceChannel` — must be `"voice"`
|
|
265
267
|
- `expectedExternalUserId` — the invitee's phone number in E.164 format (e.g., `+15551234567`)
|
|
268
|
+
- `friendName` — the invitee's first name (used in the voice greeting)
|
|
269
|
+
- `guardianName` — the guardian's first name (used in the voice greeting)
|
|
266
270
|
|
|
267
271
|
Optional fields:
|
|
268
272
|
- `maxUses` — how many times the code can be used (default: 1)
|
|
@@ -348,6 +352,8 @@ Replace `<invite_id>` with the invite's `id` from the list response. The same re
|
|
|
348
352
|
- `sourceChannel is required for create` — when creating an invite, always pass `"sourceChannel": "telegram"` for Telegram or `"sourceChannel": "voice"` for voice invites.
|
|
349
353
|
- `expectedExternalUserId is required for voice invites` — voice invites must include the invitee's phone number.
|
|
350
354
|
- `expectedExternalUserId must be in E.164 format` — the phone number must start with `+` followed by country code and number (e.g., `+15551234567`).
|
|
355
|
+
- `friendName is required for voice invites` — ask for the invitee's first name.
|
|
356
|
+
- `guardianName is required for voice invites` — ask for the guardian's (your user's) first name.
|
|
351
357
|
- `Invite not found or already revoked` — the invite ID may be invalid or the invite is already revoked.
|
|
352
358
|
|
|
353
359
|
## Typical Workflows
|
|
@@ -368,9 +374,9 @@ Replace `<invite_id>` with the invite's `id` from the list response. The same re
|
|
|
368
374
|
|
|
369
375
|
**"Revoke invite"** / **"Cancel invite link"** — List invites to identify the target, confirm, then revoke by ID.
|
|
370
376
|
|
|
371
|
-
**"Create a voice invite for +15551234567"** —
|
|
377
|
+
**"Create a voice invite for +15551234567"** — Ask for the friend's first name and the guardian's first name (if not already known), then create a voice invite with `sourceChannel: "voice"`, the phone number as `expectedExternalUserId`, and both names. Present the invite code and instructions: the person must call from that number and enter the code.
|
|
372
378
|
|
|
373
|
-
**"Let my mom call in"** / **"Invite someone by phone"** — Ask for the phone number in E.164 format,
|
|
379
|
+
**"Let my mom call in"** / **"Invite someone by phone"** — Ask for the phone number in E.164 format, the friend's first name, and the guardian's first name. Create a voice invite and present the code + calling instructions.
|
|
374
380
|
|
|
375
381
|
**"Show my voice invites"** / **"List phone invites"** — List invites filtered by `sourceChannel=voice`, present active invites with bound phone number and expiration info.
|
|
376
382
|
|
|
@@ -3,6 +3,7 @@ import * as net from 'node:net';
|
|
|
3
3
|
import type { ChannelId } from '../../channels/types.js';
|
|
4
4
|
import * as externalConversationStore from '../../memory/external-conversation-store.js';
|
|
5
5
|
import { findMember, revokeMember } from '../../memory/ingress-member-store.js';
|
|
6
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from '../../runtime/assistant-scope.js';
|
|
6
7
|
import {
|
|
7
8
|
createVerificationChallenge,
|
|
8
9
|
findActiveSession,
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
resendOutbound,
|
|
18
19
|
startOutbound,
|
|
19
20
|
} from '../../runtime/guardian-outbound-actions.js';
|
|
20
|
-
import { normalizeAssistantId } from '../../util/platform.js';
|
|
21
21
|
import type {
|
|
22
22
|
ChannelReadinessRequest,
|
|
23
23
|
GuardianVerificationRequest,
|
|
@@ -64,7 +64,7 @@ export function createGuardianChallenge(
|
|
|
64
64
|
rebind?: boolean,
|
|
65
65
|
sessionId?: string,
|
|
66
66
|
): GuardianVerificationResult {
|
|
67
|
-
const resolvedAssistantId =
|
|
67
|
+
const resolvedAssistantId = DAEMON_INTERNAL_ASSISTANT_ID;
|
|
68
68
|
const resolvedChannel = channel ?? 'telegram';
|
|
69
69
|
|
|
70
70
|
const existingBinding = getGuardianBinding(resolvedAssistantId, resolvedChannel);
|
|
@@ -89,9 +89,9 @@ export function createGuardianChallenge(
|
|
|
89
89
|
|
|
90
90
|
export function getGuardianStatus(
|
|
91
91
|
channel?: ChannelId,
|
|
92
|
-
|
|
92
|
+
_assistantId?: string,
|
|
93
93
|
): GuardianVerificationResult {
|
|
94
|
-
const resolvedAssistantId =
|
|
94
|
+
const resolvedAssistantId = DAEMON_INTERNAL_ASSISTANT_ID;
|
|
95
95
|
const resolvedChannel = channel ?? 'telegram';
|
|
96
96
|
|
|
97
97
|
const binding = getGuardianBinding(resolvedAssistantId, resolvedChannel);
|
|
@@ -161,9 +161,7 @@ export function handleGuardianVerification(
|
|
|
161
161
|
socket: net.Socket,
|
|
162
162
|
ctx: HandlerContext,
|
|
163
163
|
): void {
|
|
164
|
-
|
|
165
|
-
// same key the inbound-call path will use for lookups (typically "self").
|
|
166
|
-
const assistantId = normalizeAssistantId(msg.assistantId ?? 'self');
|
|
164
|
+
const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
|
|
167
165
|
const channel = msg.channel ?? 'telegram';
|
|
168
166
|
|
|
169
167
|
try {
|
|
@@ -40,6 +40,8 @@ export function handleIngressInvite(
|
|
|
40
40
|
note: msg.note,
|
|
41
41
|
maxUses: msg.maxUses,
|
|
42
42
|
expiresInMs: msg.expiresInMs,
|
|
43
|
+
friendName: msg.friendName,
|
|
44
|
+
guardianName: msg.guardianName,
|
|
43
45
|
});
|
|
44
46
|
if (!result.ok) {
|
|
45
47
|
ctx.send(socket, { type: 'ingress_invite_response', success: false, error: result.error });
|