@kya-os/mcp-i-core 1.3.7-canary.0 → 1.3.7-canary.clientinfo.20251126041014

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 (236) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test$colon$coverage.log +4239 -0
  3. package/.turbo/turbo-test.log +2973 -0
  4. package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
  5. package/Composer 3.md +615 -0
  6. package/GPT-5.md +1169 -0
  7. package/OPUS-plan.md +352 -0
  8. package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
  9. package/PHASE_3_SUMMARY.md +317 -0
  10. package/PHASE_4.1.3_SUMMARY.md +428 -0
  11. package/PHASE_4.1_COMPLETE.md +525 -0
  12. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
  13. package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
  14. package/TEST_PLAN.md +571 -0
  15. package/coverage/coverage-final.json +57 -0
  16. package/dist/__tests__/utils/mock-providers.d.ts +1 -2
  17. package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
  18. package/dist/__tests__/utils/mock-providers.js.map +1 -1
  19. package/dist/cache/oauth-config-cache.d.ts +69 -0
  20. package/dist/cache/oauth-config-cache.d.ts.map +1 -0
  21. package/dist/cache/oauth-config-cache.js +76 -0
  22. package/dist/cache/oauth-config-cache.js.map +1 -0
  23. package/dist/identity/idp-token-resolver.d.ts +53 -0
  24. package/dist/identity/idp-token-resolver.d.ts.map +1 -0
  25. package/dist/identity/idp-token-resolver.js +108 -0
  26. package/dist/identity/idp-token-resolver.js.map +1 -0
  27. package/dist/identity/idp-token-storage.interface.d.ts +42 -0
  28. package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
  29. package/dist/identity/idp-token-storage.interface.js +12 -0
  30. package/dist/identity/idp-token-storage.interface.js.map +1 -0
  31. package/dist/identity/user-did-manager.d.ts +39 -1
  32. package/dist/identity/user-did-manager.d.ts.map +1 -1
  33. package/dist/identity/user-did-manager.js +69 -3
  34. package/dist/identity/user-did-manager.js.map +1 -1
  35. package/dist/index.d.ts +24 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +43 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/runtime/audit-logger.d.ts +37 -0
  40. package/dist/runtime/audit-logger.d.ts.map +1 -0
  41. package/dist/runtime/audit-logger.js +9 -0
  42. package/dist/runtime/audit-logger.js.map +1 -0
  43. package/dist/runtime/base.d.ts +19 -2
  44. package/dist/runtime/base.d.ts.map +1 -1
  45. package/dist/runtime/base.js +227 -11
  46. package/dist/runtime/base.js.map +1 -1
  47. package/dist/services/access-control.service.d.ts.map +1 -1
  48. package/dist/services/access-control.service.js +199 -15
  49. package/dist/services/access-control.service.js.map +1 -1
  50. package/dist/services/authorization/authorization-registry.d.ts +29 -0
  51. package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
  52. package/dist/services/authorization/authorization-registry.js +57 -0
  53. package/dist/services/authorization/authorization-registry.js.map +1 -0
  54. package/dist/services/authorization/types.d.ts +53 -0
  55. package/dist/services/authorization/types.d.ts.map +1 -0
  56. package/dist/services/authorization/types.js +10 -0
  57. package/dist/services/authorization/types.js.map +1 -0
  58. package/dist/services/batch-delegation.service.d.ts +53 -0
  59. package/dist/services/batch-delegation.service.d.ts.map +1 -0
  60. package/dist/services/batch-delegation.service.js +95 -0
  61. package/dist/services/batch-delegation.service.js.map +1 -0
  62. package/dist/services/index.d.ts +2 -0
  63. package/dist/services/index.d.ts.map +1 -1
  64. package/dist/services/index.js +4 -1
  65. package/dist/services/index.js.map +1 -1
  66. package/dist/services/oauth-config.service.d.ts +53 -0
  67. package/dist/services/oauth-config.service.d.ts.map +1 -0
  68. package/dist/services/oauth-config.service.js +141 -0
  69. package/dist/services/oauth-config.service.js.map +1 -0
  70. package/dist/services/oauth-provider-registry.d.ts +88 -0
  71. package/dist/services/oauth-provider-registry.d.ts.map +1 -0
  72. package/dist/services/oauth-provider-registry.js +128 -0
  73. package/dist/services/oauth-provider-registry.js.map +1 -0
  74. package/dist/services/oauth-service.d.ts +77 -0
  75. package/dist/services/oauth-service.d.ts.map +1 -0
  76. package/dist/services/oauth-service.js +373 -0
  77. package/dist/services/oauth-service.js.map +1 -0
  78. package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
  79. package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
  80. package/dist/services/oauth-token-retrieval.service.js +150 -0
  81. package/dist/services/oauth-token-retrieval.service.js.map +1 -0
  82. package/dist/services/provider-resolver.d.ts +48 -0
  83. package/dist/services/provider-resolver.d.ts.map +1 -0
  84. package/dist/services/provider-resolver.js +121 -0
  85. package/dist/services/provider-resolver.js.map +1 -0
  86. package/dist/services/provider-validator.d.ts +55 -0
  87. package/dist/services/provider-validator.d.ts.map +1 -0
  88. package/dist/services/provider-validator.js +135 -0
  89. package/dist/services/provider-validator.js.map +1 -0
  90. package/dist/services/session-registration.service.d.ts +80 -0
  91. package/dist/services/session-registration.service.d.ts.map +1 -0
  92. package/dist/services/session-registration.service.js +228 -0
  93. package/dist/services/session-registration.service.js.map +1 -0
  94. package/dist/services/tool-context-builder.d.ts +57 -0
  95. package/dist/services/tool-context-builder.d.ts.map +1 -0
  96. package/dist/services/tool-context-builder.js +125 -0
  97. package/dist/services/tool-context-builder.js.map +1 -0
  98. package/dist/services/tool-protection.service.d.ts +27 -0
  99. package/dist/services/tool-protection.service.d.ts.map +1 -1
  100. package/dist/services/tool-protection.service.js +194 -4
  101. package/dist/services/tool-protection.service.js.map +1 -1
  102. package/dist/types/oauth-required-error.d.ts +40 -0
  103. package/dist/types/oauth-required-error.d.ts.map +1 -0
  104. package/dist/types/oauth-required-error.js +40 -0
  105. package/dist/types/oauth-required-error.js.map +1 -0
  106. package/dist/utils/did-helpers.d.ts +33 -0
  107. package/dist/utils/did-helpers.d.ts.map +1 -1
  108. package/dist/utils/did-helpers.js +40 -0
  109. package/dist/utils/did-helpers.js.map +1 -1
  110. package/dist/utils/index.d.ts +1 -0
  111. package/dist/utils/index.d.ts.map +1 -1
  112. package/dist/utils/index.js +1 -0
  113. package/dist/utils/index.js.map +1 -1
  114. package/docs/API_REFERENCE.md +1362 -0
  115. package/docs/COMPLIANCE_MATRIX.md +691 -0
  116. package/docs/STATUSLIST2021_GUIDE.md +696 -0
  117. package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
  118. package/package.json +23 -54
  119. package/scripts/audit-compliance.ts +724 -0
  120. package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
  121. package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
  122. package/src/__tests__/delegation-e2e.test.ts +690 -0
  123. package/src/__tests__/identity/user-did-manager.test.ts +213 -0
  124. package/src/__tests__/index.test.ts +56 -0
  125. package/src/__tests__/integration/full-flow.test.ts +776 -0
  126. package/src/__tests__/integration.test.ts +281 -0
  127. package/src/__tests__/providers/base.test.ts +173 -0
  128. package/src/__tests__/providers/memory.test.ts +319 -0
  129. package/src/__tests__/regression/phase2-regression.test.ts +429 -0
  130. package/src/__tests__/runtime/audit-logger.test.ts +154 -0
  131. package/src/__tests__/runtime/base-extensions.test.ts +593 -0
  132. package/src/__tests__/runtime/base.test.ts +869 -0
  133. package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
  134. package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
  135. package/src/__tests__/runtime/route-interception.test.ts +686 -0
  136. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
  137. package/src/__tests__/services/agentshield-integration.test.ts +784 -0
  138. package/src/__tests__/services/cache-busting.test.ts +125 -0
  139. package/src/__tests__/services/oauth-service-pkce.test.ts +556 -0
  140. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +591 -0
  141. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
  142. package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
  143. package/src/__tests__/utils/mock-providers.ts +340 -0
  144. package/src/cache/oauth-config-cache.d.ts +69 -0
  145. package/src/cache/oauth-config-cache.d.ts.map +1 -0
  146. package/src/cache/oauth-config-cache.js.map +1 -0
  147. package/src/cache/oauth-config-cache.ts +123 -0
  148. package/src/cache/tool-protection-cache.ts +171 -0
  149. package/src/compliance/EXAMPLE.md +412 -0
  150. package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
  151. package/src/compliance/index.ts +8 -0
  152. package/src/compliance/schema-registry.ts +460 -0
  153. package/src/compliance/schema-verifier.ts +708 -0
  154. package/src/config/__tests__/remote-config.spec.ts +268 -0
  155. package/src/config/remote-config.ts +174 -0
  156. package/src/config.ts +309 -0
  157. package/src/delegation/__tests__/audience-validator.test.ts +112 -0
  158. package/src/delegation/__tests__/bitstring.test.ts +346 -0
  159. package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
  160. package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
  161. package/src/delegation/__tests__/utils.test.ts +152 -0
  162. package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
  163. package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
  164. package/src/delegation/audience-validator.ts +52 -0
  165. package/src/delegation/bitstring.ts +278 -0
  166. package/src/delegation/cascading-revocation.ts +370 -0
  167. package/src/delegation/delegation-graph.ts +299 -0
  168. package/src/delegation/index.ts +14 -0
  169. package/src/delegation/statuslist-manager.ts +353 -0
  170. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
  171. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
  172. package/src/delegation/storage/index.ts +9 -0
  173. package/src/delegation/storage/memory-graph-storage.ts +178 -0
  174. package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
  175. package/src/delegation/utils.ts +42 -0
  176. package/src/delegation/vc-issuer.ts +232 -0
  177. package/src/delegation/vc-verifier.ts +568 -0
  178. package/src/identity/idp-token-resolver.ts +147 -0
  179. package/src/identity/idp-token-storage.interface.ts +59 -0
  180. package/src/identity/user-did-manager.ts +370 -0
  181. package/src/index.ts +271 -0
  182. package/src/providers/base.d.ts +91 -0
  183. package/src/providers/base.d.ts.map +1 -0
  184. package/src/providers/base.js.map +1 -0
  185. package/src/providers/base.ts +96 -0
  186. package/src/providers/memory.ts +142 -0
  187. package/src/runtime/audit-logger.ts +39 -0
  188. package/src/runtime/base.ts +1329 -0
  189. package/src/services/__tests__/access-control.integration.test.ts +443 -0
  190. package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
  191. package/src/services/__tests__/access-control.service.test.ts +970 -0
  192. package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
  193. package/src/services/__tests__/crypto.service.test.ts +531 -0
  194. package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
  195. package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
  196. package/src/services/__tests__/proof-verifier.test.ts +489 -0
  197. package/src/services/__tests__/provider-resolution.integration.test.ts +202 -0
  198. package/src/services/__tests__/provider-resolver.test.ts +213 -0
  199. package/src/services/__tests__/storage.service.test.ts +358 -0
  200. package/src/services/access-control.service.ts +990 -0
  201. package/src/services/authorization/authorization-registry.ts +66 -0
  202. package/src/services/authorization/types.ts +71 -0
  203. package/src/services/batch-delegation.service.ts +137 -0
  204. package/src/services/crypto.service.ts +302 -0
  205. package/src/services/errors.ts +76 -0
  206. package/src/services/index.ts +18 -0
  207. package/src/services/oauth-config.service.d.ts +53 -0
  208. package/src/services/oauth-config.service.d.ts.map +1 -0
  209. package/src/services/oauth-config.service.js.map +1 -0
  210. package/src/services/oauth-config.service.ts +192 -0
  211. package/src/services/oauth-provider-registry.d.ts +57 -0
  212. package/src/services/oauth-provider-registry.d.ts.map +1 -0
  213. package/src/services/oauth-provider-registry.js.map +1 -0
  214. package/src/services/oauth-provider-registry.ts +141 -0
  215. package/src/services/oauth-service.ts +544 -0
  216. package/src/services/oauth-token-retrieval.service.ts +245 -0
  217. package/src/services/proof-verifier.ts +478 -0
  218. package/src/services/provider-resolver.d.ts +48 -0
  219. package/src/services/provider-resolver.d.ts.map +1 -0
  220. package/src/services/provider-resolver.js.map +1 -0
  221. package/src/services/provider-resolver.ts +146 -0
  222. package/src/services/provider-validator.ts +170 -0
  223. package/src/services/session-registration.service.ts +317 -0
  224. package/src/services/storage.service.ts +566 -0
  225. package/src/services/tool-context-builder.ts +172 -0
  226. package/src/services/tool-protection.service.ts +982 -0
  227. package/src/types/oauth-required-error.ts +63 -0
  228. package/src/types/tool-protection.ts +155 -0
  229. package/src/utils/__tests__/did-helpers.test.ts +101 -0
  230. package/src/utils/base64.ts +148 -0
  231. package/src/utils/cors.ts +83 -0
  232. package/src/utils/did-helpers.ts +150 -0
  233. package/src/utils/index.ts +8 -0
  234. package/src/utils/storage-keys.ts +278 -0
  235. package/tsconfig.json +21 -0
  236. package/vitest.config.ts +56 -0
