@vellumai/assistant 0.4.53 → 0.4.54
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/bun.lock +62 -349
- package/docs/architecture/integrations.md +1 -1
- package/docs/architecture/keychain-broker.md +94 -29
- package/docs/architecture/security.md +2 -2
- package/knip.json +7 -29
- package/package.json +2 -9
- package/src/__tests__/agent-loop.test.ts +1 -1
- package/src/__tests__/app-git-history.test.ts +0 -2
- package/src/__tests__/app-git-service.test.ts +1 -6
- package/src/__tests__/approval-cascade.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +0 -1
- package/src/__tests__/browser-fill-credential.test.ts +1 -6
- package/src/__tests__/call-domain.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +4 -4
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +13 -11
- package/src/__tests__/claude-code-skill-regression.test.ts +0 -1
- package/src/__tests__/claude-code-tool-profiles.test.ts +1 -2
- package/src/__tests__/config-loader-backfill.test.ts +0 -3
- package/src/__tests__/config-schema.test.ts +3 -9
- package/src/__tests__/config-watcher.test.ts +11 -3
- package/src/__tests__/credential-broker-browser-fill.test.ts +27 -24
- package/src/__tests__/credential-broker-server-use.test.ts +60 -24
- package/src/__tests__/credential-security-e2e.test.ts +1 -6
- package/src/__tests__/credential-security-invariants.test.ts +13 -8
- package/src/__tests__/credential-vault-unit.test.ts +28 -12
- package/src/__tests__/credential-vault.test.ts +40 -28
- package/src/__tests__/credentials-cli.test.ts +1 -21
- package/src/__tests__/email-invite-adapter.test.ts +0 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +3 -3
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -79
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -21
- package/src/__tests__/guardian-action-conversation-turn.test.ts +8 -8
- package/src/__tests__/guardian-action-late-reply.test.ts +13 -14
- package/src/__tests__/guardian-action-store.test.ts +0 -57
- package/src/__tests__/guardian-outbound-http.test.ts +1 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +1 -3
- package/src/__tests__/hooks-blocking.test.ts +1 -1
- package/src/__tests__/hooks-config.test.ts +5 -29
- package/src/__tests__/hooks-discovery.test.ts +1 -1
- package/src/__tests__/hooks-integration.test.ts +1 -1
- package/src/__tests__/hooks-manager.test.ts +1 -1
- package/src/__tests__/hooks-runner.test.ts +1 -23
- package/src/__tests__/hooks-settings.test.ts +1 -1
- package/src/__tests__/hooks-templates.test.ts +1 -1
- package/src/__tests__/integration-status.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -3
- package/src/__tests__/llm-usage-store.test.ts +50 -0
- package/src/__tests__/managed-proxy-context.test.ts +41 -41
- package/src/__tests__/media-generate-image.test.ts +2 -2
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -6
- package/src/__tests__/memory-regressions.experimental.test.ts +4 -4
- package/src/__tests__/memory-regressions.test.ts +27 -27
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -4
- package/src/__tests__/notification-decision-fallback.test.ts +1 -1
- package/src/__tests__/oauth-cli.test.ts +1 -4
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/openai-provider.test.ts +7 -7
- package/src/__tests__/platform.test.ts +14 -4
- package/src/__tests__/pricing.test.ts +0 -223
- package/src/__tests__/provider-commit-message-generator.test.ts +1 -4
- package/src/__tests__/provider-fail-open-selection.test.ts +58 -54
- package/src/__tests__/provider-managed-proxy-integration.test.ts +63 -63
- package/src/__tests__/provider-registry-ollama.test.ts +3 -3
- package/src/__tests__/public-ingress-urls.test.ts +1 -1
- package/src/__tests__/registry.test.ts +3 -103
- package/src/__tests__/script-proxy-injection-runtime.test.ts +2 -7
- package/src/__tests__/secret-onetime-send.test.ts +1 -6
- package/src/__tests__/secret-routes-managed-proxy.test.ts +6 -13
- package/src/__tests__/secure-keys.test.ts +241 -229
- package/src/__tests__/session-abort-tool-results.test.ts +0 -1
- package/src/__tests__/session-confirmation-signals.test.ts +0 -1
- package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -7
- package/src/__tests__/session-pre-run-repair.test.ts +0 -1
- package/src/__tests__/session-provider-retry-repair.test.ts +0 -1
- package/src/__tests__/session-queue.test.ts +2 -4
- package/src/__tests__/session-slash-known.test.ts +0 -1
- package/src/__tests__/session-slash-queue.test.ts +0 -1
- package/src/__tests__/session-slash-unknown.test.ts +0 -1
- package/src/__tests__/session-workspace-injection.test.ts +0 -1
- package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/skill-projection-feature-flag.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -7
- package/src/__tests__/swarm-recursion.test.ts +0 -1
- package/src/__tests__/swarm-session-integration.test.ts +0 -1
- package/src/__tests__/swarm-tool.test.ts +0 -1
- package/src/__tests__/task-compiler.test.ts +1 -1
- package/src/__tests__/test-support/browser-skill-harness.ts +0 -18
- package/src/__tests__/test-support/computer-use-skill-harness.ts +0 -23
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trust-store.test.ts +3 -82
- package/src/__tests__/twilio-config.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -5
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/usage-cache-backfill-migration.test.ts +10 -10
- package/src/calls/guardian-question-copy.ts +1 -1
- package/src/cli/commands/doctor.ts +10 -34
- package/src/cli/commands/memory.ts +3 -5
- package/src/cli/commands/sessions.ts +1 -1
- package/src/cli/commands/usage.ts +359 -0
- package/src/cli/http-client.ts +22 -12
- package/src/cli/program.ts +2 -0
- package/src/cli/reference.ts +1 -0
- package/src/cli.ts +251 -181
- package/src/config/assistant-feature-flags.ts +0 -7
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +1 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +0 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -1
- package/src/config/bundled-skills/sequences/SKILL.md +0 -1
- package/src/config/env.ts +13 -0
- package/src/config/feature-flag-registry.json +9 -41
- package/src/config/schemas/security.ts +1 -2
- package/src/config/skills.ts +1 -1
- package/src/contacts/contact-store.ts +0 -50
- package/src/daemon/approved-devices-store.ts +0 -44
- package/src/daemon/classifier.ts +1 -1
- package/src/daemon/config-watcher.ts +12 -6
- package/src/daemon/handlers/config-model.ts +1 -1
- package/src/daemon/handlers/sessions.ts +4 -116
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/lifecycle.ts +13 -15
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +19 -3
- package/src/daemon/session-slash.ts +2 -2
- package/src/daemon/shutdown-handlers.ts +15 -0
- package/src/daemon/watch-handler.ts +2 -2
- package/src/email/guardrails.ts +1 -1
- package/src/email/service.ts +0 -5
- package/src/hooks/templates.ts +1 -1
- package/src/media/app-icon-generator.ts +2 -2
- package/src/media/avatar-router.ts +2 -2
- package/src/media/gemini-image-service.ts +5 -5
- package/src/memory/admin.ts +2 -2
- package/src/memory/app-git-service.ts +0 -7
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-title-service.ts +2 -2
- package/src/memory/embedding-backend.ts +30 -26
- package/src/memory/external-conversation-store.ts +0 -30
- package/src/memory/guardian-action-store.ts +0 -31
- package/src/memory/guardian-approvals.ts +1 -56
- package/src/memory/indexer.ts +4 -3
- package/src/memory/items-extractor.ts +1 -1
- package/src/memory/job-handlers/backfill.ts +5 -2
- package/src/memory/job-handlers/index-maintenance.ts +2 -2
- package/src/memory/job-handlers/media-processing.ts +2 -2
- package/src/memory/job-handlers/summarization.ts +1 -1
- package/src/memory/job-utils.ts +1 -2
- package/src/memory/jobs-worker.ts +2 -2
- package/src/memory/llm-usage-store.ts +57 -11
- package/src/memory/media-store.ts +4 -535
- package/src/memory/migrations/032-guardian-delivery-conversation-index.ts +2 -2
- package/src/memory/migrations/110-channel-guardian.ts +0 -1
- package/src/memory/published-pages-store.ts +0 -83
- package/src/memory/qdrant-circuit-breaker.ts +0 -8
- package/src/memory/retriever.ts +1 -1
- package/src/memory/search/semantic.ts +1 -8
- package/src/memory/shared-app-links-store.ts +0 -15
- package/src/messaging/registry.ts +0 -5
- package/src/messaging/style-analyzer.ts +1 -1
- package/src/notifications/copy-composer.ts +5 -13
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/deliveries-store.ts +0 -39
- package/src/notifications/guardian-question-mode.ts +6 -10
- package/src/notifications/preference-extractor.ts +1 -1
- package/src/oauth/byo-connection.test.ts +29 -20
- package/src/oauth/provider-behaviors.ts +1 -1
- package/src/permissions/checker.ts +1 -1
- package/src/permissions/shell-identity.ts +0 -5
- package/src/permissions/trust-store.ts +0 -37
- package/src/prompts/system-prompt.ts +3 -3
- package/src/providers/managed-proxy/constants.ts +8 -10
- package/src/providers/managed-proxy/context.ts +14 -9
- package/src/providers/provider-send-message.ts +4 -52
- package/src/providers/registry.ts +16 -50
- package/src/runtime/actor-token-store.ts +0 -23
- package/src/runtime/http-router.ts +5 -1
- package/src/runtime/http-server.ts +101 -4
- package/src/runtime/invite-instruction-generator.ts +25 -51
- package/src/runtime/invite-service.ts +0 -20
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/brain-graph-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +32 -11
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +2 -2
- package/src/runtime/routes/documents-routes.ts +3 -3
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +0 -20
- package/src/runtime/routes/guardian-refresh-routes.ts +0 -20
- package/src/runtime/routes/secret-routes.ts +4 -4
- package/src/runtime/routes/trust-rules-routes.ts +1 -1
- package/src/security/credential-backend.ts +148 -0
- package/src/security/oauth2.ts +1 -1
- package/src/security/secret-allowlist.ts +1 -1
- package/src/security/secure-keys.ts +98 -160
- package/src/security/token-manager.ts +0 -7
- package/src/sequence/guardrails.ts +0 -4
- package/src/sequence/store.ts +1 -20
- package/src/sequence/types.ts +1 -36
- package/src/signals/cancel.ts +69 -0
- package/src/signals/conversation-undo.ts +127 -0
- package/src/signals/trust-rule.ts +174 -0
- package/src/skills/clawhub.ts +5 -5
- package/src/skills/managed-store.ts +4 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +366 -0
- package/src/telemetry/usage-telemetry-reporter.ts +181 -0
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/credentials/vault.ts +8 -4
- package/src/tools/memory/handlers.test.ts +24 -26
- package/src/tools/memory/handlers.ts +1 -13
- package/src/tools/registry.ts +5 -100
- package/src/tools/terminal/parser.ts +34 -4
- package/src/tools/tool-manifest.ts +0 -10
- package/src/usage/actors.ts +0 -12
- package/src/util/canonicalize-identity.ts +0 -9
- package/src/util/errors.ts +0 -3
- package/src/util/platform.ts +24 -7
- package/src/util/pricing.ts +0 -38
- package/src/watcher/constants.ts +0 -7
- package/src/watcher/providers/linear.ts +1 -1
- package/src/work-items/work-item-store.ts +4 -4
- package/src/workspace/commit-message-provider.ts +1 -1
- package/src/workspace/git-service.ts +44 -1
- package/src/workspace/provider-commit-message-generator.ts +1 -1
- package/src/__tests__/fixtures/proxy-fixtures.ts +0 -147
- package/src/browser-extension-relay/client.ts +0 -155
- package/src/contacts/index.ts +0 -18
- package/src/daemon/tls-certs.ts +0 -270
- package/src/errors.ts +0 -41
- package/src/events/index.ts +0 -18
- package/src/followups/index.ts +0 -10
- package/src/playbooks/index.ts +0 -10
- package/src/runtime/auth/index.ts +0 -44
- package/src/tasks/candidate-store.ts +0 -95
- package/src/tools/browser/api-map.ts +0 -313
- package/src/tools/browser/auto-navigate.ts +0 -469
- package/src/tools/browser/headless-browser.ts +0 -590
- package/src/tools/browser/recording-store.ts +0 -75
- package/src/tools/computer-use/registry.ts +0 -21
- package/src/tools/tasks/index.ts +0 -27
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { getPlatformBaseUrl } from "../../config/env.js";
|
|
13
13
|
import { credentialKey } from "../../security/credential-key.js";
|
|
14
|
-
import {
|
|
14
|
+
import { getSecureKeyAsync } from "../../security/secure-keys.js";
|
|
15
15
|
import { MANAGED_PROVIDER_META } from "./constants.js";
|
|
16
16
|
|
|
17
17
|
/** Storage key for the assistant API key credential. */
|
|
@@ -35,9 +35,10 @@ export interface ManagedProxyContext {
|
|
|
35
35
|
* Returns an enabled context only when both the platform base URL and
|
|
36
36
|
* the assistant API key are present. Otherwise returns a disabled context.
|
|
37
37
|
*/
|
|
38
|
-
export function resolveManagedProxyContext(): ManagedProxyContext {
|
|
38
|
+
export async function resolveManagedProxyContext(): Promise<ManagedProxyContext> {
|
|
39
39
|
const platformBaseUrl = getPlatformBaseUrl().replace(/\/+$/, "");
|
|
40
|
-
const assistantApiKey =
|
|
40
|
+
const assistantApiKey =
|
|
41
|
+
(await getSecureKeyAsync(ASSISTANT_API_KEY_STORAGE_KEY)) ?? "";
|
|
41
42
|
|
|
42
43
|
const enabled = !!platformBaseUrl && !!assistantApiKey;
|
|
43
44
|
|
|
@@ -48,8 +49,8 @@ export function resolveManagedProxyContext(): ManagedProxyContext {
|
|
|
48
49
|
* Check whether managed proxy prerequisites are available.
|
|
49
50
|
* Shorthand for checking that both platform URL and assistant API key exist.
|
|
50
51
|
*/
|
|
51
|
-
export function hasManagedProxyPrereqs(): boolean {
|
|
52
|
-
return resolveManagedProxyContext().enabled;
|
|
52
|
+
export async function hasManagedProxyPrereqs(): Promise<boolean> {
|
|
53
|
+
return (await resolveManagedProxyContext()).enabled;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
/**
|
|
@@ -58,11 +59,13 @@ export function hasManagedProxyPrereqs(): boolean {
|
|
|
58
59
|
* Combines the platform base URL with the provider's deterministic proxy path.
|
|
59
60
|
* Returns undefined if the provider is not managed or prerequisites are missing.
|
|
60
61
|
*/
|
|
61
|
-
export function buildManagedBaseUrl(
|
|
62
|
+
export async function buildManagedBaseUrl(
|
|
63
|
+
provider: string,
|
|
64
|
+
): Promise<string | undefined> {
|
|
62
65
|
const meta = MANAGED_PROVIDER_META[provider];
|
|
63
66
|
if (!meta?.managed || !meta.proxyPath) return undefined;
|
|
64
67
|
|
|
65
|
-
const ctx = resolveManagedProxyContext();
|
|
68
|
+
const ctx = await resolveManagedProxyContext();
|
|
66
69
|
if (!ctx.enabled) return undefined;
|
|
67
70
|
|
|
68
71
|
return `${ctx.platformBaseUrl}${meta.proxyPath}`;
|
|
@@ -74,8 +77,10 @@ export function buildManagedBaseUrl(provider: string): string | undefined {
|
|
|
74
77
|
* Returns true when the provider supports managed proxy routing and
|
|
75
78
|
* all prerequisites (platform URL + assistant API key) are satisfied.
|
|
76
79
|
*/
|
|
77
|
-
export function managedFallbackEnabledFor(
|
|
80
|
+
export async function managedFallbackEnabledFor(
|
|
81
|
+
provider: string,
|
|
82
|
+
): Promise<boolean> {
|
|
78
83
|
const meta = MANAGED_PROVIDER_META[provider];
|
|
79
84
|
if (!meta?.managed) return false;
|
|
80
|
-
return hasManagedProxyPrereqs();
|
|
85
|
+
return await hasManagedProxyPrereqs();
|
|
81
86
|
}
|
|
@@ -40,12 +40,12 @@ let fallbackWarningLogged = false;
|
|
|
40
40
|
*
|
|
41
41
|
* Returns `null` when no providers are available at all.
|
|
42
42
|
*/
|
|
43
|
-
export function resolveConfiguredProvider(): ConfiguredProviderResult | null {
|
|
43
|
+
export async function resolveConfiguredProvider(): Promise<ConfiguredProviderResult | null> {
|
|
44
44
|
const config = getConfig();
|
|
45
45
|
|
|
46
46
|
if (listProviders().length === 0) {
|
|
47
47
|
try {
|
|
48
|
-
initializeProviders(config);
|
|
48
|
+
await initializeProviders(config);
|
|
49
49
|
} catch {
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
@@ -89,8 +89,8 @@ export function resolveConfiguredProvider(): ConfiguredProviderResult | null {
|
|
|
89
89
|
*
|
|
90
90
|
* Returns `null` when no providers are available.
|
|
91
91
|
*/
|
|
92
|
-
export function getConfiguredProvider(): Provider | null {
|
|
93
|
-
const result = resolveConfiguredProvider();
|
|
92
|
+
export async function getConfiguredProvider(): Promise<Provider | null> {
|
|
93
|
+
const result = await resolveConfiguredProvider();
|
|
94
94
|
return result?.provider ?? null;
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -170,51 +170,3 @@ export function extractToolUse(
|
|
|
170
170
|
export function userMessage(text: string): Message {
|
|
171
171
|
return { role: "user", content: [{ type: "text", text }] };
|
|
172
172
|
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Build a single user message with image + text content.
|
|
176
|
-
*/
|
|
177
|
-
export function userMessageWithImage(
|
|
178
|
-
imageBase64: string,
|
|
179
|
-
mediaType: string,
|
|
180
|
-
text: string,
|
|
181
|
-
): Message {
|
|
182
|
-
return {
|
|
183
|
-
role: "user",
|
|
184
|
-
content: [
|
|
185
|
-
{
|
|
186
|
-
type: "image",
|
|
187
|
-
source: {
|
|
188
|
-
type: "base64",
|
|
189
|
-
media_type: mediaType,
|
|
190
|
-
data: imageBase64,
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
{ type: "text", text },
|
|
194
|
-
],
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Build a single user message with multiple images followed by a text block.
|
|
200
|
-
* Each image becomes its own content block; the text block comes last.
|
|
201
|
-
*/
|
|
202
|
-
export function userMessageWithImages(
|
|
203
|
-
images: Array<{ base64: string; mediaType: string }>,
|
|
204
|
-
text: string,
|
|
205
|
-
): Message {
|
|
206
|
-
return {
|
|
207
|
-
role: "user",
|
|
208
|
-
content: [
|
|
209
|
-
...images.map((img) => ({
|
|
210
|
-
type: "image" as const,
|
|
211
|
-
source: {
|
|
212
|
-
type: "base64" as const,
|
|
213
|
-
media_type: img.mediaType,
|
|
214
|
-
data: img.base64,
|
|
215
|
-
},
|
|
216
|
-
})),
|
|
217
|
-
{ type: "text" as const, text },
|
|
218
|
-
],
|
|
219
|
-
};
|
|
220
|
-
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { wrapWithLogfire } from "../logfire.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getSecureKeyAsync } from "../security/secure-keys.js";
|
|
3
3
|
import { ConfigError, ProviderNotConfiguredError } from "../util/errors.js";
|
|
4
4
|
import { AnthropicProvider } from "./anthropic/client.js";
|
|
5
5
|
import { FailoverProvider, type ProviderHealthStatus } from "./failover.js";
|
|
@@ -202,7 +202,9 @@ export function getProviderDebugStatus(
|
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
export function initializeProviders(
|
|
205
|
+
export async function initializeProviders(
|
|
206
|
+
config: ProvidersConfig,
|
|
207
|
+
): Promise<void> {
|
|
206
208
|
providers.clear();
|
|
207
209
|
routingSources.clear();
|
|
208
210
|
cachedFailoverProvider = null;
|
|
@@ -211,7 +213,7 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
211
213
|
const streamTimeoutMs =
|
|
212
214
|
(config.timeouts?.providerStreamTimeoutSec ?? 300) * 1000;
|
|
213
215
|
|
|
214
|
-
const anthropicKey =
|
|
216
|
+
const anthropicKey = await getSecureKeyAsync("anthropic");
|
|
215
217
|
if (anthropicKey) {
|
|
216
218
|
const model = resolveModel(config, "anthropic");
|
|
217
219
|
registerProvider(
|
|
@@ -228,9 +230,9 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
228
230
|
routingSources.set("anthropic", "user-key");
|
|
229
231
|
} else {
|
|
230
232
|
// No user Anthropic key — route through managed proxy
|
|
231
|
-
const managedBaseUrl = buildManagedBaseUrl("anthropic");
|
|
233
|
+
const managedBaseUrl = await buildManagedBaseUrl("anthropic");
|
|
232
234
|
if (managedBaseUrl) {
|
|
233
|
-
const ctx = resolveManagedProxyContext();
|
|
235
|
+
const ctx = await resolveManagedProxyContext();
|
|
234
236
|
const model = resolveModel(config, "anthropic");
|
|
235
237
|
registerProvider(
|
|
236
238
|
"anthropic",
|
|
@@ -248,7 +250,7 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
248
250
|
routingSources.set("anthropic", "managed-proxy");
|
|
249
251
|
}
|
|
250
252
|
}
|
|
251
|
-
const openaiKey =
|
|
253
|
+
const openaiKey = await getSecureKeyAsync("openai");
|
|
252
254
|
if (openaiKey) {
|
|
253
255
|
const model = resolveModel(config, "openai");
|
|
254
256
|
registerProvider(
|
|
@@ -261,9 +263,9 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
261
263
|
);
|
|
262
264
|
routingSources.set("openai", "user-key");
|
|
263
265
|
} else {
|
|
264
|
-
const managedBaseUrl = buildManagedBaseUrl("openai");
|
|
266
|
+
const managedBaseUrl = await buildManagedBaseUrl("openai");
|
|
265
267
|
if (managedBaseUrl) {
|
|
266
|
-
const ctx = resolveManagedProxyContext();
|
|
268
|
+
const ctx = await resolveManagedProxyContext();
|
|
267
269
|
const model = resolveModel(config, "openai");
|
|
268
270
|
registerProvider(
|
|
269
271
|
"openai",
|
|
@@ -279,7 +281,7 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
279
281
|
routingSources.set("openai", "managed-proxy");
|
|
280
282
|
}
|
|
281
283
|
}
|
|
282
|
-
const geminiKey =
|
|
284
|
+
const geminiKey = await getSecureKeyAsync("gemini");
|
|
283
285
|
if (geminiKey) {
|
|
284
286
|
const model = resolveModel(config, "gemini");
|
|
285
287
|
registerProvider(
|
|
@@ -293,9 +295,9 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
293
295
|
routingSources.set("gemini", "user-key");
|
|
294
296
|
} else {
|
|
295
297
|
// No user Gemini key — route through Vertex managed proxy
|
|
296
|
-
const managedBaseUrl = buildManagedBaseUrl("vertex");
|
|
298
|
+
const managedBaseUrl = await buildManagedBaseUrl("vertex");
|
|
297
299
|
if (managedBaseUrl) {
|
|
298
|
-
const ctx = resolveManagedProxyContext();
|
|
300
|
+
const ctx = await resolveManagedProxyContext();
|
|
299
301
|
const model = resolveModel(config, "gemini");
|
|
300
302
|
registerProvider(
|
|
301
303
|
"gemini",
|
|
@@ -311,7 +313,7 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
311
313
|
routingSources.set("gemini", "managed-proxy");
|
|
312
314
|
}
|
|
313
315
|
}
|
|
314
|
-
const ollamaKey =
|
|
316
|
+
const ollamaKey = await getSecureKeyAsync("ollama");
|
|
315
317
|
if (config.provider === "ollama" || ollamaKey) {
|
|
316
318
|
const model = resolveModel(config, "ollama");
|
|
317
319
|
registerProvider(
|
|
@@ -327,7 +329,7 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
327
329
|
);
|
|
328
330
|
routingSources.set("ollama", "user-key");
|
|
329
331
|
}
|
|
330
|
-
const fireworksKey =
|
|
332
|
+
const fireworksKey = await getSecureKeyAsync("fireworks");
|
|
331
333
|
if (fireworksKey) {
|
|
332
334
|
const model = resolveModel(config, "fireworks");
|
|
333
335
|
registerProvider(
|
|
@@ -341,26 +343,8 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
341
343
|
),
|
|
342
344
|
);
|
|
343
345
|
routingSources.set("fireworks", "user-key");
|
|
344
|
-
} else {
|
|
345
|
-
const managedBaseUrl = buildManagedBaseUrl("fireworks");
|
|
346
|
-
if (managedBaseUrl) {
|
|
347
|
-
const ctx = resolveManagedProxyContext();
|
|
348
|
-
const model = resolveModel(config, "fireworks");
|
|
349
|
-
registerProvider(
|
|
350
|
-
"fireworks",
|
|
351
|
-
new RetryProvider(
|
|
352
|
-
wrapWithLogfire(
|
|
353
|
-
new FireworksProvider(ctx.assistantApiKey, model, {
|
|
354
|
-
baseURL: managedBaseUrl,
|
|
355
|
-
streamTimeoutMs,
|
|
356
|
-
}),
|
|
357
|
-
),
|
|
358
|
-
),
|
|
359
|
-
);
|
|
360
|
-
routingSources.set("fireworks", "managed-proxy");
|
|
361
|
-
}
|
|
362
346
|
}
|
|
363
|
-
const openrouterKey =
|
|
347
|
+
const openrouterKey = await getSecureKeyAsync("openrouter");
|
|
364
348
|
if (openrouterKey) {
|
|
365
349
|
const model = resolveModel(config, "openrouter");
|
|
366
350
|
registerProvider(
|
|
@@ -374,23 +358,5 @@ export function initializeProviders(config: ProvidersConfig): void {
|
|
|
374
358
|
),
|
|
375
359
|
);
|
|
376
360
|
routingSources.set("openrouter", "user-key");
|
|
377
|
-
} else {
|
|
378
|
-
const managedBaseUrl = buildManagedBaseUrl("openrouter");
|
|
379
|
-
if (managedBaseUrl) {
|
|
380
|
-
const ctx = resolveManagedProxyContext();
|
|
381
|
-
const model = resolveModel(config, "openrouter");
|
|
382
|
-
registerProvider(
|
|
383
|
-
"openrouter",
|
|
384
|
-
new RetryProvider(
|
|
385
|
-
wrapWithLogfire(
|
|
386
|
-
new OpenRouterProvider(ctx.assistantApiKey, model, {
|
|
387
|
-
baseURL: managedBaseUrl,
|
|
388
|
-
streamTimeoutMs,
|
|
389
|
-
}),
|
|
390
|
-
),
|
|
391
|
-
),
|
|
392
|
-
);
|
|
393
|
-
routingSources.set("openrouter", "managed-proxy");
|
|
394
|
-
}
|
|
395
361
|
}
|
|
396
362
|
}
|
|
@@ -155,29 +155,6 @@ export function revokeByDeviceBinding(
|
|
|
155
155
|
return matching.length;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
/**
|
|
159
|
-
* Find all active actor token records for a given guardianPrincipalId.
|
|
160
|
-
* Used for multi-device guardian fanout — returns all bound devices (macOS, iOS, etc.)
|
|
161
|
-
* so notification targeting can reach every device for the same guardian identity.
|
|
162
|
-
*/
|
|
163
|
-
export function findActiveByGuardianPrincipalId(
|
|
164
|
-
guardianPrincipalId: string,
|
|
165
|
-
): ActorTokenRecord[] {
|
|
166
|
-
const db = getDb();
|
|
167
|
-
const rows = db
|
|
168
|
-
.select()
|
|
169
|
-
.from(actorTokenRecords)
|
|
170
|
-
.where(
|
|
171
|
-
and(
|
|
172
|
-
eq(actorTokenRecords.guardianPrincipalId, guardianPrincipalId),
|
|
173
|
-
eq(actorTokenRecords.status, "active"),
|
|
174
|
-
),
|
|
175
|
-
)
|
|
176
|
-
.all();
|
|
177
|
-
|
|
178
|
-
return rows.map(rowToRecord);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
158
|
/**
|
|
182
159
|
* Revoke a single token by its hash.
|
|
183
160
|
*/
|
|
@@ -86,10 +86,14 @@ export class HttpRouter {
|
|
|
86
86
|
server: ReturnType<typeof Bun.serve>,
|
|
87
87
|
authContext: AuthContext,
|
|
88
88
|
): Promise<Response | null> {
|
|
89
|
+
// Normalize trailing slashes so "/integrations/twilio/config/" matches
|
|
90
|
+
// a route defined as "integrations/twilio/config".
|
|
91
|
+
const normalized = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
|
|
92
|
+
|
|
89
93
|
for (const compiled of this.compiledRoutes) {
|
|
90
94
|
if (compiled.def.method !== req.method) continue;
|
|
91
95
|
|
|
92
|
-
const match =
|
|
96
|
+
const match = normalized.match(compiled.regex);
|
|
93
97
|
if (!match) continue;
|
|
94
98
|
|
|
95
99
|
// Extract named params
|
|
@@ -42,6 +42,10 @@ import {
|
|
|
42
42
|
recordConversationSeenSignal,
|
|
43
43
|
type SignalType,
|
|
44
44
|
} from "../memory/conversation-attention-store.js";
|
|
45
|
+
import {
|
|
46
|
+
getConversation,
|
|
47
|
+
getDisplayMetaForConversations,
|
|
48
|
+
} from "../memory/conversation-crud.js";
|
|
45
49
|
import {
|
|
46
50
|
countConversations,
|
|
47
51
|
listConversations,
|
|
@@ -271,16 +275,16 @@ export class RuntimeHttpServer {
|
|
|
271
275
|
}
|
|
272
276
|
|
|
273
277
|
private get pairingContext(): PairingHandlerContext {
|
|
274
|
-
const
|
|
278
|
+
const broadcast = this.pairingBroadcast;
|
|
275
279
|
return {
|
|
276
280
|
pairingStore: this.pairingStore,
|
|
277
281
|
bearerToken: this.bearerToken,
|
|
278
282
|
featureFlagToken: this.readFeatureFlagToken(),
|
|
279
|
-
pairingBroadcast:
|
|
283
|
+
pairingBroadcast: broadcast
|
|
280
284
|
? (msg) => {
|
|
281
285
|
// Broadcast to all clients via the event hub so HTTP/SSE clients
|
|
282
286
|
// (e.g. macOS app) receive pairing approval requests.
|
|
283
|
-
|
|
287
|
+
broadcast(msg);
|
|
284
288
|
void assistantEventHub.publish(
|
|
285
289
|
buildAssistantEvent(DAEMON_INTERNAL_ASSISTANT_ID, msg),
|
|
286
290
|
);
|
|
@@ -795,6 +799,7 @@ export class RuntimeHttpServer {
|
|
|
795
799
|
const conversations = listConversations(limit, false, offset);
|
|
796
800
|
const totalCount = countConversations();
|
|
797
801
|
const conversationIds = conversations.map((c) => c.id);
|
|
802
|
+
const displayMeta = getDisplayMetaForConversations(conversationIds);
|
|
798
803
|
const bindings =
|
|
799
804
|
externalConversationStore.getBindingsForConversations(
|
|
800
805
|
conversationIds,
|
|
@@ -856,13 +861,22 @@ export class RuntimeHttpServer {
|
|
|
856
861
|
? { conversationOriginChannel: originChannel }
|
|
857
862
|
: {}),
|
|
858
863
|
...(assistantAttention ? { assistantAttention } : {}),
|
|
864
|
+
...(displayMeta.get(c.id)?.isPinned
|
|
865
|
+
? {
|
|
866
|
+
isPinned: true,
|
|
867
|
+
displayOrder: displayMeta.get(c.id)!.displayOrder,
|
|
868
|
+
}
|
|
869
|
+
: displayMeta.get(c.id)?.displayOrder != null
|
|
870
|
+
? {
|
|
871
|
+
displayOrder: displayMeta.get(c.id)!.displayOrder,
|
|
872
|
+
}
|
|
873
|
+
: {}),
|
|
859
874
|
};
|
|
860
875
|
}),
|
|
861
876
|
hasMore: offset + conversations.length < totalCount,
|
|
862
877
|
});
|
|
863
878
|
},
|
|
864
879
|
},
|
|
865
|
-
|
|
866
880
|
...conversationAttentionRouteDefinitions(),
|
|
867
881
|
|
|
868
882
|
...(this.sessionManagementDeps
|
|
@@ -933,6 +947,89 @@ export class RuntimeHttpServer {
|
|
|
933
947
|
},
|
|
934
948
|
},
|
|
935
949
|
|
|
950
|
+
// conversations/:id must be registered AFTER all literal conversations/<word>
|
|
951
|
+
// routes above (attention, seen, unread) so the parameterized :id does not
|
|
952
|
+
// shadow them.
|
|
953
|
+
{
|
|
954
|
+
endpoint: "conversations/:id",
|
|
955
|
+
method: "GET",
|
|
956
|
+
handler: ({ params }) => {
|
|
957
|
+
const conversation = getConversation(params.id);
|
|
958
|
+
if (!conversation) {
|
|
959
|
+
return httpError(
|
|
960
|
+
"NOT_FOUND",
|
|
961
|
+
`Conversation ${params.id} not found`,
|
|
962
|
+
404,
|
|
963
|
+
);
|
|
964
|
+
}
|
|
965
|
+
const bindings =
|
|
966
|
+
externalConversationStore.getBindingsForConversations([
|
|
967
|
+
conversation.id,
|
|
968
|
+
]);
|
|
969
|
+
const attentionStates = getAttentionStateByConversationIds([
|
|
970
|
+
conversation.id,
|
|
971
|
+
]);
|
|
972
|
+
const binding = bindings.get(conversation.id);
|
|
973
|
+
const originChannel = parseChannelId(conversation.originChannel);
|
|
974
|
+
const attn = attentionStates.get(conversation.id);
|
|
975
|
+
const assistantAttention = attn
|
|
976
|
+
? {
|
|
977
|
+
hasUnseenLatestAssistantMessage:
|
|
978
|
+
attn.latestAssistantMessageAt != null &&
|
|
979
|
+
(attn.lastSeenAssistantMessageAt == null ||
|
|
980
|
+
attn.lastSeenAssistantMessageAt <
|
|
981
|
+
attn.latestAssistantMessageAt),
|
|
982
|
+
...(attn.latestAssistantMessageAt != null
|
|
983
|
+
? {
|
|
984
|
+
latestAssistantMessageAt: attn.latestAssistantMessageAt,
|
|
985
|
+
}
|
|
986
|
+
: {}),
|
|
987
|
+
...(attn.lastSeenAssistantMessageAt != null
|
|
988
|
+
? {
|
|
989
|
+
lastSeenAssistantMessageAt:
|
|
990
|
+
attn.lastSeenAssistantMessageAt,
|
|
991
|
+
}
|
|
992
|
+
: {}),
|
|
993
|
+
...(attn.lastSeenConfidence != null
|
|
994
|
+
? { lastSeenConfidence: attn.lastSeenConfidence }
|
|
995
|
+
: {}),
|
|
996
|
+
...(attn.lastSeenSignalType != null
|
|
997
|
+
? { lastSeenSignalType: attn.lastSeenSignalType }
|
|
998
|
+
: {}),
|
|
999
|
+
}
|
|
1000
|
+
: undefined;
|
|
1001
|
+
return Response.json({
|
|
1002
|
+
session: {
|
|
1003
|
+
id: conversation.id,
|
|
1004
|
+
title: conversation.title ?? "Untitled",
|
|
1005
|
+
createdAt: conversation.createdAt,
|
|
1006
|
+
updatedAt: conversation.updatedAt,
|
|
1007
|
+
threadType:
|
|
1008
|
+
conversation.threadType === "private" ? "private" : "standard",
|
|
1009
|
+
source: conversation.source ?? "user",
|
|
1010
|
+
...(conversation.scheduleJobId
|
|
1011
|
+
? { scheduleJobId: conversation.scheduleJobId }
|
|
1012
|
+
: {}),
|
|
1013
|
+
...(binding
|
|
1014
|
+
? {
|
|
1015
|
+
channelBinding: {
|
|
1016
|
+
sourceChannel: binding.sourceChannel,
|
|
1017
|
+
externalChatId: binding.externalChatId,
|
|
1018
|
+
externalUserId: binding.externalUserId,
|
|
1019
|
+
displayName: binding.displayName,
|
|
1020
|
+
username: binding.username,
|
|
1021
|
+
},
|
|
1022
|
+
}
|
|
1023
|
+
: {}),
|
|
1024
|
+
...(originChannel
|
|
1025
|
+
? { conversationOriginChannel: originChannel }
|
|
1026
|
+
: {}),
|
|
1027
|
+
...(assistantAttention ? { assistantAttention } : {}),
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
},
|
|
1031
|
+
},
|
|
1032
|
+
|
|
936
1033
|
...btwRouteDefinitions({
|
|
937
1034
|
sendMessageDeps: this.sendMessageDeps,
|
|
938
1035
|
}),
|
|
@@ -23,49 +23,6 @@ const GENERATION_TIMEOUT_MS = 5_000;
|
|
|
23
23
|
/** Maximum allowed length for a generated instruction. */
|
|
24
24
|
const MAX_INSTRUCTION_LENGTH = 500;
|
|
25
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 "email":
|
|
36
|
-
return "Email";
|
|
37
|
-
case "slack":
|
|
38
|
-
return "Slack";
|
|
39
|
-
case "phone":
|
|
40
|
-
return "Voice";
|
|
41
|
-
default:
|
|
42
|
-
return type.charAt(0).toUpperCase() + type.slice(1);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
// Deterministic fallback
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Build a deterministic fallback instruction when the LLM is unavailable.
|
|
52
|
-
*/
|
|
53
|
-
export function buildFallbackInstruction(params: {
|
|
54
|
-
contactName?: string;
|
|
55
|
-
channelLabel: string;
|
|
56
|
-
channelHandle?: string;
|
|
57
|
-
shareUrl?: string;
|
|
58
|
-
}): string {
|
|
59
|
-
const contact = params.contactName || "the contact";
|
|
60
|
-
const handle = params.channelHandle
|
|
61
|
-
? ` at ${params.channelHandle}`
|
|
62
|
-
: ` on ${params.channelLabel}`;
|
|
63
|
-
if (params.shareUrl) {
|
|
64
|
-
return `Send ${contact} this link: ${params.shareUrl} — or tell them to message me${handle} with the code below.`;
|
|
65
|
-
}
|
|
66
|
-
return `Tell ${contact} to message me${handle} with the code below.`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
26
|
// ---------------------------------------------------------------------------
|
|
70
27
|
// LLM-powered generation
|
|
71
28
|
// ---------------------------------------------------------------------------
|
|
@@ -88,15 +45,32 @@ export async function generateInviteInstruction(params: {
|
|
|
88
45
|
*/
|
|
89
46
|
shareUrl?: string;
|
|
90
47
|
}): Promise<string> {
|
|
91
|
-
const channelLabel =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
48
|
+
const channelLabel = (() => {
|
|
49
|
+
switch (params.channelType) {
|
|
50
|
+
case "telegram":
|
|
51
|
+
return "Telegram";
|
|
52
|
+
case "email":
|
|
53
|
+
return "Email";
|
|
54
|
+
case "slack":
|
|
55
|
+
return "Slack";
|
|
56
|
+
case "phone":
|
|
57
|
+
return "Voice";
|
|
58
|
+
default:
|
|
59
|
+
return (
|
|
60
|
+
params.channelType.charAt(0).toUpperCase() +
|
|
61
|
+
params.channelType.slice(1)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
const contact = params.contactName || "the contact";
|
|
66
|
+
const handle = params.channelHandle
|
|
67
|
+
? ` at ${params.channelHandle}`
|
|
68
|
+
: ` on ${channelLabel}`;
|
|
69
|
+
const fallback = params.shareUrl
|
|
70
|
+
? `Send ${contact} this link: ${params.shareUrl} — or tell them to message me${handle} with the code below.`
|
|
71
|
+
: `Tell ${contact} to message me${handle} with the code below.`;
|
|
98
72
|
|
|
99
|
-
const resolved = resolveConfiguredProvider();
|
|
73
|
+
const resolved = await resolveConfiguredProvider();
|
|
100
74
|
if (!resolved) {
|
|
101
75
|
log.debug(
|
|
102
76
|
"No provider available for invite instruction generation, using fallback",
|
|
@@ -34,7 +34,6 @@ import {
|
|
|
34
34
|
} from "./channel-invite-transport.js";
|
|
35
35
|
import { generateInviteInstruction } from "./invite-instruction-generator.js";
|
|
36
36
|
import {
|
|
37
|
-
type InviteRedemptionOutcome,
|
|
38
37
|
redeemInvite as redeemInviteTyped,
|
|
39
38
|
redeemVoiceInviteCode as redeemVoiceInviteCodeTyped,
|
|
40
39
|
type VoiceRedemptionOutcome,
|
|
@@ -372,25 +371,6 @@ export function redeemIngressInvite(params: {
|
|
|
372
371
|
return { ok: true, data: inviteToResponse(inv) };
|
|
373
372
|
}
|
|
374
373
|
|
|
375
|
-
// ---------------------------------------------------------------------------
|
|
376
|
-
// Typed invite redemption — preferred entry point for new callers
|
|
377
|
-
// ---------------------------------------------------------------------------
|
|
378
|
-
|
|
379
|
-
export { type InviteRedemptionOutcome } from "./invite-redemption-service.js";
|
|
380
|
-
export { type VoiceRedemptionOutcome } from "./invite-redemption-service.js";
|
|
381
|
-
|
|
382
|
-
export function redeemIngressInviteTyped(params: {
|
|
383
|
-
rawToken: string;
|
|
384
|
-
sourceChannel: string;
|
|
385
|
-
externalUserId?: string;
|
|
386
|
-
externalChatId?: string;
|
|
387
|
-
displayName?: string;
|
|
388
|
-
username?: string;
|
|
389
|
-
assistantId?: string;
|
|
390
|
-
}): InviteRedemptionOutcome {
|
|
391
|
-
return redeemInviteTyped(params);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
374
|
export function redeemVoiceInviteCode(params: {
|
|
395
375
|
assistantId?: string;
|
|
396
376
|
callerExternalUserId: string;
|
|
@@ -101,7 +101,7 @@ export async function handleDeleteAttachment(req: Request): Promise<Response> {
|
|
|
101
101
|
return new Response(null, { status: 204 });
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
function handleGetAttachment(attachmentId: string): Response {
|
|
105
105
|
const attachment = attachmentsStore.getAttachmentById(attachmentId);
|
|
106
106
|
if (!attachment) {
|
|
107
107
|
return httpError("NOT_FOUND", "Attachment not found", 404);
|
|
@@ -148,7 +148,7 @@ export async function handleStartCall(
|
|
|
148
148
|
/**
|
|
149
149
|
* GET /v1/calls/:callSessionId
|
|
150
150
|
*/
|
|
151
|
-
|
|
151
|
+
function handleGetCallStatus(callSessionId: string): Response {
|
|
152
152
|
const result = getCallStatus(callSessionId);
|
|
153
153
|
|
|
154
154
|
if (!result.ok) {
|