@vellumai/assistant 0.12.4-staging.1 → 0.12.4-staging.2

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.
Files changed (36) hide show
  1. package/openapi.yaml +3 -1
  2. package/package.json +1 -1
  3. package/src/__tests__/config-loader-backfill.test.ts +1 -0
  4. package/src/__tests__/default-profile-catalog-fallback.test.ts +1 -0
  5. package/src/__tests__/inference-profile-validation-jev.test.ts +17 -0
  6. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  7. package/src/__tests__/llm-resolver-override-or-default.test.ts +43 -0
  8. package/src/__tests__/plugin-api-model-profiles.test.ts +7 -0
  9. package/src/__tests__/workspace-migration-159-rename-colliding-jev-profile-name.test.ts +197 -0
  10. package/src/api/requests/dictation.ts +2 -2
  11. package/src/calls/__tests__/typesafe-noul.test.ts +66 -0
  12. package/src/calls/typesafe-noul.ts +8 -2
  13. package/src/calls/voice-escalation-judge.ts +5 -4
  14. package/src/config/__tests__/default-profile-catalog.test.ts +33 -1
  15. package/src/config/__tests__/jev-managed-profile.test.ts +82 -0
  16. package/src/config/call-site-defaults.ts +11 -11
  17. package/src/config/default-profile-catalog.ts +88 -18
  18. package/src/config/default-profile-names.ts +35 -0
  19. package/src/config/inference-profile-validation.ts +7 -6
  20. package/src/config/llm-resolver.ts +16 -0
  21. package/src/config/profile-text-generation.test.ts +9 -0
  22. package/src/config/schemas/call-site-catalog.ts +1 -1
  23. package/src/config/schemas/llm.ts +54 -9
  24. package/src/daemon/conversation-slash.ts +2 -2
  25. package/src/daemon/dictation-text-processing.ts +4 -4
  26. package/src/plugin-api/model-profiles.ts +5 -3
  27. package/src/providers/model-catalog.ts +23 -4
  28. package/src/runtime/routes/__tests__/dictation-processing.test.ts +174 -0
  29. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +16 -1
  30. package/src/runtime/routes/conversation-routes.ts +4 -2
  31. package/src/runtime/routes/diagnostics-routes.ts +66 -229
  32. package/src/runtime/routes/llm-call-sites-routes.ts +19 -10
  33. package/src/tools/workflows/manage-workflows.ts +4 -2
  34. package/src/workspace/migrations/159-rename-colliding-jev-profile-name.ts +344 -0
  35. package/src/workspace/migrations/registry.ts +2 -0
  36. package/src/runtime/routes/__tests__/dictation-cleanup-guard.test.ts +0 -107
package/openapi.yaml CHANGED
@@ -12060,7 +12060,9 @@ paths:
12060
12060
  post:
12061
12061
  operationId: dictation_post
12062
12062
  summary: Process dictation
12063
- description: Classify voice input as dictation or action, clean up text, and apply user style preferences.
12063
+ description:
12064
+ Preserve dictated words with explicit dictionary and snippet replacements, classify actions, or edit
12065
+ selected text when instructed.
12064
12066
  tags:
12065
12067
  - diagnostics
12066
12068
  requestBody:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.12.4-staging.1",
3
+ "version": "0.12.4-staging.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1869,6 +1869,7 @@ describe("seedInferenceProfiles BYOK-mode default profiles", () => {
1869
1869
  "quality-optimized-backup",
1870
1870
  "cost-optimized-backup",
1871
1871
  "latency-optimized-backup",
1872
+ "jev-managed",
1872
1873
  "my-custom",
1873
1874
  ]);
1874
1875
  });
@@ -116,6 +116,7 @@ describe("managed-column-only scoping", () => {
116
116
  expect(Object.keys(MANAGED_PROFILE_TEMPLATES)).toEqual([
117
117
  ...DEFAULT_PROFILE_KEYS,
118
118
  ...BACKUP_PROFILE_KEYS,
119
+ "jev-managed",
119
120
  ]);
120
121
  });
121
122
 
