@vellumai/assistant 0.4.35 → 0.4.37
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/AGENTS.md +1 -1
- package/ARCHITECTURE.md +44 -49
- package/README.md +32 -20
- package/docs/architecture/keychain-broker.md +186 -0
- package/docs/architecture/security.md +110 -116
- package/docs/runbook-trusted-contacts.md +2 -2
- package/docs/skills.md +25 -25
- package/package.json +5 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
- package/src/__tests__/actor-token-service.test.ts +1 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
- package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/bundle-scanner.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +102 -102
- package/src/__tests__/channel-invite-transport.test.ts +155 -256
- package/src/__tests__/channel-readiness-routes.test.ts +336 -0
- package/src/__tests__/checker.test.ts +6 -6
- package/src/__tests__/chrome-cdp.test.ts +350 -0
- package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
- package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
- package/src/__tests__/config-loader-migration.test.ts +85 -0
- package/src/__tests__/conversation-pairing.test.ts +370 -5
- package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
- package/src/__tests__/credential-broker-server-use.test.ts +1 -10
- package/src/__tests__/credential-security-e2e.test.ts +7 -1
- package/src/__tests__/credential-security-invariants.test.ts +14 -20
- package/src/__tests__/credential-vault-unit.test.ts +1 -11
- package/src/__tests__/credential-vault.test.ts +5 -19
- package/src/__tests__/credentials-cli.test.ts +814 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
- package/src/__tests__/email-invite-adapter.test.ts +78 -0
- package/src/__tests__/email-service-config-fallback.test.ts +102 -0
- package/src/__tests__/encrypted-store.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
- package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
- package/src/__tests__/guardian-outbound-http.test.ts +53 -47
- package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
- package/src/__tests__/handlers-telegram-config.test.ts +8 -2
- package/src/__tests__/handlers-twitter-config.test.ts +2 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
- package/src/__tests__/ingress-reconcile.test.ts +6 -0
- package/src/__tests__/intent-routing.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -0
- package/src/__tests__/ipc-snapshot.test.ts +8 -2
- package/src/__tests__/keychain-broker-client.test.ts +543 -0
- package/src/__tests__/llm-usage-store.test.ts +344 -0
- package/src/__tests__/mcp-client-auth.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/migration-transport.test.ts +49 -0
- package/src/__tests__/notification-broadcaster.test.ts +205 -5
- package/src/__tests__/notification-deep-link.test.ts +365 -1
- package/src/__tests__/oauth-connect-handler.test.ts +2 -2
- package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
- package/src/__tests__/proxy-approval-callback.test.ts +1 -1
- package/src/__tests__/recording-handler.test.ts +1 -1
- package/src/__tests__/recording-intent-handler.test.ts +6 -1
- package/src/__tests__/recording-state-machine.test.ts +1 -1
- package/src/__tests__/relay-server.test.ts +9 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
- package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +8 -2
- package/src/__tests__/secure-keys.test.ts +175 -216
- package/src/__tests__/session-confirmation-signals.test.ts +1 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
- package/src/__tests__/session-queue.test.ts +2 -1
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
- package/src/__tests__/skill-feature-flags.test.ts +12 -9
- package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skills.test.ts +34 -4
- package/src/__tests__/slack-channel-config.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +26 -4
- package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
- package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
- package/src/__tests__/twitter-auth-handler.test.ts +2 -2
- package/src/__tests__/twitter-oauth-client.test.ts +1 -1
- package/src/__tests__/usage-routes.test.ts +339 -0
- package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
- package/src/agent/loop.ts +3 -0
- package/src/amazon/checkout.ts +0 -1
- package/src/approvals/guardian-request-resolvers.ts +9 -1
- package/src/bundler/app-bundler.ts +28 -12
- package/src/bundler/bundle-scanner.ts +1 -1
- package/src/bundler/bundle-signer.ts +3 -3
- package/src/bundler/manifest.ts +1 -1
- package/src/bundler/signature-verifier.ts +3 -3
- package/src/channels/config.ts +1 -1
- package/src/cli/AGENTS.md +63 -0
- package/src/cli/__tests__/notifications.test.ts +470 -0
- package/src/cli/amazon.ts +344 -167
- package/src/cli/audit.ts +85 -0
- package/src/cli/autonomy.ts +369 -0
- package/src/cli/channels.ts +51 -0
- package/src/cli/completions.ts +208 -0
- package/src/cli/config.ts +220 -0
- package/src/cli/contacts.ts +471 -0
- package/src/cli/credentials.ts +564 -0
- package/src/cli/default-action.ts +14 -0
- package/src/cli/dev.ts +131 -0
- package/src/cli/doctor.ts +398 -0
- package/src/cli/email.ts +494 -0
- package/src/cli/influencer.ts +72 -0
- package/src/cli/integrations.ts +248 -57
- package/src/cli/keys.ts +114 -0
- package/src/cli/map.ts +46 -54
- package/src/cli/mcp.ts +111 -3
- package/src/cli/{config-commands.ts → memory.ts} +134 -245
- package/src/cli/notifications.ts +407 -0
- package/src/cli/program.ts +65 -0
- package/src/cli/reference.ts +48 -0
- package/src/cli/sequence.ts +154 -0
- package/src/cli/sessions.ts +262 -0
- package/src/cli/trust.ts +175 -0
- package/src/cli/twitter.ts +323 -106
- package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
- package/src/config/bundled-skills/amazon/SKILL.md +2 -2
- package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
- package/src/config/bundled-skills/contacts/SKILL.md +178 -10
- package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
- package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
- package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/core-schema.ts +7 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +26 -0
- package/src/config/schema.ts +4 -0
- package/src/config/skill-state.ts +0 -13
- package/src/config/system-prompt.ts +27 -0
- package/src/contacts/contact-store.ts +25 -0
- package/src/daemon/computer-use-session.ts +1 -1
- package/src/daemon/handlers/apps.ts +1 -0
- package/src/daemon/handlers/config-channels.ts +3 -3
- package/src/daemon/handlers/config-dispatch.ts +29 -0
- package/src/daemon/handlers/config-inbox.ts +4 -3
- package/src/daemon/handlers/config.ts +3 -43
- package/src/daemon/handlers/contacts.ts +34 -0
- package/src/daemon/handlers/index.ts +17 -3
- package/src/daemon/handlers/session-user-message.ts +7 -0
- package/src/daemon/handlers/sessions.ts +21 -2
- package/src/daemon/handlers/shared.ts +17 -0
- package/src/daemon/ipc-contract/apps.ts +2 -0
- package/src/daemon/ipc-contract/computer-use.ts +9 -0
- package/src/daemon/ipc-contract/contacts.ts +3 -3
- package/src/daemon/ipc-contract/inbox.ts +2 -0
- package/src/daemon/ipc-contract/messages.ts +4 -0
- package/src/daemon/ipc-contract/sessions.ts +8 -0
- package/src/daemon/ipc-contract-inventory.json +1 -0
- package/src/daemon/lifecycle.ts +0 -5
- package/src/daemon/ride-shotgun-handler.ts +139 -25
- package/src/daemon/session-agent-loop-handlers.ts +100 -0
- package/src/daemon/session-agent-loop.ts +72 -0
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/daemon/session.ts +23 -1
- package/src/daemon/tool-side-effects.ts +39 -1
- package/src/email/service.ts +59 -2
- package/src/index.ts +2 -60
- package/src/mcp/mcp-oauth-provider.ts +90 -8
- package/src/media/app-icon-generator.ts +86 -0
- package/src/memory/db-init.ts +11 -0
- package/src/memory/llm-usage-store.ts +186 -0
- package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
- package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema-migration.ts +1 -0
- package/src/memory/shared-app-links-store.ts +1 -1
- package/src/messaging/registry.ts +27 -0
- package/src/notifications/README.md +79 -70
- package/src/notifications/broadcaster.ts +2 -1
- package/src/notifications/conversation-pairing.ts +147 -13
- package/src/notifications/copy-composer.ts +7 -3
- package/src/notifications/destination-resolver.ts +14 -1
- package/src/notifications/emit-signal.ts +3 -2
- package/src/notifications/signal.ts +105 -1
- package/src/notifications/types.ts +16 -0
- package/src/permissions/checker.ts +29 -3
- package/src/permissions/prompter.ts +11 -3
- package/src/runtime/access-request-helper.ts +2 -1
- package/src/runtime/auth/route-policy.ts +7 -1
- package/src/runtime/channel-invite-transport.ts +40 -63
- package/src/runtime/channel-invite-transports/email.ts +13 -39
- package/src/runtime/channel-invite-transports/slack.ts +5 -34
- package/src/runtime/channel-invite-transports/sms.ts +8 -29
- package/src/runtime/channel-invite-transports/telegram.ts +69 -28
- package/src/runtime/channel-invite-transports/voice.ts +0 -7
- package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
- package/src/runtime/channel-readiness-service.ts +202 -45
- package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
- package/src/runtime/guardian-outbound-actions.ts +8 -5
- package/src/runtime/http-server.ts +2 -0
- package/src/runtime/invite-instruction-generator.ts +178 -0
- package/src/runtime/invite-service.ts +22 -25
- package/src/runtime/migrations/migration-transport.ts +13 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
- package/src/runtime/routes/channel-readiness-routes.ts +30 -11
- package/src/runtime/routes/contact-routes.ts +54 -26
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
- package/src/runtime/routes/integration-routes.ts +1 -1
- package/src/runtime/routes/invite-routes.ts +1 -1
- package/src/runtime/routes/secret-routes.ts +31 -7
- package/src/runtime/routes/twilio-routes.ts +32 -1
- package/src/runtime/routes/usage-routes.ts +114 -0
- package/src/runtime/tool-grant-request-helper.ts +2 -1
- package/src/security/encrypted-store.ts +9 -5
- package/src/security/keychain-broker-client.ts +393 -0
- package/src/security/secure-keys.ts +106 -321
- package/src/tools/apps/executors.ts +73 -0
- package/src/tools/browser/auto-navigate.ts +15 -6
- package/src/tools/browser/chrome-cdp.ts +211 -0
- package/src/tools/browser/network-recorder.test.ts +83 -0
- package/src/tools/browser/network-recorder.ts +8 -7
- package/src/tools/browser/x-auto-navigate.ts +12 -6
- package/src/tools/credentials/policy-types.ts +24 -0
- package/src/tools/credentials/vault.ts +22 -27
- package/src/tools/network/script-proxy/session-manager.ts +47 -3
- package/src/tools/permission-checker.ts +1 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/ui-surface/definitions.ts +1 -2
- package/src/tools/watch/watch-state.ts +2 -0
- package/src/__tests__/key-migration.test.ts +0 -240
- package/src/__tests__/keychain.test.ts +0 -286
- package/src/cli/core-commands.ts +0 -899
- package/src/security/keychain-to-encrypted-migration.ts +0 -66
- package/src/security/keychain.ts +0 -490
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generative invite instructions.
|
|
3
|
+
*
|
|
4
|
+
* Uses the configured provider to generate a short, first-person instruction
|
|
5
|
+
* telling the guardian how to invite a contact via a specific channel. Falls
|
|
6
|
+
* back to a deterministic template when the provider is unavailable or
|
|
7
|
+
* generation fails/times out.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
createTimeout,
|
|
12
|
+
extractText,
|
|
13
|
+
resolveConfiguredProvider,
|
|
14
|
+
userMessage,
|
|
15
|
+
} from "../providers/provider-send-message.js";
|
|
16
|
+
import { getLogger } from "../util/logger.js";
|
|
17
|
+
|
|
18
|
+
const log = getLogger("invite-instruction-generator");
|
|
19
|
+
|
|
20
|
+
/** Timeout for the generative instruction call (ms). */
|
|
21
|
+
const GENERATION_TIMEOUT_MS = 5_000;
|
|
22
|
+
|
|
23
|
+
/** Maximum allowed length for a generated instruction. */
|
|
24
|
+
const MAX_INSTRUCTION_LENGTH = 500;
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Channel display label
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
/** Human-readable label for a channel type. */
|
|
31
|
+
export function channelDisplayLabel(type: string): string {
|
|
32
|
+
switch (type) {
|
|
33
|
+
case "telegram":
|
|
34
|
+
return "Telegram";
|
|
35
|
+
case "sms":
|
|
36
|
+
return "SMS";
|
|
37
|
+
case "email":
|
|
38
|
+
return "Email";
|
|
39
|
+
case "slack":
|
|
40
|
+
return "Slack";
|
|
41
|
+
case "voice":
|
|
42
|
+
return "Voice";
|
|
43
|
+
default:
|
|
44
|
+
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Deterministic fallback
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build a deterministic fallback instruction when the LLM is unavailable.
|
|
54
|
+
*/
|
|
55
|
+
export function buildFallbackInstruction(params: {
|
|
56
|
+
contactName?: string;
|
|
57
|
+
channelLabel: string;
|
|
58
|
+
channelHandle?: string;
|
|
59
|
+
shareUrl?: string;
|
|
60
|
+
}): string {
|
|
61
|
+
const contact = params.contactName || "the contact";
|
|
62
|
+
const handle = params.channelHandle
|
|
63
|
+
? ` at ${params.channelHandle}`
|
|
64
|
+
: ` on ${params.channelLabel}`;
|
|
65
|
+
if (params.shareUrl) {
|
|
66
|
+
return `Send ${contact} this link: ${params.shareUrl} — or tell them to message me${handle} with the code below.`;
|
|
67
|
+
}
|
|
68
|
+
return `Tell ${contact} to message me${handle} with the code below.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// LLM-powered generation
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Generate an invite instruction via the configured LLM provider. Returns a
|
|
77
|
+
* deterministic fallback when the provider is unavailable, generation times
|
|
78
|
+
* out (5s), or the output fails validation.
|
|
79
|
+
*/
|
|
80
|
+
export async function generateInviteInstruction(params: {
|
|
81
|
+
contactName?: string;
|
|
82
|
+
channelType: string;
|
|
83
|
+
channelHandle?: string;
|
|
84
|
+
/** Whether a share URL is available (shown separately in the UI). */
|
|
85
|
+
hasShareUrl?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Actual share URL for the deterministic fallback only. Never sent to the
|
|
88
|
+
* LLM — the URL contains the raw invite token which is a redemption
|
|
89
|
+
* credential.
|
|
90
|
+
*/
|
|
91
|
+
shareUrl?: string;
|
|
92
|
+
}): Promise<string> {
|
|
93
|
+
const channelLabel = channelDisplayLabel(params.channelType);
|
|
94
|
+
const fallback = buildFallbackInstruction({
|
|
95
|
+
contactName: params.contactName,
|
|
96
|
+
channelLabel,
|
|
97
|
+
channelHandle: params.channelHandle,
|
|
98
|
+
shareUrl: params.shareUrl,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const resolved = resolveConfiguredProvider();
|
|
102
|
+
if (!resolved) {
|
|
103
|
+
log.debug(
|
|
104
|
+
"No provider available for invite instruction generation, using fallback",
|
|
105
|
+
);
|
|
106
|
+
return fallback;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const { signal, cleanup } = createTimeout(GENERATION_TIMEOUT_MS);
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const parts: string[] = [
|
|
113
|
+
"Generate a 1–2 sentence instruction from the assistant's perspective telling the user how to invite a contact.",
|
|
114
|
+
"",
|
|
115
|
+
`Channel: ${channelLabel}`,
|
|
116
|
+
];
|
|
117
|
+
if (params.contactName) {
|
|
118
|
+
parts.push(`Contact name: ${params.contactName}`);
|
|
119
|
+
}
|
|
120
|
+
if (params.channelHandle) {
|
|
121
|
+
parts.push(`Channel handle: ${params.channelHandle}`);
|
|
122
|
+
}
|
|
123
|
+
if (params.hasShareUrl) {
|
|
124
|
+
parts.push("A share link is available (displayed separately in the UI).");
|
|
125
|
+
} else {
|
|
126
|
+
parts.push(
|
|
127
|
+
"No share link is available for this channel. Do NOT mention sharing a link or URL.",
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
parts.push(
|
|
131
|
+
"",
|
|
132
|
+
"Requirements:",
|
|
133
|
+
'- Write from the assistant\'s perspective using first person ("message me"), NOT third person ("message the assistant").',
|
|
134
|
+
"- Do NOT include the invite code — it is displayed separately in the UI.",
|
|
135
|
+
);
|
|
136
|
+
if (params.hasShareUrl) {
|
|
137
|
+
parts.push(
|
|
138
|
+
"- When a share link is available, mention that the user can share the link.",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
parts.push(
|
|
142
|
+
"- Keep the instruction concise (1–2 sentences, under 500 characters).",
|
|
143
|
+
'- Refer to the invite code as "the code below" since it is shown beneath this instruction.',
|
|
144
|
+
"",
|
|
145
|
+
"Respond with the instruction text only — no labels, no extra formatting.",
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const prompt = parts.join("\n");
|
|
149
|
+
|
|
150
|
+
const response = await resolved.provider.sendMessage(
|
|
151
|
+
[userMessage(prompt)],
|
|
152
|
+
undefined,
|
|
153
|
+
undefined,
|
|
154
|
+
{ signal, config: { modelIntent: "latency-optimized" } },
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const text = extractText(response).trim();
|
|
158
|
+
|
|
159
|
+
if (!text || text.length > MAX_INSTRUCTION_LENGTH) {
|
|
160
|
+
log.warn(
|
|
161
|
+
{ length: text.length },
|
|
162
|
+
"Generated invite instruction failed validation, using fallback",
|
|
163
|
+
);
|
|
164
|
+
return fallback;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return text;
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (signal.aborted) {
|
|
170
|
+
log.warn("Invite instruction generation timed out, using fallback");
|
|
171
|
+
} else {
|
|
172
|
+
log.warn({ err }, "Invite instruction generation failed, using fallback");
|
|
173
|
+
}
|
|
174
|
+
return fallback;
|
|
175
|
+
} finally {
|
|
176
|
+
cleanup();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -25,7 +25,11 @@ import {
|
|
|
25
25
|
} from "../memory/invite-store.js";
|
|
26
26
|
import { isValidE164 } from "../util/phone.js";
|
|
27
27
|
import { generateVoiceCode, hashVoiceCode } from "../util/voice-code.js";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
getInviteAdapterRegistry,
|
|
30
|
+
resolveAdapterHandle,
|
|
31
|
+
} from "./channel-invite-transport.js";
|
|
32
|
+
import { generateInviteInstruction } from "./invite-instruction-generator.js";
|
|
29
33
|
import {
|
|
30
34
|
type InviteRedemptionOutcome,
|
|
31
35
|
redeemInvite as redeemInviteTyped,
|
|
@@ -140,19 +144,19 @@ export type IngressResult<T> =
|
|
|
140
144
|
// Invite operations
|
|
141
145
|
// ---------------------------------------------------------------------------
|
|
142
146
|
|
|
143
|
-
export function createIngressInvite(params: {
|
|
147
|
+
export async function createIngressInvite(params: {
|
|
144
148
|
sourceChannel?: string;
|
|
145
149
|
note?: string;
|
|
146
150
|
maxUses?: number;
|
|
147
151
|
expiresInMs?: number;
|
|
148
|
-
// Contact display name for personalizing
|
|
152
|
+
// Contact display name for personalizing invite instructions
|
|
149
153
|
contactName?: string;
|
|
150
154
|
// Voice invite parameters
|
|
151
155
|
expectedExternalUserId?: string;
|
|
152
156
|
voiceCodeDigits?: number;
|
|
153
157
|
friendName?: string;
|
|
154
158
|
guardianName?: string;
|
|
155
|
-
}): IngressResult<InviteResponseData
|
|
159
|
+
}): Promise<IngressResult<InviteResponseData>> {
|
|
156
160
|
if (!params.sourceChannel) {
|
|
157
161
|
return { ok: false, error: "sourceChannel is required for create" };
|
|
158
162
|
}
|
|
@@ -220,7 +224,7 @@ export function createIngressInvite(params: {
|
|
|
220
224
|
: { inviteCodeHash }),
|
|
221
225
|
});
|
|
222
226
|
|
|
223
|
-
// Build
|
|
227
|
+
// Build invite instruction for non-voice invites via LLM generation
|
|
224
228
|
let guardianInstruction: string | undefined;
|
|
225
229
|
let channelHandle: string | undefined;
|
|
226
230
|
if (!isVoice && inviteCode) {
|
|
@@ -230,27 +234,20 @@ export function createIngressInvite(params: {
|
|
|
230
234
|
const adapter = channelId
|
|
231
235
|
? getInviteAdapterRegistry().get(channelId)
|
|
232
236
|
: undefined;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
inviteCode,
|
|
238
|
-
contactName: params.contactName,
|
|
239
|
-
});
|
|
240
|
-
if (adapterResult) {
|
|
241
|
-
guardianInstruction = adapterResult.instruction;
|
|
242
|
-
channelHandle = adapterResult.channelHandle;
|
|
243
|
-
}
|
|
244
|
-
} catch {
|
|
245
|
-
// Fall through to generic instruction if adapter fails
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (!guardianInstruction) {
|
|
250
|
-
const contactLabel = params.contactName || "the contact";
|
|
251
|
-
const channelLabel = params.sourceChannel;
|
|
252
|
-
guardianInstruction = `Tell ${contactLabel} to contact the assistant on ${channelLabel} and provide the code ${inviteCode}.`;
|
|
237
|
+
if (params.sourceChannel === "telegram") {
|
|
238
|
+
const { ensureTelegramBotUsernameResolved } =
|
|
239
|
+
await import("./channel-invite-transports/telegram.js");
|
|
240
|
+
await ensureTelegramBotUsernameResolved();
|
|
253
241
|
}
|
|
242
|
+
channelHandle = adapter ? await resolveAdapterHandle(adapter) : undefined;
|
|
243
|
+
const share = buildSharePayload(params.sourceChannel, rawToken);
|
|
244
|
+
guardianInstruction = await generateInviteInstruction({
|
|
245
|
+
contactName: params.contactName,
|
|
246
|
+
channelType: params.sourceChannel,
|
|
247
|
+
channelHandle,
|
|
248
|
+
hasShareUrl: !!share?.url,
|
|
249
|
+
shareUrl: share?.url,
|
|
250
|
+
});
|
|
254
251
|
}
|
|
255
252
|
|
|
256
253
|
// Voice invites must not expose the token — callers must redeem via the
|
|
@@ -40,6 +40,13 @@ export interface TransportConfig {
|
|
|
40
40
|
authHeader?: string;
|
|
41
41
|
/** Header name for auth. Defaults to "Authorization" for runtime, "X-Session-Token" for managed. */
|
|
42
42
|
authHeaderName?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Additional headers to include with every request.
|
|
45
|
+
* Merged after Content-Type but before auth-header injection,
|
|
46
|
+
* so an explicit auth header from the transport always wins
|
|
47
|
+
* over a same-named entry in defaultHeaders.
|
|
48
|
+
*/
|
|
49
|
+
defaultHeaders?: Record<string, string>;
|
|
43
50
|
/** Custom fetch implementation for testing or environments without global fetch. */
|
|
44
51
|
fetchFn?: typeof fetch;
|
|
45
52
|
}
|
|
@@ -277,6 +284,12 @@ function buildHeaders(
|
|
|
277
284
|
headers["Content-Type"] = contentType;
|
|
278
285
|
}
|
|
279
286
|
|
|
287
|
+
// Merge defaultHeaders after Content-Type but before auth injection
|
|
288
|
+
if (config.defaultHeaders) {
|
|
289
|
+
Object.assign(headers, config.defaultHeaders);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Auth header is applied last so it always wins over defaultHeaders
|
|
280
293
|
if (config.authHeader) {
|
|
281
294
|
const headerName =
|
|
282
295
|
config.authHeaderName ??
|
|
@@ -167,7 +167,7 @@ export function handleDownloadSharedApp(shareToken: string): Response {
|
|
|
167
167
|
return new Response(new Uint8Array(record.bundleData), {
|
|
168
168
|
headers: {
|
|
169
169
|
"Content-Type": "application/zip",
|
|
170
|
-
"Content-Disposition": 'attachment; filename="app.
|
|
170
|
+
"Content-Disposition": 'attachment; filename="app.vellum"',
|
|
171
171
|
},
|
|
172
172
|
});
|
|
173
173
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
type GuardianApprovalRequest,
|
|
12
12
|
} from "../../../memory/channel-guardian-store.js";
|
|
13
13
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
14
|
+
import type { NotificationSourceChannel } from "../../../notifications/signal.js";
|
|
14
15
|
import { getLogger } from "../../../util/logger.js";
|
|
15
16
|
import { runApprovalConversationTurn } from "../../approval-conversation-turn.js";
|
|
16
17
|
import { composeApprovalMessageGenerative } from "../../approval-message-composer.js";
|
|
@@ -791,7 +792,7 @@ async function handleAccessRequestApproval(
|
|
|
791
792
|
// Emit both guardian_decision and denied signals so all lifecycle
|
|
792
793
|
// observers are notified of the denial.
|
|
793
794
|
const deniedPayload = {
|
|
794
|
-
sourceChannel: approval.channel,
|
|
795
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
795
796
|
requesterExternalUserId: approval.requesterExternalUserId,
|
|
796
797
|
requesterChatId: approval.requesterChatId,
|
|
797
798
|
decidedByExternalUserId,
|
|
@@ -800,7 +801,7 @@ async function handleAccessRequestApproval(
|
|
|
800
801
|
|
|
801
802
|
void emitNotificationSignal({
|
|
802
803
|
sourceEventName: "ingress.trusted_contact.guardian_decision",
|
|
803
|
-
sourceChannel: approval.channel,
|
|
804
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
804
805
|
sourceSessionId: approval.conversationId,
|
|
805
806
|
attentionHints: {
|
|
806
807
|
requiresAction: false,
|
|
@@ -814,7 +815,7 @@ async function handleAccessRequestApproval(
|
|
|
814
815
|
|
|
815
816
|
void emitNotificationSignal({
|
|
816
817
|
sourceEventName: "ingress.trusted_contact.denied",
|
|
817
|
-
sourceChannel: approval.channel,
|
|
818
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
818
819
|
sourceSessionId: approval.conversationId,
|
|
819
820
|
attentionHints: {
|
|
820
821
|
requiresAction: false,
|
|
@@ -882,7 +883,7 @@ async function handleAccessRequestApproval(
|
|
|
882
883
|
if (!decisionResult.verificationSessionId) {
|
|
883
884
|
void emitNotificationSignal({
|
|
884
885
|
sourceEventName: "ingress.trusted_contact.guardian_decision",
|
|
885
|
-
sourceChannel: approval.channel,
|
|
886
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
886
887
|
sourceSessionId: approval.conversationId,
|
|
887
888
|
attentionHints: {
|
|
888
889
|
requiresAction: false,
|
|
@@ -891,7 +892,7 @@ async function handleAccessRequestApproval(
|
|
|
891
892
|
visibleInSourceNow: false,
|
|
892
893
|
},
|
|
893
894
|
contextPayload: {
|
|
894
|
-
sourceChannel: approval.channel,
|
|
895
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
895
896
|
requesterExternalUserId: approval.requesterExternalUserId,
|
|
896
897
|
requesterChatId: approval.requesterChatId,
|
|
897
898
|
decidedByExternalUserId,
|
|
@@ -908,7 +909,7 @@ async function handleAccessRequestApproval(
|
|
|
908
909
|
if (decisionResult.verificationSessionId && codeDelivered) {
|
|
909
910
|
void emitNotificationSignal({
|
|
910
911
|
sourceEventName: "ingress.trusted_contact.verification_sent",
|
|
911
|
-
sourceChannel: approval.channel,
|
|
912
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
912
913
|
sourceSessionId: approval.conversationId,
|
|
913
914
|
attentionHints: {
|
|
914
915
|
requiresAction: false,
|
|
@@ -917,7 +918,7 @@ async function handleAccessRequestApproval(
|
|
|
917
918
|
visibleInSourceNow: true,
|
|
918
919
|
},
|
|
919
920
|
contextPayload: {
|
|
920
|
-
sourceChannel: approval.channel,
|
|
921
|
+
sourceChannel: approval.channel as NotificationSourceChannel,
|
|
921
922
|
requesterExternalUserId: approval.requesterExternalUserId,
|
|
922
923
|
requesterChatId: approval.requesterChatId,
|
|
923
924
|
verificationSessionId: decisionResult.verificationSessionId,
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
import type { ChannelId } from "../../channels/types.js";
|
|
9
9
|
import { getReadinessService } from "../../daemon/handlers/config-channels.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getInviteAdapterRegistry,
|
|
12
|
+
resolveAdapterHandle,
|
|
13
|
+
} from "../channel-invite-transport.js";
|
|
11
14
|
import type { RouteDefinition } from "../http-router.js";
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -18,16 +21,20 @@ import type { RouteDefinition } from "../http-router.js";
|
|
|
18
21
|
export async function handleGetChannelReadiness(url: URL): Promise<Response> {
|
|
19
22
|
const channel =
|
|
20
23
|
(url.searchParams.get("channel") as ChannelId | null) ?? undefined;
|
|
21
|
-
|
|
24
|
+
// Default to including remote checks — they're cached for 5 minutes and
|
|
25
|
+
// required for accurate readiness (e.g. email inbox existence).
|
|
26
|
+
const includeRemote = url.searchParams.get("includeRemote") !== "false";
|
|
22
27
|
|
|
23
28
|
const service = getReadinessService();
|
|
24
29
|
const snapshots = await service.getReadiness(channel, includeRemote);
|
|
25
30
|
const adapterRegistry = getInviteAdapterRegistry();
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
snapshots: snapshots.map((s) => {
|
|
32
|
+
const enriched = await Promise.all(
|
|
33
|
+
snapshots.map(async (s) => {
|
|
30
34
|
const adapter = adapterRegistry.get(s.channel);
|
|
35
|
+
const channelHandle = adapter
|
|
36
|
+
? await resolveAdapterHandle(adapter)
|
|
37
|
+
: undefined;
|
|
31
38
|
return {
|
|
32
39
|
channel: s.channel,
|
|
33
40
|
ready: s.ready,
|
|
@@ -36,9 +43,14 @@ export async function handleGetChannelReadiness(url: URL): Promise<Response> {
|
|
|
36
43
|
reasons: s.reasons,
|
|
37
44
|
localChecks: s.localChecks,
|
|
38
45
|
remoteChecks: s.remoteChecks,
|
|
39
|
-
channelHandle
|
|
46
|
+
channelHandle,
|
|
40
47
|
};
|
|
41
48
|
}),
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return Response.json({
|
|
52
|
+
success: true,
|
|
53
|
+
snapshots: enriched,
|
|
42
54
|
});
|
|
43
55
|
}
|
|
44
56
|
|
|
@@ -66,14 +78,16 @@ export async function handleRefreshChannelReadiness(
|
|
|
66
78
|
|
|
67
79
|
const snapshots = await service.getReadiness(
|
|
68
80
|
body.channel,
|
|
69
|
-
body.includeRemote,
|
|
81
|
+
body.includeRemote ?? true,
|
|
70
82
|
);
|
|
71
83
|
const adapterRegistry = getInviteAdapterRegistry();
|
|
72
84
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
snapshots: snapshots.map((s) => {
|
|
85
|
+
const enriched = await Promise.all(
|
|
86
|
+
snapshots.map(async (s) => {
|
|
76
87
|
const adapter = adapterRegistry.get(s.channel);
|
|
88
|
+
const channelHandle = adapter
|
|
89
|
+
? await resolveAdapterHandle(adapter)
|
|
90
|
+
: undefined;
|
|
77
91
|
return {
|
|
78
92
|
channel: s.channel,
|
|
79
93
|
ready: s.ready,
|
|
@@ -82,9 +96,14 @@ export async function handleRefreshChannelReadiness(
|
|
|
82
96
|
reasons: s.reasons,
|
|
83
97
|
localChecks: s.localChecks,
|
|
84
98
|
remoteChecks: s.remoteChecks,
|
|
85
|
-
channelHandle
|
|
99
|
+
channelHandle,
|
|
86
100
|
};
|
|
87
101
|
}),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return Response.json({
|
|
105
|
+
success: true,
|
|
106
|
+
snapshots: enriched,
|
|
88
107
|
});
|
|
89
108
|
}
|
|
90
109
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for contact management endpoints.
|
|
3
3
|
*
|
|
4
|
-
* GET
|
|
5
|
-
* POST
|
|
6
|
-
* GET
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* GET /v1/contacts — list contacts
|
|
5
|
+
* POST /v1/contacts — create or update a contact
|
|
6
|
+
* GET /v1/contacts/:id — get a contact by ID
|
|
7
|
+
* DELETE /v1/contacts/:id — delete a contact
|
|
8
|
+
* POST /v1/contacts/merge — merge two contacts
|
|
9
|
+
* PATCH /v1/contact-channels/:contactChannelId — update a contact channel's status/policy
|
|
10
|
+
* POST /v1/contact-channels/:contactChannelId/verify — initiate trusted contact verification
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { createHash, randomBytes } from "node:crypto";
|
|
@@ -14,6 +15,7 @@ import { createHash, randomBytes } from "node:crypto";
|
|
|
14
15
|
import type { ChannelId } from "../../channels/types.js";
|
|
15
16
|
import { resolveGuardianName } from "../../config/user-reference.js";
|
|
16
17
|
import {
|
|
18
|
+
deleteContact,
|
|
17
19
|
getAssistantContactMetadata,
|
|
18
20
|
getChannelById,
|
|
19
21
|
getContact,
|
|
@@ -25,7 +27,6 @@ import {
|
|
|
25
27
|
upsertContact,
|
|
26
28
|
validateSpeciesMetadata,
|
|
27
29
|
} from "../../contacts/contact-store.js";
|
|
28
|
-
import type { ContactChannel } from "../../contacts/types.js";
|
|
29
30
|
import type {
|
|
30
31
|
AssistantSpecies,
|
|
31
32
|
ChannelPolicy,
|
|
@@ -143,6 +144,20 @@ export function handleGetContact(contactId: string): Response {
|
|
|
143
144
|
});
|
|
144
145
|
}
|
|
145
146
|
|
|
147
|
+
/**
|
|
148
|
+
* DELETE /v1/contacts/:id
|
|
149
|
+
*/
|
|
150
|
+
export function handleDeleteContact(contactId: string): Response {
|
|
151
|
+
const result = deleteContact(contactId);
|
|
152
|
+
if (result === "not_found") {
|
|
153
|
+
return httpError("NOT_FOUND", `Contact "${contactId}" not found`, 404);
|
|
154
|
+
}
|
|
155
|
+
if (result === "is_guardian") {
|
|
156
|
+
return httpError("FORBIDDEN", "Cannot delete a guardian contact", 403);
|
|
157
|
+
}
|
|
158
|
+
return new Response(null, { status: 204 });
|
|
159
|
+
}
|
|
160
|
+
|
|
146
161
|
/**
|
|
147
162
|
* POST /v1/contacts/merge { keepId, mergeId }
|
|
148
163
|
*/
|
|
@@ -339,7 +354,7 @@ export async function handleUpsertContact(req: Request): Promise<Response> {
|
|
|
339
354
|
}
|
|
340
355
|
|
|
341
356
|
/**
|
|
342
|
-
* PATCH /v1/
|
|
357
|
+
* PATCH /v1/contact-channels/:contactChannelId { status?, policy?, reason? }
|
|
343
358
|
*/
|
|
344
359
|
export async function handleUpdateContactChannel(
|
|
345
360
|
req: Request,
|
|
@@ -459,27 +474,32 @@ function getTelegramBotUsername(): string | undefined {
|
|
|
459
474
|
}
|
|
460
475
|
|
|
461
476
|
/**
|
|
462
|
-
* POST /v1/
|
|
477
|
+
* POST /v1/contact-channels/:contactChannelId/verify
|
|
463
478
|
*
|
|
464
479
|
* Initiate trusted contact verification for a specific channel. Sends a
|
|
465
480
|
* verification code via SMS, Telegram, Slack, or voice and returns session
|
|
466
481
|
* info so the client can track the verification flow.
|
|
467
482
|
*/
|
|
468
483
|
export async function handleVerifyContactChannel(
|
|
469
|
-
|
|
470
|
-
channelId: string,
|
|
484
|
+
contactChannelId: string,
|
|
471
485
|
assistantId: string,
|
|
472
486
|
): Promise<Response> {
|
|
473
|
-
const
|
|
474
|
-
if (!
|
|
475
|
-
return httpError(
|
|
487
|
+
const channel = getChannelById(contactChannelId);
|
|
488
|
+
if (!channel) {
|
|
489
|
+
return httpError(
|
|
490
|
+
"NOT_FOUND",
|
|
491
|
+
`Channel "${contactChannelId}" not found`,
|
|
492
|
+
404,
|
|
493
|
+
);
|
|
476
494
|
}
|
|
477
495
|
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
496
|
+
const contact = getContact(channel.contactId);
|
|
497
|
+
if (!contact) {
|
|
498
|
+
return httpError(
|
|
499
|
+
"NOT_FOUND",
|
|
500
|
+
`Contact "${channel.contactId}" not found`,
|
|
501
|
+
404,
|
|
502
|
+
);
|
|
483
503
|
}
|
|
484
504
|
|
|
485
505
|
// Already verified — no need to re-verify
|
|
@@ -605,6 +625,9 @@ export async function handleVerifyContactChannel(
|
|
|
605
625
|
}
|
|
606
626
|
|
|
607
627
|
// Telegram handle only (no chat ID): bootstrap flow
|
|
628
|
+
const { ensureTelegramBotUsernameResolved } =
|
|
629
|
+
await import("../channel-invite-transports/telegram.js");
|
|
630
|
+
await ensureTelegramBotUsernameResolved();
|
|
608
631
|
const botUsername = getTelegramBotUsername();
|
|
609
632
|
if (!botUsername) {
|
|
610
633
|
return httpError(
|
|
@@ -713,20 +736,19 @@ export function contactRouteDefinitions(): RouteDefinition[] {
|
|
|
713
736
|
handler: async ({ req }) => handleMergeContacts(req),
|
|
714
737
|
},
|
|
715
738
|
{
|
|
716
|
-
endpoint: "
|
|
739
|
+
endpoint: "contact-channels/:contactChannelId",
|
|
717
740
|
method: "PATCH",
|
|
718
|
-
policyKey: "
|
|
741
|
+
policyKey: "contact-channels",
|
|
719
742
|
handler: async ({ req, params }) =>
|
|
720
|
-
handleUpdateContactChannel(req, params.
|
|
743
|
+
handleUpdateContactChannel(req, params.contactChannelId),
|
|
721
744
|
},
|
|
722
745
|
{
|
|
723
|
-
endpoint: "
|
|
746
|
+
endpoint: "contact-channels/:contactChannelId/verify",
|
|
724
747
|
method: "POST",
|
|
725
|
-
policyKey: "
|
|
748
|
+
policyKey: "contact-channels",
|
|
726
749
|
handler: async ({ params, authContext }) =>
|
|
727
750
|
handleVerifyContactChannel(
|
|
728
|
-
params.
|
|
729
|
-
params.channelId,
|
|
751
|
+
params.contactChannelId,
|
|
730
752
|
authContext.assistantId,
|
|
731
753
|
),
|
|
732
754
|
},
|
|
@@ -746,5 +768,11 @@ export function contactCatchAllRouteDefinitions(): RouteDefinition[] {
|
|
|
746
768
|
policyKey: "contacts",
|
|
747
769
|
handler: ({ params }) => handleGetContact(params.id),
|
|
748
770
|
},
|
|
771
|
+
{
|
|
772
|
+
endpoint: "contacts/:id",
|
|
773
|
+
method: "DELETE",
|
|
774
|
+
policyKey: "contacts",
|
|
775
|
+
handler: ({ params }) => handleDeleteContact(params.id),
|
|
776
|
+
},
|
|
749
777
|
];
|
|
750
778
|
}
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { ChannelId } from "../../../channels/types.js";
|
|
15
15
|
import { getGatewayInternalBaseUrl } from "../../../config/env.js";
|
|
16
|
-
import { RESEND_COOLDOWN_MS } from "../../../daemon/handlers/config-channels.js";
|
|
17
16
|
import { getLogger } from "../../../util/logger.js";
|
|
18
17
|
import { mintDaemonDeliveryToken } from "../../auth/token-service.js";
|
|
19
18
|
import {
|
|
@@ -23,6 +22,7 @@ import {
|
|
|
23
22
|
updateSessionDelivery,
|
|
24
23
|
updateSessionStatus,
|
|
25
24
|
} from "../../channel-guardian-service.js";
|
|
25
|
+
import { RESEND_COOLDOWN_MS } from "../../guardian-outbound-actions.js";
|
|
26
26
|
import {
|
|
27
27
|
composeVerificationTelegram,
|
|
28
28
|
GUARDIAN_VERIFY_TEMPLATE_KEYS,
|
|
@@ -11,6 +11,7 @@ import type { ChannelId, InterfaceId } from "../../../channels/types.js";
|
|
|
11
11
|
import { createCanonicalGuardianRequest } from "../../../memory/canonical-guardian-store.js";
|
|
12
12
|
import * as channelDeliveryStore from "../../../memory/channel-delivery-store.js";
|
|
13
13
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
14
|
+
import type { NotificationSourceChannel } from "../../../notifications/signal.js";
|
|
14
15
|
import { getLogger } from "../../../util/logger.js";
|
|
15
16
|
import { getGuardianBinding } from "../../channel-guardian-service.js";
|
|
16
17
|
import { GUARDIAN_APPROVAL_TTL_MS } from "../channel-route-shared.js";
|
|
@@ -132,7 +133,7 @@ export function handleEscalationIntercept(
|
|
|
132
133
|
// channels, supplementing the direct guardian notification below.
|
|
133
134
|
void emitNotificationSignal({
|
|
134
135
|
sourceEventName: "ingress.escalation",
|
|
135
|
-
sourceChannel,
|
|
136
|
+
sourceChannel: sourceChannel as NotificationSourceChannel,
|
|
136
137
|
sourceSessionId: conversationId,
|
|
137
138
|
attentionHints: {
|
|
138
139
|
requiresAction: true,
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from "../../../contacts/contacts-write.js";
|
|
23
23
|
import * as channelDeliveryStore from "../../../memory/channel-delivery-store.js";
|
|
24
24
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
25
|
+
import type { NotificationSourceChannel } from "../../../notifications/signal.js";
|
|
25
26
|
import { canonicalizeInboundIdentity } from "../../../util/canonicalize-identity.js";
|
|
26
27
|
import { getLogger } from "../../../util/logger.js";
|
|
27
28
|
import {
|
|
@@ -230,7 +231,7 @@ export async function handleVerificationIntercept(
|
|
|
230
231
|
if (verifyResult.verificationType === "trusted_contact") {
|
|
231
232
|
void emitNotificationSignal({
|
|
232
233
|
sourceEventName: "ingress.trusted_contact.activated",
|
|
233
|
-
sourceChannel,
|
|
234
|
+
sourceChannel: sourceChannel as NotificationSourceChannel,
|
|
234
235
|
sourceSessionId: conversationId,
|
|
235
236
|
attentionHints: {
|
|
236
237
|
requiresAction: false,
|
|
@@ -235,7 +235,7 @@ export async function handleStartOutbound(req: Request): Promise<Response> {
|
|
|
235
235
|
);
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
const result = startOutbound({
|
|
238
|
+
const result = await startOutbound({
|
|
239
239
|
channel: body.channel,
|
|
240
240
|
destination: body.destination,
|
|
241
241
|
rebind: body.rebind,
|