@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
package/Dockerfile CHANGED
@@ -14,7 +14,7 @@ RUN curl -fsSL https://bun.sh/install | bash -s "bun-v1.3.11"
14
14
  ENV PATH="/root/.bun/bin:${PATH}"
15
15
 
16
16
  # Copy shared packages first (needed for repo-local dependencies)
17
- COPY packages/ces-contracts ./packages/ces-contracts
17
+ COPY packages/service-contracts ./packages/service-contracts
18
18
  COPY packages/credential-storage ./packages/credential-storage
19
19
  COPY packages/egress-proxy ./packages/egress-proxy
20
20
 
package/bun.lock CHANGED
@@ -5,9 +5,9 @@
5
5
  "": {
6
6
  "name": "@vellumai/credential-executor",
7
7
  "dependencies": {
8
- "@vellumai/ces-contracts": "file:../packages/ces-contracts",
9
8
  "@vellumai/credential-storage": "file:../packages/credential-storage",
10
9
  "@vellumai/egress-proxy": "file:../packages/egress-proxy",
10
+ "@vellumai/service-contracts": "file:../packages/service-contracts",
11
11
  "pino": "9.14.0",
12
12
  "pino-pretty": "13.1.3",
13
13
  },
@@ -22,13 +22,15 @@
22
22
 
23
23
  "@types/bun": ["@types/bun@1.3.10", "", { "dependencies": { "bun-types": "1.3.10" } }, "sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ=="],
24
24
 
25
- "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
25
+ "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="],
26
26
 
27
- "@vellumai/ces-contracts": ["@vellumai/ces-contracts@file:../packages/ces-contracts", { "dependencies": { "zod": "^4.3.6" }, "devDependencies": { "@types/bun": "^1.2.4", "typescript": "^5.7.3" } }],
27
+ "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="],
28
28
 
29
- "@vellumai/credential-storage": ["@vellumai/credential-storage@file:../packages/credential-storage", { "devDependencies": { "@types/bun": "^1.2.4", "typescript": "^5.7.3" } }],
29
+ "@vellumai/credential-storage": ["@vellumai/credential-storage@file:../packages/credential-storage", { "devDependencies": { "@types/bun": "1.3.10", "typescript": "5.9.3" } }],
30
30
 
31
- "@vellumai/egress-proxy": ["@vellumai/egress-proxy@file:../packages/egress-proxy", { "devDependencies": { "@types/bun": "^1.2.4", "typescript": "^5.7.3" } }],
31
+ "@vellumai/egress-proxy": ["@vellumai/egress-proxy@file:../packages/egress-proxy", { "devDependencies": { "@types/bun": "1.3.10", "typescript": "5.9.3" } }],
32
+
33
+ "@vellumai/service-contracts": ["@vellumai/service-contracts@file:../packages/service-contracts", { "dependencies": { "zod": "4.3.6" }, "devDependencies": { "@types/bun": "1.2.4", "typescript": "5.7.3" } }],
32
34
 
33
35
  "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
34
36
 
@@ -40,7 +42,7 @@
40
42
 
41
43
  "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
42
44
 
43
- "fast-copy": ["fast-copy@4.0.2", "", {}, "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw=="],
45
+ "fast-copy": ["fast-copy@4.0.3", "", {}, "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw=="],
44
46
 
45
47
  "fast-safe-stringify": ["fast-safe-stringify@2.1.1", "", {}, "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="],
46
48
 
@@ -84,12 +86,18 @@
84
86
 
85
87
  "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
86
88
 
87
- "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
89
+ "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="],
88
90
 
89
91
  "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
90
92
 
91
93
  "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
92
94
 
95
+ "@vellumai/service-contracts/@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="],
96
+
97
+ "@vellumai/service-contracts/typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="],
98
+
93
99
  "pino-pretty/pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="],
100
+
101
+ "@vellumai/service-contracts/@types/bun/bun-types": ["bun-types@1.2.4", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-nDPymR207ZZEoWD4AavvEaa/KZe/qlrbMSchqpQwovPZCKc7pwMoENjEtHgMKaAjJhy+x6vfqSBA1QU3bJgs0Q=="],
94
102
  }
95
103
  }
@@ -3,8 +3,13 @@
3
3
  *
4
4
  * Ensures the package:
5
5
  * 1. Does NOT import from the assistant daemon or CES modules.