@@ -0,0 +1,351 @@
1
+ /**
2
+ * Batch Delegation Service Tests
3
+ *
4
+ * @package @kya-os/mcp-i-core
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, vi } from "vitest";
8
+ import { BatchDelegationService } from "../batch-delegation.service.js";
9
+ import { ProviderResolver } from "../provider-resolver.js";
10
+ import { ToolProtectionService } from "../tool-protection.service.js";
11
+ import type { ToolProtection } from "@kya-os/contracts/tool-protection";
12
+
13
+ describe("BatchDelegationService", () => {
14
+ let mockProviderResolver: ProviderResolver;
15
+ let mockToolProtectionService: ToolProtectionService;
16
+ let service: BatchDelegationService;
17
+
18
+ beforeEach(() => {
19
+ mockProviderResolver = {
20
+ resolveProvider: vi.fn(),
21
+ } as any;
22
+
23
+ mockToolProtectionService = {
24
+ checkToolProtection: vi.fn(),
25
+ } as any;
26
+
27
+ service = new BatchDelegationService(
28
+ mockProviderResolver,
29
+ mockToolProtectionService
30
+ );
31
+ });
32
+
33
+ describe("groupToolsByProvider", () => {
34
+ it("should group tools by provider", async () => {
35
+ const toolProtection1: ToolProtection = {
36
+ requiresDelegation: true,
37
+ requiredScopes: ["github:repo:read"],
38
+ oauthProvider: "github",
39
+ };
40
+
41
+ const toolProtection2: ToolProtection = {
42
+ requiresDelegation: true,
43
+ requiredScopes: ["github:repo:write"],
44
+ oauthProvider: "github",
45
+ };
46
+
47
+ const toolProtection3: ToolProtection = {
48
+ requiresDelegation: true,
49
+ requiredScopes: ["google:calendar:read"],
50
+ oauthProvider: "google",
51
+ };
52
+
53
+ (mockToolProtectionService.checkToolProtection as any)
54
+ .mockResolvedValueOnce(toolProtection1)
55
+ .mockResolvedValueOnce(toolProtection2)
56
+ .mockResolvedValueOnce(toolProtection3);
57
+
58
+ (mockProviderResolver.resolveProvider as any)
59
+ .mockResolvedValueOnce("github")
60
+ .mockResolvedValueOnce("github")
61
+ .mockResolvedValueOnce("google");
62
+
63
+ const groups = await service.groupToolsByProvider(
64
+ ["tool1", "tool2", "tool3"],
65
+ "test-project",
66
+ "did:key:test"
67
+ );
68
+
69
+ expect(groups.size).toBe(2);
70
+ expect(groups.get("github")?.tools).toEqual(["tool1", "tool2"]);
71
+ expect(groups.get("google")?.tools).toEqual(["tool3"]);
72
+ });
73
+
74
+ it("should merge scopes for tools in same group", async () => {
75
+ const toolProtection1: ToolProtection = {
76
+ requiresDelegation: true,
77
+ requiredScopes: ["github:repo:read", "github:user:read"],
78
+ oauthProvider: "github",
79
+ };
80
+
81
+ const toolProtection2: ToolProtection = {
82
+ requiresDelegation: true,
83
+ requiredScopes: ["github:repo:write"],
84
+ oauthProvider: "github",
85
+ };
86
+
87
+ (mockToolProtectionService.checkToolProtection as any)
88
+ .mockResolvedValueOnce(toolProtection1)
89
+ .mockResolvedValueOnce(toolProtection2);
90
+
91
+ (mockProviderResolver.resolveProvider as any)
92
+ .mockResolvedValue("github");
93
+
94
+ const groups = await service.groupToolsByProvider(
95
+ ["tool1", "tool2"],
96
+ "test-project",
97
+ "did:key:test"
98
+ );
99
+
100
+ const githubGroup = groups.get("github");
101
+ expect(githubGroup?.scopes).toContain("github:repo:read");
102
+ expect(githubGroup?.scopes).toContain("github:user:read");
103
+ expect(githubGroup?.scopes).toContain("github:repo:write");
104
+ expect(githubGroup?.scopes.length).toBe(3); // No duplicates
105
+ });
106
+
107
+ it("should use highest risk level in group", async () => {
108
+ const toolProtection1: ToolProtection = {
109
+ requiresDelegation: true,
110
+ requiredScopes: ["github:repo:read"],
111
+ oauthProvider: "github",
112
+ riskLevel: "low",
113
+ };
114
+
115
+ const toolProtection2: ToolProtection = {
116
+ requiresDelegation: true,
117
+ requiredScopes: ["github:repo:write"],
118
+ oauthProvider: "github",
119
+ riskLevel: "high",
120
+ };
121
+
122
+ (mockToolProtectionService.checkToolProtection as any)
123
+ .mockResolvedValueOnce(toolProtection1)
124
+ .mockResolvedValueOnce(toolProtection2);
125
+
126
+ (mockProviderResolver.resolveProvider as any)
127
+ .mockResolvedValue("github");
128
+
129
+ const groups = await service.groupToolsByProvider(
130
+ ["tool1", "tool2"],
131
+ "test-project",
132
+ "did:key:test"
133
+ );
134
+
135
+ expect(groups.get("github")?.riskLevel).toBe("high");
136
+ });
137
+
138
+ it("should skip tools that don't require delegation", async () => {
139
+ const toolProtection1: ToolProtection = {
140
+ requiresDelegation: true,
141
+ requiredScopes: ["github:repo:read"],
142
+ oauthProvider: "github",
143
+ };
144
+
145
+ const toolProtection2: ToolProtection = {
146
+ requiresDelegation: false,
147
+ requiredScopes: [],
148
+ };
149
+
150
+ (mockToolProtectionService.checkToolProtection as any)
151
+ .mockResolvedValueOnce(toolProtection1)
152
+ .mockResolvedValueOnce(toolProtection2);
153
+
154
+ (mockProviderResolver.resolveProvider as any).mockResolvedValue("github");
155
+
156
+ const groups = await service.groupToolsByProvider(
157
+ ["tool1", "tool2"],
158
+ "test-project",
159
+ "did:key:test"
160
+ );
161
+
162
+ expect(groups.get("github")?.tools).toEqual(["tool1"]);
163
+ expect(groups.get("github")?.tools).not.toContain("tool2");
164
+ });
165
+
166
+ it("should handle tools with provider resolution errors gracefully", async () => {
167
+ const toolProtection: ToolProtection = {
168
+ requiresDelegation: true,
169
+ requiredScopes: ["github:repo:read"],
170
+ oauthProvider: "github",
171
+ };
172
+
173
+ (mockToolProtectionService.checkToolProtection as any).mockResolvedValue(
174
+ toolProtection
175
+ );
176
+
177
+ (mockProviderResolver.resolveProvider as any).mockRejectedValue(
178
+ new Error("Provider not found")
179
+ );
180
+
181
+ const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
182
+
183
+ const groups = await service.groupToolsByProvider(
184
+ ["tool1"],
185
+ "test-project",
186
+ "did:key:test"
187
+ );
188
+
189
+ expect(groups.size).toBe(0);
190
+ expect(consoleSpy).toHaveBeenCalled();
191
+
192
+ consoleSpy.mockRestore();
193
+ });
194
+
195
+ it("should return empty map when no tools require delegation", async () => {
196
+ const toolProtection: ToolProtection = {
197
+ requiresDelegation: false,
198
+ requiredScopes: [],
199
+ };
200
+
201
+ (mockToolProtectionService.checkToolProtection as any).mockResolvedValue(
202
+ toolProtection
203
+ );
204
+
205
+ const groups = await service.groupToolsByProvider(
206
+ ["tool1"],
207
+ "test-project",
208
+ "did:key:test"
209
+ );
210
+
211
+ expect(groups.size).toBe(0);
212
+ });
213
+
214
+ it("should group tools with oauthProvider field specified", async () => {
215
+ const toolProtection1: ToolProtection = {
216
+ requiresDelegation: true,
217
+ requiredScopes: ["repo:read"],
218
+ oauthProvider: "github", // Explicit provider
219
+ };
220
+
221
+ const toolProtection2: ToolProtection = {
222
+ requiresDelegation: true,
223
+ requiredScopes: ["repo:write"],
224
+ oauthProvider: "github", // Same explicit provider
225
+ };
226
+
227
+ (mockToolProtectionService.checkToolProtection as any)
228
+ .mockResolvedValueOnce(toolProtection1)
229
+ .mockResolvedValueOnce(toolProtection2);
230
+
231
+ (mockProviderResolver.resolveProvider as any)
232
+ .mockResolvedValueOnce("github")
233
+ .mockResolvedValueOnce("github");
234
+
235
+ const groups = await service.groupToolsByProvider(
236
+ ["tool1", "tool2"],
237
+ "test-project",
238
+ "did:key:test"
239
+ );
240
+
241
+ expect(groups.size).toBe(1);
242
+ expect(groups.get("github")?.tools).toEqual(["tool1", "tool2"]);
243
+ expect(mockProviderResolver.resolveProvider).toHaveBeenCalledTimes(2);
244
+ });
245
+
246
+ it("should group tools with oauthProvider field missing (fallback behavior)", async () => {
247
+ const toolProtection1: ToolProtection = {
248
+ requiresDelegation: true,
249
+ requiredScopes: ["github:repo:read"],
250
+ // No oauthProvider - should use scope inference
251
+ };
252
+
253
+ const toolProtection2: ToolProtection = {
254
+ requiresDelegation: true,
255
+ requiredScopes: ["github:repo:write"],
256
+ // No oauthProvider - should use scope inference
257
+ };
258
+
259
+ (mockToolProtectionService.checkToolProtection as any)
260
+ .mockResolvedValueOnce(toolProtection1)
261
+ .mockResolvedValueOnce(toolProtection2);
262
+
263
+ // ProviderResolver should infer from scopes
264
+ (mockProviderResolver.resolveProvider as any)
265
+ .mockResolvedValueOnce("github")
266
+ .mockResolvedValueOnce("github");
267
+
268
+ const groups = await service.groupToolsByProvider(
269
+ ["tool1", "tool2"],
270
+ "test-project",
271
+ "did:key:test"
272
+ );
273
+
274
+ expect(groups.size).toBe(1);
275
+ expect(groups.get("github")?.tools).toEqual(["tool1", "tool2"]);
276
+ // Should still call resolveProvider (which handles fallback)
277
+ expect(mockProviderResolver.resolveProvider).toHaveBeenCalledTimes(2);
278
+ });
279
+
280
+ it("should handle empty groups when all tools skip delegation", async () => {
281
+ const toolProtection1: ToolProtection = {
282
+ requiresDelegation: false,
283
+ requiredScopes: [],
284
+ };
285
+
286
+ const toolProtection2: ToolProtection = {
287
+ requiresDelegation: false,
288
+ requiredScopes: [],
289
+ };
290
+
291
+ (mockToolProtectionService.checkToolProtection as any)
292
+ .mockResolvedValueOnce(toolProtection1)
293
+ .mockResolvedValueOnce(toolProtection2);
294
+
295
+ const groups = await service.groupToolsByProvider(
296
+ ["tool1", "tool2"],
297
+ "test-project",
298
+ "did:key:test"
299
+ );
300
+
301
+ expect(groups.size).toBe(0);
302
+ // Should not call resolveProvider if no tools require delegation
303
+ expect(mockProviderResolver.resolveProvider).not.toHaveBeenCalled();
304
+ });
305
+
306
+ it("should handle mixed tools with and without oauthProvider", async () => {
307
+ const toolProtection1: ToolProtection = {
308
+ requiresDelegation: true,
309
+ requiredScopes: ["repo:read"],
310
+ oauthProvider: "github", // Explicit
311
+ };
312
+
313
+ const toolProtection2: ToolProtection = {
314
+ requiresDelegation: true,
315
+ requiredScopes: ["gmail:read"],
316
+ // No oauthProvider - should infer google from scope
317
+ };
318
+
319
+ (mockToolProtectionService.checkToolProtection as any)
320
+ .mockResolvedValueOnce(toolProtection1)
321
+ .mockResolvedValueOnce(toolProtection2);
322
+
323
+ (mockProviderResolver.resolveProvider as any)
324
+ .mockResolvedValueOnce("github")
325
+ .mockResolvedValueOnce("google");
326
+
327
+ const groups = await service.groupToolsByProvider(
328
+ ["tool1", "tool2"],
329
+ "test-project",
330
+ "did:key:test"
331
+ );
332
+
333
+ expect(groups.size).toBe(2);
334
+ expect(groups.get("github")?.tools).toEqual(["tool1"]);
335
+ expect(groups.get("google")?.tools).toEqual(["tool2"]);
336
+ });
337
+ });
338
+
339
+ describe("getToolsForProvider", () => {
340
+ it("should return empty array (not yet implemented)", async () => {
341
+ const tools = await service.getToolsForProvider(
342
+ "github",
343
+ "test-project",
344
+ "did:key:test"
345
+ );
346
+
347
+ expect(tools).toEqual([]);
348
+ });
349
+ });
350
+ });
351
+