@vellumai/assistant 0.10.2-dev.202606250106.466483e → 0.10.2-dev.202606250318.5e7cfb0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.10.2-dev.202606250106.466483e",
3
+ "version": "0.10.2-dev.202606250318.5e7cfb0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -373,6 +373,48 @@ describe("extractVellumLinks", () => {
373
373
  expect(result.directiveRequests[1].path).toBe("/tmp/b.pdf");
374
374
  });
375
375
 
376
+ test("decodes URL-encoded spaces in workspace paths", () => {
377
+ const text =
378
+ "[file with spaces.txt](vellum://workspace/scratch/file%20with%20spaces.txt)";
379
+ const result = extractVellumLinks(text);
380
+
381
+ expect(result.directiveRequests).toHaveLength(1);
382
+ expect(result.directiveRequests[0].source).toBe("sandbox");
383
+ expect(result.directiveRequests[0].path).toBe(
384
+ "scratch/file with spaces.txt",
385
+ );
386
+ });
387
+
388
+ test("decodes URL-encoded spaces in host paths", () => {
389
+ const text =
390
+ "[my file.pdf](vellum://host/Users/me/my%20file.pdf)";
391
+ const result = extractVellumLinks(text);
392
+
393
+ expect(result.directiveRequests).toHaveLength(1);
394
+ expect(result.directiveRequests[0].source).toBe("host");
395
+ expect(result.directiveRequests[0].path).toBe("/Users/me/my file.pdf");
396
+ });
397
+
398
+ test("warns on malformed percent-encoding instead of throwing", () => {
399
+ const text =
400
+ "[100% complete.txt](vellum://workspace/scratch/100%25complete.txt)";
401
+ const result = extractVellumLinks(text);
402
+
403
+ // %25 decodes to %, so this should succeed
404
+ expect(result.directiveRequests).toHaveLength(1);
405
+ expect(result.directiveRequests[0].path).toBe("scratch/100%complete.txt");
406
+ });
407
+
408
+ test("warns on malformed percent-encoding and skips the link", () => {
409
+ const text =
410
+ "[bad file](vellum://workspace/scratch/100%complete.txt)";
411
+ const result = extractVellumLinks(text);
412
+
413
+ expect(result.directiveRequests).toHaveLength(0);
414
+ expect(result.parseWarnings).toHaveLength(1);
415
+ expect(result.parseWarnings[0]).toContain("malformed percent-encoding");
416
+ });
417
+
376
418
  test("warns on empty workspace path", () => {
377
419
  const text = "[file](vellum://workspace/)";
378
420
  const result = extractVellumLinks(text);
@@ -1703,8 +1703,8 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1703
1703
  // ---------------------------------------------------------------------------
1704
1704
  // Tests: OS Beta flag-gated managed profile. The template is defined but
1705
1705
  // intentionally NOT part of MANAGED_PROFILE_TEMPLATES, so seedInferenceProfiles
1706
- // must never create it. A later PR reconciles it in/out based on the `os-beta`
1707
- // feature flag.
1706
+ // must never create it. The flag-gated reconcile creates or removes it based on
1707
+ // the `os-beta` feature flag.
1708
1708
  // ---------------------------------------------------------------------------
1709
1709
 
1710
1710
  describe("OS Beta managed profile template", () => {
@@ -1751,20 +1751,21 @@ describe("OS Beta managed profile template", () => {
1751
1751
  expect(MANAGED_PROFILE_NAMES.has("os-beta")).toBe(true);
1752
1752
  });
1753
1753
 
1754
- test("materializeProfile honors the explicit OS Beta model", () => {
1754
+ test("materializeProfile resolves OS Beta to the Balanced model with low effort", () => {
1755
1755
  const entry = materializeProfile(
1756
1756
  OS_BETA_PROFILE_TEMPLATE,
1757
- "fireworks",
1758
- "fireworks-managed",
1757
+ "together",
1758
+ "together-managed",
1759
1759
  );
1760
1760
 
1761
- expect(entry.model).toBe("accounts/fireworks/models/glm-5p2");
1762
- expect(entry.provider_connection).toBe("fireworks-managed");
1763
- expect(entry.provider).toBe("fireworks");
1761
+ expect(entry.model).toBe("MiniMaxAI/MiniMax-M3");
1762
+ expect(entry.provider_connection).toBe("together-managed");
1763
+ expect(entry.provider).toBe("together");
1764
1764
  expect(entry.label).toBe("OS Beta");
1765
1765
  expect(entry.source).toBe("managed");
1766
1766
  expect(entry.maxTokens).toBe(32000);
1767
- expect(entry.effort).toBe("high");
1767
+ expect(entry.effort).toBe("low");
1768
1768
  expect(entry.thinking?.enabled).toBe(true);
1769
+ expect(entry.topP).toBe(0.95);
1769
1770
  });
1770
1771
  });
@@ -127,6 +127,44 @@ mock.module("../runtime/local-actor-identity.js", () => ({
127
127
  )?.principalId as string | undefined,
128
128
  }));
129
129
 
130
+ // Capture the sourceActorPrincipalId that handleSendMessage threads into
131
+ // shouldAttachHostProxyForCapability / preactivateHostProxySkills, so tests
132
+ // can assert the dev-bypass translation landed before the CU proxy gate.
133
+ // The macOS "native_support" path short-circuits before reading the
134
+ // principal, so only web/ios turns exercise the same-actor branch.
135
+ const hostProxyAttachCalls: Array<{
136
+ capability: string;
137
+ sourceInterface: unknown;
138
+ sourceActorPrincipalId: string | undefined;
139
+ }> = [];
140
+ const preactivateCalls: Array<{
141
+ sourceInterface: unknown;
142
+ sourceActorPrincipalId: string | undefined;
143
+ }> = [];
144
+ mock.module("../daemon/host-proxy-preactivation.js", () => ({
145
+ shouldAttachHostProxyForCapability: (
146
+ capability: string,
147
+ sourceInterface: unknown,
148
+ sourceActorPrincipalId: string | undefined,
149
+ ) => {
150
+ hostProxyAttachCalls.push({
151
+ capability,
152
+ sourceInterface,
153
+ sourceActorPrincipalId,
154
+ });
155
+ // Return false so the route skips proxy instantiation; we only care
156
+ // that the translated principal reached the gate.
157
+ return false;
158
+ },
159
+ preactivateHostProxySkills: (
160
+ _conversation: unknown,
161
+ sourceInterface: unknown,
162
+ sourceActorPrincipalId: string | undefined,
163
+ ) => {
164
+ preactivateCalls.push({ sourceInterface, sourceActorPrincipalId });
165
+ },
166
+ }));
167
+
130
168
  let mockGuardians: Array<Record<string, unknown>> | null = [
131
169
  {
132
170
  channelType: "vellum",
@@ -612,4 +650,89 @@ describe("HTTP POST /v1/messages trust context from the gateway binding", () =>
612
650
  expect(ctx.trustClass).toBe("guardian");
613
651
  expect(ctx.sourceChannel).toBe("telegram");
614
652
  });
653
+
654
+ // A web turn's "dev-bypass" principal must translate to the real guardian
655
+ // principal before the CU/app-control same-actor proxy-attachment gate,
656
+ // so it matches the macOS client's SSE-registered principal.
657
+ test("dev-bypass is translated to the guardian principal before the CU proxy attach gate (web turn)", async () => {
658
+ hostProxyAttachCalls.length = 0;
659
+ preactivateCalls.length = 0;
660
+ const conversation = makeConversation();
661
+ const res = await callHandler(
662
+ (args) =>
663
+ handleSendMessage(args, {
664
+ sendMessageDeps: {
665
+ getOrCreateConversation: async () => conversation,
666
+ assistantEventHub: { publish: async () => {} } as any,
667
+ resolveAttachments: () => [],
668
+ },
669
+ }),
670
+ new Request("http://localhost/v1/messages", {
671
+ method: "POST",
672
+ headers: {
673
+ "Content-Type": "application/json",
674
+ "x-vellum-actor-principal-id": "dev-bypass",
675
+ "x-vellum-principal-type": "actor",
676
+ },
677
+ body: JSON.stringify({
678
+ conversationKey: "cu-attach-key",
679
+ content: "hi",
680
+ sourceChannel: "vellum",
681
+ interface: "web",
682
+ }),
683
+ }),
684
+ undefined,
685
+ 202,
686
+ );
687
+ expect(res.status).toBe(202);
688
+
689
+ // The CU attach gate receives the translated guardian principal, not
690
+ // the raw "dev-bypass" string.
691
+ const cuCall = hostProxyAttachCalls.find(
692
+ (c) => c.capability === "host_cu",
693
+ );
694
+ expect(cuCall).toBeDefined();
695
+ expect(cuCall?.sourceActorPrincipalId).toBe("test-user");
696
+ expect(cuCall?.sourceActorPrincipalId).not.toBe("dev-bypass");
697
+
698
+ // Preactivation receives the same translated principal.
699
+ const preactivateCall = preactivateCalls[0];
700
+ expect(preactivateCall?.sourceActorPrincipalId).toBe("test-user");
701
+ });
702
+
703
+ test("real (non-dev-bypass) principal passes through the CU proxy attach gate unchanged", async () => {
704
+ hostProxyAttachCalls.length = 0;
705
+ const conversation = makeConversation();
706
+ await callHandler(
707
+ (args) =>
708
+ handleSendMessage(args, {
709
+ sendMessageDeps: {
710
+ getOrCreateConversation: async () => conversation,
711
+ assistantEventHub: { publish: async () => {} } as any,
712
+ resolveAttachments: () => [],
713
+ },
714
+ }),
715
+ new Request("http://localhost/v1/messages", {
716
+ method: "POST",
717
+ headers: {
718
+ "Content-Type": "application/json",
719
+ "x-vellum-actor-principal-id": "real-jwt-principal",
720
+ "x-vellum-principal-type": "actor",
721
+ },
722
+ body: JSON.stringify({
723
+ conversationKey: "cu-attach-real-key",
724
+ content: "hi",
725
+ sourceChannel: "vellum",
726
+ interface: "web",
727
+ }),
728
+ }),
729
+ undefined,
730
+ 202,
731
+ );
732
+
733
+ const cuCall = hostProxyAttachCalls.find(
734
+ (c) => c.capability === "host_cu",
735
+ );
736
+ expect(cuCall?.sourceActorPrincipalId).toBe("real-jwt-principal");
737
+ });
615
738
  });
@@ -0,0 +1,390 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { makeMockLogger } from "./helpers/mock-logger.js";
4
+
5
+ mock.module("../util/logger.js", () => ({
6
+ LOG_FILE_PATTERN: /^assistant-(\d{4}-\d{2}-\d{2})\.log$/,
7
+ getCliLogger: () => makeMockLogger(),
8
+ getLogger: () => makeMockLogger(),
9
+ initLogger: () => {},
10
+ pruneOldLogFiles: () => 0,
11
+ truncateForLog: (value: string, maxLen = 500) => value.slice(0, maxLen),
12
+ }));
13
+
14
+ let rawConfig: Record<string, unknown> = {};
15
+ let savedRawConfig: Record<string, unknown> | null = null;
16
+
17
+ function deepMerge(
18
+ target: Record<string, unknown>,
19
+ patch: Record<string, unknown>,
20
+ ): void {
21
+ for (const [key, value] of Object.entries(patch)) {
22
+ if (
23
+ value !== null &&
24
+ typeof value === "object" &&
25
+ !Array.isArray(value) &&
26
+ target[key] !== null &&
27
+ typeof target[key] === "object" &&
28
+ !Array.isArray(target[key])
29
+ ) {
30
+ deepMerge(
31
+ target[key] as Record<string, unknown>,
32
+ value as Record<string, unknown>,
33
+ );
34
+ } else {
35
+ target[key] = value;
36
+ }
37
+ }
38
+ }
39
+
40
+ function setNestedValue(
41
+ obj: Record<string, unknown>,
42
+ path: string,
43
+ value: unknown,
44
+ ): void {
45
+ const keys = path.split(".");
46
+ let current = obj;
47
+ for (const key of keys.slice(0, -1)) {
48
+ if (
49
+ current[key] === null ||
50
+ typeof current[key] !== "object" ||
51
+ Array.isArray(current[key])
52
+ ) {
53
+ current[key] = {};
54
+ }
55
+ current = current[key] as Record<string, unknown>;
56
+ }
57
+ current[keys[keys.length - 1]!] = value;
58
+ }
59
+
60
+ mock.module("../config/loader.js", () => ({
61
+ API_KEY_PROVIDERS: [],
62
+ applyNestedDefaults: (config: unknown) => config,
63
+ loadRawConfig: () => structuredClone(savedRawConfig ?? rawConfig),
64
+ saveRawConfig: (raw: Record<string, unknown>) => {
65
+ savedRawConfig = structuredClone(raw);
66
+ },
67
+ deepMergeOverwrite: deepMerge,
68
+ fillContextDefaultsForMissingKeys: () => {},
69
+ loadConfig: () => structuredClone(savedRawConfig ?? rawConfig),
70
+ getConfig: () => structuredClone(savedRawConfig ?? rawConfig),
71
+ getConfigReadOnly: () => structuredClone(savedRawConfig ?? rawConfig),
72
+ getDeploymentContextDefaults: () => ({}),
73
+ getNestedValue: (obj: Record<string, unknown>, path: string) =>
74
+ path.split(".").reduce<unknown>((current, key) => {
75
+ if (
76
+ current === null ||
77
+ typeof current !== "object" ||
78
+ Array.isArray(current)
79
+ ) {
80
+ return undefined;
81
+ }
82
+ return (current as Record<string, unknown>)[key];
83
+ }, obj),
84
+ invalidateConfigCache: () => {},
85
+ mergeDefaultWorkspaceConfig: () => ({
86
+ merged: false,
87
+ config: structuredClone(savedRawConfig ?? rawConfig),
88
+ }),
89
+ setNestedValue,
90
+ withSuppressedConfigDiskWrites: async (fn: () => unknown) => fn(),
91
+ withSuppressedConfigDiskWritesSync: (fn: () => unknown) => fn(),
92
+ _writeQuarantineNotice: () => {},
93
+ }));
94
+
95
+ mock.module("../daemon/config-watcher.js", () => ({
96
+ getConfigWatcher: () => ({
97
+ suppressConfigReload: false,
98
+ timers: { schedule: () => {} },
99
+ updateFingerprint: () => {},
100
+ }),
101
+ }));
102
+
103
+ mock.module("../providers/registry.js", () => ({
104
+ clearConnectionProviderCache: () => {},
105
+ getProvider: () => {
106
+ throw new Error("provider registry mock not implemented");
107
+ },
108
+ getProviderRoutingSource: () => null,
109
+ initializeProviders: async () => {},
110
+ isNativeWebSearchCapableProvider: () => false,
111
+ listProviders: () => [],
112
+ resolveProviderFromConnection: async () => null,
113
+ }));
114
+
115
+ mock.module("../memory/embedding-backend.js", () => ({
116
+ EmbeddingBackendUnavailableError: class EmbeddingBackendUnavailableError extends Error {},
117
+ SPARSE_EMBEDDING_VERSION: 4,
118
+ clearEmbeddingBackendCache: () => {},
119
+ embedWithBackend: async () => ({
120
+ provider: "local",
121
+ model: "test",
122
+ vectors: [],
123
+ }),
124
+ generateSparseEmbedding: () => ({ indices: [], values: [] }),
125
+ getMemoryBackendStatus: async () => ({
126
+ enabled: false,
127
+ provider: null,
128
+ model: null,
129
+ }),
130
+ resetLocalEmbeddingFailureState: () => {},
131
+ selectEmbeddingBackend: async () => null,
132
+ selectedBackendSupportsMultimodal: async () => false,
133
+ }));
134
+
135
+ mock.module("../security/secret-allowlist.js", () => ({
136
+ isAllowlisted: () => false,
137
+ loadAllowlist: () => {},
138
+ resetAllowlist: () => {},
139
+ validateAllowlistFile: () => null,
140
+ }));
141
+
142
+ const { ROUTES } =
143
+ await import("../runtime/routes/conversation-query-routes.js");
144
+ const { BadRequestError } = await import("../runtime/routes/errors.js");
145
+
146
+ function findRoute(operationId: string) {
147
+ const route = ROUTES.find((r) => r.operationId === operationId);
148
+ if (!route) throw new Error(`Route not found: ${operationId}`);
149
+ return route;
150
+ }
151
+
152
+ const configGetRoute = findRoute("config_get");
153
+ const configPatchRoute = findRoute("config_patch");
154
+ const configSetRoute = findRoute("config_set");
155
+
156
+ describe("MCP config secret boundary", () => {
157
+ beforeEach(() => {
158
+ rawConfig = {};
159
+ savedRawConfig = null;
160
+ });
161
+
162
+ test("config_get omits legacy MCP transport headers from settings-read responses", () => {
163
+ rawConfig = {
164
+ mcp: {
165
+ servers: {
166
+ remote: {
167
+ transport: {
168
+ type: "streamable-http",
169
+ url: "https://mcp.example.com",
170
+ headers: {
171
+ Authorization: "Bearer mcp-secret",
172
+ "X-API-Key": "mcp-api-secret",
173
+ },
174
+ },
175
+ },
176
+ },
177
+ },
178
+ };
179
+
180
+ const result = configGetRoute.handler({}) as Record<string, unknown>;
181
+
182
+ expect(JSON.stringify(result)).not.toContain("mcp-secret");
183
+ expect(JSON.stringify(result)).not.toContain("mcp-api-secret");
184
+ const mcp = result.mcp as {
185
+ servers: { remote: { transport: Record<string, unknown> } };
186
+ };
187
+ expect(mcp.servers.remote.transport).toEqual({
188
+ type: "streamable-http",
189
+ url: "https://mcp.example.com",
190
+ });
191
+ });
192
+
193
+ test("config_get omits headers inside malformed MCP server trees", () => {
194
+ rawConfig = {
195
+ mcp: {
196
+ servers: [
197
+ {
198
+ transport: {
199
+ headers: { Authorization: "Bearer malformed-secret" },
200
+ },
201
+ },
202
+ ],
203
+ },
204
+ };
205
+
206
+ const result = configGetRoute.handler({}) as Record<string, unknown>;
207
+
208
+ expect(JSON.stringify(result)).not.toContain("malformed-secret");
209
+ expect(result).toEqual({
210
+ mcp: {
211
+ servers: [
212
+ {
213
+ transport: {},
214
+ },
215
+ ],
216
+ },
217
+ });
218
+ });
219
+
220
+ test("config_get preserves an MCP server named headers", () => {
221
+ rawConfig = {
222
+ mcp: {
223
+ servers: {
224
+ headers: {
225
+ transport: {
226
+ type: "streamable-http",
227
+ url: "https://mcp.example.com",
228
+ },
229
+ },
230
+ },
231
+ },
232
+ };
233
+
234
+ const result = configGetRoute.handler({}) as Record<string, unknown>;
235
+
236
+ expect(result).toEqual(rawConfig);
237
+ });
238
+
239
+ test("config_get preserves non-credential headers env vars", () => {
240
+ rawConfig = {
241
+ mcp: {
242
+ servers: {
243
+ local: {
244
+ transport: {
245
+ type: "stdio",
246
+ command: "npx",
247
+ env: {
248
+ headers: "not-a-transport-header",
249
+ },
250
+ },
251
+ },
252
+ },
253
+ },
254
+ };
255
+
256
+ const result = configGetRoute.handler({}) as Record<string, unknown>;
257
+
258
+ expect(result).toEqual(rawConfig);
259
+ });
260
+
261
+ test("config_patch rejects MCP transport headers so generic writes cannot reintroduce plaintext credentials", async () => {
262
+ await expect(
263
+ configPatchRoute.handler({
264
+ body: {
265
+ mcp: {
266
+ servers: {
267
+ remote: {
268
+ transport: {
269
+ type: "streamable-http",
270
+ url: "https://mcp.example.com",
271
+ headers: { Authorization: "Bearer mcp-secret" },
272
+ },
273
+ },
274
+ },
275
+ },
276
+ },
277
+ }),
278
+ ).rejects.toThrow(BadRequestError);
279
+ expect(savedRawConfig).toBeNull();
280
+ });
281
+
282
+ test("config_patch allows an MCP server named headers when its value has no header credentials", async () => {
283
+ const result = await configPatchRoute.handler({
284
+ body: {
285
+ mcp: {
286
+ servers: {
287
+ headers: {
288
+ transport: {
289
+ type: "streamable-http",
290
+ url: "https://mcp.example.com",
291
+ },
292
+ },
293
+ },
294
+ },
295
+ },
296
+ });
297
+
298
+ expect(result).toEqual({
299
+ mcp: {
300
+ servers: {
301
+ headers: {
302
+ transport: {
303
+ type: "streamable-http",
304
+ url: "https://mcp.example.com",
305
+ },
306
+ },
307
+ },
308
+ },
309
+ });
310
+ });
311
+
312
+ test("config_patch allows non-credential headers env vars", async () => {
313
+ const result = await configPatchRoute.handler({
314
+ body: {
315
+ mcp: {
316
+ servers: {
317
+ local: {
318
+ transport: {
319
+ type: "stdio",
320
+ command: "npx",
321
+ env: {
322
+ headers: "not-a-transport-header",
323
+ },
324
+ },
325
+ },
326
+ },
327
+ },
328
+ },
329
+ });
330
+
331
+ expect(result).toEqual({
332
+ mcp: {
333
+ servers: {
334
+ local: {
335
+ transport: {
336
+ type: "stdio",
337
+ command: "npx",
338
+ env: {
339
+ headers: "not-a-transport-header",
340
+ },
341
+ },
342
+ },
343
+ },
344
+ },
345
+ });
346
+ });
347
+
348
+ test("config_set rejects malformed MCP server trees containing headers", async () => {
349
+ await expect(
350
+ configSetRoute.handler({
351
+ body: {
352
+ path: "mcp.servers",
353
+ value: [
354
+ {
355
+ transport: {
356
+ headers: { Authorization: "Bearer malformed-secret" },
357
+ },
358
+ },
359
+ ],
360
+ },
361
+ }),
362
+ ).rejects.toThrow(BadRequestError);
363
+ expect(savedRawConfig).toBeNull();
364
+ });
365
+
366
+ test("config_set rejects direct MCP transport header paths", async () => {
367
+ rawConfig = {
368
+ mcp: {
369
+ servers: {
370
+ remote: {
371
+ transport: {
372
+ type: "streamable-http",
373
+ url: "https://mcp.example.com",
374
+ },
375
+ },
376
+ },
377
+ },
378
+ };
379
+
380
+ await expect(
381
+ configSetRoute.handler({
382
+ body: {
383
+ path: "mcp.servers.remote.transport.headers.Authorization",
384
+ value: "Bearer mcp-secret",
385
+ },
386
+ }),
387
+ ).rejects.toThrow(BadRequestError);
388
+ expect(savedRawConfig).toBeNull();
389
+ });
390
+ });
@@ -109,10 +109,13 @@ describe("reconcileFlagGatedProfiles", () => {
109
109
 
110
110
  const raw = readConfig();
111
111
  const osBeta = raw.llm.profiles["os-beta"]!;
112
- expect(osBeta.model).toBe("accounts/fireworks/models/glm-5p2");
113
- expect(osBeta.provider_connection).toBe("fireworks-managed");
112
+ expect(osBeta.model).toBe("MiniMaxAI/MiniMax-M3");
113
+ expect(osBeta.provider_connection).toBe("together-managed");
114
+ expect(osBeta.provider).toBe("together");
114
115
  expect(osBeta.source).toBe("managed");
115
116
  expect(osBeta.label).toBe("OS Beta");
117
+ expect(osBeta.effort).toBe("low");
118
+ expect(osBeta.topP).toBe(0.95);
116
119
 
117
120
  const order = raw.llm.profileOrder;
118
121
  expect(order.indexOf("os-beta")).toBe(order.indexOf("balanced") + 1);
@@ -128,6 +131,7 @@ describe("reconcileFlagGatedProfiles", () => {
128
131
  expect(osBeta.status).toBe("disabled");
129
132
  expect(osBeta.label).toBe("OS Beta (Managed)");
130
133
  expect(osBeta.source).toBe("managed");
134
+ expect(osBeta.effort).toBe("low");
131
135
  });
132
136
 
133
137
  test("flag on is idempotent across repeated runs", () => {
@@ -150,6 +154,7 @@ describe("reconcileFlagGatedProfiles", () => {
150
154
  raw.llm.profiles["os-beta"]!.label = "My OS Beta";
151
155
  raw.llm.profiles["os-beta"]!.status = "disabled";
152
156
  raw.llm.profiles["os-beta"]!.advisorEnabled = true;
157
+ raw.llm.profiles["os-beta"]!.topP = 0.8;
153
158
  writeConfig(raw);
154
159
  invalidateConfigCache();
155
160
 
@@ -159,7 +164,10 @@ describe("reconcileFlagGatedProfiles", () => {
159
164
  expect(after.label).toBe("My OS Beta");
160
165
  expect(after.status).toBe("disabled");
161
166
  expect(after.advisorEnabled).toBe(true);
162
- expect(after.model).toBe("accounts/fireworks/models/glm-5p2");
167
+ expect(after.topP).toBe(0.8);
168
+ expect(after.model).toBe("MiniMaxAI/MiniMax-M3");
169
+ expect(after.provider_connection).toBe("together-managed");
170
+ expect(after.effort).toBe("low");
163
171
  });
164
172
 
165
173
  test("flag off removes a managed os-beta and applies fallbacks", () => {
@@ -415,7 +415,7 @@
415
415
  "scope": "assistant",
416
416
  "key": "os-beta",
417
417
  "label": "OS Beta",
418
- "description": "Enable the OS Beta model profile (GLM 5.2 / Fireworks) in the assistant's model profile selection.",
418
+ "description": "Enable the OS Beta model profile (MiniMax M3 / Together) in the assistant's model profile selection.",
419
419
  "defaultEnabled": false
420
420
  }
421
421
  ]
@@ -164,19 +164,20 @@ export const OS_BETA_FEATURE_FLAG_KEY = "os-beta";
164
164
  * Flag-gated managed profile. NOT in MANAGED_PROFILE_TEMPLATES, so the
165
165
  * unconditional boot seed never creates it. Reconciled in/out by
166
166
  * the flag-gated profile reconcile based on the `os-beta` feature flag.
167
- * Balanced-parity defaults; GLM 5.2 pinned explicitly via `model`.
167
+ * Balanced defaults, with lower reasoning effort while the profile is in beta.
168
168
  */
169
169
  export const OS_BETA_PROFILE_TEMPLATE: ManagedProfileTemplate = {
170
- model: "accounts/fireworks/models/glm-5p2",
171
- provider: "fireworks",
172
- connectionName: "fireworks-managed",
170
+ intent: "balanced",
171
+ provider: "together",
172
+ connectionName: "together-managed",
173
173
  source: "managed",
174
174
  label: "OS Beta",
175
- description: "Open-source frontier model (GLM 5.2), in beta",
175
+ description: "Good balance of quality, cost, and speed, in beta",
176
176
  maxTokens: 32000,
177
- effort: "high",
177
+ effort: "low",
178
178
  thinking: { enabled: true, streamThinking: true },
179
179
  contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
180
+ topP: 0.95,
180
181
  };
181
182
 
182
183
  // Membership here marks a name as managed. The route layer applies managed
@@ -23,12 +23,12 @@ const log = getLogger("sync-gated-profiles");
23
23
  * Reconcile flag-gated managed profiles against the current feature-flag state.
24
24
  *
25
25
  * `seedInferenceProfiles()` runs synchronously at boot before feature flags are
26
- * available, so the OS Beta profile (GLM 5.2 / fireworks-managed) is materialized
27
- * here once flags have loaded. When the `os-beta` flag is on, the managed profile
28
- * is created (ordered right after `balanced`); when it is off, a previously
29
- * managed entry is removed with `profileOrder` / `activeProfile` / `advisorProfile`
30
- * fallbacks. The reconcile is idempotent and never touches a user-owned profile of
31
- * the same name.
26
+ * available, so the OS Beta profile (MiniMax M3 / together-managed) is
27
+ * materialized here once flags have loaded. When the `os-beta` flag is on, the
28
+ * managed profile is created (ordered right after `balanced`); when it is off, a
29
+ * previously managed entry is removed with `profileOrder` / `activeProfile` /
30
+ * `advisorProfile` fallbacks. The reconcile is idempotent and never touches a
31
+ * user-owned profile of the same name.
32
32
  *
33
33
  * Returns whether the on-disk config changed.
34
34
  */
@@ -105,23 +105,22 @@ function enableProfile(
105
105
  OS_BETA_PROFILE_TEMPLATE.connectionName,
106
106
  ) as Record<string, unknown>;
107
107
 
108
- // BYOK installs seed managed profiles disabled: the platform-auth
109
- // `fireworks-managed` connection backing this profile isn't usable until the
110
- // user enables it, so a fresh OS Beta entry starts disabled to avoid offering
111
- // an unusable route. A user's own status override (preserved below) wins on
112
- // later reconciles.
108
+ // BYOK installs seed managed profiles disabled: the managed inference
109
+ // connection backing this profile isn't usable until the user enables it, so a
110
+ // fresh OS Beta entry starts disabled to avoid offering an unusable route. A
111
+ // user's own status override (preserved below) wins on later reconciles.
113
112
  if (isByokMode && !previous) {
114
113
  next.status = "disabled";
115
114
  }
116
115
 
117
116
  if (previous) {
118
- // The only fields a user may override on a managed profile. Carry `label`
119
- // by key-presence so an explicit null (user cleared it) survives too.
117
+ // Preserve user-owned overrides across reconciles.
120
118
  if ("label" in previous) next.label = previous.label;
121
119
  if ("status" in previous) next.status = previous.status;
122
120
  if ("advisorEnabled" in previous) {
123
121
  next.advisorEnabled = previous.advisorEnabled;
124
122
  }
123
+ if ("topP" in previous) next.topP = previous.topP;
125
124
  }
126
125
 
127
126
  let changed = false;
@@ -284,6 +284,19 @@ interface VellumLinkExtractResult {
284
284
  * markdown links from assistant text and return corresponding directive
285
285
  * requests. The text is NOT modified — the links remain as rendered markdown.
286
286
  */
287
+ /**
288
+ * Decode a vellum:// path segment, returning null on malformed percent-encoding
289
+ * (e.g. a literal `%` not followed by two hex digits). This prevents a single
290
+ * bad link from throwing URIError and aborting the entire assistant message.
291
+ */
292
+ function safeDecodePath(rawPath: string): string | null {
293
+ try {
294
+ return decodeURIComponent(rawPath);
295
+ } catch {
296
+ return null;
297
+ }
298
+ }
299
+
287
300
  export function extractVellumLinks(text: string): VellumLinkExtractResult {
288
301
  const directiveRequests: DirectiveRequest[] = [];
289
302
  const parseWarnings: string[] = [];
@@ -294,9 +307,19 @@ export function extractVellumLinks(text: string): VellumLinkExtractResult {
294
307
  const authority = m[2]!;
295
308
  const rawPath = m[3]!;
296
309
 
310
+ const decodedPath = safeDecodePath(rawPath);
311
+ if (decodedPath === null) {
312
+ parseWarnings.push(
313
+ `Ignored vellum://${authority} link "${linkText}": malformed percent-encoding in path.`,
314
+ );
315
+ continue;
316
+ }
317
+
297
318
  if (authority === "workspace") {
298
319
  // Strip the leading "/" to get a workspace-relative path
299
- const path = rawPath.startsWith("/") ? rawPath.slice(1) : rawPath;
320
+ const path = decodedPath.startsWith("/")
321
+ ? decodedPath.slice(1)
322
+ : decodedPath;
300
323
  if (!path) {
301
324
  parseWarnings.push(
302
325
  `Ignored vellum://workspace link "${linkText}": empty path.`,
@@ -310,8 +333,8 @@ export function extractVellumLinks(text: string): VellumLinkExtractResult {
310
333
  mimeType: undefined,
311
334
  });
312
335
  } else {
313
- // host: rawPath is already absolute (starts with /)
314
- if (!rawPath || rawPath === "/") {
336
+ // host: decodedPath is already absolute (starts with /)
337
+ if (!decodedPath || decodedPath === "/") {
315
338
  parseWarnings.push(
316
339
  `Ignored vellum://host link "${linkText}": empty path.`,
317
340
  );
@@ -319,7 +342,7 @@ export function extractVellumLinks(text: string): VellumLinkExtractResult {
319
342
  }
320
343
  directiveRequests.push({
321
344
  source: "host",
322
- path: rawPath,
345
+ path: decodedPath,
323
346
  filename: linkText || undefined,
324
347
  mimeType: undefined,
325
348
  });
@@ -54,6 +54,8 @@ describe("buildCheckinDescription", () => {
54
54
  expect(html).toContain(
55
55
  "https://www.vellum.ai/assistant/conversations/uuid-123?prompt=What%20would%20you%20recommend",
56
56
  );
57
+ // Carries onboarding attribution for the calendar-event CTA.
58
+ expect(html).toContain("&utm_source=onboarding&utm_medium=calendar_event");
57
59
  // Only sanitization-safe tags; the CTA is a bold link, not a styled button.
58
60
  expect(html).toContain("<a href=");
59
61
  expect(html).toContain("<strong>");
@@ -66,7 +66,7 @@ export function buildCheckinTitle({
66
66
  * (`uuid`) pre-seeded with the first-week prompt.
67
67
  */
68
68
  export function buildCheckinDescription(uuid: string): string {
69
- const href = `https://www.vellum.ai/assistant/conversations/${uuid}?prompt=${CTA_ENCODED_PROMPT}`;
69
+ const href = `https://www.vellum.ai/assistant/conversations/${uuid}?prompt=${CTA_ENCODED_PROMPT}&utm_source=onboarding&utm_medium=calendar_event`;
70
70
  return [
71
71
  "<p>👋 <strong>Hi, it was great to meet you properly.</strong></p>",
72
72
  "<p>You just set me up, and I've already started learning <strong>what you're working on</strong>. This 15 minutes is the natural place to put that to work. I'll walk you through one thing I'd like to do for you this week.</p>",
@@ -486,6 +486,74 @@ function readPlainObject(value: unknown): Record<string, unknown> | undefined {
486
486
  return value as Record<string, unknown>;
487
487
  }
488
488
 
489
+ function stripTransportHeadersRecursively(value: unknown): void {
490
+ if (Array.isArray(value)) {
491
+ for (const item of value) {
492
+ stripTransportHeadersRecursively(item);
493
+ }
494
+ return;
495
+ }
496
+
497
+ const object = readPlainObject(value);
498
+ if (!object) return;
499
+ const transport = readPlainObject(object.transport);
500
+ if (transport) delete transport.headers;
501
+ for (const child of Object.values(object)) {
502
+ stripTransportHeadersRecursively(child);
503
+ }
504
+ }
505
+
506
+ function containsTransportHeadersRecursively(value: unknown): boolean {
507
+ if (Array.isArray(value)) {
508
+ return value.some((item) => containsTransportHeadersRecursively(item));
509
+ }
510
+
511
+ const object = readPlainObject(value);
512
+ if (!object) return false;
513
+ const transport = readPlainObject(object.transport);
514
+ if (transport && Object.hasOwn(transport, "headers")) return true;
515
+ return Object.values(object).some((child) =>
516
+ containsTransportHeadersRecursively(child),
517
+ );
518
+ }
519
+
520
+ function sanitizeMcpTransportHeadersForSettingsRead(config: unknown): void {
521
+ const root = readPlainObject(config);
522
+ if (!root) return;
523
+ const mcp = readPlainObject(root.mcp);
524
+ if (!mcp || !Object.hasOwn(mcp, "servers")) return;
525
+ if (Array.isArray(mcp.servers)) {
526
+ stripTransportHeadersRecursively(mcp.servers);
527
+ return;
528
+ }
529
+ const servers = readPlainObject(mcp.servers);
530
+ if (!servers) return;
531
+ for (const server of Object.values(servers)) {
532
+ stripTransportHeadersRecursively(server);
533
+ }
534
+ }
535
+
536
+ function patchContainsMcpTransportHeaders(patch: unknown): boolean {
537
+ const root = readPlainObject(patch);
538
+ const mcp = readPlainObject(root?.mcp);
539
+ if (!mcp || !Object.hasOwn(mcp, "servers")) return false;
540
+ if (Array.isArray(mcp.servers)) {
541
+ return containsTransportHeadersRecursively(mcp.servers);
542
+ }
543
+ const servers = readPlainObject(mcp.servers);
544
+ if (!servers) return false;
545
+ return Object.values(servers).some((server) =>
546
+ containsTransportHeadersRecursively(server),
547
+ );
548
+ }
549
+
550
+ function rejectMcpTransportHeaderWrite(patch: unknown): void {
551
+ if (!patchContainsMcpTransportHeaders(patch)) return;
552
+ throw new BadRequestError(
553
+ "MCP authentication headers must be managed through MCP server add/update APIs, not generic config writes.",
554
+ );
555
+ }
556
+
489
557
  const WireProfileEntry = ProfileEntry.extend({
490
558
  supportsVision: z.boolean().optional(),
491
559
  })
@@ -688,6 +756,7 @@ const ConfigPatchRequestSchema = z
688
756
  function handleGetConfig() {
689
757
  try {
690
758
  const config = applyContextDefaultsToRawConfig(loadRawConfig());
759
+ sanitizeMcpTransportHeadersForSettingsRead(config);
691
760
  enrichProfilesWithVisionFlag(config);
692
761
  return config;
693
762
  } catch (err) {
@@ -840,6 +909,7 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
840
909
  throw new BadRequestError("Body must be a non-empty JSON object");
841
910
  }
842
911
  rejectManagedProfileDeletion(body as Record<string, unknown>);
912
+ rejectMcpTransportHeaderWrite(body);
843
913
 
844
914
  const raw = loadRawConfig();
845
915
  const patch = body as Record<string, unknown>;
@@ -848,6 +918,7 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
848
918
  await commitConfigWrite(raw, "patch");
849
919
 
850
920
  const merged = applyContextDefaultsToRawConfig(loadRawConfig());
921
+ sanitizeMcpTransportHeadersForSettingsRead(merged);
851
922
  enrichProfilesWithVisionFlag(merged);
852
923
  return merged;
853
924
  }
@@ -892,6 +963,7 @@ async function handleSetConfig({ body }: RouteHandlerArgs) {
892
963
  const patchShape: Record<string, unknown> = {};
893
964
  setNestedValue(patchShape, path, value);
894
965
  rejectManagedProfileDeletion(patchShape);
966
+ rejectMcpTransportHeaderWrite(patchShape);
895
967
 
896
968
  const raw = loadRawConfig();
897
969
  setNestedValue(raw, path, value);
@@ -135,7 +135,10 @@ import type {
135
135
  RuntimeMessagePayload,
136
136
  SendMessageDeps,
137
137
  } from "../http-types.js";
138
- import { findLocalGuardianPrincipalId } from "../local-actor-identity.js";
138
+ import {
139
+ findLocalGuardianPrincipalId,
140
+ resolveActorPrincipalIdForLocalGuardian,
141
+ } from "../local-actor-identity.js";
139
142
  import { resolveLocalPrincipalTrustContext } from "../local-principal-trust.js";
140
143
  import * as pendingInteractions from "../pending-interactions.js";
141
144
  import {
@@ -1509,10 +1512,13 @@ export async function handleSendMessage(
1509
1512
  }
1510
1513
 
1511
1514
  const isInteractive = isInteractiveInterface(sourceInterface);
1512
- // Use the JWT-verified requester principal not guardianPrincipalId,
1513
- // which is the workspace owner and would let a trusted contact's web
1514
- // turn match against the guardian's macOS client.
1515
- const sourceActorPrincipalId = actorPrincipalId ?? undefined;
1515
+ // Translate the dev-bypass actor principal to the real guardian principal
1516
+ // before the same-actor host-proxy gate so web/iOS turns match the macOS
1517
+ // client's SSE-registered principal. No-op for real JWT principals in
1518
+ // non-dev-bypass deployments.
1519
+ const sourceActorPrincipalId = await resolveActorPrincipalIdForLocalGuardian(
1520
+ actorPrincipalId ?? undefined,
1521
+ );
1516
1522
  // Bash/File/Transfer singletons are globally available via isAvailable() —
1517
1523
  // no per-conversation gating needed. CU is per-conversation (owns step
1518
1524
  // count, AX tree history, loop detection).