@vellumai/assistant 0.4.49 → 0.4.51
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
package/src/calls/call-domain.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { revokeScopedApprovalGrantsForContext } from "../memory/scoped-approval-
|
|
|
21
21
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
22
22
|
import { isGuardian } from "../runtime/channel-verification-service.js";
|
|
23
23
|
import { credentialKey } from "../security/credential-key.js";
|
|
24
|
-
import {
|
|
24
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
25
25
|
import { getLogger } from "../util/logger.js";
|
|
26
26
|
import { upsertActiveCallLease } from "./active-call-lease.js";
|
|
27
27
|
import { isDeniedNumber } from "./call-constants.js";
|
|
@@ -177,7 +177,7 @@ export async function resolveCallerIdentity(
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
if (mode === "assistant_number") {
|
|
180
|
-
const twilioConfig = getTwilioConfig();
|
|
180
|
+
const twilioConfig = await getTwilioConfig();
|
|
181
181
|
log.info(
|
|
182
182
|
{ mode, source, fromNumber: twilioConfig.phoneNumber },
|
|
183
183
|
"Resolved caller identity",
|
|
@@ -193,7 +193,7 @@ export async function resolveCallerIdentity(
|
|
|
193
193
|
userNumber = identityConfig.userNumber;
|
|
194
194
|
numberSource = "user_config";
|
|
195
195
|
} else {
|
|
196
|
-
const secureKeyValue =
|
|
196
|
+
const secureKeyValue = await getSecureKeyAsync(
|
|
197
197
|
credentialKey("twilio", "user_phone_number"),
|
|
198
198
|
);
|
|
199
199
|
if (secureKeyValue) {
|
|
@@ -1017,3 +1017,134 @@ export async function startVerificationCall(
|
|
|
1017
1017
|
};
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
1020
|
+
|
|
1021
|
+
// ── Invite call ───────────────────────────────────────────────────────
|
|
1022
|
+
|
|
1023
|
+
export type StartInviteCallInput = {
|
|
1024
|
+
phoneNumber: string;
|
|
1025
|
+
friendName: string;
|
|
1026
|
+
guardianName: string;
|
|
1027
|
+
assistantId?: string;
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
export type StartInviteCallResult = { ok: true; callSid: string } | CallError;
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Initiate an outbound call to deliver a voice invite to a contact.
|
|
1034
|
+
*
|
|
1035
|
+
* Creates a minimal call session with a voice channel binding and
|
|
1036
|
+
* passes invite-specific custom parameters so the relay server can
|
|
1037
|
+
* detect this is an invite redemption call.
|
|
1038
|
+
*/
|
|
1039
|
+
export async function startInviteCall(
|
|
1040
|
+
input: StartInviteCallInput,
|
|
1041
|
+
): Promise<StartInviteCallResult> {
|
|
1042
|
+
const { phoneNumber, friendName, guardianName } = input;
|
|
1043
|
+
|
|
1044
|
+
if (!phoneNumber || !E164_REGEX.test(phoneNumber)) {
|
|
1045
|
+
return {
|
|
1046
|
+
ok: false,
|
|
1047
|
+
error: "phone_number must be in E.164 format",
|
|
1048
|
+
status: 400,
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
let sessionId: string | null = null;
|
|
1053
|
+
|
|
1054
|
+
try {
|
|
1055
|
+
const config = loadConfig();
|
|
1056
|
+
const provider = new TwilioConversationRelayProvider();
|
|
1057
|
+
|
|
1058
|
+
// Resolve the assistant's Twilio number as the caller ID
|
|
1059
|
+
const identityResult = await resolveCallerIdentity(config);
|
|
1060
|
+
if (!identityResult.ok) {
|
|
1061
|
+
return { ok: false, error: identityResult.error, status: 400 };
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const preflightResult = await preflightVoiceIngress();
|
|
1065
|
+
if (!preflightResult.ok) {
|
|
1066
|
+
return preflightResult;
|
|
1067
|
+
}
|
|
1068
|
+
const ingressConfig = preflightResult.ingressConfig;
|
|
1069
|
+
|
|
1070
|
+
// Create a minimal conversation so the call session has a valid FK,
|
|
1071
|
+
// and bind it to the voice channel so it never appears as an unbound
|
|
1072
|
+
// desktop thread.
|
|
1073
|
+
const timestamp = Date.now();
|
|
1074
|
+
const convKey = `invite-call:${phoneNumber}:${timestamp}`;
|
|
1075
|
+
const { conversationId } = getOrCreateConversation(convKey);
|
|
1076
|
+
|
|
1077
|
+
upsertBinding({
|
|
1078
|
+
conversationId,
|
|
1079
|
+
sourceChannel: "phone",
|
|
1080
|
+
externalChatId: `invite-call:${phoneNumber}:${timestamp}`,
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
const session = createCallSession({
|
|
1084
|
+
conversationId,
|
|
1085
|
+
provider: "twilio",
|
|
1086
|
+
fromNumber: identityResult.fromNumber,
|
|
1087
|
+
toNumber: phoneNumber,
|
|
1088
|
+
callMode: "invite",
|
|
1089
|
+
inviteFriendName: friendName,
|
|
1090
|
+
inviteGuardianName: guardianName,
|
|
1091
|
+
});
|
|
1092
|
+
sessionId = session.id;
|
|
1093
|
+
|
|
1094
|
+
const webhookUrl = await resolveCallbackUrl(
|
|
1095
|
+
() => getTwilioVoiceWebhookUrl(ingressConfig, session.id),
|
|
1096
|
+
"webhooks/twilio/voice",
|
|
1097
|
+
"twilio_voice",
|
|
1098
|
+
{ callSessionId: session.id },
|
|
1099
|
+
);
|
|
1100
|
+
const statusCallbackUrl = await resolveCallbackUrl(
|
|
1101
|
+
() => getTwilioStatusCallbackUrl(ingressConfig),
|
|
1102
|
+
"webhooks/twilio/status",
|
|
1103
|
+
"twilio_status",
|
|
1104
|
+
);
|
|
1105
|
+
|
|
1106
|
+
upsertActiveCallLease({ callSessionId: session.id });
|
|
1107
|
+
|
|
1108
|
+
const { callSid } = await provider.initiateCall({
|
|
1109
|
+
from: identityResult.fromNumber,
|
|
1110
|
+
to: phoneNumber,
|
|
1111
|
+
webhookUrl,
|
|
1112
|
+
statusCallbackUrl,
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
updateCallSession(session.id, { providerCallSid: callSid });
|
|
1116
|
+
|
|
1117
|
+
log.info(
|
|
1118
|
+
{
|
|
1119
|
+
callSessionId: session.id,
|
|
1120
|
+
callSid,
|
|
1121
|
+
to: phoneNumber,
|
|
1122
|
+
friendName,
|
|
1123
|
+
guardianName,
|
|
1124
|
+
},
|
|
1125
|
+
"Invite call initiated",
|
|
1126
|
+
);
|
|
1127
|
+
|
|
1128
|
+
return { ok: true, callSid };
|
|
1129
|
+
} catch (err) {
|
|
1130
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1131
|
+
log.error(
|
|
1132
|
+
{ err, phoneNumber, friendName, guardianName },
|
|
1133
|
+
"Failed to initiate invite call",
|
|
1134
|
+
);
|
|
1135
|
+
|
|
1136
|
+
if (sessionId) {
|
|
1137
|
+
updateCallSession(sessionId, {
|
|
1138
|
+
status: "failed",
|
|
1139
|
+
endedAt: Date.now(),
|
|
1140
|
+
lastError: msg,
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
return {
|
|
1145
|
+
ok: false,
|
|
1146
|
+
error: `Error initiating invite call: ${msg}`,
|
|
1147
|
+
status: 500,
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
}
|
package/src/calls/call-store.ts
CHANGED
|
@@ -37,6 +37,8 @@ const parseCallSession = createRowMapper<
|
|
|
37
37
|
status: { from: "status", transform: cast<CallSession["status"]>() },
|
|
38
38
|
callMode: { from: "callMode", transform: cast<CallSession["callMode"]>() },
|
|
39
39
|
verificationSessionId: "verificationSessionId",
|
|
40
|
+
inviteFriendName: "inviteFriendName",
|
|
41
|
+
inviteGuardianName: "inviteGuardianName",
|
|
40
42
|
callerIdentityMode: "callerIdentityMode",
|
|
41
43
|
callerIdentitySource: "callerIdentitySource",
|
|
42
44
|
initiatedFromConversationId: "initiatedFromConversationId",
|
|
@@ -81,6 +83,8 @@ export function createCallSession(opts: {
|
|
|
81
83
|
task?: string;
|
|
82
84
|
callMode?: string;
|
|
83
85
|
verificationSessionId?: string;
|
|
86
|
+
inviteFriendName?: string;
|
|
87
|
+
inviteGuardianName?: string;
|
|
84
88
|
callerIdentityMode?: string;
|
|
85
89
|
callerIdentitySource?: string;
|
|
86
90
|
initiatedFromConversationId?: string;
|
|
@@ -98,6 +102,8 @@ export function createCallSession(opts: {
|
|
|
98
102
|
status: "initiated" as const,
|
|
99
103
|
callMode: (opts.callMode ?? null) as CallSession["callMode"],
|
|
100
104
|
verificationSessionId: opts.verificationSessionId ?? null,
|
|
105
|
+
inviteFriendName: opts.inviteFriendName ?? null,
|
|
106
|
+
inviteGuardianName: opts.inviteGuardianName ?? null,
|
|
101
107
|
callerIdentityMode: opts.callerIdentityMode ?? null,
|
|
102
108
|
callerIdentitySource: opts.callerIdentitySource ?? null,
|
|
103
109
|
initiatedFromConversationId: opts.initiatedFromConversationId ?? null,
|
|
@@ -575,6 +575,7 @@ export class RelayConnection {
|
|
|
575
575
|
outcome.fromNumber,
|
|
576
576
|
outcome.friendName,
|
|
577
577
|
outcome.guardianName,
|
|
578
|
+
!resolved.isInbound,
|
|
578
579
|
);
|
|
579
580
|
return;
|
|
580
581
|
case "name_capture":
|
|
@@ -772,6 +773,12 @@ export class RelayConnection {
|
|
|
772
773
|
fromNumber: string;
|
|
773
774
|
callerName?: string;
|
|
774
775
|
skipMemberActivation?: boolean;
|
|
776
|
+
activationReason?:
|
|
777
|
+
| "invite_redeemed"
|
|
778
|
+
| "access_approved"
|
|
779
|
+
| "trusted_contact_verified";
|
|
780
|
+
friendName?: string;
|
|
781
|
+
guardianName?: string;
|
|
775
782
|
}): void {
|
|
776
783
|
const { assistantId, fromNumber, callerName } = params;
|
|
777
784
|
|
|
@@ -808,7 +815,25 @@ export class RelayConnection {
|
|
|
808
815
|
updateCallSession(this.callSessionId, { status: "in_progress" });
|
|
809
816
|
|
|
810
817
|
const guardianLabel = this.resolveGuardianLabel();
|
|
811
|
-
|
|
818
|
+
let handoffText: string;
|
|
819
|
+
|
|
820
|
+
if (params.activationReason === "invite_redeemed") {
|
|
821
|
+
const name = params.friendName;
|
|
822
|
+
const assistantName = this.resolveAssistantLabel();
|
|
823
|
+
const gLabel = params.guardianName || guardianLabel;
|
|
824
|
+
if (name) {
|
|
825
|
+
handoffText = assistantName
|
|
826
|
+
? `Great, I've verified that you are ${name}. It's nice to meet you! I'm ${assistantName}, ${gLabel}'s assistant. How can I help?`
|
|
827
|
+
: `Great, I've verified that you are ${name}. It's nice to meet you! How can I help?`;
|
|
828
|
+
} else {
|
|
829
|
+
handoffText = assistantName
|
|
830
|
+
? `Great, I've verified your identity. It's nice to meet you! I'm ${assistantName}, ${gLabel}'s assistant. How can I help?`
|
|
831
|
+
: `Great, I've verified your identity. It's nice to meet you! How can I help?`;
|
|
832
|
+
}
|
|
833
|
+
} else {
|
|
834
|
+
handoffText = `Great! ${guardianLabel} said I can speak with you. How can I help?`;
|
|
835
|
+
}
|
|
836
|
+
|
|
812
837
|
this.sendTextToken(handoffText, true);
|
|
813
838
|
|
|
814
839
|
recordCallEvent(this.callSessionId, "assistant_spoke", {
|
|
@@ -1000,6 +1025,7 @@ export class RelayConnection {
|
|
|
1000
1025
|
this.continueCallAfterTrustedContactActivation({
|
|
1001
1026
|
assistantId,
|
|
1002
1027
|
fromNumber,
|
|
1028
|
+
activationReason: "trusted_contact_verified",
|
|
1003
1029
|
});
|
|
1004
1030
|
} else {
|
|
1005
1031
|
// Inbound guardian verification: binding already handled above,
|
|
@@ -1096,6 +1122,7 @@ export class RelayConnection {
|
|
|
1096
1122
|
fromNumber: string,
|
|
1097
1123
|
friendName: string | null,
|
|
1098
1124
|
guardianName: string | null,
|
|
1125
|
+
isOutbound: boolean,
|
|
1099
1126
|
): void {
|
|
1100
1127
|
this.inviteRedemptionActive = true;
|
|
1101
1128
|
this.inviteRedemptionAssistantId = assistantId;
|
|
@@ -1116,14 +1143,21 @@ export class RelayConnection {
|
|
|
1116
1143
|
|
|
1117
1144
|
const displayFriend = friendName ?? "there";
|
|
1118
1145
|
const displayGuardian = guardianName ?? "your contact";
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1146
|
+
|
|
1147
|
+
let promptText: string;
|
|
1148
|
+
if (isOutbound) {
|
|
1149
|
+
const assistantName = this.resolveAssistantLabel();
|
|
1150
|
+
promptText = assistantName
|
|
1151
|
+
? `Hi ${displayFriend}, this is ${assistantName}, ${displayGuardian}'s assistant. To get started, please enter the 6-digit code that ${displayGuardian} shared with you.`
|
|
1152
|
+
: `Hi ${displayFriend}, this is ${displayGuardian}'s assistant. To get started, please enter the 6-digit code that ${displayGuardian} shared with you.`;
|
|
1153
|
+
} else {
|
|
1154
|
+
promptText = `Welcome ${displayFriend}. Please enter the 6-digit code that ${displayGuardian} provided you to verify your identity.`;
|
|
1155
|
+
}
|
|
1156
|
+
this.sendTextToken(promptText, true);
|
|
1123
1157
|
|
|
1124
1158
|
log.info(
|
|
1125
1159
|
{ callSessionId: this.callSessionId, assistantId },
|
|
1126
|
-
"Inbound voice invite redemption started
|
|
1160
|
+
`${isOutbound ? "Outbound" : "Inbound"} voice invite redemption started`,
|
|
1127
1161
|
);
|
|
1128
1162
|
}
|
|
1129
1163
|
|
|
@@ -1358,6 +1392,7 @@ export class RelayConnection {
|
|
|
1358
1392
|
assistantId,
|
|
1359
1393
|
fromNumber,
|
|
1360
1394
|
callerName: callerName ?? undefined,
|
|
1395
|
+
activationReason: "access_approved",
|
|
1361
1396
|
});
|
|
1362
1397
|
|
|
1363
1398
|
recordCallEvent(
|
|
@@ -1541,6 +1576,9 @@ export class RelayConnection {
|
|
|
1541
1576
|
fromNumber: this.inviteRedemptionFromNumber,
|
|
1542
1577
|
callerName: this.inviteRedemptionFriendName ?? undefined,
|
|
1543
1578
|
skipMemberActivation: true,
|
|
1579
|
+
activationReason: "invite_redeemed",
|
|
1580
|
+
friendName: this.inviteRedemptionFriendName ?? undefined,
|
|
1581
|
+
guardianName: this.inviteRedemptionGuardianName ?? undefined,
|
|
1544
1582
|
});
|
|
1545
1583
|
} else {
|
|
1546
1584
|
this.inviteRedemptionActive = false;
|
|
@@ -99,8 +99,24 @@ export function routeSetup(ctx: SetupContext): {
|
|
|
99
99
|
actorTrust,
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
-
// ── Outbound
|
|
102
|
+
// ── Outbound flow selection based on persisted call mode ──────────
|
|
103
103
|
const persistedMode = ctx.session?.callMode;
|
|
104
|
+
|
|
105
|
+
// ── Outbound invite redemption (persisted mode) ─────────────────
|
|
106
|
+
if (persistedMode === "invite") {
|
|
107
|
+
return {
|
|
108
|
+
outcome: {
|
|
109
|
+
action: "invite_redemption" as const,
|
|
110
|
+
assistantId,
|
|
111
|
+
fromNumber: ctx.to,
|
|
112
|
+
friendName: ctx.session?.inviteFriendName ?? null,
|
|
113
|
+
guardianName: ctx.session?.inviteGuardianName ?? null,
|
|
114
|
+
},
|
|
115
|
+
resolved,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Outbound guardian verification (persisted mode) ──────────────
|
|
104
120
|
const persistedVsId = ctx.session?.verificationSessionId;
|
|
105
121
|
const customParamVsId = ctx.customParameters?.verificationSessionId;
|
|
106
122
|
const verificationSessionId = persistedVsId ?? customParamVsId;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
getTwilioRelayUrl,
|
|
5
5
|
} from "../inbound/public-ingress-urls.js";
|
|
6
6
|
import { credentialKey } from "../security/credential-key.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
8
8
|
import { ConfigError } from "../util/errors.js";
|
|
9
9
|
import { getLogger } from "../util/logger.js";
|
|
10
10
|
|
|
@@ -20,7 +20,7 @@ export interface TwilioConfig {
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Resolve the Twilio phone number using a unified fallback chain so that
|
|
23
|
-
* all callers (calls,
|
|
23
|
+
* all callers (calls, readiness checks, invite transports)
|
|
24
24
|
* agree on the same number.
|
|
25
25
|
*
|
|
26
26
|
* Resolution order:
|
|
@@ -38,10 +38,11 @@ export function resolveTwilioPhoneNumber(): string {
|
|
|
38
38
|
return "";
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export function getTwilioConfig(): TwilioConfig {
|
|
41
|
+
export async function getTwilioConfig(): Promise<TwilioConfig> {
|
|
42
42
|
const config = loadConfig();
|
|
43
43
|
const accountSid = config.twilio?.accountSid || "";
|
|
44
|
-
const authToken =
|
|
44
|
+
const authToken =
|
|
45
|
+
(await getSecureKeyAsync(credentialKey("twilio", "auth_token"))) || "";
|
|
45
46
|
const phoneNumber = resolveTwilioPhoneNumber();
|
|
46
47
|
const webhookBaseUrl = getPublicBaseUrl(config);
|
|
47
48
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
2
|
|
|
3
3
|
import { credentialKey } from "../security/credential-key.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
5
5
|
import { ProviderError } from "../util/errors.js";
|
|
6
6
|
import { getLogger } from "../util/logger.js";
|
|
7
7
|
import {
|
|
@@ -24,8 +24,11 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
24
24
|
|
|
25
25
|
// ── Credential helpers ──────────────────────────────────────────────
|
|
26
26
|
|
|
27
|
-
private getCredentials(): {
|
|
28
|
-
|
|
27
|
+
private async getCredentials(): Promise<{
|
|
28
|
+
accountSid: string;
|
|
29
|
+
authToken: string;
|
|
30
|
+
}> {
|
|
31
|
+
return await getTwilioCredentials();
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
private authHeader(accountSid: string, authToken: string): string {
|
|
@@ -39,7 +42,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
39
42
|
// ── VoiceProvider interface ─────────────────────────────────────────
|
|
40
43
|
|
|
41
44
|
async initiateCall(opts: InitiateCallOptions): Promise<{ callSid: string }> {
|
|
42
|
-
const { accountSid, authToken } = this.getCredentials();
|
|
45
|
+
const { accountSid, authToken } = await this.getCredentials();
|
|
43
46
|
|
|
44
47
|
const body = new URLSearchParams({
|
|
45
48
|
From: opts.from,
|
|
@@ -104,7 +107,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
104
107
|
}
|
|
105
108
|
|
|
106
109
|
async endCall(callSid: string): Promise<void> {
|
|
107
|
-
const { accountSid, authToken } = this.getCredentials();
|
|
110
|
+
const { accountSid, authToken } = await this.getCredentials();
|
|
108
111
|
|
|
109
112
|
log.info({ callSid }, "Ending Twilio call");
|
|
110
113
|
|
|
@@ -139,7 +142,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
async getCallStatus(callSid: string): Promise<string> {
|
|
142
|
-
const { accountSid, authToken } = this.getCredentials();
|
|
145
|
+
const { accountSid, authToken } = await this.getCredentials();
|
|
143
146
|
|
|
144
147
|
const res = await fetch(
|
|
145
148
|
`${this.baseUrl(accountSid)}/Calls/${callSid}.json`,
|
|
@@ -179,7 +182,7 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
179
182
|
async checkCallerIdEligibility(
|
|
180
183
|
phoneNumber: string,
|
|
181
184
|
): Promise<{ eligible: boolean; reason?: string }> {
|
|
182
|
-
const { accountSid, authToken } = this.getCredentials();
|
|
185
|
+
const { accountSid, authToken } = await this.getCredentials();
|
|
183
186
|
const encodedNumber = encodeURIComponent(phoneNumber);
|
|
184
187
|
|
|
185
188
|
let incomingOk = false;
|
|
@@ -280,8 +283,10 @@ export class TwilioConversationRelayProvider implements VoiceProvider {
|
|
|
280
283
|
* Exposed as a static method so callers (e.g. the HTTP server webhook
|
|
281
284
|
* middleware) can check availability independently.
|
|
282
285
|
*/
|
|
283
|
-
static getAuthToken(): string | null {
|
|
284
|
-
return
|
|
286
|
+
static async getAuthToken(): Promise<string | null> {
|
|
287
|
+
return (
|
|
288
|
+
(await getSecureKeyAsync(credentialKey("twilio", "auth_token"))) || null
|
|
289
|
+
);
|
|
285
290
|
}
|
|
286
291
|
|
|
287
292
|
/**
|
package/src/calls/twilio-rest.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { loadConfig } from "../config/loader.js";
|
|
10
10
|
import { credentialKey } from "../security/credential-key.js";
|
|
11
|
-
import {
|
|
11
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
12
12
|
import { ConfigError, ProviderError } from "../util/errors.js";
|
|
13
13
|
|
|
14
14
|
export interface TwilioCredentials {
|
|
@@ -28,14 +28,17 @@ function resolveAccountSid(): string | undefined {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/** Resolve the Twilio Auth Token from the credential store. */
|
|
31
|
-
function resolveAuthToken(): string | undefined {
|
|
32
|
-
return
|
|
31
|
+
async function resolveAuthToken(): Promise<string | undefined> {
|
|
32
|
+
return (
|
|
33
|
+
(await getSecureKeyAsync(credentialKey("twilio", "auth_token"))) ||
|
|
34
|
+
undefined
|
|
35
|
+
);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
/** Resolve Twilio credentials from config (SID) and credential store (token). Throws if not configured. */
|
|
36
|
-
export function getTwilioCredentials(): TwilioCredentials {
|
|
39
|
+
export async function getTwilioCredentials(): Promise<TwilioCredentials> {
|
|
37
40
|
const accountSid = resolveAccountSid();
|
|
38
|
-
const authToken = resolveAuthToken();
|
|
41
|
+
const authToken = await resolveAuthToken();
|
|
39
42
|
if (!accountSid || !authToken) {
|
|
40
43
|
throw new ConfigError(
|
|
41
44
|
"Twilio credentials not configured. Set twilio.accountSid via config and store auth token via credential store.",
|
|
@@ -45,9 +48,9 @@ export function getTwilioCredentials(): TwilioCredentials {
|
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
/** Check whether Twilio credentials are present (non-throwing). */
|
|
48
|
-
export function hasTwilioCredentials(): boolean {
|
|
51
|
+
export async function hasTwilioCredentials(): Promise<boolean> {
|
|
49
52
|
try {
|
|
50
|
-
return !!resolveAccountSid() && !!resolveAuthToken();
|
|
53
|
+
return !!resolveAccountSid() && !!(await resolveAuthToken());
|
|
51
54
|
} catch {
|
|
52
55
|
return false;
|
|
53
56
|
}
|
package/src/calls/types.ts
CHANGED
|
@@ -58,7 +58,7 @@ export type PendingQuestionStatus =
|
|
|
58
58
|
* uses this as the primary signal for deterministic flow selection,
|
|
59
59
|
* with Twilio setup custom parameters as a secondary/observability signal.
|
|
60
60
|
*/
|
|
61
|
-
export type CallMode = "normal" | "verification";
|
|
61
|
+
export type CallMode = "normal" | "verification" | "invite";
|
|
62
62
|
|
|
63
63
|
export interface CallSession {
|
|
64
64
|
id: string;
|
|
@@ -71,6 +71,8 @@ export interface CallSession {
|
|
|
71
71
|
status: CallStatus;
|
|
72
72
|
callMode: CallMode | null;
|
|
73
73
|
verificationSessionId: string | null;
|
|
74
|
+
inviteFriendName: string | null;
|
|
75
|
+
inviteGuardianName: string | null;
|
|
74
76
|
callerIdentityMode: string | null;
|
|
75
77
|
callerIdentitySource: string | null;
|
|
76
78
|
initiatedFromConversationId?: string | null;
|
|
@@ -39,10 +39,13 @@ export function registerConfigCommand(program: Command): void {
|
|
|
39
39
|
"after",
|
|
40
40
|
`
|
|
41
41
|
Configuration is stored in config.json in the assistant workspace directory.
|
|
42
|
-
Keys support dotted paths for nested values (e.g. calls.enabled,
|
|
42
|
+
Keys support dotted paths for nested values (e.g. calls.enabled, twilio.accountSid).
|
|
43
43
|
Values are auto-parsed as JSON (booleans, numbers, objects) with fallback to
|
|
44
44
|
plain string if parsing fails.
|
|
45
45
|
|
|
46
|
+
API keys are managed separately via secure storage. Use "assistant keys list"
|
|
47
|
+
and "assistant keys set <provider> <key>" to view and manage API keys.
|
|
48
|
+
|
|
46
49
|
Examples:
|
|
47
50
|
$ assistant config list
|
|
48
51
|
$ assistant config get provider
|
|
@@ -53,14 +56,14 @@ Examples:
|
|
|
53
56
|
config
|
|
54
57
|
.command("set <key> <value>")
|
|
55
58
|
.description(
|
|
56
|
-
"Set a config value (supports dotted paths like
|
|
59
|
+
"Set a config value (supports dotted paths like calls.enabled)",
|
|
57
60
|
)
|
|
58
61
|
.addHelpText(
|
|
59
62
|
"after",
|
|
60
63
|
`
|
|
61
64
|
Arguments:
|
|
62
65
|
key Dotted path to the config key (e.g. provider, calls.enabled,
|
|
63
|
-
|
|
66
|
+
twilio.accountSid). Intermediate objects are created automatically.
|
|
64
67
|
value The value to store. Parsed as JSON first (so "true" becomes boolean
|
|
65
68
|
true, "42" becomes number 42). Falls back to plain string if JSON
|
|
66
69
|
parsing fails.
|
|
@@ -68,10 +71,11 @@ Arguments:
|
|
|
68
71
|
After writing the value to config.json, the lockfile is automatically synced
|
|
69
72
|
to reflect the updated configuration.
|
|
70
73
|
|
|
74
|
+
To manage API keys, use "assistant keys set <provider> <key>" instead.
|
|
75
|
+
|
|
71
76
|
Examples:
|
|
72
77
|
$ assistant config set provider anthropic
|
|
73
|
-
$ assistant config set calls.enabled true
|
|
74
|
-
$ assistant config set apiKeys.anthropic sk-ant-abc123`,
|
|
78
|
+
$ assistant config set calls.enabled true`,
|
|
75
79
|
)
|
|
76
80
|
.action((key: string, value: string) => {
|
|
77
81
|
const raw = loadRawConfig();
|
|
@@ -100,10 +104,11 @@ Arguments:
|
|
|
100
104
|
Prints the value at the given key path. If the key is not set, prints
|
|
101
105
|
"(not set)". Object values are pretty-printed as indented JSON.
|
|
102
106
|
|
|
107
|
+
To view API keys, use "assistant keys list" instead.
|
|
108
|
+
|
|
103
109
|
Examples:
|
|
104
110
|
$ assistant config get provider
|
|
105
|
-
$ assistant config get calls.enabled
|
|
106
|
-
$ assistant config get apiKeys`,
|
|
111
|
+
$ assistant config get calls.enabled`,
|
|
107
112
|
)
|
|
108
113
|
.action((key: string) => {
|
|
109
114
|
const raw = loadRawConfig();
|
|
@@ -133,8 +138,8 @@ Dumps the full raw configuration from config.json as pretty-printed JSON.
|
|
|
133
138
|
If no configuration has been set, prints "No configuration set".
|
|
134
139
|
|
|
135
140
|
The --search flag filters results by case-insensitive substring match against
|
|
136
|
-
flattened dotted key paths. For example, --search
|
|
137
|
-
|
|
141
|
+
flattened dotted key paths. For example, --search calls matches calls.enabled,
|
|
142
|
+
calls.recordingEnabled, and any other key containing "calls".
|
|
138
143
|
|
|
139
144
|
Examples:
|
|
140
145
|
$ assistant config list
|
|
@@ -497,6 +497,7 @@ Examples:
|
|
|
497
497
|
"--guardian-name <name>",
|
|
498
498
|
"Guardian name (required for voice invites)",
|
|
499
499
|
)
|
|
500
|
+
.requiredOption("--contact-id <id>", "Contact ID to bind the invite to")
|
|
500
501
|
.addHelpText(
|
|
501
502
|
"after",
|
|
502
503
|
`
|
|
@@ -529,6 +530,7 @@ Examples:
|
|
|
529
530
|
expectedExternalUserId?: string;
|
|
530
531
|
friendName?: string;
|
|
531
532
|
guardianName?: string;
|
|
533
|
+
contactId: string;
|
|
532
534
|
},
|
|
533
535
|
cmd: Command,
|
|
534
536
|
) => {
|
|
@@ -563,6 +565,7 @@ Examples:
|
|
|
563
565
|
expectedExternalUserId: opts.expectedExternalUserId,
|
|
564
566
|
friendName: opts.friendName,
|
|
565
567
|
guardianName: opts.guardianName,
|
|
568
|
+
contactId: opts.contactId,
|
|
566
569
|
});
|
|
567
570
|
if (result.ok) {
|
|
568
571
|
writeOutput(cmd, { ok: true, invite: result.data });
|