6
- * 2. Exposes only local storage/runtime abstractions.
7
- * 3. Remains portable and self-contained.
6
+ * 2. Does NOT import from x-client packages (@vellumai/assistant-client,
7
+ * @vellumai/ces-client, @vellumai/gateway-client).
8
+ * 3. Does NOT import from @vellumai/service-contracts runtime/internal modules
9
+ * (the aggregate root or any subpath beyond what credential-storage needs
10
+ * for pure type definitions is not required at all).
11
+ * 4. Exposes only local storage/runtime abstractions.
12
+ * 5. Remains portable and self-contained.
8
13
  */
9
14
 
10
15
  import { describe, expect, test } from "bun:test";
@@ -59,6 +64,21 @@ const FORBIDDEN_IMPORT_PATTERNS = [
59
64
  /from\s+["'].*\/tools\//,
60
65
  /from\s+["'].*\/oauth\/oauth-store/,
61
66
  /from\s+["'].*\/security\/secure-keys/,
67
+
68
+ // x-client packages (must not depend on any typed service client)
69
+ /from\s+["']@vellumai\/assistant-client(?:\/|["'])/,
70
+ /require\s*\(\s*["']@vellumai\/assistant-client(?:\/|["'])/,
71
+ /from\s+["']@vellumai\/ces-client(?:\/|["'])/,
72
+ /require\s*\(\s*["']@vellumai\/ces-client(?:\/|["'])/,
73
+ /from\s+["']@vellumai\/gateway-client(?:\/|["'])/,
74
+ /require\s*\(\s*["']@vellumai\/gateway-client(?:\/|["'])/,
75
+
76
+ // service-contracts aggregate root or internal subpaths
77
+ // credential-storage is a lower-layer package and must not depend on
78
+ // service-contracts (which sits at the same layer but deals with RPC
79
+ // protocol types, not storage abstractions).
80
+ /from\s+["']@vellumai\/service-contracts(?:\/|["'])/,
81
+ /require\s*\(\s*["']@vellumai\/service-contracts(?:\/|["'])/,
62
82
  ];
63
83
 
64
84
  describe("package boundary", () => {
@@ -68,7 +88,7 @@ describe("package boundary", () => {
68
88
  expect(sourceFiles.length).toBeGreaterThan(0);
69
89
  });
70
90
 
71
- test("does not import from assistant or CES modules", () => {
91
+ test("does not import from assistant, CES, x-client, or service-contracts modules", () => {
72
92
  const violations: string[] = [];
73
93
 
74
94
  for (const file of sourceFiles) {
@@ -101,7 +121,7 @@ describe("package boundary", () => {
101
121
  expect(pkg.private).toBe(true);
102
122
  });
103
123
 
104
- test("package.json does not depend on assistant or CES packages", () => {
124
+ test("package.json does not depend on assistant, CES, x-client, or service-contracts packages", () => {
105
125
  const pkg = JSON.parse(
106
126
  readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8"),
107
127
  );
@@ -113,8 +133,14 @@ describe("package boundary", () => {
113
133
  const forbidden = Object.keys(allDeps).filter(
114
134
  (dep) =>
115
135
  dep.includes("assistant") ||
116
- dep.includes("ces") ||
117
- dep.includes("daemon"),
136
+ dep.includes("daemon") ||
137
+ [
138
+ "@vellumai/ces-client",
139
+ "@vellumai/ces-contracts",
140
+ "@vellumai/service-contracts",
141
+ "@vellumai/assistant-client",
142
+ "@vellumai/gateway-client",
143
+ ].includes(dep),
118
144
  );
119
145
  expect(forbidden).toEqual([]);
120
146
  });
@@ -4,6 +4,13 @@
4
4
  * These tests ensure the egress-proxy package remains isolated from
5
5
  * assistant runtime and CES server implementation modules. If a direct
6
6
  * import of those modules is introduced, these tests will fail.
7
+ *
8
+ * Tightened boundaries (added):
9
+ * - Does NOT import from x-client packages (@vellumai/assistant-client,
10
+ * @vellumai/ces-client, @vellumai/gateway-client).
11
+ * - Does NOT import from @vellumai/service-contracts (the egress-proxy
12
+ * package deals only with proxy session lifecycle and must not depend on
13
+ * CES wire-protocol types).
7
14
  */
8
15
 
9
16
  import { describe, expect, test } from "bun:test";
@@ -66,6 +73,25 @@ const FORBIDDEN_PATTERNS = [
66
73
  /require\s*\(.*\/gateway\/src\//,
67
74
  /import\s+['"].*\/gateway\/src\//,
68
75
  /import\s+['"]@vellumai\/vellum-gateway/,
76
+
77
+ // x-client packages (must not depend on any typed service client)
78
+ /from\s+['"]@vellumai\/assistant-client(?:\/|['"])/,
79
+ /import\s+['"]@vellumai\/assistant-client(?:\/|['"])/,
80
+ /require\s*\(['"]@vellumai\/assistant-client(?:\/|['"])/,
81
+ /from\s+['"]@vellumai\/ces-client(?:\/|['"])/,
82
+ /import\s+['"]@vellumai\/ces-client(?:\/|['"])/,
83
+ /require\s*\(['"]@vellumai\/ces-client(?:\/|['"])/,
84
+ /from\s+['"]@vellumai\/gateway-client(?:\/|['"])/,
85
+ /import\s+['"]@vellumai\/gateway-client(?:\/|['"])/,
86
+ /require\s*\(['"]@vellumai\/gateway-client(?:\/|['"])/,
87
+
88
+ // service-contracts (RPC protocol types — egress-proxy must not depend on CES wire types)
89
+ /from\s+['"]@vellumai\/service-contracts(?:\/|['"])/,
90
+ /import\s+['"]@vellumai\/service-contracts(?:\/|['"])/,
91
+ /require\s*\(['"]@vellumai\/service-contracts(?:\/|['"])/,
92
+ /from\s+['"]@vellumai\/ces-contracts(?:\/|['"])/,
93
+ /import\s+['"]@vellumai\/ces-contracts(?:\/|['"])/,
94
+ /require\s*\(['"]@vellumai\/ces-contracts(?:\/|['"])/,
69
95
  ];
70
96
 
71
97
  describe("package boundary", () => {
@@ -94,7 +120,7 @@ describe("package boundary", () => {
94
120
  }
95
121
  });
96
122
 
97
- test("package.json has no dependencies on assistant, CES, or gateway", async () => {
123
+ test("package.json has no dependencies on assistant, CES, gateway, x-client, or service-contracts", async () => {
98
124
  const pkgJsonPath = resolve(PKG_SRC, "..", "package.json");
99
125
  const pkgJson = JSON.parse(await Bun.file(pkgJsonPath).text());
100
126
 
@@ -109,6 +135,11 @@ describe("package boundary", () => {
109
135
  "@vellumai/assistant",
110
136
  "@vellumai/ces",
111
137
  "@vellumai/vellum-gateway",
138
+ "@vellumai/assistant-client",
139
+ "@vellumai/ces-client",
140
+ "@vellumai/ces-contracts",
141
+ "@vellumai/gateway-client",
142
+ "@vellumai/service-contracts",
112
143
  ];
113
144
 
114
145
  for (const dep of forbidden) {
@@ -3,7 +3,7 @@
3
3
  "configVersion": 1,
4
4
  "workspaces": {
5
5
  "": {
6
- "name": "@vellumai/ces-contracts",
6
+ "name": "@vellumai/service-contracts",
7
7
  "dependencies": {
8
8
  "zod": "4.3.6",
9
9
  },
@@ -1,16 +1,18 @@
1
1
  {
2
- "name": "@vellumai/ces-contracts",
2
+ "name": "@vellumai/service-contracts",
3
3
  "version": "0.0.1",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "exports": {
8
8
  ".": "./src/index.ts",
9
+ "./credential-rpc": "./src/credential-rpc.ts",
10
+ "./trust-rules": "./src/trust-rules.ts",
9
11
  "./handles": "./src/handles.ts",
10
12
  "./grants": "./src/grants.ts",
11
13
  "./rpc": "./src/rpc.ts",
12
14
  "./rendering": "./src/rendering.ts",
13
- "./trust-rules": "./src/trust-rules.ts"
15
+ "./error": "./src/error.ts"
14
16
  },
15
17
  "scripts": {
16
18
  "typecheck": "bunx tsc --noEmit",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Tests for @vellumai/ces-contracts
2
+ * Tests for @vellumai/service-contracts
3
3
  *
4
4
  * These tests verify:
5
5
  * 1. The package can be consumed independently (no assistant/ or CES imports).
@@ -30,6 +30,10 @@ describe("package independence", () => {
30
30
  "../grants.ts",
31
31
  "../rpc.ts",
32
32
  "../rendering.ts",
33
+ "../transport.ts",
34
+ "../credential-rpc.ts",
35
+ "../trust-rules.ts",
36
+ "../error.ts",
33
37
  ];
34
38
 
35
39
  for (const file of sourceFiles) {
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Package boundary tests for @vellumai/service-contracts.
3
+ *
4
+ * Ensures the package:
5
+ * 1. Does NOT import from assistant, gateway, credential-executor, or other
6
+ * service runtime modules.
7
+ * 2. Does NOT import from runtime shared packages that sit above it in the
8
+ * dependency hierarchy (@vellumai/credential-storage, @vellumai/egress-proxy,
9
+ * @vellumai/skill-host-contracts).
10
+ * 3. Does NOT import from x-client packages (@vellumai/assistant-client,
11
+ * @vellumai/ces-client, @vellumai/gateway-client).
12
+ * 4. Remains a pure schema/type package — no runtime dependencies beyond zod.
13
+ *
14
+ * @vellumai/service-contracts is the lowest layer of the shared packages
15
+ * hierarchy and must not depend on any higher-layer package.
16
+ */
17
+
18
+ import { describe, expect, test } from "bun:test";
19
+ import { readFileSync, readdirSync, statSync } from "node:fs";
20
+ import { join, resolve } from "node:path";
21
+
22
+ const PACKAGE_ROOT = resolve(import.meta.dirname, "../..");
23
+ const SRC_DIR = join(PACKAGE_ROOT, "src");
24
+
25
+ /**
26
+ * Recursively collect all .ts source files, excluding test and declaration files.
27
+ */
28
+ function collectSourceFiles(dir: string): string[] {
29
+ const files: string[] = [];
30
+ for (const entry of readdirSync(dir)) {
31
+ const full = join(dir, entry);
32
+ const stat = statSync(full);
33
+ if (stat.isDirectory()) {
34
+ if (entry === "node_modules" || entry === "__tests__") continue;
35
+ files.push(...collectSourceFiles(full));
36
+ } else if (
37
+ entry.endsWith(".ts") &&
38
+ !entry.endsWith(".test.ts") &&
39
+ !entry.endsWith(".d.ts")
40
+ ) {
41
+ files.push(full);
42
+ }
43
+ }
44
+ return files;
45
+ }
46
+
47
+ /**
48
+ * Patterns that must NOT appear in any import/require statement within
49
+ * source files. These catch imports from higher-layer packages that would
50
+ * create a forbidden upward dependency.
51
+ */
52
+ const FORBIDDEN_IMPORT_PATTERNS = [
53
+ // Assistant runtime
54
+ /from\s+["'](?:\.\.\/)*assistant(?:\/|["'])/,
55
+ /require\s*\(\s*["'](?:\.\.\/)*assistant(?:\/|["'])/,
56
+ /from\s+["']@vellumai\/assistant(?:\/|["'])/,
57
+ /require\s*\(\s*["']@vellumai\/assistant(?:\/|["'])/,
58
+
59
+ // Gateway
60
+ /from\s+["'](?:\.\.\/)*gateway(?:\/|["'])/,
61
+ /require\s*\(\s*["'](?:\.\.\/)*gateway(?:\/|["'])/,
62
+ /from\s+["']@vellumai\/(?:vellum-)?gateway(?:\/|["'])/,
63
+ /require\s*\(\s*["']@vellumai\/(?:vellum-)?gateway(?:\/|["'])/,
64
+
65
+ // Credential executor
66
+ /from\s+["'](?:\.\.\/)*credential-executor(?:\/|["'])/,
67
+ /require\s*\(\s*["'](?:\.\.\/)*credential-executor(?:\/|["'])/,
68
+ /from\s+["']@vellumai\/credential-executor(?:\/|["'])/,
69
+ /require\s*\(\s*["']@vellumai\/credential-executor(?:\/|["'])/,
70
+
71
+ // Runtime shared packages (higher layer)
72
+ /from\s+["']@vellumai\/credential-storage(?:\/|["'])/,
73
+ /require\s*\(\s*["']@vellumai\/credential-storage(?:\/|["'])/,
74
+ /from\s+["']@vellumai\/egress-proxy(?:\/|["'])/,
75
+ /require\s*\(\s*["']@vellumai\/egress-proxy(?:\/|["'])/,
76
+ /from\s+["']@vellumai\/skill-host-contracts(?:\/|["'])/,
77
+ /require\s*\(\s*["']@vellumai\/skill-host-contracts(?:\/|["'])/,
78
+
79
+ // x-client packages (higher layer)
80
+ /from\s+["']@vellumai\/assistant-client(?:\/|["'])/,
81
+ /require\s*\(\s*["']@vellumai\/assistant-client(?:\/|["'])/,
82
+ /from\s+["']@vellumai\/ces-client(?:\/|["'])/,
83
+ /require\s*\(\s*["']@vellumai\/ces-client(?:\/|["'])/,
84
+ /from\s+["']@vellumai\/gateway-client(?:\/|["'])/,
85
+ /require\s*\(\s*["']@vellumai\/gateway-client(?:\/|["'])/,
86
+ ];
87
+
88
+ describe("package boundary", () => {
89
+ const sourceFiles = collectSourceFiles(SRC_DIR);
90
+
91
+ test("has source files to validate", () => {
92
+ expect(sourceFiles.length).toBeGreaterThan(0);
93
+ });
94
+
95
+ test("does not import from runtime or higher-layer packages", () => {
96
+ const violations: string[] = [];
97
+
98
+ for (const file of sourceFiles) {
99
+ const content = readFileSync(file, "utf-8");
100
+ const lines = content.split("\n");
101
+
102
+ for (let i = 0; i < lines.length; i++) {
103
+ const line = lines[i];
104
+ for (const pattern of FORBIDDEN_IMPORT_PATTERNS) {
105
+ if (pattern.test(line)) {
106
+ const relative = file.replace(PACKAGE_ROOT + "/", "");
107
+ violations.push(`${relative}:${i + 1}: ${line.trim()}`);
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ if (violations.length > 0) {
114
+ throw new Error(
115
+ `Found ${violations.length} forbidden import(s) in service-contracts package:\n` +
116
+ violations.map((v) => ` - ${v}`).join("\n") +
117
+ "\n\n" +
118
+ "@vellumai/service-contracts is a pure schema/type package and must not\n" +
119
+ "import from runtime services or higher-layer packages.",
120
+ );
121
+ }
122
+ });
123
+
124
+ test("package.json declares it as private", () => {
125
+ const pkg = JSON.parse(
126
+ readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8"),
127
+ );
128
+ expect(pkg.private).toBe(true);
129
+ });
130
+
131
+ test("package.json does not depend on runtime or higher-layer packages", () => {
132
+ const pkg = JSON.parse(
133
+ readFileSync(join(PACKAGE_ROOT, "package.json"), "utf-8"),
134
+ );
135
+ const allDeps = {
136
+ ...pkg.dependencies,
137
+ ...pkg.devDependencies,
138
+ ...pkg.peerDependencies,
139
+ };
140
+
141
+ const forbidden = Object.keys(allDeps).filter((dep) =>
142
+ [
143
+ "@vellumai/assistant",
144
+ "@vellumai/credential-storage",
145
+ "@vellumai/egress-proxy",
146
+ "@vellumai/skill-host-contracts",
147
+ "@vellumai/assistant-client",
148
+ "@vellumai/ces-client",
149
+ "@vellumai/gateway-client",
150
+ ].includes(dep),
151
+ );
152
+
153
+ expect(forbidden).toEqual([]);
154
+ });
155
+ });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @vellumai/service-contracts/credential-rpc
3
+ *
4
+ * Domain entrypoint for the CES (Credential Execution Service) transport and
5
+ * RPC surface. Re-exports the transport/RPC/handles/grants/rendering/error
6
+ * contracts without the trust-rule helpers.
7
+ *
8
+ * Prefer this subpath over the root `.` import when you only need the
9
+ * credential RPC surface:
10
+ *
11
+ * import { CesRpcMethod, MakeAuthenticatedRequestSchema } from "@vellumai/service-contracts/credential-rpc";
12
+ *
13
+ * For trust-rule types use the dedicated subpath:
14
+ *
15
+ * import { TrustRule, parseTrustRule } from "@vellumai/service-contracts/trust-rules";
16
+ */
17
+
18
+ export * from "./transport.js";
19
+ export * from "./error.js";
20
+ export * from "./handles.js";
21
+ export * from "./grants.js";
22
+ export * from "./rendering.js";
23
+ export * from "./rpc.js";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @vellumai/service-contracts — aggregate export (compat entry point)
3
+ *
4
+ * This is a compatibility aggregate that re-exports everything from all
5
+ * submodules. Prefer the explicit domain subpaths for new code:
6
+ *
7
+ * - `@vellumai/service-contracts/credential-rpc` — transport, RPC, handles, grants, rendering, error
8
+ * - `@vellumai/service-contracts/trust-rules` — trust-rule types and parsing helpers
9
+ *
10
+ * Fine-grained subpaths are also available for low-friction migration:
11
+ * `./rpc`, `./handles`, `./grants`, `./rendering`, `./error`, `./trust-rules`
12
+ *
13
+ * Neutral wire-protocol contracts for communication between the assistant
14
+ * daemon and the Credential Execution Service (CES). This package is
15
+ * intentionally free of imports from `assistant/` or any CES implementation
16
+ * module so that both sides can depend on it without circular references.
17
+ */
18
+
19
+ export * from "./transport.js";
20
+ export * from "./error.js";
21
+ export * from "./handles.js";
22
+ export * from "./grants.js";
23
+ export * from "./rpc.js";
24
+ export * from "./rendering.js";
25
+ export * from "./trust-rules.js";
@@ -1,10 +1,9 @@
1
1
  /**
2
- * @vellumai/ces-contracts
2
+ * Transport-level handshake and envelope schemas.
3
3
  *
4
- * Neutral wire-protocol contracts for communication between the assistant
5
- * daemon and the Credential Execution Service (CES). This package is
6
- * intentionally free of imports from `assistant/` or any CES implementation
7
- * module so that both sides can depend on it without circular references.
4
+ * Extracted from the aggregate index to allow credential-rpc.ts to import
5
+ * the full transport surface without creating a circular dependency through
6
+ * index.ts.
8
7
  */
9
8
 
10
9
  import { z } from "zod";
@@ -35,9 +34,8 @@ export const HandshakeRequestSchema = z.object({
35
34
  assistantApiKey: z.string().optional(),
36
35
  /**
37
36
  * Optional platform assistant ID passed from the assistant runtime.
38
- * In managed (sidecar) mode with warm pools, the PLATFORM_ASSISTANT_ID
39
- * env var may not be set at CES startup. The assistant forwards it here
40
- * so CES can use it for platform credential materialisation.
37
+ * The assistant ID is not available at CES startup (warm-pool pods),
38
+ * so the assistant forwards it here for platform credential materialisation.
41
39
  */
42
40
  assistantId: z.string().optional(),
43
41
  });
@@ -75,16 +73,6 @@ export const RpcEnvelopeSchema = z.object({
75
73
  });
76
74
  export type RpcEnvelope = z.infer<typeof RpcEnvelopeSchema>;
77
75
 
78
- // ---------------------------------------------------------------------------
79
- // RPC error (defined in error.ts to avoid circular deps with rpc.ts)
80
- // ---------------------------------------------------------------------------
81
-
82
- export {
83
- RpcErrorSchema,
84
- type RpcError,
85
- MANAGED_LOCAL_STATIC_REJECTION_ERROR,
86
- } from "./error.js";
87
-
88
76
  // ---------------------------------------------------------------------------
89
77
  // Tool request / response base shapes
90
78
  // ---------------------------------------------------------------------------
@@ -143,13 +131,3 @@ export const TransportMessageSchema = z.discriminatedUnion("type", [
143
131
  RpcEnvelopeSchema.extend({ type: z.literal("rpc") }),
144
132
  ]);
145
133
  export type TransportMessage = z.infer<typeof TransportMessageSchema>;
146
-
147
- // ---------------------------------------------------------------------------
148
- // Re-exports from sub-modules
149
- // ---------------------------------------------------------------------------
150
-
151
- export * from "./handles.js";
152
- export * from "./grants.js";
153
- export * from "./rpc.js";
154
- export * from "./rendering.js";
155
- export * from "./trust-rules.js";
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Trust rule types shared between the assistant daemon and the gateway.
3
+ *
4
+ * These are extracted from `assistant/src/permissions/types.ts` and
5
+ * `assistant/src/permissions/trust-store.ts` so that both packages can
6
+ * reference a single canonical definition.
7
+ *
8
+ * Tools are grouped into "families" based on how their permission candidates
9
+ * are constructed and matched:
10
+ *
11
+ * - **Scoped**: tools whose candidates include a filesystem path and obey
12
+ * directory-boundary scope constraints (`file_read`, `file_write`,
13
+ * `file_edit`, `host_file_read`, `host_file_write`, `host_file_edit`,
14
+ * `host_file_transfer`, `bash`, `host_bash`).
15
+ * - **URL**: tools whose candidates include a URL (`web_fetch`,
16
+ * `network_request`).
17
+ * - **Managed skill**: tools that manage first-party skill packages
18
+ * (`scaffold_managed_skill`, `delete_managed_skill`).
19
+ * - **Skill load**: the `skill_load` tool, which uses a distinct candidate
20
+ * namespace (`skill_load:selector` or `skill_load_dynamic:selector`).
21
+ * - **Generic**: everything else (computer-use tools, UI surface tools,
22
+ * recall, skill_execute, etc.).
23
+ */
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Trust decision
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /** The possible decisions a trust rule can make. */
30
+ export type TrustDecision = "allow" | "deny" | "ask";
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Tool family constants
34
+ // ---------------------------------------------------------------------------
35
+
36
+ /**
37
+ * Tools whose permission candidates are scoped to a filesystem path and obey
38
+ * directory-boundary scope constraints.
39
+ */
40
+ export const SCOPED_TOOLS = [
41
+ "file_read",
42
+ "file_write",
43
+ "file_edit",
44
+ "host_file_read",
45
+ "host_file_write",
46
+ "host_file_edit",
47
+ "host_file_transfer",
48
+ "bash",
49
+ "host_bash",
50
+ ] as const;
51
+
52
+ /**
53
+ * Tools whose permission candidates include a URL.
54
+ */
55
+ export const URL_TOOLS = ["web_fetch", "network_request"] as const;
56
+
57
+ /**
58
+ * Tools that manage first-party skill packages (scaffold/delete).
59
+ */
60
+ export const MANAGED_SKILL_TOOLS = [
61
+ "scaffold_managed_skill",
62
+ "delete_managed_skill",
63
+ ] as const;
64
+
65
+ /**
66
+ * The skill_load tool name. Separated from the array constants because
67
+ * skill_load is a singleton, not a family with multiple members.
68
+ */
69
+ export const SKILL_LOAD_TOOL = "skill_load" as const;
70
+
71
+ /** Set for O(1) lookups when classifying tool names. */
72
+ const SCOPED_TOOLS_SET: ReadonlySet<string> = new Set(SCOPED_TOOLS);
73
+ const URL_TOOLS_SET: ReadonlySet<string> = new Set(URL_TOOLS);
74
+ const MANAGED_SKILL_TOOLS_SET: ReadonlySet<string> = new Set(
75
+ MANAGED_SKILL_TOOLS,
76
+ );
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Type guards
80
+ // ---------------------------------------------------------------------------
81
+
82
+ /** Returns true when the rule's tool belongs to the scoped-tool family. */
83
+ export function isScopedRule(rule: { tool: string }): boolean {
84
+ return SCOPED_TOOLS_SET.has(rule.tool);
85
+ }
86
+
87
+ /** Returns true when the rule's tool belongs to the URL-tool family. */
88
+ export function isUrlRule(rule: { tool: string }): boolean {
89
+ return URL_TOOLS_SET.has(rule.tool);
90
+ }
91
+
92
+ /** Returns true when the rule's tool belongs to the managed-skill-tool family. */
93
+ export function isManagedSkillRule(rule: { tool: string }): boolean {
94
+ return MANAGED_SKILL_TOOLS_SET.has(rule.tool);
95
+ }
96
+
97
+ /** Returns true when the rule's tool is the skill_load tool. */
98
+ export function isSkillLoadRule(rule: { tool: string }): boolean {
99
+ return rule.tool === SKILL_LOAD_TOOL;
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Scope helper
104
+ // ---------------------------------------------------------------------------
105
+
106
+ /**
107
+ * Return the effective scope for any trust rule. Only scoped rules carry a
108
+ * `scope` field; all other rule families return `"everywhere"`.
109
+ */
110
+ export function ruleScope(rule: { tool: string; scope?: string }): string {
111
+ if (isScopedRule(rule)) {
112
+ return rule.scope ?? "everywhere";
113
+ }
114
+ return "everywhere";
115
+ }
116
+