@vellumai/assistant 0.12.3 → 0.12.4-dev.202609231715.8da971c
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/Dockerfile +7 -0
- package/README.md +12 -12
- package/docker/mousepad/90-vellum.gschema.override +4 -0
- package/node_modules/@vellumai/avatar-manifest/src/__tests__/notification-avatar.test.ts +48 -4
- package/node_modules/@vellumai/avatar-manifest/src/notification-avatar.ts +12 -8
- package/openapi.yaml +97 -4
- package/package.json +1 -1
- package/src/__tests__/approval-routes-http.test.ts +4 -2
- package/src/__tests__/call-routes-http.test.ts +4 -2
- package/src/__tests__/channel-readiness-discord.test.ts +8 -0
- package/src/__tests__/channel-readiness-slack-remote.test.ts +8 -0
- package/src/__tests__/config-loader-backfill.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +10 -0
- package/src/__tests__/conversation-resurface-done.test.ts +289 -0
- package/src/__tests__/default-profile-catalog-fallback.test.ts +1 -0
- package/src/__tests__/gemini-provider.test.ts +31 -0
- package/src/__tests__/inference-profile-validation-jev.test.ts +17 -0
- package/src/__tests__/job-handler-registration-guard.test.ts +1 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +43 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +3 -1
- package/src/__tests__/platform-callback-registration.test.ts +46 -1
- package/src/__tests__/plugin-api-model-profiles.test.ts +7 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
- package/src/__tests__/pricing.test.ts +33 -0
- package/src/__tests__/provider-error-scenarios.test.ts +46 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +45 -15
- package/src/__tests__/retry-fallback-escalation.test.ts +22 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +4 -2
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +116 -66
- package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -2
- package/src/__tests__/skill-load-inline-command.test.ts +35 -0
- package/src/__tests__/skill-load-tool.test.ts +42 -11
- package/src/__tests__/skill-tool-factory.test.ts +50 -2
- package/src/__tests__/steer-on-enqueue-question.test.ts +265 -34
- package/src/__tests__/validate-input.test.ts +73 -0
- package/src/__tests__/workspace-migration-159-rename-colliding-jev-profile-name.test.ts +197 -0
- package/src/api/ask-question-tool.ts +50 -0
- package/src/api/events/tool-result.ts +11 -0
- package/src/api/index.ts +7 -0
- package/src/api/requests/dictation.ts +2 -2
- package/src/api/responses/home.ts +27 -0
- package/src/avatar/notification-avatar.test.ts +42 -34
- package/src/avatar/notification-avatar.ts +1 -0
- package/src/calls/__tests__/call-controller.test.ts +18 -0
- package/src/calls/__tests__/voice-control-protocol.test.ts +6 -1
- package/src/calls/__tests__/voice-leg-coordinator.test.ts +42 -0
- package/src/calls/__tests__/voice-session-bridge.test.ts +97 -4
- package/src/calls/__tests__/voice-triage-escalate.test.ts +69 -11
- package/src/calls/call-controller.ts +3 -1
- package/src/calls/voice-control-protocol.ts +9 -4
- package/src/calls/voice-escalation-judge.ts +7 -6
- package/src/calls/voice-leg-coordinator.ts +15 -4
- package/src/calls/voice-screen-annotation.ts +44 -0
- package/src/calls/voice-session-bridge.ts +50 -20
- package/src/calls/voice-triage-escalate.ts +61 -26
- package/src/cli/commands/__tests__/plugins.test.ts +29 -1
- package/src/cli/commands/channel-verification-sessions.help.ts +12 -2
- package/src/cli/commands/oauth/index.help.ts +8 -2
- package/src/cli/commands/oauth/request.test.ts +32 -0
- package/src/cli/commands/platform/index.help.ts +2 -1
- package/src/cli/commands/platform/index.ts +8 -2
- package/src/cli/commands/plugins.ts +16 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +29 -0
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +92 -2
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +15 -0
- package/src/cli/lib/__tests__/plugin-catalog-platform.test.ts +66 -2
- package/src/cli/lib/__tests__/plugin-catalog-resolve.test.ts +21 -0
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +65 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +43 -0
- package/src/cli/lib/__tests__/plugins-install-offline.test.ts +26 -0
- package/src/cli/lib/bundled-marketplace.json +4 -4
- package/src/cli/lib/inspect-plugin.ts +5 -0
- package/src/cli/lib/plugin-catalog-cache.ts +41 -4
- package/src/cli/lib/plugin-catalog-local.ts +9 -0
- package/src/cli/lib/plugin-catalog-platform.ts +48 -9
- package/src/cli/lib/plugin-catalog-visibility.ts +41 -0
- package/src/cli/lib/plugin-details.ts +10 -7
- package/src/cli/lib/search-plugins.ts +2 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +61 -1
- package/src/config/__tests__/jev-managed-profile.test.ts +82 -0
- package/src/config/__tests__/platform-identity.test.ts +59 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +71 -0
- package/src/config/bundled-skills/screen-annotation/SKILL.md +6 -3
- package/src/config/call-site-defaults.ts +17 -11
- package/src/config/default-profile-catalog.ts +118 -19
- package/src/config/default-profile-names.ts +69 -0
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/inference-profile-validation.ts +7 -6
- package/src/config/llm-resolver.ts +16 -0
- package/src/config/platform-identity.ts +22 -5
- package/src/config/profile-text-generation.test.ts +9 -0
- package/src/config/schemas/call-site-catalog.ts +8 -1
- package/src/config/schemas/llm.ts +70 -9
- package/src/config/sidebar-done-gate.ts +27 -0
- package/src/config/sync-gated-profiles.ts +96 -44
- package/src/daemon/__tests__/auto-profile-router.test.ts +228 -0
- package/src/daemon/auto-profile-router.ts +220 -0
- package/src/daemon/conversation-agent-loop.ts +77 -4
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-turn-finalize.ts +12 -0
- package/src/daemon/dictation-text-processing.ts +4 -4
- package/src/daemon/handlers/__tests__/conversation-history-search.test.ts +61 -0
- package/src/daemon/handlers/config-channels.ts +3 -1
- package/src/daemon/handlers/conversation-history.ts +11 -1
- package/src/daemon/handlers/conversations.ts +118 -10
- package/src/daemon/pkb-reminder-builder.test.ts +10 -3
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/desktop/desktop-browser-endpoint.ts +1 -0
- package/src/home/feed-types.ts +2 -0
- package/src/inbound/platform-callback-registration.ts +19 -6
- package/src/ipc/routes/__tests__/conversation-sync-ipc-routes.test.ts +49 -1
- package/src/ipc/routes/conversation-sync-ipc-routes.ts +38 -2
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +27 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +37 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +34 -2
- package/src/live-voice/__tests__/protocol.test.ts +22 -0
- package/src/live-voice/continuation-judge.ts +1 -1
- package/src/live-voice/live-voice-session.ts +17 -14
- package/src/live-voice/protocol.ts +16 -5
- package/src/messaging/providers/gmail/client.ts +4 -0
- package/src/notifications/README.md +11 -3
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +13 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +87 -3
- package/src/notifications/conversation-pairing.ts +23 -8
- package/src/notifications/home-feed-mirror.ts +39 -0
- package/src/notifications/home-feed-side-effect.ts +46 -19
- package/src/oauth/AGENTS.md +9 -1
- package/src/oauth/byo-connection.test.ts +28 -0
- package/src/permissions/question-prompter.ts +14 -4
- package/src/persistence/conversation-crud.ts +116 -3
- package/src/persistence/conversation-queries.ts +48 -3
- package/src/persistence/conversation-types.ts +6 -2
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +228 -32
- package/src/persistence/embeddings/embedding-backend-shutdown.test.ts +47 -0
- package/src/persistence/embeddings/embedding-backend.ts +23 -41
- package/src/persistence/embeddings/embedding-gemini.test.ts +17 -0
- package/src/persistence/embeddings/embedding-gemini.ts +3 -1
- package/src/persistence/embeddings/embedding-local.ts +72 -17
- package/src/persistence/embeddings/embedding-types.ts +0 -19
- package/src/persistence/jobs-store.ts +229 -0
- package/src/persistence/migrations/383-normalize-opencode-host-connections.ts +44 -0
- package/src/persistence/migrations/__tests__/383-normalize-opencode-host-connections.test.ts +102 -0
- package/src/persistence/steps.ts +12 -0
- package/src/platform/sync-avatar.test.ts +29 -0
- package/src/plugin-api/model-profiles.ts +5 -3
- package/src/plugins/defaults/memory/AGENTS.md +5 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-skill-card.test.ts +2 -0
- package/src/plugins/defaults/memory/__tests__/skill-update-receipt-boundary-guard.test.ts +45 -0
- package/src/plugins/defaults/memory/__tests__/skill-update-receipt-job.test.ts +580 -0
- package/src/plugins/defaults/memory/graph/tools.ts +9 -5
- package/src/plugins/defaults/memory/hooks/conversation-deleted.ts +4 -0
- package/src/plugins/defaults/memory/job-handlers.ts +8 -2
- package/src/plugins/defaults/memory/memory-retrospective-skill-card.ts +5 -0
- package/src/plugins/defaults/memory/skill-update-receipt-job.ts +334 -0
- package/src/plugins/defaults/memory/substrate/skill-content.ts +1 -1
- package/src/plugins/defaults/memory/tools.test.ts +17 -0
- package/src/plugins/defaults/memory/v3/__tests__/render-injection.test.ts +3 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +2 -0
- package/src/providers/__tests__/search-provider-catalog.test.ts +8 -0
- package/src/providers/anthropic/client.ts +2 -2
- package/src/providers/gemini/client.ts +17 -1
- package/src/providers/inference/auth.ts +25 -0
- package/src/providers/jev/ask.test.ts +66 -0
- package/src/{calls/typesafe-noul.ts → providers/jev/ask.ts} +75 -36
- package/src/providers/malformed-tool-call.ts +55 -0
- package/src/providers/model-catalog.ts +257 -4
- package/src/providers/openai/__tests__/chat-completions-malformed-tool-call.test.ts +105 -0
- package/src/providers/openai/chat-completions-provider.ts +30 -0
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/platform-proxy/constants.ts +5 -0
- package/src/providers/retry.ts +68 -36
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/vellum-model-routing.test.ts +1 -0
- package/src/runtime/__tests__/agent-wake-archived-gate.test.ts +90 -0
- package/src/runtime/__tests__/question-resolution.test.ts +32 -0
- package/src/runtime/agent-wake.ts +9 -2
- package/src/runtime/auth/__tests__/middleware.test.ts +138 -0
- package/src/runtime/auth/middleware.ts +15 -11
- package/src/runtime/middleware/auth.ts +7 -1
- package/src/runtime/pending-interactions.ts +8 -8
- package/src/runtime/question-resolution.ts +54 -8
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +146 -0
- package/src/runtime/routes/__tests__/dictation-processing.test.ts +174 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +74 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +60 -0
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +16 -1
- package/src/runtime/routes/__tests__/oauth-proxy-routes.test.ts +61 -3
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +8 -0
- package/src/runtime/routes/conversation-list-routes.ts +16 -14
- package/src/runtime/routes/conversation-query-routes.ts +4 -0
- package/src/runtime/routes/conversation-routes.ts +4 -2
- package/src/runtime/routes/diagnostics-routes.ts +66 -229
- package/src/runtime/routes/global-search-routes.ts +22 -7
- package/src/runtime/routes/inference-provider-connection-routes.ts +25 -8
- package/src/runtime/routes/llm-call-sites-routes.ts +19 -10
- package/src/runtime/routes/oauth-proxy-passthrough.test.ts +32 -0
- package/src/runtime/routes/platform-routes.ts +6 -1
- package/src/runtime/routes/plugins-routes.ts +5 -2
- package/src/runtime/sync/documents-sidecar-publish.test.ts +50 -4
- package/src/runtime/sync/resource-sync-events.ts +11 -0
- package/src/runtime/sync/worker-daemon-notify.test.ts +30 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -0
- package/src/runtime/verification-outbound-actions.ts +2 -2
- package/src/skills/validate-input.ts +75 -11
- package/src/telegram/__tests__/webhook-health.test.ts +8 -0
- package/src/tools/ask-question/ask-question-tool.test.ts +38 -0
- package/src/tools/ask-question/ask-question-tool.ts +12 -1
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +28 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +34 -0
- package/src/tools/network/__tests__/web-fetch-tinyfish.test.ts +35 -1
- package/src/tools/network/__tests__/web-search.test.ts +96 -0
- package/src/tools/network/web-fetch.ts +30 -9
- package/src/tools/network/web-search.ts +235 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/format-input-schema.ts +15 -0
- package/src/tools/skills/load.ts +15 -43
- package/src/tools/skills/scaffold-managed.ts +86 -71
- package/src/tools/skills/skill-tool-factory.ts +2 -1
- package/src/tools/workflows/manage-workflows.ts +4 -2
- package/src/util/oauth-request-body.test.ts +5 -1
- package/src/util/worker-pipe.test.ts +146 -0
- package/src/util/worker-pipe.ts +49 -0
- package/src/workspace/migrations/158-repoint-opencode-host-provider-fragments.ts +163 -0
- package/src/workspace/migrations/159-rename-colliding-jev-profile-name.ts +344 -0
- package/src/workspace/migrations/__tests__/158-repoint-opencode-host-provider-fragments.test.ts +188 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
|
@@ -7,15 +7,17 @@
|
|
|
7
7
|
* catalog sourced from the platform is indistinguishable from one read off
|
|
8
8
|
* GitHub.
|
|
9
9
|
*
|
|
10
|
-
* Every failure mode (non-2xx, unreachable/aborted, malformed
|
|
11
|
-
* {@link PluginCatalogUnavailableError}
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Every failure mode (non-2xx, unreachable/aborted, malformed envelope)
|
|
11
|
+
* throws {@link PluginCatalogUnavailableError}, so a caller never mistakes an
|
|
12
|
+
* outage for "no plugins". Rows are validated one at a time: a row with an
|
|
13
|
+
* invalid `integration` is kept without it, and a row that fails the base
|
|
14
|
+
* row schema is skipped, so one bad row never blanks the whole catalog.
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { z } from "zod";
|
|
17
18
|
|
|
18
19
|
import { getPlatformBaseUrl } from "../../config/env.js";
|
|
20
|
+
import { getLogger } from "../../util/logger.js";
|
|
19
21
|
import {
|
|
20
22
|
type MarketplaceEntry,
|
|
21
23
|
mcpIntegrationSchema,
|
|
@@ -27,9 +29,12 @@ import {
|
|
|
27
29
|
type SearchPluginsDeps,
|
|
28
30
|
} from "./search-plugins.js";
|
|
29
31
|
|
|
32
|
+
const log = getLogger("plugin-catalog");
|
|
33
|
+
|
|
30
34
|
/**
|
|
31
35
|
* One flattened row from the platform catalog. Unknown keys (`id`,
|
|
32
|
-
* `display_name`) are accepted and dropped
|
|
36
|
+
* `display_name`) are accepted and dropped (zod strips them by default).
|
|
37
|
+
* `integration` is validated separately by {@link integrationSchema}.
|
|
33
38
|
*/
|
|
34
39
|
const platformPluginRowSchema = z.object({
|
|
35
40
|
name: z.string(),
|
|
@@ -41,11 +46,13 @@ const platformPluginRowSchema = z.object({
|
|
|
41
46
|
category: z.string().nullable().optional(),
|
|
42
47
|
homepage: z.string().nullable().optional(),
|
|
43
48
|
license: z.string().nullable().optional(),
|
|
44
|
-
integration:
|
|
49
|
+
integration: z.unknown(),
|
|
45
50
|
});
|
|
46
51
|
|
|
52
|
+
const integrationSchema = mcpIntegrationSchema.nullable().optional();
|
|
53
|
+
|
|
47
54
|
const platformCatalogSchema = z.object({
|
|
48
|
-
plugins: z.array(
|
|
55
|
+
plugins: z.array(z.unknown()),
|
|
49
56
|
});
|
|
50
57
|
|
|
51
58
|
/**
|
|
@@ -99,10 +106,22 @@ export async function fetchPluginCatalogFromPlatform(
|
|
|
99
106
|
}
|
|
100
107
|
|
|
101
108
|
const entries: MarketplaceEntry[] = [];
|
|
102
|
-
|
|
109
|
+
const skippedRows: string[] = [];
|
|
110
|
+
const droppedIntegrations: string[] = [];
|
|
111
|
+
for (const [index, rawRow] of parsed.data.plugins.entries()) {
|
|
112
|
+
const rowResult = platformPluginRowSchema.safeParse(rawRow);
|
|
113
|
+
if (!rowResult.success) {
|
|
114
|
+
skippedRows.push(describeInvalidRow(rawRow, index));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const row = rowResult.data;
|
|
103
118
|
if (!row.repo || !row.ref) {
|
|
104
119
|
continue;
|
|
105
120
|
}
|
|
121
|
+
const integrationResult = integrationSchema.safeParse(row.integration);
|
|
122
|
+
if (!integrationResult.success) {
|
|
123
|
+
droppedIntegrations.push(row.name);
|
|
124
|
+
}
|
|
106
125
|
entries.push({
|
|
107
126
|
name: row.name,
|
|
108
127
|
source: {
|
|
@@ -116,12 +135,32 @@ export async function fetchPluginCatalogFromPlatform(
|
|
|
116
135
|
category: row.category ?? undefined,
|
|
117
136
|
homepage: row.homepage ?? undefined,
|
|
118
137
|
license: row.license ?? undefined,
|
|
119
|
-
integration:
|
|
138
|
+
integration: integrationResult.success
|
|
139
|
+
? (integrationResult.data ?? undefined)
|
|
140
|
+
: undefined,
|
|
120
141
|
});
|
|
121
142
|
}
|
|
122
143
|
|
|
144
|
+
if (skippedRows.length > 0 || droppedIntegrations.length > 0) {
|
|
145
|
+
log.warn(
|
|
146
|
+
{ skippedRows, droppedIntegrations },
|
|
147
|
+
"platform plugin catalog contained invalid rows; skipped rows and dropped integrations",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
return {
|
|
124
152
|
ref: opts?.ref ?? "platform",
|
|
125
153
|
matches: projectMarketplaceEntries(entries),
|
|
126
154
|
};
|
|
127
155
|
}
|
|
156
|
+
|
|
157
|
+
/** Name of an invalid row for logging, or its position when it has none. */
|
|
158
|
+
function describeInvalidRow(row: unknown, index: number): string {
|
|
159
|
+
if (row && typeof row === "object" && "name" in row) {
|
|
160
|
+
const name = (row as { name: unknown }).name;
|
|
161
|
+
if (typeof name === "string" && name.length > 0) {
|
|
162
|
+
return name;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return `#${index}`;
|
|
166
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
2
|
+
import type { PluginCatalog } from "./search-plugins.js";
|
|
3
|
+
|
|
4
|
+
export const MCP_CATALOG_QA_INTEGRATIONS_FLAG = "mcp-catalog-qa-integrations";
|
|
5
|
+
|
|
6
|
+
const MCP_CATALOG_QA_INTEGRATION_NAMES = new Set([
|
|
7
|
+
"gamma",
|
|
8
|
+
"guru",
|
|
9
|
+
"intercom",
|
|
10
|
+
"ramp",
|
|
11
|
+
"semrush",
|
|
12
|
+
"typeform",
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export type PluginCatalogFeatureFlagResolver = (key: string) => boolean;
|
|
16
|
+
|
|
17
|
+
export function isPluginCatalogEntryVisible(
|
|
18
|
+
name: string,
|
|
19
|
+
isFeatureFlagEnabled: PluginCatalogFeatureFlagResolver = isAssistantFeatureFlagEnabled,
|
|
20
|
+
): boolean {
|
|
21
|
+
return (
|
|
22
|
+
!MCP_CATALOG_QA_INTEGRATION_NAMES.has(name) ||
|
|
23
|
+
isFeatureFlagEnabled(MCP_CATALOG_QA_INTEGRATIONS_FLAG)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Hide catalog entries whose OAuth flows are still being validated. Installed
|
|
29
|
+
* plugins are unaffected because this filter only applies to catalog reads.
|
|
30
|
+
*/
|
|
31
|
+
export function filterPluginCatalogByFeatureFlags(
|
|
32
|
+
catalog: PluginCatalog,
|
|
33
|
+
isFeatureFlagEnabled: PluginCatalogFeatureFlagResolver = isAssistantFeatureFlagEnabled,
|
|
34
|
+
): PluginCatalog {
|
|
35
|
+
const matches = catalog.matches.filter((match) =>
|
|
36
|
+
isPluginCatalogEntryVisible(match.name, isFeatureFlagEnabled),
|
|
37
|
+
);
|
|
38
|
+
return matches.length === catalog.matches.length
|
|
39
|
+
? catalog
|
|
40
|
+
: { ...catalog, matches };
|
|
41
|
+
}
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
findCatalogEntry,
|
|
47
47
|
resolveSourceFromMatch,
|
|
48
48
|
} from "./plugin-catalog-resolve.js";
|
|
49
|
+
import { isPluginCatalogEntryVisible } from "./plugin-catalog-visibility.js";
|
|
49
50
|
import { DEFAULT_PLUGIN_REF } from "./plugin-constants.js";
|
|
50
51
|
import { readValidatedPluginIcon } from "./plugin-icon-file.js";
|
|
51
52
|
import { fetchMarketplaceEntries } from "./plugin-marketplace.js";
|
|
@@ -156,8 +157,8 @@ export class PluginDetailsNotFoundError extends Error {
|
|
|
156
157
|
* Resolve the detail view for {@link opts.name}.
|
|
157
158
|
*
|
|
158
159
|
* Throws {@link PluginDetailsNotFoundError} when the name is neither installed
|
|
159
|
-
* locally nor present in the catalog
|
|
160
|
-
*
|
|
160
|
+
* locally nor present in the visibility-gated catalog at either the default
|
|
161
|
+
* or an explicit historical ref. A gated-catalog outage
|
|
161
162
|
* ({@link PluginCatalogUnavailableError}) degrades to the on-disk fields only
|
|
162
163
|
* when a local copy exists; with nothing installed to render it propagates so
|
|
163
164
|
* the caller can map the transient failure to a retryable 503 rather than a
|
|
@@ -400,11 +401,10 @@ async function fetchRawFile(
|
|
|
400
401
|
/**
|
|
401
402
|
* Resolve the external catalog entry claiming {@link name}.
|
|
402
403
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
* history / inspect carve-out.
|
|
404
|
+
* Both default and historical-ref lookups apply catalog visibility first. The
|
|
405
|
+
* default ref then reads the gated catalog used by search and install, while an
|
|
406
|
+
* explicit historical {@link ref} reads the GitHub marketplace manifest at
|
|
407
|
+
* that revision.
|
|
408
408
|
*
|
|
409
409
|
* A gated-catalog outage (fail-hard {@link PluginCatalogUnavailableError})
|
|
410
410
|
* propagates when nothing is installed — there is nothing to render and the
|
|
@@ -420,6 +420,9 @@ async function resolveCatalogEntry(
|
|
|
420
420
|
fetchFn: FetchLike,
|
|
421
421
|
installed: boolean,
|
|
422
422
|
): Promise<PluginSearchMatch | null> {
|
|
423
|
+
if (!isPluginCatalogEntryVisible(name)) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
423
426
|
try {
|
|
424
427
|
const match =
|
|
425
428
|
ref === DEFAULT_PLUGIN_REF
|
|
@@ -19,6 +19,8 @@ import type { MarketplaceEntry } from "./plugin-marketplace.js";
|
|
|
19
19
|
export interface SearchPluginsDeps {
|
|
20
20
|
/** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
|
|
21
21
|
readonly fetch: FetchLike;
|
|
22
|
+
/** Optional feature-flag resolver for deterministic tests. */
|
|
23
|
+
readonly featureFlagEnabled?: (key: string) => boolean;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
/** Where a catalog match comes from. */
|
|
@@ -5,10 +5,12 @@ import { resolveModelIntent } from "../../providers/model-intents.js";
|
|
|
5
5
|
import { CALL_SITE_DEFAULTS } from "../call-site-defaults.js";
|
|
6
6
|
import {
|
|
7
7
|
CODE_DEFAULT_PROFILE_ENTRIES,
|
|
8
|
+
getConversationProfilesForProvider,
|
|
8
9
|
getEffectiveProfile,
|
|
9
10
|
getEffectiveProfiles,
|
|
10
11
|
getEffectiveProfilesForProvider,
|
|
11
12
|
getUserSelectableProfilesForProvider,
|
|
13
|
+
MANAGED_PROFILE_TEMPLATES,
|
|
12
14
|
PROFILE_IMPLS,
|
|
13
15
|
resolveDefaultProfileForProvider,
|
|
14
16
|
} from "../default-profile-catalog.js";
|
|
@@ -59,6 +61,64 @@ describe("getEffectiveProfiles", () => {
|
|
|
59
61
|
}
|
|
60
62
|
});
|
|
61
63
|
|
|
64
|
+
test("the managed Jev profile routes jev-latest through TypeSafe and is managed-only", () => {
|
|
65
|
+
const jev = CODE_DEFAULT_PROFILE_ENTRIES["jev-managed"];
|
|
66
|
+
expect(jev.model).toBe("jev-latest");
|
|
67
|
+
expect(jev.provider_connection).toBeUndefined();
|
|
68
|
+
expect(resolveRoutingIdentity(jev.provider, jev.model)).toEqual({
|
|
69
|
+
connectionName: "vellum",
|
|
70
|
+
expectedProvider: "typesafe",
|
|
71
|
+
});
|
|
72
|
+
expect(
|
|
73
|
+
resolveDefaultProfileForProvider(undefined, "jev-managed", null),
|
|
74
|
+
).toBeDefined();
|
|
75
|
+
expect(
|
|
76
|
+
resolveDefaultProfileForProvider(undefined, "jev-managed", {
|
|
77
|
+
provider: "anthropic",
|
|
78
|
+
connectionName: "anthropic-personal",
|
|
79
|
+
}),
|
|
80
|
+
).toBeUndefined();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("the Auto profile is the managed Balanced body relabeled, managed-only, and flag-gated", () => {
|
|
84
|
+
const auto = CODE_DEFAULT_PROFILE_ENTRIES.auto;
|
|
85
|
+
const balanced = CODE_DEFAULT_PROFILE_ENTRIES.balanced;
|
|
86
|
+
expect(auto.label).toBe("Auto");
|
|
87
|
+
expect(auto.model).toBe(balanced.model);
|
|
88
|
+
expect(auto.provider).toBe(balanced.provider);
|
|
89
|
+
expect(auto.fallbackProfile).toBeUndefined();
|
|
90
|
+
// Not seeded unconditionally: the flag reconcile writes the stub.
|
|
91
|
+
expect(Object.keys(MANAGED_PROFILE_TEMPLATES)).not.toContain("auto");
|
|
92
|
+
expect(
|
|
93
|
+
resolveDefaultProfileForProvider(undefined, "auto", null),
|
|
94
|
+
).toBeUndefined();
|
|
95
|
+
const stub: Record<string, ProfileEntry> = { auto: { source: "managed" } };
|
|
96
|
+
expect(resolveDefaultProfileForProvider(stub, "auto", null)?.model).toBe(
|
|
97
|
+
balanced.model,
|
|
98
|
+
);
|
|
99
|
+
expect(
|
|
100
|
+
resolveDefaultProfileForProvider(stub, "auto", {
|
|
101
|
+
provider: "anthropic",
|
|
102
|
+
connectionName: "anthropic-personal",
|
|
103
|
+
}),
|
|
104
|
+
).toBeUndefined();
|
|
105
|
+
expect(
|
|
106
|
+
Object.keys(getConversationProfilesForProvider(stub, null)),
|
|
107
|
+
).toContain("auto");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("the conversation view drops the managed Jev profile but keeps it selectable for call sites", () => {
|
|
111
|
+
expect(
|
|
112
|
+
Object.keys(getConversationProfilesForProvider(undefined, null)),
|
|
113
|
+
).not.toContain("jev-managed");
|
|
114
|
+
expect(
|
|
115
|
+
Object.keys(getUserSelectableProfilesForProvider(undefined, null)),
|
|
116
|
+
).toContain("jev-managed");
|
|
117
|
+
expect(
|
|
118
|
+
Object.keys(getConversationProfilesForProvider(undefined, null)),
|
|
119
|
+
).toContain("balanced");
|
|
120
|
+
});
|
|
121
|
+
|
|
62
122
|
test("the managed Balanced profile routes GLM 5.3 Flash through Fireworks", () => {
|
|
63
123
|
const balanced = CODE_DEFAULT_PROFILE_ENTRIES.balanced;
|
|
64
124
|
expect(balanced.model).toBe("accounts/fireworks/models/glm-5p3-flash");
|
|
@@ -80,7 +140,7 @@ describe("getEffectiveProfiles", () => {
|
|
|
80
140
|
test("defaults absent from the workspace resolve from the catalog; os-beta stays flag-gated", () => {
|
|
81
141
|
const effective = getEffectiveProfiles(undefined);
|
|
82
142
|
expect(Object.keys(effective).sort()).toEqual(
|
|
83
|
-
[...DEFAULT_PROFILE_KEYS, ...BACKUP_PROFILE_KEYS].sort(),
|
|
143
|
+
[...DEFAULT_PROFILE_KEYS, ...BACKUP_PROFILE_KEYS, "jev-managed"].sort(),
|
|
84
144
|
);
|
|
85
145
|
expect(getEffectiveProfile({}, "balanced")?.model).toBe(
|
|
86
146
|
CODE_DEFAULT_PROFILE_ENTRIES.balanced.model as string,
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { LLMSchema } from "../schemas/llm.js";
|
|
4
|
+
|
|
5
|
+
const PIN = { callSites: { voiceEscalationJudge: { profile: "jev-managed" } } };
|
|
6
|
+
|
|
7
|
+
describe("jev-managed as a call-site pin target", () => {
|
|
8
|
+
test("resolves under the managed default provider", () => {
|
|
9
|
+
expect(LLMSchema.safeParse(PIN).success).toBe(true);
|
|
10
|
+
expect(
|
|
11
|
+
LLMSchema.safeParse({
|
|
12
|
+
...PIN,
|
|
13
|
+
defaultProvider: { provider: "vellum" },
|
|
14
|
+
}).success,
|
|
15
|
+
).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("can be pinned only to the verdict-consuming call sites", () => {
|
|
19
|
+
for (const site of [
|
|
20
|
+
"voiceEscalationJudge",
|
|
21
|
+
"voiceContinuationJudge",
|
|
22
|
+
"memoryV3SelectL2",
|
|
23
|
+
]) {
|
|
24
|
+
expect(
|
|
25
|
+
LLMSchema.safeParse({
|
|
26
|
+
callSites: { [site]: { profile: "jev-managed" } },
|
|
27
|
+
}).success,
|
|
28
|
+
).toBe(true);
|
|
29
|
+
}
|
|
30
|
+
const result = LLMSchema.safeParse({
|
|
31
|
+
callSites: { conversationTitle: { profile: "jev-managed" } },
|
|
32
|
+
});
|
|
33
|
+
expect(result.success).toBe(false);
|
|
34
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
35
|
+
"can only be pinned to",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("cannot be a mix constituent", () => {
|
|
40
|
+
const result = LLMSchema.safeParse({
|
|
41
|
+
profiles: {
|
|
42
|
+
blend: {
|
|
43
|
+
mix: [
|
|
44
|
+
{ profile: "jev-managed", weight: 1 },
|
|
45
|
+
{ profile: "balanced", weight: 1 },
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
expect(result.success).toBe(false);
|
|
51
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
52
|
+
"cannot be a mix constituent",
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("is rejected as the active or advisor profile even on the managed column", () => {
|
|
57
|
+
for (const field of ["activeProfile", "advisorProfile"] as const) {
|
|
58
|
+
const result = LLMSchema.safeParse({ [field]: "jev-managed" });
|
|
59
|
+
expect(result.success).toBe(false);
|
|
60
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
61
|
+
"structured answers rather than chat text",
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
expect(LLMSchema.safeParse({ activeProfile: "balanced" }).success).toBe(
|
|
65
|
+
true,
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("is rejected under a BYOK default provider, where it has no body", () => {
|
|
70
|
+
const result = LLMSchema.safeParse({
|
|
71
|
+
...PIN,
|
|
72
|
+
defaultProvider: {
|
|
73
|
+
provider: "anthropic",
|
|
74
|
+
connectionName: "anthropic-personal",
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
expect(result.success).toBe(false);
|
|
78
|
+
expect(JSON.stringify(result.error?.issues)).toContain(
|
|
79
|
+
"managed Jev profile",
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -116,6 +116,7 @@ describe("fetchPlatformIdentityIds", () => {
|
|
|
116
116
|
describe("resolvePlatformAssistantId", () => {
|
|
117
117
|
const originalFetch = globalThis.fetch;
|
|
118
118
|
const originalAssistantApiKeyEnv = process.env.ASSISTANT_API_KEY;
|
|
119
|
+
const originalPlatformUrlEnv = process.env.VELLUM_PLATFORM_URL;
|
|
119
120
|
const fetchCalls: Array<{ url: string; init?: RequestInit }> = [];
|
|
120
121
|
let fetchImpl: (
|
|
121
122
|
input: RequestInfo | URL,
|
|
@@ -149,6 +150,11 @@ describe("resolvePlatformAssistantId", () => {
|
|
|
149
150
|
} else {
|
|
150
151
|
process.env.ASSISTANT_API_KEY = originalAssistantApiKeyEnv;
|
|
151
152
|
}
|
|
153
|
+
if (originalPlatformUrlEnv === undefined) {
|
|
154
|
+
delete process.env.VELLUM_PLATFORM_URL;
|
|
155
|
+
} else {
|
|
156
|
+
process.env.VELLUM_PLATFORM_URL = originalPlatformUrlEnv;
|
|
157
|
+
}
|
|
152
158
|
setPlatformBaseUrl(undefined);
|
|
153
159
|
setPlatformAssistantId(undefined);
|
|
154
160
|
setPlatformOrganizationId(undefined);
|
|
@@ -198,6 +204,59 @@ describe("resolvePlatformAssistantId", () => {
|
|
|
198
204
|
expect(fetchCalls).toHaveLength(1);
|
|
199
205
|
});
|
|
200
206
|
|
|
207
|
+
test("retries validate inside the cooldown once a different API key is stored", async () => {
|
|
208
|
+
process.env.ASSISTANT_API_KEY = "rejected-key";
|
|
209
|
+
setPlatformBaseUrl(BASE_URL);
|
|
210
|
+
fetchImpl = async () => new Response("no", { status: 401 });
|
|
211
|
+
|
|
212
|
+
await expect(resolvePlatformAssistantId()).resolves.toBe("");
|
|
213
|
+
expect(fetchCalls).toHaveLength(1);
|
|
214
|
+
|
|
215
|
+
process.env.ASSISTANT_API_KEY = "replacement-key";
|
|
216
|
+
fetchImpl = async () =>
|
|
217
|
+
new Response(
|
|
218
|
+
JSON.stringify({
|
|
219
|
+
assistant_id: ASSISTANT_ID,
|
|
220
|
+
organization_id: ORG_ID,
|
|
221
|
+
user_id: USER_ID,
|
|
222
|
+
}),
|
|
223
|
+
{ status: 200 },
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
await expect(resolvePlatformAssistantId()).resolves.toBe(ASSISTANT_ID);
|
|
227
|
+
expect(fetchCalls).toHaveLength(2);
|
|
228
|
+
const headers = new Headers(fetchCalls[1]?.init?.headers);
|
|
229
|
+
expect(headers.get("Authorization")).toBe("Api-Key replacement-key");
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("retries validate inside the cooldown once a different base URL is stored", async () => {
|
|
233
|
+
process.env.ASSISTANT_API_KEY = "assistant-key";
|
|
234
|
+
// The environment URL outranks the in-memory override (and the test
|
|
235
|
+
// preload sets one), so the base URL is changed through the environment.
|
|
236
|
+
process.env.VELLUM_PLATFORM_URL = "https://old.example.com";
|
|
237
|
+
fetchImpl = async () => new Response("no", { status: 401 });
|
|
238
|
+
|
|
239
|
+
await expect(resolvePlatformAssistantId()).resolves.toBe("");
|
|
240
|
+
expect(fetchCalls).toHaveLength(1);
|
|
241
|
+
|
|
242
|
+
process.env.VELLUM_PLATFORM_URL = BASE_URL;
|
|
243
|
+
fetchImpl = async () =>
|
|
244
|
+
new Response(
|
|
245
|
+
JSON.stringify({
|
|
246
|
+
assistant_id: ASSISTANT_ID,
|
|
247
|
+
organization_id: ORG_ID,
|
|
248
|
+
user_id: USER_ID,
|
|
249
|
+
}),
|
|
250
|
+
{ status: 200 },
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
await expect(resolvePlatformAssistantId()).resolves.toBe(ASSISTANT_ID);
|
|
254
|
+
expect(fetchCalls).toHaveLength(2);
|
|
255
|
+
expect(fetchCalls[1]?.url).toBe(
|
|
256
|
+
`${BASE_URL}${PLATFORM_IDENTITY_VALIDATE_PATH}`,
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
201
260
|
test("retries validate after a failed attempt once the cooldown is cleared", async () => {
|
|
202
261
|
process.env.ASSISTANT_API_KEY = "assistant-key";
|
|
203
262
|
setPlatformBaseUrl(BASE_URL);
|
|
@@ -368,4 +368,75 @@ describe("reconcileFlagGatedProfiles", () => {
|
|
|
368
368
|
expect(reconcileFlagGatedProfiles()).toBe(false);
|
|
369
369
|
expect(readFileSync(CONFIG_PATH, "utf-8")).toBe(before);
|
|
370
370
|
});
|
|
371
|
+
|
|
372
|
+
test("auto-profile flag on materializes the managed auto profile at the head of the order", () => {
|
|
373
|
+
process.env.IS_PLATFORM = "true";
|
|
374
|
+
seedBalancedConfig();
|
|
375
|
+
setOverridesForTesting({ "auto-profile": true });
|
|
376
|
+
|
|
377
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
378
|
+
|
|
379
|
+
const raw = readConfig();
|
|
380
|
+
expect(raw.llm.profiles.auto).toEqual({ source: "managed" });
|
|
381
|
+
expect(raw.llm.profileOrder[0]).toBe("auto");
|
|
382
|
+
const effective = getEffectiveProfile(raw.llm.profiles, "auto")!;
|
|
383
|
+
expect(effective.label).toBe("Auto");
|
|
384
|
+
expect(effective.model).toBe(
|
|
385
|
+
getEffectiveProfile(raw.llm.profiles, "balanced")!.model,
|
|
386
|
+
);
|
|
387
|
+
expect(LLMSchema.safeParse(raw.llm).success).toBe(true);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("auto-profile flag on in BYOK mode writes a plain stub that the effective view hides", () => {
|
|
391
|
+
seedBalancedConfig();
|
|
392
|
+
setOverridesForTesting({ "auto-profile": true });
|
|
393
|
+
|
|
394
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
395
|
+
|
|
396
|
+
const raw = readConfig();
|
|
397
|
+
expect(raw.llm.profiles.auto).toEqual({ source: "managed" });
|
|
398
|
+
expect(getEffectiveProfile(raw.llm.profiles, "auto")).toBeDefined();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test("auto-profile flag off removes the managed auto profile and repoints its references", () => {
|
|
402
|
+
process.env.IS_PLATFORM = "true";
|
|
403
|
+
seedBalancedConfig();
|
|
404
|
+
setOverridesForTesting({ "auto-profile": true });
|
|
405
|
+
reconcileFlagGatedProfiles();
|
|
406
|
+
|
|
407
|
+
const raw = readConfig();
|
|
408
|
+
raw.llm.activeProfile = "auto";
|
|
409
|
+
(raw.llm as Record<string, unknown>).callSites = {
|
|
410
|
+
subagentSpawn: { profile: "auto" },
|
|
411
|
+
};
|
|
412
|
+
writeConfig(raw);
|
|
413
|
+
invalidateConfigCache();
|
|
414
|
+
|
|
415
|
+
setOverridesForTesting({ "auto-profile": false });
|
|
416
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
417
|
+
|
|
418
|
+
const after = readConfig();
|
|
419
|
+
expect(after.llm.profiles.auto).toBeUndefined();
|
|
420
|
+
expect(after.llm.profileOrder.includes("auto")).toBe(false);
|
|
421
|
+
expect(after.llm.activeProfile).toBe("balanced");
|
|
422
|
+
expect((after.llm as Record<string, unknown>).callSites).toEqual({
|
|
423
|
+
subagentSpawn: {},
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("os-beta and auto-profile reconcile independently in one pass", () => {
|
|
428
|
+
process.env.IS_PLATFORM = "true";
|
|
429
|
+
seedBalancedConfig();
|
|
430
|
+
setOverridesForTesting({ "os-beta": true, "auto-profile": true });
|
|
431
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
432
|
+
let order = readConfig().llm.profileOrder;
|
|
433
|
+
expect(order[0]).toBe("auto");
|
|
434
|
+
expect(order.indexOf("os-beta")).toBe(order.indexOf("balanced") + 1);
|
|
435
|
+
|
|
436
|
+
setOverridesForTesting({ "os-beta": false, "auto-profile": true });
|
|
437
|
+
expect(reconcileFlagGatedProfiles()).toBe(true);
|
|
438
|
+
order = readConfig().llm.profileOrder;
|
|
439
|
+
expect(order[0]).toBe("auto");
|
|
440
|
+
expect(order.includes("os-beta")).toBe(false);
|
|
441
|
+
});
|
|
371
442
|
});
|
|
@@ -46,9 +46,12 @@ nothing, because no control is called that. Give the label on its own:
|
|
|
46
46
|
The arrow points at the middle of the control and stops just short, so what
|
|
47
47
|
you are sending someone to stays visible the whole time.
|
|
48
48
|
|
|
49
|
-
You are answered with what was drawn and the name it resolved
|
|
50
|
-
|
|
51
|
-
the
|
|
49
|
+
You are answered with what was drawn and the accessibility name it resolved
|
|
50
|
+
to. Some apps expose internal names such as `ToolbarExportControl` instead of
|
|
51
|
+
the visible label. When a returned name identifies the intended control in
|
|
52
|
+
the shared image, use that exact name as `target`; use the visible label in
|
|
53
|
+
the caption and speech. Do not invent an internal name or choose an unclear
|
|
54
|
+
candidate.
|
|
52
55
|
|
|
53
56
|
A name the surface does not carry draws nothing and comes back with the names
|
|
54
57
|
it does carry. That is the answer, not a setback: the thing is nearly always
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JEV_MANAGED_PROFILE_KEY } from "./default-profile-names.js";
|
|
1
2
|
import { type LLMCallSite } from "./schemas/llm.js";
|
|
2
3
|
|
|
3
4
|
type CallSiteDefaultConfig = {
|
|
@@ -123,9 +124,8 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
123
124
|
effort: "low",
|
|
124
125
|
thinking: { enabled: false },
|
|
125
126
|
},
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
// answers, so it is priced as a latency call.
|
|
127
|
+
// Voice intent classification and explicit selected-text edits are
|
|
128
|
+
// latency-sensitive: the caller is waiting for insertion or dispatch.
|
|
129
129
|
interactionClassifier: {
|
|
130
130
|
profile: "latency-optimized",
|
|
131
131
|
effort: "low",
|
|
@@ -153,14 +153,20 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
|
|
|
153
153
|
effort: "low",
|
|
154
154
|
thinking: { enabled: false },
|
|
155
155
|
},
|
|
156
|
-
// The
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
//
|
|
156
|
+
// The managed-only profile resolves through the Vellum connection. On a
|
|
157
|
+
// BYOK install it has no body, so the judge is unavailable and the front
|
|
158
|
+
// door's own decision stands.
|
|
159
|
+
voiceEscalationJudge: {
|
|
160
|
+
profile: JEV_MANAGED_PROFILE_KEY,
|
|
161
|
+
},
|
|
162
|
+
// Picks the default profile for a turn pinned to the Auto profile. On a
|
|
163
|
+
// BYOK install the managed-only Jev profile has no body, so the router is
|
|
164
|
+
// unavailable and the turn runs on Balanced.
|
|
165
|
+
autoProfileRouter: {
|
|
166
|
+
profile: JEV_MANAGED_PROFILE_KEY,
|
|
167
|
+
},
|
|
168
|
+
// The barge-in continuation judge remains off until a TypeSafe profile is
|
|
169
|
+
// pinned here (see live-voice/continuation-judge.ts).
|
|
164
170
|
voiceContinuationJudge: { profile: "cost-optimized" },
|
|
165
171
|
// Names the background continuation a barge-in spawns, from the interrupted
|
|
166
172
|
// transcript. The label is fixed at spawn, so the call runs under a short
|