@kya-os/mcp-i-core 1.3.12 → 1.3.14

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 (254) hide show
  1. package/dist/config/remote-config.js +9 -12
  2. package/dist/runtime/base.js +11 -0
  3. package/dist/services/access-control.service.js +5 -0
  4. package/dist/services/tool-protection.service.js +17 -8
  5. package/package.json +2 -2
  6. package/.turbo/turbo-build.log +0 -4
  7. package/.turbo/turbo-test$colon$coverage.log +0 -4586
  8. package/.turbo/turbo-test.log +0 -3169
  9. package/COMPLIANCE_IMPROVEMENT_REPORT.md +0 -483
  10. package/Composer 3.md +0 -615
  11. package/GPT-5.md +0 -1169
  12. package/OPUS-plan.md +0 -352
  13. package/PHASE_3_AND_4.1_SUMMARY.md +0 -585
  14. package/PHASE_3_SUMMARY.md +0 -317
  15. package/PHASE_4.1.3_SUMMARY.md +0 -428
  16. package/PHASE_4.1_COMPLETE.md +0 -525
  17. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +0 -1240
  18. package/SCHEMA_COMPLIANCE_REPORT.md +0 -275
  19. package/TEST_PLAN.md +0 -571
  20. package/coverage/coverage-final.json +0 -60
  21. package/dist/cache/oauth-config-cache.d.ts.map +0 -1
  22. package/dist/cache/oauth-config-cache.js.map +0 -1
  23. package/dist/cache/tool-protection-cache.d.ts.map +0 -1
  24. package/dist/cache/tool-protection-cache.js.map +0 -1
  25. package/dist/compliance/index.d.ts.map +0 -1
  26. package/dist/compliance/index.js.map +0 -1
  27. package/dist/compliance/schema-registry.d.ts.map +0 -1
  28. package/dist/compliance/schema-registry.js.map +0 -1
  29. package/dist/compliance/schema-verifier.d.ts.map +0 -1
  30. package/dist/compliance/schema-verifier.js.map +0 -1
  31. package/dist/config/remote-config.d.ts.map +0 -1
  32. package/dist/config/remote-config.js.map +0 -1
  33. package/dist/config.d.ts.map +0 -1
  34. package/dist/config.js.map +0 -1
  35. package/dist/delegation/audience-validator.d.ts.map +0 -1
  36. package/dist/delegation/audience-validator.js.map +0 -1
  37. package/dist/delegation/bitstring.d.ts.map +0 -1
  38. package/dist/delegation/bitstring.js.map +0 -1
  39. package/dist/delegation/cascading-revocation.d.ts.map +0 -1
  40. package/dist/delegation/cascading-revocation.js.map +0 -1
  41. package/dist/delegation/delegation-graph.d.ts.map +0 -1
  42. package/dist/delegation/delegation-graph.js.map +0 -1
  43. package/dist/delegation/did-key-resolver.d.ts.map +0 -1
  44. package/dist/delegation/did-key-resolver.js.map +0 -1
  45. package/dist/delegation/index.d.ts.map +0 -1
  46. package/dist/delegation/index.js.map +0 -1
  47. package/dist/delegation/statuslist-manager.d.ts.map +0 -1
  48. package/dist/delegation/statuslist-manager.js.map +0 -1
  49. package/dist/delegation/storage/index.d.ts.map +0 -1
  50. package/dist/delegation/storage/index.js.map +0 -1
  51. package/dist/delegation/storage/memory-graph-storage.d.ts.map +0 -1
  52. package/dist/delegation/storage/memory-graph-storage.js.map +0 -1
  53. package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +0 -1
  54. package/dist/delegation/storage/memory-statuslist-storage.js.map +0 -1
  55. package/dist/delegation/utils.d.ts.map +0 -1
  56. package/dist/delegation/utils.js.map +0 -1
  57. package/dist/delegation/vc-issuer.d.ts.map +0 -1
  58. package/dist/delegation/vc-issuer.js.map +0 -1
  59. package/dist/delegation/vc-verifier.d.ts.map +0 -1
  60. package/dist/delegation/vc-verifier.js.map +0 -1
  61. package/dist/identity/idp-token-resolver.d.ts.map +0 -1
  62. package/dist/identity/idp-token-resolver.js.map +0 -1
  63. package/dist/identity/idp-token-storage.interface.d.ts.map +0 -1
  64. package/dist/identity/idp-token-storage.interface.js.map +0 -1
  65. package/dist/identity/user-did-manager.d.ts.map +0 -1
  66. package/dist/identity/user-did-manager.js.map +0 -1
  67. package/dist/index.d.ts.map +0 -1
  68. package/dist/index.js.map +0 -1
  69. package/dist/providers/base.d.ts.map +0 -1
  70. package/dist/providers/base.js.map +0 -1
  71. package/dist/providers/memory.d.ts.map +0 -1
  72. package/dist/providers/memory.js.map +0 -1
  73. package/dist/runtime/audit-logger.d.ts.map +0 -1
  74. package/dist/runtime/audit-logger.js.map +0 -1
  75. package/dist/runtime/base.d.ts.map +0 -1
  76. package/dist/runtime/base.js.map +0 -1
  77. package/dist/services/access-control.service.d.ts.map +0 -1
  78. package/dist/services/access-control.service.js.map +0 -1
  79. package/dist/services/authorization/authorization-registry.d.ts.map +0 -1
  80. package/dist/services/authorization/authorization-registry.js.map +0 -1
  81. package/dist/services/authorization/types.d.ts.map +0 -1
  82. package/dist/services/authorization/types.js.map +0 -1
  83. package/dist/services/batch-delegation.service.d.ts.map +0 -1
  84. package/dist/services/batch-delegation.service.js.map +0 -1
  85. package/dist/services/crypto.service.d.ts.map +0 -1
  86. package/dist/services/crypto.service.js.map +0 -1
  87. package/dist/services/errors.d.ts.map +0 -1
  88. package/dist/services/errors.js.map +0 -1
  89. package/dist/services/index.d.ts.map +0 -1
  90. package/dist/services/index.js.map +0 -1
  91. package/dist/services/oauth-config.service.d.ts.map +0 -1
  92. package/dist/services/oauth-config.service.js.map +0 -1
  93. package/dist/services/oauth-provider-registry.d.ts.map +0 -1
  94. package/dist/services/oauth-provider-registry.js.map +0 -1
  95. package/dist/services/oauth-service.d.ts.map +0 -1
  96. package/dist/services/oauth-service.js.map +0 -1
  97. package/dist/services/oauth-token-retrieval.service.d.ts.map +0 -1
  98. package/dist/services/oauth-token-retrieval.service.js.map +0 -1
  99. package/dist/services/proof-verifier.d.ts.map +0 -1
  100. package/dist/services/proof-verifier.js.map +0 -1
  101. package/dist/services/provider-resolver.d.ts.map +0 -1
  102. package/dist/services/provider-resolver.js.map +0 -1
  103. package/dist/services/provider-validator.d.ts.map +0 -1
  104. package/dist/services/provider-validator.js.map +0 -1
  105. package/dist/services/session-registration.service.d.ts.map +0 -1
  106. package/dist/services/session-registration.service.js.map +0 -1
  107. package/dist/services/storage.service.d.ts.map +0 -1
  108. package/dist/services/storage.service.js.map +0 -1
  109. package/dist/services/tool-context-builder.d.ts.map +0 -1
  110. package/dist/services/tool-context-builder.js.map +0 -1
  111. package/dist/services/tool-protection.service.d.ts.map +0 -1
  112. package/dist/services/tool-protection.service.js.map +0 -1
  113. package/dist/types/oauth-required-error.d.ts.map +0 -1
  114. package/dist/types/oauth-required-error.js.map +0 -1
  115. package/dist/types/tool-protection.d.ts.map +0 -1
  116. package/dist/types/tool-protection.js.map +0 -1
  117. package/dist/utils/base58.d.ts.map +0 -1
  118. package/dist/utils/base58.js.map +0 -1
  119. package/dist/utils/base64.d.ts.map +0 -1
  120. package/dist/utils/base64.js.map +0 -1
  121. package/dist/utils/cors.d.ts.map +0 -1
  122. package/dist/utils/cors.js.map +0 -1
  123. package/dist/utils/did-helpers.d.ts.map +0 -1
  124. package/dist/utils/did-helpers.js.map +0 -1
  125. package/dist/utils/index.d.ts.map +0 -1
  126. package/dist/utils/index.js.map +0 -1
  127. package/dist/utils/storage-keys.d.ts.map +0 -1
  128. package/dist/utils/storage-keys.js.map +0 -1
  129. package/docs/API_REFERENCE.md +0 -1362
  130. package/docs/COMPLIANCE_MATRIX.md +0 -691
  131. package/docs/STATUSLIST2021_GUIDE.md +0 -696
  132. package/docs/W3C_VC_DELEGATION_GUIDE.md +0 -710
  133. package/src/__tests__/cache/tool-protection-cache.test.ts +0 -640
  134. package/src/__tests__/config/provider-runtime-config.test.ts +0 -309
  135. package/src/__tests__/delegation-e2e.test.ts +0 -690
  136. package/src/__tests__/identity/user-did-manager.test.ts +0 -232
  137. package/src/__tests__/index.test.ts +0 -56
  138. package/src/__tests__/integration/full-flow.test.ts +0 -789
  139. package/src/__tests__/integration.test.ts +0 -281
  140. package/src/__tests__/providers/base.test.ts +0 -173
  141. package/src/__tests__/providers/memory.test.ts +0 -319
  142. package/src/__tests__/regression/phase2-regression.test.ts +0 -429
  143. package/src/__tests__/runtime/audit-logger.test.ts +0 -154
  144. package/src/__tests__/runtime/base-extensions.test.ts +0 -595
  145. package/src/__tests__/runtime/base.test.ts +0 -869
  146. package/src/__tests__/runtime/delegation-flow.test.ts +0 -164
  147. package/src/__tests__/runtime/proof-client-did.test.ts +0 -376
  148. package/src/__tests__/runtime/route-interception.test.ts +0 -686
  149. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +0 -908
  150. package/src/__tests__/services/agentshield-integration.test.ts +0 -791
  151. package/src/__tests__/services/cache-busting.test.ts +0 -125
  152. package/src/__tests__/services/oauth-service-pkce.test.ts +0 -556
  153. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +0 -591
  154. package/src/__tests__/services/tool-protection-merged-config.test.ts +0 -485
  155. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +0 -480
  156. package/src/__tests__/services/tool-protection.service.test.ts +0 -1373
  157. package/src/__tests__/utils/mock-providers.ts +0 -340
  158. package/src/cache/oauth-config-cache.d.ts +0 -69
  159. package/src/cache/oauth-config-cache.d.ts.map +0 -1
  160. package/src/cache/oauth-config-cache.js.map +0 -1
  161. package/src/cache/oauth-config-cache.ts +0 -123
  162. package/src/cache/tool-protection-cache.ts +0 -171
  163. package/src/compliance/EXAMPLE.md +0 -412
  164. package/src/compliance/__tests__/schema-verifier.test.ts +0 -797
  165. package/src/compliance/index.ts +0 -8
  166. package/src/compliance/schema-registry.ts +0 -460
  167. package/src/compliance/schema-verifier.ts +0 -708
  168. package/src/config/__tests__/merged-config.spec.ts +0 -445
  169. package/src/config/__tests__/remote-config.spec.ts +0 -268
  170. package/src/config/remote-config.ts +0 -264
  171. package/src/config.ts +0 -312
  172. package/src/delegation/__tests__/audience-validator.test.ts +0 -112
  173. package/src/delegation/__tests__/bitstring.test.ts +0 -346
  174. package/src/delegation/__tests__/cascading-revocation.test.ts +0 -628
  175. package/src/delegation/__tests__/delegation-graph.test.ts +0 -584
  176. package/src/delegation/__tests__/did-key-resolver.test.ts +0 -265
  177. package/src/delegation/__tests__/utils.test.ts +0 -152
  178. package/src/delegation/__tests__/vc-issuer.test.ts +0 -442
  179. package/src/delegation/__tests__/vc-verifier.test.ts +0 -922
  180. package/src/delegation/audience-validator.ts +0 -52
  181. package/src/delegation/bitstring.ts +0 -278
  182. package/src/delegation/cascading-revocation.ts +0 -370
  183. package/src/delegation/delegation-graph.ts +0 -299
  184. package/src/delegation/did-key-resolver.ts +0 -179
  185. package/src/delegation/index.ts +0 -14
  186. package/src/delegation/statuslist-manager.ts +0 -353
  187. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +0 -366
  188. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +0 -228
  189. package/src/delegation/storage/index.ts +0 -9
  190. package/src/delegation/storage/memory-graph-storage.ts +0 -178
  191. package/src/delegation/storage/memory-statuslist-storage.ts +0 -77
  192. package/src/delegation/utils.ts +0 -221
  193. package/src/delegation/vc-issuer.ts +0 -232
  194. package/src/delegation/vc-verifier.ts +0 -568
  195. package/src/identity/idp-token-resolver.ts +0 -181
  196. package/src/identity/idp-token-storage.interface.ts +0 -94
  197. package/src/identity/user-did-manager.ts +0 -526
  198. package/src/index.ts +0 -310
  199. package/src/providers/base.d.ts +0 -91
  200. package/src/providers/base.d.ts.map +0 -1
  201. package/src/providers/base.js.map +0 -1
  202. package/src/providers/base.ts +0 -96
  203. package/src/providers/memory.ts +0 -142
  204. package/src/runtime/audit-logger.ts +0 -39
  205. package/src/runtime/base.ts +0 -1392
  206. package/src/services/__tests__/access-control.integration.test.ts +0 -443
  207. package/src/services/__tests__/access-control.proof-response-validation.test.ts +0 -578
  208. package/src/services/__tests__/access-control.service.test.ts +0 -970
  209. package/src/services/__tests__/batch-delegation.service.test.ts +0 -351
  210. package/src/services/__tests__/crypto.service.test.ts +0 -531
  211. package/src/services/__tests__/oauth-provider-registry.test.ts +0 -142
  212. package/src/services/__tests__/proof-verifier.integration.test.ts +0 -485
  213. package/src/services/__tests__/proof-verifier.test.ts +0 -489
  214. package/src/services/__tests__/provider-resolution.integration.test.ts +0 -202
  215. package/src/services/__tests__/provider-resolver.test.ts +0 -213
  216. package/src/services/__tests__/storage.service.test.ts +0 -358
  217. package/src/services/access-control.service.ts +0 -990
  218. package/src/services/authorization/authorization-registry.ts +0 -66
  219. package/src/services/authorization/types.ts +0 -71
  220. package/src/services/batch-delegation.service.ts +0 -137
  221. package/src/services/crypto.service.ts +0 -302
  222. package/src/services/errors.ts +0 -76
  223. package/src/services/index.ts +0 -18
  224. package/src/services/oauth-config.service.d.ts +0 -53
  225. package/src/services/oauth-config.service.d.ts.map +0 -1
  226. package/src/services/oauth-config.service.js.map +0 -1
  227. package/src/services/oauth-config.service.ts +0 -192
  228. package/src/services/oauth-provider-registry.d.ts +0 -57
  229. package/src/services/oauth-provider-registry.d.ts.map +0 -1
  230. package/src/services/oauth-provider-registry.js.map +0 -1
  231. package/src/services/oauth-provider-registry.ts +0 -141
  232. package/src/services/oauth-service.ts +0 -544
  233. package/src/services/oauth-token-retrieval.service.ts +0 -245
  234. package/src/services/proof-verifier.ts +0 -478
  235. package/src/services/provider-resolver.d.ts +0 -48
  236. package/src/services/provider-resolver.d.ts.map +0 -1
  237. package/src/services/provider-resolver.js.map +0 -1
  238. package/src/services/provider-resolver.ts +0 -146
  239. package/src/services/provider-validator.ts +0 -170
  240. package/src/services/session-registration.service.ts +0 -251
  241. package/src/services/storage.service.ts +0 -566
  242. package/src/services/tool-context-builder.ts +0 -237
  243. package/src/services/tool-protection.service.ts +0 -1070
  244. package/src/types/oauth-required-error.ts +0 -63
  245. package/src/types/tool-protection.ts +0 -155
  246. package/src/utils/__tests__/did-helpers.test.ts +0 -156
  247. package/src/utils/base58.ts +0 -109
  248. package/src/utils/base64.ts +0 -148
  249. package/src/utils/cors.ts +0 -83
  250. package/src/utils/did-helpers.ts +0 -210
  251. package/src/utils/index.ts +0 -8
  252. package/src/utils/storage-keys.ts +0 -278
  253. package/tsconfig.json +0 -21
  254. package/vitest.config.ts +0 -56