@@ -0,0 +1,17 @@
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import { validateInferenceProfileKey } from "../config/inference-profile-validation.js";
4
+ import { setConfig } from "./helpers/set-config.js";
5
+
6
+ describe("validateInferenceProfileKey and the managed Jev profile", () => {
7
+ beforeEach(() => {
8
+ setConfig("llm", { profiles: {} });
9
+ });
10
+
11
+ test("a schedule or subagent turn cannot run on the verdict model", () => {
12
+ expect(validateInferenceProfileKey("balanced")).toBeNull();
13
+ expect(validateInferenceProfileKey("jev-managed")).toContain(
14
+ "is not defined in llm.profiles",
15
+ );
16
+ });
17
+ });
@@ -235,6 +235,10 @@ describe("LLM catalog parity: daemon vs client", () => {
235
235
 
236
236
  test("jev-latest opts out of chat text generation", () => {
237
237
  expect(catalogModelSupportsText("typesafe", "jev-latest")).toBe(false);
238
+ // A managed Jev profile is configured as `vellum`; the routing identity
239
+ // owns no catalog models, so the lookup must judge by the catalog owner.
240
+ expect(catalogModelSupportsText("vellum", "jev-latest")).toBe(false);
241
+ expect(catalogModelSupportsText("vellum", "claude-opus-5")).toBe(true);
238
242
  expect(catalogModelSupportsText("anthropic", "claude-opus-4-8")).toBe(true);
239
243
  expect(catalogModelSupportsText("openai-compatible", "local-model")).toBe(
240
244
  true,
@@ -225,6 +225,49 @@ describe("composeCallSiteTweak", () => {
225
225
  });
226
226
 
227
227
  describe("shipped call-site tuning", () => {
228
+ test("the escalation judge uses managed Jev by default", () => {
229
+ const resolved = resolveCallSiteConfig(
230
+ "voiceEscalationJudge",
231
+ LLMSchema.parse({}),
232
+ );
233
+
234
+ expect(resolved.provider).toBe("vellum");
235
+ expect(resolved.provider_connection).toBeUndefined();
236
+ expect(resolved.model).toBe("jev-latest");
237
+ });
238
+
239
+ test("the escalation judge falls through on a BYOK default provider", () => {
240
+ const resolved = resolveCallSiteConfig(
241
+ "voiceEscalationJudge",
242
+ LLMSchema.parse(anthropicDp),
243
+ );
244
+
245
+ expect(resolved.provider).toBe("anthropic");
246
+ expect(resolved.model).not.toBe("jev-latest");
247
+ });
248
+
249
+ test("a BYOK Jev call-site override replaces the managed default", () => {
250
+ const llm = LLMSchema.parse({
251
+ ...anthropicDp,
252
+ profiles: {
253
+ jev: {
254
+ ...completeCustom,
255
+ provider: "typesafe",
256
+ provider_connection: "typesafe-personal",
257
+ model: "jev-latest",
258
+ },
259
+ },
260
+ callSites: {
261
+ voiceEscalationJudge: { profile: "jev" },
262
+ },
263
+ });
264
+ const resolved = resolveCallSiteConfig("voiceEscalationJudge", llm);
265
+
266
+ expect(resolved.provider).toBe("typesafe");
267
+ expect(resolved.provider_connection).toBe("typesafe-personal");
268
+ expect(resolved.model).toBe("jev-latest");
269
+ });
270
+
228
271
  // A workspace entry that only repoints the profile must not cost the call
229
272
  // site the tuning it ships with. `recall` carries a full set: a token
230
273
  // budget, effort, sampling, thinking, and cache posture.
@@ -40,6 +40,13 @@ describe("getModelProfiles", () => {
40
40
  writeFixtureConfig({});
41
41
  });
42
42
 
43
+ test("omits the managed Jev profile, whose model returns verdicts rather than text", () => {
44
+ writeFixtureConfig({});
45
+ const keys = getModelProfiles().map((p) => p.key);
46
+ expect(keys).toContain("balanced");
47
+ expect(keys).not.toContain("jev-managed");
48
+ });
49
+
43
50
  test("orders profiles by profileOrder then the remaining keys alphabetically", () => {
44
51
  // GIVEN a workspace whose profileOrder names some keys, with a duplicate
45
52
  // ("balanced") and a key that resolves to no profile ("ghost").
@@ -0,0 +1,197 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ rmSync,
6
+ writeFileSync,
7
+ } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { Database } from "bun:sqlite";
11
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
12
+
13
+ import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
14
+ import { assertNotLiveDb } from "./assert-not-live-db.js";
15
+
16
+ const migration = WORKSPACE_MIGRATIONS.find(
17
+ (entry) => entry.id === "159-rename-colliding-jev-profile-name",
18
+ )!;
19
+
20
+ let workspaceDir: string;
21
+
22
+ function writeConfig(data: Record<string, unknown>): void {
23
+ writeFileSync(
24
+ join(workspaceDir, "config.json"),
25
+ JSON.stringify(data, null, 2) + "\n",
26
+ );
27
+ }
28
+
29
+ function readConfig(): Record<string, any> {
30
+ return JSON.parse(readFileSync(join(workspaceDir, "config.json"), "utf-8"));
31
+ }
32
+
33
+ function seedProfilePinTables(rows: {
34
+ conversations: string[];
35
+ cronJobs: string[];
36
+ }): void {
37
+ mkdirSync(join(workspaceDir, "data", "db"), { recursive: true });
38
+ const db = new Database(join(workspaceDir, "data", "db", "assistant.db"));
39
+ db.run(
40
+ `CREATE TABLE conversations (id TEXT PRIMARY KEY, inference_profile TEXT)`,
41
+ );
42
+ db.run(
43
+ `CREATE TABLE cron_jobs (id TEXT PRIMARY KEY, inference_profile TEXT)`,
44
+ );
45
+ rows.conversations.forEach((profile, index) => {
46
+ db.query(
47
+ `INSERT INTO conversations (id, inference_profile) VALUES (?, ?)`,
48
+ ).run(`conv-${index}`, profile);
49
+ });
50
+ rows.cronJobs.forEach((profile, index) => {
51
+ db.query(`INSERT INTO cron_jobs (id, inference_profile) VALUES (?, ?)`).run(
52
+ `job-${index}`,
53
+ profile,
54
+ );
55
+ });
56
+ db.close();
57
+ }
58
+
59
+ function readPins(table: string): string[] {
60
+ const db = new Database(join(workspaceDir, "data", "db", "assistant.db"));
61
+ try {
62
+ return (
63
+ db
64
+ .query(`SELECT inference_profile AS p FROM ${table} ORDER BY id`)
65
+ .all() as Array<{ p: string }>
66
+ ).map((row) => row.p);
67
+ } finally {
68
+ db.close();
69
+ }
70
+ }
71
+
72
+ beforeEach(() => {
73
+ workspaceDir = join(
74
+ tmpdir(),
75
+ `vellum-migration-159-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
76
+ );
77
+ mkdirSync(workspaceDir, { recursive: true });
78
+ });
79
+
80
+ afterEach(() => {
81
+ if (existsSync(workspaceDir)) {
82
+ assertNotLiveDb(workspaceDir);
83
+ rmSync(workspaceDir, { recursive: true, force: true });
84
+ }
85
+ });
86
+
87
+ describe("159-rename-colliding-jev-profile-name migration", () => {
88
+ test("is registered under its id with checkpoint retry", () => {
89
+ expect(migration).toBeDefined();
90
+ expect(migration.retryFailedCheckpoint).toBe(true);
91
+ });
92
+
93
+ test("renames a colliding user profile and rewrites every reference", async () => {
94
+ writeConfig({
95
+ llm: {
96
+ defaultProvider: { provider: "vellum" },
97
+ profiles: {
98
+ "jev-managed": {
99
+ source: "user",
100
+ provider: "anthropic",
101
+ model: "claude-opus-4-7",
102
+ label: "Mine",
103
+ },
104
+ scratch: { source: "user", fallbackProfile: "jev-managed" },
105
+ armA: { source: "user", speed: "fast" },
106
+ blend: {
107
+ source: "user",
108
+ mix: [
109
+ { profile: "jev-managed", weight: 1 },
110
+ { profile: "armA", weight: 1 },
111
+ ],
112
+ },
113
+ },
114
+ profileOrder: ["balanced", "jev-managed", "blend"],
115
+ activeProfile: "jev-managed",
116
+ advisorProfile: "jev-managed",
117
+ callSites: { recall: { profile: "jev-managed", maxTokens: 4096 } },
118
+ },
119
+ });
120
+ seedProfilePinTables({
121
+ conversations: ["jev-managed", "balanced"],
122
+ cronJobs: ["jev-managed"],
123
+ });
124
+
125
+ await migration.run(workspaceDir);
126
+
127
+ const llm = readConfig().llm;
128
+ expect(llm.profiles["jev-managed"]).toBeUndefined();
129
+ expect(llm.profiles["jev-managed-custom"]).toEqual({
130
+ source: "user",
131
+ provider: "anthropic",
132
+ model: "claude-opus-4-7",
133
+ label: "Mine",
134
+ });
135
+ expect(Object.keys(llm.profiles)).toEqual([
136
+ "jev-managed-custom",
137
+ "scratch",
138
+ "armA",
139
+ "blend",
140
+ ]);
141
+ expect(llm.activeProfile).toBe("jev-managed-custom");
142
+ expect(llm.advisorProfile).toBe("jev-managed-custom");
143
+ expect(llm.profileOrder).toEqual([
144
+ "balanced",
145
+ "jev-managed-custom",
146
+ "blend",
147
+ ]);
148
+ expect(llm.callSites.recall.profile).toBe("jev-managed-custom");
149
+ expect(llm.profiles.scratch.fallbackProfile).toBe("jev-managed-custom");
150
+ expect(llm.profiles.blend.mix[0].profile).toBe("jev-managed-custom");
151
+ expect(readPins("conversations")).toEqual([
152
+ "jev-managed-custom",
153
+ "balanced",
154
+ ]);
155
+ expect(readPins("cron_jobs")).toEqual(["jev-managed-custom"]);
156
+ });
157
+
158
+ test("keeps suffixing past taken and reserved names", async () => {
159
+ writeConfig({
160
+ llm: {
161
+ profiles: {
162
+ "jev-managed": {
163
+ source: "user",
164
+ provider: "openai",
165
+ model: "gpt-5.5",
166
+ },
167
+ "jev-managed-custom": { source: "user", provider: "openai" },
168
+ },
169
+ },
170
+ });
171
+
172
+ await migration.run(workspaceDir);
173
+
174
+ const keys = Object.keys(readConfig().llm.profiles);
175
+ expect(keys).toEqual(["jev-managed-custom-2", "jev-managed-custom"]);
176
+ });
177
+
178
+ test("leaves a managed stub alone and is idempotent", async () => {
179
+ const original = {
180
+ llm: {
181
+ profiles: {
182
+ "jev-managed": { source: "managed", status: "disabled" },
183
+ balanced: { source: "managed" },
184
+ },
185
+ },
186
+ };
187
+ writeConfig(original);
188
+
189
+ await migration.run(workspaceDir);
190
+ await migration.run(workspaceDir);
191
+
192
+ expect(readConfig()).toEqual(original);
193
+ expect(existsSync(join(workspaceDir, "data", "db", "assistant.db"))).toBe(
194
+ false,
195
+ );
196
+ });
197
+ });
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Wire contract for the dictation REST endpoint (`POST /dictation`).
3
3
  *
4
- * The body web/macOS sends when asking the daemon to clean up a raw voice
5
- * transcript and classify it as dictation vs. action. `context` is its own
4
+ * The body web/macOS sends to apply explicit dictation replacements, classify
5
+ * voice input, or edit selected text. `context` is its own
6
6
  * named schema/type so consumers (web client) can import `DictationContext`
7
7
  * directly instead of inferring it from the generated client.
8
8
  *
@@ -0,0 +1,66 @@
1
+ import { afterAll, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { Provider } from "../../providers/types.js";
4
+
5
+ let configured: {
6
+ provider: Provider;
7
+ configuredProviderName: string;
8
+ entryRouted: boolean;
9
+ } | null = null;
10
+
11
+ const actual = await import("../../providers/provider-send-message.js");
12
+ mock.module("../../providers/provider-send-message.js", () => ({
13
+ ...actual,
14
+ resolveConfiguredProvider: async () => configured,
15
+ }));
16
+
17
+ const { resolveTypesafeProvider } = await import("../typesafe-noul.js");
18
+
19
+ afterAll(() => {
20
+ mock.restore();
21
+ });
22
+
23
+ function stub(name: string): Provider {
24
+ return {
25
+ name,
26
+ defaultModel: "jev-latest",
27
+ sendMessage: async () => {
28
+ throw new Error("not dispatched in this test");
29
+ },
30
+ } as unknown as Provider;
31
+ }
32
+
33
+ describe("resolveTypesafeProvider", () => {
34
+ test("a BYOK TypeSafe profile resolves", async () => {
35
+ configured = {
36
+ provider: stub("typesafe"),
37
+ configuredProviderName: "typesafe",
38
+ entryRouted: false,
39
+ };
40
+ expect((await resolveTypesafeProvider("voiceEscalationJudge"))?.name).toBe(
41
+ "typesafe",
42
+ );
43
+ });
44
+
45
+ test("a managed Jev profile resolves although it is configured as vellum", async () => {
46
+ configured = {
47
+ provider: stub("typesafe"),
48
+ configuredProviderName: "vellum",
49
+ entryRouted: false,
50
+ };
51
+ expect((await resolveTypesafeProvider("voiceEscalationJudge"))?.name).toBe(
52
+ "typesafe",
53
+ );
54
+ });
55
+
56
+ test("any other dispatched provider is unavailable", async () => {
57
+ configured = {
58
+ provider: stub("anthropic"),
59
+ configuredProviderName: "vellum",
60
+ entryRouted: false,
61
+ };
62
+ expect(await resolveTypesafeProvider("voiceEscalationJudge")).toBeNull();
63
+ configured = null;
64
+ expect(await resolveTypesafeProvider("voiceEscalationJudge")).toBeNull();
65
+ });
66
+ });
@@ -28,12 +28,18 @@ export interface TypesafeNoulResult {
28
28
  latencyMs: number;
29
29
  }
30
30
 
31
- /** The call site's provider when it resolves to TypeSafe, else null. */
31
+ /**
32
+ * The call site's provider when it resolves to TypeSafe, else null. Judged
33
+ * on the dispatched adapter's name rather than the configured provider: a
34
+ * managed profile is configured as `vellum` and only substitutes the
35
+ * TypeSafe upstream at dispatch, so the configured name alone would report
36
+ * the managed Jev profile as unavailable.
37
+ */
32
38
  export async function resolveTypesafeProvider(
33
39
  callSite: LLMCallSite,
34
40
  ): Promise<Provider | null> {
35
41
  const resolved = await resolveConfiguredProvider(callSite);
36
- return resolved?.configuredProviderName === TYPESAFE_PROVIDER_NAME
42
+ return resolved?.provider.name === TYPESAFE_PROVIDER_NAME
37
43
  ? resolved.provider
38
44
  : null;
39
45
  }
@@ -9,10 +9,11 @@
9
9
  * correct reply need something the front door cannot do from here? A
10
10
  * confident yes overrules a front-door answer and hands the turn off.
11
11
  *
12
- * The judge runs only when the `voiceEscalationJudge` call site resolves to
13
- * the TypeSafe provider. Every other outcome (no such profile, provider
14
- * error, timeout, unparseable answer) is a "clear" verdict, so the front
15
- * door's own decision stands exactly as it would without the judge.
12
+ * The judge uses managed Jev by default when Vellum-managed inference is
13
+ * available. An explicit TypeSafe call-site override can use a user-owned
14
+ * connection. Every other outcome (unavailable route, provider error,
15
+ * timeout, unparseable answer) is a "clear" verdict, so the front door's own
16
+ * decision stands exactly as it would without the judge.
16
17
  */
17
18
 
18
19
  import type { Message, Provider } from "../providers/types.js";
@@ -5,6 +5,7 @@ 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,
@@ -59,6 +60,37 @@ describe("getEffectiveProfiles", () => {
59
60
  }
60
61
  });
61
62
 
63
+ test("the managed Jev profile routes jev-latest through TypeSafe and is managed-only", () => {
64
+ const jev = CODE_DEFAULT_PROFILE_ENTRIES["jev-managed"];
65
+ expect(jev.model).toBe("jev-latest");
66
+ expect(jev.provider_connection).toBeUndefined();
67
+ expect(resolveRoutingIdentity(jev.provider, jev.model)).toEqual({
68
+ connectionName: "vellum",
69
+ expectedProvider: "typesafe",
70
+ });
71
+ expect(
72
+ resolveDefaultProfileForProvider(undefined, "jev-managed", null),
73
+ ).toBeDefined();
74
+ expect(
75
+ resolveDefaultProfileForProvider(undefined, "jev-managed", {
76
+ provider: "anthropic",
77
+ connectionName: "anthropic-personal",
78
+ }),
79
+ ).toBeUndefined();
80
+ });
81
+
82
+ test("the conversation view drops the managed Jev profile but keeps it selectable for call sites", () => {
83
+ expect(
84
+ Object.keys(getConversationProfilesForProvider(undefined, null)),
85
+ ).not.toContain("jev-managed");
86
+ expect(
87
+ Object.keys(getUserSelectableProfilesForProvider(undefined, null)),
88
+ ).toContain("jev-managed");
89
+ expect(
90
+ Object.keys(getConversationProfilesForProvider(undefined, null)),
91
+ ).toContain("balanced");
92
+ });
93
+
62
94
  test("the managed Balanced profile routes GLM 5.3 Flash through Fireworks", () => {
63
95
  const balanced = CODE_DEFAULT_PROFILE_ENTRIES.balanced;
64
96
  expect(balanced.model).toBe("accounts/fireworks/models/glm-5p3-flash");
@@ -80,7 +112,7 @@ describe("getEffectiveProfiles", () => {
80
112
  test("defaults absent from the workspace resolve from the catalog; os-beta stays flag-gated", () => {
81
113
  const effective = getEffectiveProfiles(undefined);
82
114
  expect(Object.keys(effective).sort()).toEqual(
83
- [...DEFAULT_PROFILE_KEYS, ...BACKUP_PROFILE_KEYS].sort(),
115
+ [...DEFAULT_PROFILE_KEYS, ...BACKUP_PROFILE_KEYS, "jev-managed"].sort(),
84
116
  );
85
117
  expect(getEffectiveProfile({}, "balanced")?.model).toBe(
86
118
  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
+ });
@@ -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
- // The dictation cleanup pass, and nothing else runs on this site. The
127
- // caller has already stopped speaking and has nothing on screen until this
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,14 @@ export const CALL_SITE_DEFAULTS: Record<LLMCallSite, CallSiteDefaultConfig> = {
153
153
  effort: "low",
154
154
  thinking: { enabled: false },
155
155
  },
156
- // The escalation judge only runs when this site resolves to the TypeSafe
157
- // provider (see calls/voice-escalation-judge.ts), so the shipped default is
158
- // any ordinary profile: the judge stays off until a user pins a TypeSafe
159
- // profile here.
160
- voiceEscalationJudge: { profile: "cost-optimized" },
161
- // Same arrangement for the barge-in continuation judge
162
- // (live-voice/continuation-judge.ts): off until a TypeSafe profile is
163
- // pinned here.
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
+ // The barge-in continuation judge remains off until a TypeSafe profile is
163
+ // pinned here (see live-voice/continuation-judge.ts).
164
164
  voiceContinuationJudge: { profile: "cost-optimized" },
165
165
  // Names the background continuation a barge-in spawns, from the interrupted
166
166
  // transcript. The label is fixed at spawn, so the call runs under a short