@@ -1,789 +0,0 @@
1
- /**
2
- * Full Flow Integration Tests
3
- *
4
- * End-to-end integration tests covering:
5
- * - Complete handshake → tool call → proof flow
6
- * - Tool protection enforcement
7
- * - Delegation flow (intercept → authorize → resume)
8
- * - AgentShield integration
9
- * - Caching behavior
10
- */
11
-
12
- import { describe, test, expect, beforeEach, vi, afterEach } from "vitest";
13
- import { MCPIRuntimeBase } from "../../runtime/base";
14
- import { ToolProtectionService } from "../../services/tool-protection.service";
15
- import {
16
- InMemoryToolProtectionCache,
17
- type ToolProtectionCache,
18
- } from "../../cache/tool-protection-cache";
19
- import {
20
- MemoryStorageProvider,
21
- MemoryNonceCacheProvider,
22
- MemoryIdentityProvider,
23
- } from "../../providers/memory";
24
- import {
25
- createMockProviders,
26
- MockCryptoProvider,
27
- MockClockProvider,
28
- MockFetchProvider,
29
- } from "../utils/mock-providers";
30
- import type { ToolProtectionServiceConfig } from "../../types/tool-protection";
31
- import type { ProviderRuntimeConfig } from "../../config";
32
-
33
- // Type declaration for global (polyfilled by Vitest)
34
- declare const global: typeof globalThis;
35
-
36
- // Mock global fetch
37
- global.fetch = vi.fn();
38
-
39
- describe("Full Flow Integration", () => {
40
- let runtime: MCPIRuntimeBase;
41
- let toolProtectionService: ToolProtectionService;
42
- let cache: ToolProtectionCache;
43
- let config: ProviderRuntimeConfig;
44
- let mockProviders: ReturnType<typeof createMockProviders>;
45
- const mockAgentDid =
46
- "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
47
-
48
- beforeEach(async () => {
49
- vi.clearAllMocks();
50
- mockProviders = createMockProviders();
51
-
52
- cache = new InMemoryToolProtectionCache();
53
- const toolProtectionConfig: ToolProtectionServiceConfig = {
54
- apiUrl: "https://kya.vouched.id",
55
- apiKey: "test-api-key",
56
- projectId: "test-project",
57
- cacheTtl: 300000,
58
- debug: false,
59
- };
60
- toolProtectionService = new ToolProtectionService(
61
- toolProtectionConfig,
62
- cache
63
- );
64
-
65
- config = {
66
- ...mockProviders,
67
- environment: "development",
68
- session: {
69
- timestampSkewSeconds: 120,
70
- ttlMinutes: 30,
71
- },
72
- audit: {
73
- enabled: true,
74
- logFunction: vi.fn(),
75
- includePayloads: true,
76
- },
77
- toolProtection: {
78
- source: "agentshield",
79
- agentShield: {
80
- apiUrl: "https://kya.vouched.id",
81
- apiKey: "test-api-key",
82
- },
83
- },
84
- };
85
-
86
- runtime = new MCPIRuntimeBase(config);
87
- await runtime.initialize();
88
- });
89
-
90
- afterEach(() => {
91
- vi.restoreAllMocks();
92
- });
93
-
94
- describe("Complete handshake → tool call → proof flow", () => {
95
- test("should complete full authentication and tool execution cycle", async () => {
96
- // Step 1: Client initiates handshake
97
- const handshakeRequest = {
98
- clientDid: "did:key:zclient789",
99
- audience: "https://app.example.com",
100
- };
101
-
102
- const handshakeResponse = await runtime.handleHandshake(handshakeRequest);
103
-
104
- expect(handshakeResponse.sessionId).toBeDefined();
105
- expect(handshakeResponse.agentDid).toMatch(/^did:key:z/);
106
- expect(handshakeResponse.signature).toBeDefined();
107
- expect(handshakeResponse.capabilities).toContain("identity");
108
-
109
- // Step 2: Get session and issue nonce
110
- const session = await runtime.getCurrentSession();
111
- expect(session).toBeDefined();
112
- expect(session?.clientDid).toBe(handshakeRequest.clientDid);
113
- expect(session?.audience).toBe(handshakeRequest.audience);
114
-
115
- const nonce = await runtime.issueNonce(session!.id);
116
- session!.nonce = nonce;
117
-
118
- // Step 3: Execute unprotected tool
119
- const toolHandler = async (args: any) => {
120
- return {
121
- message: `Hello ${args.name}!`,
122
- timestamp: Date.now(),
123
- };
124
- };
125
-
126
- const result = await runtime.processToolCall(
127
- "greetingTool",
128
- { name: "Alice" },
129
- toolHandler,
130
- session
131
- );
132
-
133
- expect(result.message).toBe("Hello Alice!");
134
-
135
- // Step 4: Verify proof was created
136
- const proof = runtime.getLastProof();
137
- expect(proof).toBeDefined();
138
- expect(proof?.nonce).toBe(nonce);
139
- expect(proof?.sessionId).toBe(session!.id);
140
- expect(proof?.audience).toBe("https://app.example.com");
141
- });
142
-
143
- test("should handle multiple tool calls in sequence", async () => {
144
- const handshakeResponse = await runtime.handleHandshake({
145
- clientDid: "did:key:zclient789",
146
- audience: "https://app.example.com",
147
- });
148
-
149
- const session = await runtime.getCurrentSession();
150
- expect(session).toBeDefined();
151
-
152
- const nonce1 = await runtime.issueNonce(session!.id);
153
- session!.nonce = nonce1;
154
-
155
- // First tool call
156
- const result1 = await runtime.processToolCall(
157
- "tool1",
158
- { arg: "value1" },
159
- async (args) => ({ result: args.arg }),
160
- session
161
- );
162
-
163
- expect(result1.result).toBe("value1");
164
-
165
- // Second tool call with new nonce
166
- const nonce2 = await runtime.issueNonce(session!.id);
167
- session!.nonce = nonce2;
168
-
169
- const result2 = await runtime.processToolCall(
170
- "tool2",
171
- { arg: "value2" },
172
- async (args) => ({ result: args.arg }),
173
- session
174
- );
175
-
176
- expect(result2.result).toBe("value2");
177
-
178
- // Verify both proofs were created
179
- const proof = runtime.getLastProof();
180
- expect(proof?.nonce).toBe(nonce2);
181
- });
182
-
183
- test("should propagate client DID through entire flow", async () => {
184
- const clientDid = "did:key:zclient789";
185
- const handshakeResponse = await runtime.handleHandshake({
186
- clientDid,
187
- audience: "https://app.example.com",
188
- });
189
-
190
- const session = await runtime.getCurrentSession();
191
- expect(session?.clientDid).toBe(clientDid);
192
-
193
- const nonce = await runtime.issueNonce(session!.id);
194
- session!.nonce = nonce;
195
-
196
- await runtime.processToolCall(
197
- "testTool",
198
- { test: "data" },
199
- async () => ({}),
200
- session
201
- );
202
-
203
- const proof = runtime.getLastProof();
204
- expect(proof).toBeDefined();
205
- // Client DID should be in proof metadata
206
- expect((proof as any).clientDid || session?.clientDid).toBe(clientDid);
207
- });
208
- });
209
-
210
- describe("Tool protection enforcement flow", () => {
211
- test("should allow unprotected tool calls", async () => {
212
- // Mock API response with unprotected tool
213
- const apiResponse = {
214
- success: true,
215
- data: {
216
- toolProtections: {
217
- search: {
218
- requiresDelegation: false,
219
- requiredScopes: [],
220
- },
221
- },
222
- },
223
- metadata: {},
224
- };
225
-
226
- (global.fetch as any).mockResolvedValueOnce({
227
- ok: true,
228
- json: async () => apiResponse,
229
- });
230
-
231
- const handshakeResponse = await runtime.handleHandshake({
232
- clientDid: "did:key:zclient",
233
- audience: "https://app.example.com",
234
- });
235
-
236
- const session = await runtime.getCurrentSession();
237
- const nonce = await runtime.issueNonce(session!.id);
238
- session!.nonce = nonce;
239
-
240
- // Mock tool protection service
241
- const protection = await toolProtectionService.checkToolProtection(
242
- "search",
243
- mockAgentDid
244
- );
245
- expect(protection).toBeNull(); // No protection required
246
-
247
- // Tool call should proceed normally
248
- const result = await runtime.processToolCall(
249
- "search",
250
- { query: "test" },
251
- async (args) => ({ results: [] }),
252
- session
253
- );
254
-
255
- expect(result).toBeDefined();
256
- });
257
-
258
- test("should intercept protected tool calls without delegation", async () => {
259
- // Mock API response with protected tool
260
- const apiResponse = {
261
- success: true,
262
- data: {
263
- toolProtections: {
264
- checkout: {
265
- requiresDelegation: true,
266
- requiredScopes: ["cart:write"],
267
- },
268
- },
269
- },
270
- metadata: {},
271
- };
272
-
273
- (global.fetch as any).mockResolvedValueOnce({
274
- ok: true,
275
- json: async () => apiResponse,
276
- });
277
-
278
- const handshakeResponse = await runtime.handleHandshake({
279
- clientDid: "did:key:zclient",
280
- audience: "https://app.example.com",
281
- });
282
-
283
- const session = await runtime.getCurrentSession();
284
- const nonce = await runtime.issueNonce(session!.id);
285
- session!.nonce = nonce;
286
-
287
- // Check protection
288
- const protection = await toolProtectionService.checkToolProtection(
289
- "checkout",
290
- mockAgentDid
291
- );
292
- expect(protection).toBeDefined();
293
- expect(protection?.requiresDelegation).toBe(true);
294
-
295
- // Tool call should be intercepted
296
- // Note: Actual interception happens in runtime.processToolCall
297
- // This test verifies the protection check works correctly
298
- expect(protection?.requiredScopes).toContain("cart:write");
299
- });
300
- });
301
-
302
- describe("Delegation flow (intercept → authorize → resume)", () => {
303
- test("should store intercepted call for resumption", async () => {
304
- const handshakeResponse = await runtime.handleHandshake({
305
- clientDid: "did:key:zclient",
306
- audience: "https://app.example.com",
307
- });
308
-
309
- const session = await runtime.getCurrentSession();
310
- expect(session).toBeDefined();
311
-
312
- // Simulate intercepted call storage
313
- const interceptedCall = {
314
- toolName: "checkout",
315
- args: { cartId: "123" },
316
- sessionId: session!.id,
317
- timestamp: Date.now(),
318
- expiresAt: Date.now() + 3600000, // 1 hour
319
- };
320
-
321
- // Verify intercepted call structure
322
- expect(interceptedCall.toolName).toBe("checkout");
323
- expect(interceptedCall.args.cartId).toBe("123");
324
- expect(interceptedCall.sessionId).toBe(session!.id);
325
- });
326
-
327
- test("should generate resume token for intercepted calls", async () => {
328
- const handshakeResponse = await runtime.handleHandshake({
329
- clientDid: "did:key:zclient",
330
- audience: "https://app.example.com",
331
- });
332
-
333
- const session = await runtime.getCurrentSession();
334
-
335
- // Generate resume token (simulated)
336
- const resumeToken = `resume_${session!.id}_${Date.now()}`;
337
-
338
- expect(resumeToken).toMatch(/^resume_/);
339
- expect(resumeToken).toContain(session!.id);
340
- });
341
-
342
- test("should build consent URL with resume token", async () => {
343
- const handshakeResponse = await runtime.handleHandshake({
344
- clientDid: "did:key:zclient",
345
- audience: "https://app.example.com",
346
- });
347
-
348
- const session = await runtime.getCurrentSession();
349
- const resumeToken = `resume_${session!.id}_${Date.now()}`;
350
-
351
- // Build consent URL
352
- const consentUrl = `https://kya.vouched.id/consent?resume_token=${resumeToken}&tool=checkout&scopes=cart:write`;
353
-
354
- expect(consentUrl).toContain("resume_token=");
355
- expect(consentUrl).toContain("tool=checkout");
356
- expect(consentUrl).toContain("scopes=cart:write");
357
- });
358
- });
359
-
360
- describe("AgentShield integration flow", () => {
361
- test("should fetch tool protection config from AgentShield merged config", async () => {
362
- // Merged config format - tools embedded at config.toolProtection.tools
363
- const apiResponse = {
364
- success: true,
365
- data: {
366
- config: {
367
- toolProtection: {
368
- source: 'agentshield',
369
- tools: {
370
- protected_tool: {
371
- requiresDelegation: true,
372
- requiredScopes: ["scope1"],
373
- },
374
- },
375
- },
376
- },
377
- },
378
- metadata: {},
379
- };
380
-
381
- (global.fetch as any).mockResolvedValueOnce({
382
- ok: true,
383
- json: async () => apiResponse,
384
- });
385
-
386
- const config =
387
- await toolProtectionService.getToolProtectionConfig(mockAgentDid);
388
-
389
- expect(config.toolProtections.protected_tool).toBeDefined();
390
- expect(config.toolProtections.protected_tool.requiresDelegation).toBe(
391
- true
392
- );
393
- // Now calls /config endpoint (not /tool-protections)
394
- expect(global.fetch).toHaveBeenCalledWith(
395
- expect.stringContaining(
396
- "/api/v1/bouncer/projects/test-project/config"
397
- ),
398
- expect.any(Object)
399
- );
400
- });
401
-
402
- test("should cache tool protection config", async () => {
403
- // Merged config format - tools embedded at config.toolProtection.tools
404
- const apiResponse = {
405
- success: true,
406
- data: {
407
- config: {
408
- toolProtection: {
409
- source: 'agentshield',
410
- tools: {
411
- tool1: {
412
- requiresDelegation: true,
413
- requiredScopes: ["scope1"],
414
- },
415
- },
416
- },
417
- },
418
- },
419
- metadata: {},
420
- };
421
-
422
- (global.fetch as any).mockResolvedValueOnce({
423
- ok: true,
424
- json: async () => apiResponse,
425
- });
426
-
427
- // First call - should fetch from API
428
- const config1 =
429
- await toolProtectionService.getToolProtectionConfig(mockAgentDid);
430
-
431
- // Second call - should use cache
432
- const config2 =
433
- await toolProtectionService.getToolProtectionConfig(mockAgentDid);
434
-
435
- expect(config1).toEqual(config2);
436
- expect(global.fetch).toHaveBeenCalledTimes(1);
437
- });
438
-
439
- test("should use fallback config when API fails", async () => {
440
- const fallbackConfig = {
441
- toolProtections: {
442
- fallback_tool: {
443
- requiresDelegation: true,
444
- requiredScopes: ["fallback"],
445
- },
446
- },
447
- };
448
-
449
- const toolProtectionConfig: ToolProtectionServiceConfig = {
450
- apiUrl: "https://kya.vouched.id",
451
- apiKey: "test-api-key",
452
- projectId: "test-project",
453
- cacheTtl: 300000,
454
- fallbackConfig,
455
- debug: false,
456
- };
457
-
458
- const serviceWithFallback = new ToolProtectionService(
459
- toolProtectionConfig,
460
- cache
461
- );
462
-
463
- (global.fetch as any).mockRejectedValueOnce(new Error("Network error"));
464
-
465
- const result =
466
- await serviceWithFallback.getToolProtectionConfig(mockAgentDid);
467
-
468
- expect(result).toEqual(fallbackConfig);
469
- });
470
- });
471
-
472
- describe("Session expiry handling", () => {
473
- test("should handle expired sessions correctly", async () => {
474
- const clock = mockProviders.clockProvider as MockClockProvider;
475
- const currentTime = Date.now();
476
- clock.setTime(currentTime);
477
-
478
- await runtime.handleHandshake({
479
- clientDid: "did:key:zclient",
480
- audience: "https://app.example.com",
481
- });
482
-
483
- let session = await runtime.getCurrentSession();
484
- expect(session).toBeDefined();
485
-
486
- // Advance time past expiry (30 minutes + 1 second)
487
- clock.setTime(currentTime + 30 * 60 * 1000 + 1000);
488
-
489
- session = await runtime.getCurrentSession();
490
- expect(session).toBeNull();
491
- });
492
-
493
- test("should reject tool calls with expired session", async () => {
494
- const clock = mockProviders.clockProvider as MockClockProvider;
495
- const currentTime = Date.now();
496
- clock.setTime(currentTime);
497
-
498
- await runtime.handleHandshake({
499
- clientDid: "did:key:zclient",
500
- audience: "https://app.example.com",
501
- });
502
-
503
- const session = await runtime.getCurrentSession();
504
- expect(session).toBeDefined();
505
-
506
- // Advance time past expiry
507
- clock.setTime(currentTime + 30 * 60 * 1000 + 1000);
508
-
509
- // Session should be expired
510
- const expiredSession = await runtime.getCurrentSession();
511
- expect(expiredSession).toBeNull();
512
- });
513
- });
514
-
515
- describe("Error handling in full flow", () => {
516
- test("should handle tool protection service errors gracefully", async () => {
517
- // Mock API failure
518
- (global.fetch as any).mockRejectedValueOnce(new Error("Network error"));
519
-
520
- // Should fail-closed (deny-all) by default
521
- const config =
522
- await toolProtectionService.getToolProtectionConfig(mockAgentDid);
523
-
524
- expect(config.toolProtections['*']).toBeDefined();
525
- expect(config.toolProtections['*']?.requiresDelegation).toBe(true);
526
- });
527
-
528
- test("should handle invalid API responses", async () => {
529
- (global.fetch as any).mockResolvedValueOnce({
530
- ok: true,
531
- json: async () => ({
532
- success: false,
533
- error: "Invalid request",
534
- }),
535
- });
536
-
537
- await expect(
538
- toolProtectionService.getToolProtectionConfig(mockAgentDid)
539
- ).rejects.toThrow("API returned success: false");
540
- });
541
-
542
- test("should handle network timeouts", async () => {
543
- const fallbackConfig = {
544
- toolProtections: {
545
- tool1: {
546
- requiresDelegation: true,
547
- requiredScopes: ["scope1"],
548
- },
549
- },
550
- };
551
-
552
- const toolProtectionConfig: ToolProtectionServiceConfig = {
553
- apiUrl: "https://kya.vouched.id",
554
- apiKey: "test-api-key",
555
- projectId: "test-project",
556
- cacheTtl: 300000,
557
- fallbackConfig,
558
- debug: false,
559
- };
560
-
561
- const serviceWithFallback = new ToolProtectionService(
562
- toolProtectionConfig,
563
- cache
564
- );
565
-
566
- (global.fetch as any).mockRejectedValueOnce(new Error("Network timeout"));
567
-
568
- const result =
569
- await serviceWithFallback.getToolProtectionConfig(mockAgentDid);
570
-
571
- expect(result).toEqual(fallbackConfig);
572
- });
573
- });
574
-
575
- describe("Cache integration in full flow", () => {
576
- test("should share cache across multiple service instances", async () => {
577
- const apiResponse = {
578
- success: true,
579
- data: {
580
- toolProtections: {
581
- tool1: {
582
- requiresDelegation: true,
583
- requiredScopes: ["scope1"],
584
- },
585
- },
586
- },
587
- metadata: {},
588
- };
589
-
590
- (global.fetch as any).mockResolvedValueOnce({
591
- ok: true,
592
- json: async () => apiResponse,
593
- });
594
-
595
- const service1 = new ToolProtectionService(
596
- {
597
- apiUrl: "https://kya.vouched.id",
598
- apiKey: "test-api-key",
599
- projectId: "test-project",
600
- cacheTtl: 300000,
601
- },
602
- cache
603
- );
604
-
605
- const service2 = new ToolProtectionService(
606
- {
607
- apiUrl: "https://kya.vouched.id",
608
- apiKey: "test-api-key",
609
- projectId: "test-project",
610
- cacheTtl: 300000,
611
- },
612
- cache
613
- );
614
-
615
- // First service fetches from API
616
- await service1.getToolProtectionConfig(mockAgentDid);
617
-
618
- // Second service should use cached value
619
- const config2 = await service2.getToolProtectionConfig(mockAgentDid);
620
-
621
- expect(global.fetch).toHaveBeenCalledTimes(1);
622
- expect(config2.toolProtections.tool1).toBeDefined();
623
- });
624
-
625
- test("should clear cache when needed", async () => {
626
- const apiResponse = {
627
- success: true,
628
- data: {
629
- toolProtections: {
630
- tool1: {
631
- requiresDelegation: true,
632
- requiredScopes: ["scope1"],
633
- },
634
- },
635
- },
636
- metadata: {},
637
- };
638
-
639
- (global.fetch as any).mockResolvedValue({
640
- ok: true,
641
- json: async () => apiResponse,
642
- });
643
-
644
- // Fetch and cache
645
- await toolProtectionService.getToolProtectionConfig(mockAgentDid);
646
-
647
- // Clear cache
648
- await toolProtectionService.clearCache(mockAgentDid);
649
-
650
- // Next fetch should go to API again
651
- await toolProtectionService.getToolProtectionConfig(mockAgentDid);
652
-
653
- expect(global.fetch).toHaveBeenCalledTimes(2);
654
- });
655
- });
656
-
657
- describe("Real-world e-commerce scenario", () => {
658
- test("should handle complete e-commerce flow with tool protection", async () => {
659
- // Step 1: Set up tool protection config
660
- const apiResponse = {
661
- success: true,
662
- data: {
663
- toolProtections: {
664
- search_products: {
665
- requiresDelegation: false,
666
- requiredScopes: [],
667
- },
668
- add_to_cart: {
669
- requiresDelegation: true,
670
- requiredScopes: ["cart:write"],
671
- },
672
- checkout: {
673
- requiresDelegation: true,
674
- requiredScopes: ["cart:write", "payment:process"],
675
- },
676
- },
677
- },
678
- metadata: {},
679
- };
680
-
681
- (global.fetch as any).mockResolvedValueOnce({
682
- ok: true,
683
- json: async () => apiResponse,
684
- });
685
-
686
- // Step 2: Handshake
687
- const handshakeResponse = await runtime.handleHandshake({
688
- clientDid: "did:key:zclient",
689
- audience: "https://shop.example.com",
690
- });
691
-
692
- const session = await runtime.getCurrentSession();
693
- expect(session).toBeDefined();
694
-
695
- // Step 3: Unprotected tool call (search)
696
- const nonce1 = await runtime.issueNonce(session!.id);
697
- session!.nonce = nonce1;
698
-
699
- const searchProtection = await toolProtectionService.checkToolProtection(
700
- "search_products",
701
- mockAgentDid
702
- );
703
- expect(searchProtection).toBeNull(); // No protection
704
-
705
- const searchResult = await runtime.processToolCall(
706
- "search_products",
707
- { query: "laptop" },
708
- async () => ({ products: [] }),
709
- session
710
- );
711
-
712
- expect(searchResult).toBeDefined();
713
-
714
- // Step 4: Check protection for add_to_cart (should require delegation)
715
- const nonce2 = await runtime.issueNonce(session!.id);
716
- session!.nonce = nonce2;
717
-
718
- const cartProtection = await toolProtectionService.checkToolProtection(
719
- "add_to_cart",
720
- mockAgentDid
721
- );
722
-
723
- expect(cartProtection).toBeDefined();
724
- expect(cartProtection?.requiresDelegation).toBe(true);
725
- expect(cartProtection?.requiredScopes).toContain("cart:write");
726
-
727
- // Step 5: Verify proof was created for search tool call (not for protection check)
728
- const proof = runtime.getLastProof();
729
- expect(proof).toBeDefined();
730
- expect(proof?.nonce).toBe(nonce1); // Proof from search_products call
731
- });
732
- });
733
-
734
- describe("Concurrent operations", () => {
735
- test("should handle concurrent tool calls", async () => {
736
- await runtime.handleHandshake({
737
- clientDid: "did:key:zclient",
738
- audience: "https://app.example.com",
739
- });
740
-
741
- const session = await runtime.getCurrentSession();
742
- expect(session).toBeDefined();
743
-
744
- // Issue multiple nonces
745
- const nonces = await Promise.all([
746
- runtime.issueNonce(session!.id),
747
- runtime.issueNonce(session!.id),
748
- runtime.issueNonce(session!.id),
749
- ]);
750
-
751
- expect(nonces).toHaveLength(3);
752
- expect(new Set(nonces).size).toBe(3); // All unique
753
- });
754
-
755
- test("should handle concurrent cache operations", async () => {
756
- const apiResponse = {
757
- success: true,
758
- data: {
759
- toolProtections: {
760
- tool1: {
761
- requiresDelegation: true,
762
- requiredScopes: ["scope1"],
763
- },
764
- },
765
- },
766
- metadata: {},
767
- };
768
-
769
- (global.fetch as any).mockResolvedValue({
770
- ok: true,
771
- json: async () => apiResponse,
772
- });
773
-
774
- // Concurrent config fetches
775
- const promises = [
776
- toolProtectionService.getToolProtectionConfig(mockAgentDid),
777
- toolProtectionService.getToolProtectionConfig(mockAgentDid),
778
- toolProtectionService.getToolProtectionConfig(mockAgentDid),
779
- ];
780
-
781
- const results = await Promise.all(promises);
782
-
783
- // All should return same config
784
- results.forEach((result) => {
785
- expect(result.toolProtections.tool1).toBeDefined();
786
- });
787
- });
788
- });
789
- });