effect 4.1.0 → 4.1.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 (354) hide show
  1. package/README.md +30 -0
  2. package/THIRD_PARTY_NOTICES.md +62 -0
  3. package/dist/BigDecimal.d.ts +139 -7
  4. package/dist/BigDecimal.d.ts.map +1 -1
  5. package/dist/BigDecimal.js +83 -14
  6. package/dist/BigDecimal.js.map +1 -1
  7. package/dist/Equal.d.ts +2 -2
  8. package/dist/Equal.d.ts.map +1 -1
  9. package/dist/Equal.js +3 -3
  10. package/dist/Equal.js.map +1 -1
  11. package/dist/Hash.d.ts +3 -2
  12. package/dist/Hash.d.ts.map +1 -1
  13. package/dist/Hash.js +5 -4
  14. package/dist/Hash.js.map +1 -1
  15. package/dist/Iterable.d.ts.map +1 -1
  16. package/dist/Iterable.js +11 -8
  17. package/dist/Iterable.js.map +1 -1
  18. package/dist/Path.d.ts.map +1 -1
  19. package/dist/Path.js +5 -1
  20. package/dist/Path.js.map +1 -1
  21. package/dist/Pool.d.ts.map +1 -1
  22. package/dist/Pool.js +2 -1
  23. package/dist/Pool.js.map +1 -1
  24. package/dist/Schema.d.ts +2 -1
  25. package/dist/Schema.d.ts.map +1 -1
  26. package/dist/Schema.js +2 -1
  27. package/dist/Schema.js.map +1 -1
  28. package/dist/SchemaRepresentation.d.ts +4 -2
  29. package/dist/SchemaRepresentation.d.ts.map +1 -1
  30. package/dist/SchemaRepresentation.js +4 -2
  31. package/dist/SchemaRepresentation.js.map +1 -1
  32. package/dist/SchemaTransformation.d.ts +2 -2
  33. package/dist/SchemaTransformation.js +2 -2
  34. package/dist/ai/McpClient.d.ts +171 -19
  35. package/dist/ai/McpClient.d.ts.map +1 -1
  36. package/dist/ai/McpClient.js +277 -66
  37. package/dist/ai/McpClient.js.map +1 -1
  38. package/dist/ai/McpSchema.d.ts +108 -0
  39. package/dist/ai/McpSchema.d.ts.map +1 -1
  40. package/dist/ai/McpSchema.js +99 -0
  41. package/dist/ai/McpSchema.js.map +1 -1
  42. package/dist/ai/McpServer.d.ts +120 -6
  43. package/dist/ai/McpServer.d.ts.map +1 -1
  44. package/dist/ai/McpServer.js +215 -34
  45. package/dist/ai/McpServer.js.map +1 -1
  46. package/dist/ai/Prompt.d.ts.map +1 -1
  47. package/dist/ai/Prompt.js +20 -11
  48. package/dist/ai/Prompt.js.map +1 -1
  49. package/dist/ai/Tool.d.ts.map +1 -1
  50. package/dist/ai/Tool.js +5 -7
  51. package/dist/ai/Tool.js.map +1 -1
  52. package/dist/ai/internal/mcpCore.js +72 -3
  53. package/dist/ai/internal/mcpCore.js.map +1 -1
  54. package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
  55. package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
  56. package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
  57. package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
  58. package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
  59. package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
  60. package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
  61. package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
  62. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +42 -7
  63. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  64. package/dist/ai/internal/mcpProtocol.js +12 -0
  65. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  66. package/dist/ai/internal/mcpRuntime.js +3 -3
  67. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  68. package/dist/ai/internal/mcpSkills.d.ts +2 -0
  69. package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
  70. package/dist/ai/internal/mcpSkills.js +428 -0
  71. package/dist/ai/internal/mcpSkills.js.map +1 -0
  72. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  73. package/dist/cluster/ClusterWorkflowEngine.js +15 -5
  74. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  75. package/dist/cluster/Reply.d.ts +1 -1
  76. package/dist/cluster/Reply.d.ts.map +1 -1
  77. package/dist/cluster/Reply.js.map +1 -1
  78. package/dist/cluster/Sharding.d.ts.map +1 -1
  79. package/dist/cluster/Sharding.js +4 -1
  80. package/dist/cluster/Sharding.js.map +1 -1
  81. package/dist/cluster/internal/entityManager.js +15 -7
  82. package/dist/cluster/internal/entityManager.js.map +1 -1
  83. package/dist/crypto/Aead.d.ts +2 -1
  84. package/dist/crypto/Aead.d.ts.map +1 -1
  85. package/dist/crypto/Aead.js +2 -2
  86. package/dist/crypto/Aead.js.map +1 -1
  87. package/dist/crypto/Cipher.d.ts +2 -1
  88. package/dist/crypto/Cipher.d.ts.map +1 -1
  89. package/dist/crypto/Cipher.js +2 -2
  90. package/dist/crypto/Cipher.js.map +1 -1
  91. package/dist/crypto/KeyAgreement.d.ts +3 -2
  92. package/dist/crypto/KeyAgreement.d.ts.map +1 -1
  93. package/dist/crypto/KeyAgreement.js +2 -2
  94. package/dist/crypto/KeyAgreement.js.map +1 -1
  95. package/dist/crypto/KeyFormat.d.ts +33 -1
  96. package/dist/crypto/KeyFormat.d.ts.map +1 -1
  97. package/dist/crypto/KeyFormat.js.map +1 -1
  98. package/dist/crypto/KeyWrap.d.ts +4 -3
  99. package/dist/crypto/KeyWrap.d.ts.map +1 -1
  100. package/dist/crypto/KeyWrap.js +2 -2
  101. package/dist/crypto/KeyWrap.js.map +1 -1
  102. package/dist/crypto/Mac.d.ts +2 -1
  103. package/dist/crypto/Mac.d.ts.map +1 -1
  104. package/dist/crypto/Mac.js +2 -2
  105. package/dist/crypto/Mac.js.map +1 -1
  106. package/dist/crypto/Signature.d.ts +3 -2
  107. package/dist/crypto/Signature.d.ts.map +1 -1
  108. package/dist/crypto/Signature.js +2 -2
  109. package/dist/crypto/Signature.js.map +1 -1
  110. package/dist/crypto/WebCrypto.d.ts +19 -0
  111. package/dist/crypto/WebCrypto.d.ts.map +1 -1
  112. package/dist/crypto/WebCrypto.js.map +1 -1
  113. package/dist/crypto/internal/asymmetric.js.map +1 -1
  114. package/dist/crypto/internal/key.js +3 -2
  115. package/dist/crypto/internal/key.js.map +1 -1
  116. package/dist/encoding/Base64Url.d.ts.map +1 -1
  117. package/dist/encoding/Base64Url.js +26 -3
  118. package/dist/encoding/Base64Url.js.map +1 -1
  119. package/dist/encoding/Ndjson.d.ts +5 -4
  120. package/dist/encoding/Ndjson.d.ts.map +1 -1
  121. package/dist/encoding/Ndjson.js +6 -5
  122. package/dist/encoding/Ndjson.js.map +1 -1
  123. package/dist/encoding/Sse.d.ts +3 -1
  124. package/dist/encoding/Sse.d.ts.map +1 -1
  125. package/dist/encoding/Sse.js +5 -3
  126. package/dist/encoding/Sse.js.map +1 -1
  127. package/dist/eventlog/EventLog.d.ts +7 -0
  128. package/dist/eventlog/EventLog.d.ts.map +1 -1
  129. package/dist/eventlog/EventLog.js +7 -0
  130. package/dist/eventlog/EventLog.js.map +1 -1
  131. package/dist/eventlog/EventLogServer.d.ts +7 -1
  132. package/dist/eventlog/EventLogServer.d.ts.map +1 -1
  133. package/dist/eventlog/EventLogServer.js +27 -12
  134. package/dist/eventlog/EventLogServer.js.map +1 -1
  135. package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
  136. package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
  137. package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
  138. package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
  139. package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
  140. package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
  141. package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
  142. package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
  143. package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
  144. package/dist/eventlog/EventLogSessionAuth.js +22 -0
  145. package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
  146. package/dist/graphql/GraphQLClient.d.ts +3 -1
  147. package/dist/graphql/GraphQLClient.d.ts.map +1 -1
  148. package/dist/graphql/GraphQLClient.js.map +1 -1
  149. package/dist/graphql/GraphQLClientError.d.ts +14 -9
  150. package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
  151. package/dist/graphql/GraphQLClientError.js +14 -21
  152. package/dist/graphql/GraphQLClientError.js.map +1 -1
  153. package/dist/graphql/GraphQLProtocol.d.ts +12 -3
  154. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
  155. package/dist/graphql/GraphQLProtocol.js +4 -1
  156. package/dist/graphql/GraphQLProtocol.js.map +1 -1
  157. package/dist/http/HttpClient.d.ts +189 -7
  158. package/dist/http/HttpClient.d.ts.map +1 -1
  159. package/dist/http/HttpClient.js +75 -2
  160. package/dist/http/HttpClient.js.map +1 -1
  161. package/dist/http/HttpServerRequest.d.ts.map +1 -1
  162. package/dist/http/HttpServerRequest.js +38 -22
  163. package/dist/http/HttpServerRequest.js.map +1 -1
  164. package/dist/http/internal/mimeTypes.d.ts +4 -1
  165. package/dist/http/internal/mimeTypes.d.ts.map +1 -1
  166. package/dist/http/internal/mimeTypes.js +4 -1
  167. package/dist/http/internal/mimeTypes.js.map +1 -1
  168. package/dist/http-api/HttpApiSchema.js +1 -1
  169. package/dist/http-api/HttpApiSchema.js.map +1 -1
  170. package/dist/internal/dateTime.js +49 -24
  171. package/dist/internal/dateTime.js.map +1 -1
  172. package/dist/internal/effect.js +10 -0
  173. package/dist/internal/effect.js.map +1 -1
  174. package/dist/internal/glob.js +234 -56
  175. package/dist/internal/glob.js.map +1 -1
  176. package/dist/internal/graphql.js +21 -7
  177. package/dist/internal/graphql.js.map +1 -1
  178. package/dist/internal/graphqlHeaders.d.ts +7 -0
  179. package/dist/internal/graphqlHeaders.d.ts.map +1 -1
  180. package/dist/internal/graphqlHeaders.js +11 -0
  181. package/dist/internal/graphqlHeaders.js.map +1 -1
  182. package/dist/internal/request.js +29 -42
  183. package/dist/internal/request.js.map +1 -1
  184. package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
  185. package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
  186. package/dist/internal/version.js +1 -1
  187. package/dist/jose/JoseError.d.ts +285 -0
  188. package/dist/jose/JoseError.d.ts.map +1 -0
  189. package/dist/jose/JoseError.js +269 -0
  190. package/dist/jose/JoseError.js.map +1 -0
  191. package/dist/jose/Jwe.d.ts +128 -0
  192. package/dist/jose/Jwe.d.ts.map +1 -0
  193. package/dist/jose/Jwe.js +134 -0
  194. package/dist/jose/Jwe.js.map +1 -0
  195. package/dist/jose/Jwk.d.ts +597 -0
  196. package/dist/jose/Jwk.d.ts.map +1 -0
  197. package/dist/jose/Jwk.js +503 -0
  198. package/dist/jose/Jwk.js.map +1 -0
  199. package/dist/jose/Jwks.d.ts +236 -0
  200. package/dist/jose/Jwks.d.ts.map +1 -0
  201. package/dist/jose/Jwks.js +306 -0
  202. package/dist/jose/Jwks.js.map +1 -0
  203. package/dist/jose/Jws.d.ts +177 -0
  204. package/dist/jose/Jws.d.ts.map +1 -0
  205. package/dist/jose/Jws.js +183 -0
  206. package/dist/jose/Jws.js.map +1 -0
  207. package/dist/jose/Jwt.d.ts +270 -0
  208. package/dist/jose/Jwt.d.ts.map +1 -0
  209. package/dist/jose/Jwt.js +293 -0
  210. package/dist/jose/Jwt.js.map +1 -0
  211. package/dist/jose/index.d.ts +51 -0
  212. package/dist/jose/index.d.ts.map +1 -0
  213. package/dist/jose/index.js +52 -0
  214. package/dist/jose/index.js.map +1 -0
  215. package/dist/jose/internal/compact.d.ts +2 -0
  216. package/dist/jose/internal/compact.d.ts.map +1 -0
  217. package/dist/jose/internal/compact.js +179 -0
  218. package/dist/jose/internal/compact.js.map +1 -0
  219. package/dist/net/Host.d.ts.map +1 -1
  220. package/dist/net/Host.js +5 -10
  221. package/dist/net/Host.js.map +1 -1
  222. package/dist/observability/OtlpSerialization.d.ts +1 -1
  223. package/dist/observability/OtlpSerialization.d.ts.map +1 -1
  224. package/dist/observability/OtlpSerialization.js +10 -4
  225. package/dist/observability/OtlpSerialization.js.map +1 -1
  226. package/dist/observability/internal/otlpProtobuf.js +3 -3
  227. package/dist/observability/internal/otlpProtobuf.js.map +1 -1
  228. package/dist/observability/internal/protobuf.js +6 -0
  229. package/dist/observability/internal/protobuf.js.map +1 -1
  230. package/dist/persistence/CircuitBreaker.d.ts +98 -38
  231. package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
  232. package/dist/persistence/CircuitBreaker.js +67 -29
  233. package/dist/persistence/CircuitBreaker.js.map +1 -1
  234. package/dist/persistence/RateLimiter.d.ts +8 -0
  235. package/dist/persistence/RateLimiter.d.ts.map +1 -1
  236. package/dist/persistence/RateLimiter.js +63 -12
  237. package/dist/persistence/RateLimiter.js.map +1 -1
  238. package/dist/persistence/index.d.ts +1 -1
  239. package/dist/persistence/index.js +1 -1
  240. package/dist/reactivity/AtomRegistry.js +17 -7
  241. package/dist/reactivity/AtomRegistry.js.map +1 -1
  242. package/dist/rpc/Rpc.d.ts +1 -1
  243. package/dist/rpc/Rpc.d.ts.map +1 -1
  244. package/dist/rpc/Rpc.js.map +1 -1
  245. package/dist/rpc/RpcClient.d.ts +14 -2
  246. package/dist/rpc/RpcClient.d.ts.map +1 -1
  247. package/dist/rpc/RpcClient.js +38 -21
  248. package/dist/rpc/RpcClient.js.map +1 -1
  249. package/dist/rpc/RpcMessage.d.ts +3 -2
  250. package/dist/rpc/RpcMessage.d.ts.map +1 -1
  251. package/dist/rpc/RpcMessage.js.map +1 -1
  252. package/dist/sql/Migrator.d.ts +9 -1
  253. package/dist/sql/Migrator.d.ts.map +1 -1
  254. package/dist/sql/Migrator.js +18 -6
  255. package/dist/sql/Migrator.js.map +1 -1
  256. package/dist/sql/SqlClient.d.ts +38 -3
  257. package/dist/sql/SqlClient.d.ts.map +1 -1
  258. package/dist/sql/SqlClient.js +4 -3
  259. package/dist/sql/SqlClient.js.map +1 -1
  260. package/dist/sql/Statement.d.ts +41 -3
  261. package/dist/sql/Statement.d.ts.map +1 -1
  262. package/dist/sql/Statement.js +24 -20
  263. package/dist/sql/Statement.js.map +1 -1
  264. package/dist/workflow/Workflow.d.ts +4 -0
  265. package/dist/workflow/Workflow.d.ts.map +1 -1
  266. package/dist/workflow/Workflow.js.map +1 -1
  267. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  268. package/dist/workflow/WorkflowEngine.js +5 -0
  269. package/dist/workflow/WorkflowEngine.js.map +1 -1
  270. package/package.json +5 -1
  271. package/src/BigDecimal.ts +170 -16
  272. package/src/Equal.ts +3 -3
  273. package/src/Hash.ts +5 -4
  274. package/src/Iterable.ts +8 -5
  275. package/src/Path.ts +6 -1
  276. package/src/Pool.ts +2 -1
  277. package/src/Schema.ts +3 -2
  278. package/src/SchemaRepresentation.ts +4 -2
  279. package/src/SchemaTransformation.ts +2 -2
  280. package/src/ai/McpClient.ts +488 -91
  281. package/src/ai/McpSchema.ts +119 -0
  282. package/src/ai/McpServer.ts +261 -38
  283. package/src/ai/Prompt.ts +16 -11
  284. package/src/ai/Tool.ts +5 -10
  285. package/src/ai/internal/mcpCore.ts +165 -12
  286. package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
  287. package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
  288. package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
  289. package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
  290. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +41 -7
  291. package/src/ai/internal/mcpProtocol.ts +15 -0
  292. package/src/ai/internal/mcpRuntime.ts +6 -3
  293. package/src/ai/internal/mcpSkills.ts +480 -0
  294. package/src/cluster/ClusterWorkflowEngine.ts +22 -8
  295. package/src/cluster/Reply.ts +1 -1
  296. package/src/cluster/Sharding.ts +4 -1
  297. package/src/cluster/internal/entityManager.ts +24 -10
  298. package/src/crypto/Aead.ts +3 -3
  299. package/src/crypto/Cipher.ts +3 -3
  300. package/src/crypto/KeyAgreement.ts +4 -4
  301. package/src/crypto/KeyFormat.ts +30 -1
  302. package/src/crypto/KeyWrap.ts +6 -6
  303. package/src/crypto/Mac.ts +3 -3
  304. package/src/crypto/Signature.ts +4 -4
  305. package/src/crypto/WebCrypto.ts +20 -0
  306. package/src/crypto/internal/asymmetric.ts +2 -1
  307. package/src/crypto/internal/key.ts +12 -5
  308. package/src/encoding/Base64Url.ts +27 -3
  309. package/src/encoding/Ndjson.ts +6 -5
  310. package/src/encoding/Sse.ts +5 -3
  311. package/src/eventlog/EventLog.ts +7 -0
  312. package/src/eventlog/EventLogServer.ts +31 -12
  313. package/src/eventlog/EventLogServerEncrypted.ts +5 -0
  314. package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
  315. package/src/eventlog/EventLogSessionAuth.ts +26 -0
  316. package/src/graphql/GraphQLClient.ts +3 -1
  317. package/src/graphql/GraphQLClientError.ts +15 -22
  318. package/src/graphql/GraphQLProtocol.ts +12 -3
  319. package/src/http/HttpClient.ts +210 -11
  320. package/src/http/HttpServerRequest.ts +61 -23
  321. package/src/http/internal/mimeTypes.ts +4 -1
  322. package/src/http-api/HttpApiSchema.ts +1 -1
  323. package/src/internal/dateTime.ts +54 -24
  324. package/src/internal/effect.ts +6 -0
  325. package/src/internal/glob.ts +279 -63
  326. package/src/internal/graphql.ts +27 -9
  327. package/src/internal/graphqlHeaders.ts +13 -0
  328. package/src/internal/request.ts +51 -58
  329. package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
  330. package/src/internal/version.ts +1 -1
  331. package/src/jose/JoseError.ts +329 -0
  332. package/src/jose/Jwe.ts +196 -0
  333. package/src/jose/Jwk.ts +777 -0
  334. package/src/jose/Jwks.ts +418 -0
  335. package/src/jose/Jws.ts +312 -0
  336. package/src/jose/Jwt.ts +493 -0
  337. package/src/jose/index.ts +58 -0
  338. package/src/jose/internal/compact.ts +195 -0
  339. package/src/net/Host.ts +5 -10
  340. package/src/observability/OtlpSerialization.ts +13 -5
  341. package/src/observability/internal/otlpProtobuf.ts +6 -6
  342. package/src/observability/internal/protobuf.ts +10 -0
  343. package/src/persistence/CircuitBreaker.ts +113 -48
  344. package/src/persistence/RateLimiter.ts +84 -15
  345. package/src/persistence/index.ts +1 -1
  346. package/src/reactivity/AtomRegistry.ts +19 -8
  347. package/src/rpc/Rpc.ts +3 -1
  348. package/src/rpc/RpcClient.ts +44 -24
  349. package/src/rpc/RpcMessage.ts +3 -2
  350. package/src/sql/Migrator.ts +35 -18
  351. package/src/sql/SqlClient.ts +43 -8
  352. package/src/sql/Statement.ts +52 -22
  353. package/src/workflow/Workflow.ts +4 -0
  354. package/src/workflow/WorkflowEngine.ts +5 -0
@@ -1382,6 +1382,125 @@ export class ResourceUpdatedNotification extends Rpc.make("notifications/resourc
1382
1382
  }
1383
1383
  }) {}
1384
1384
 
1385
+ // =============================================================================
1386
+ // Skills
1387
+ // =============================================================================
1388
+
1389
+ /**
1390
+ * Schema for a file of an Agent Skill served under the MCP Skills extension
1391
+ * (SEP-2640), with the digest and size of its content.
1392
+ *
1393
+ * @stability unstable
1394
+ * @category schemas
1395
+ * @since 4.1.1
1396
+ */
1397
+ export class SkillResource extends Schema.Class<SkillResource>(
1398
+ "@effect/ai/McpSchema/SkillResource"
1399
+ )({
1400
+ /**
1401
+ * The resource URI of the file.
1402
+ */
1403
+ uri: Schema.String,
1404
+ /**
1405
+ * The SHA-256 digest of the file's bytes, as `sha256:` followed by 64
1406
+ * lowercase hexadecimal digits.
1407
+ */
1408
+ digest: Schema.String.check(Schema.isPattern(/^sha256:[0-9a-f]{64}$/)),
1409
+ /**
1410
+ * The length of the file's bytes.
1411
+ */
1412
+ size: Schema.Natural
1413
+ }) {}
1414
+
1415
+ /**
1416
+ * Schema for the YAML frontmatter of an Agent Skill's `SKILL.md`, as the
1417
+ * Agent Skills specification defines it (https://agentskills.io/specification).
1418
+ *
1419
+ * **Details**
1420
+ *
1421
+ * Fields the specification does not define are kept unchanged. `McpServer`
1422
+ * checks a skill's frontmatter against this schema before serving it. The
1423
+ * frontmatter of a {@link Skill} received from a server is only checked for
1424
+ * the `name` and `description` that the Skills extension requires; decode it
1425
+ * with this schema for the other fields.
1426
+ *
1427
+ * @stability unstable
1428
+ * @category schemas
1429
+ * @since 4.1.1
1430
+ */
1431
+ export const SkillFrontmatter = Schema.StructWithRest(
1432
+ Schema.Struct({
1433
+ /**
1434
+ * 1 to 64 lowercase letters, digits and hyphens, with no leading, trailing
1435
+ * or consecutive hyphen. It names the directory holding `SKILL.md`.
1436
+ */
1437
+ name: Schema.String.check(Schema.isMaxLength(64), Schema.isPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)),
1438
+ /**
1439
+ * What the skill does and when to use it, in 1 to 1024 characters.
1440
+ */
1441
+ description: Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(1024)),
1442
+ /**
1443
+ * The skill's license, or the name of a bundled license file.
1444
+ */
1445
+ license: Schema.optionalKey(Schema.String),
1446
+ /**
1447
+ * Environment requirements, in 1 to 500 characters.
1448
+ */
1449
+ compatibility: Schema.optionalKey(Schema.String.check(Schema.isNonEmpty(), Schema.isMaxLength(500))),
1450
+ /**
1451
+ * Additional properties not defined by the specification.
1452
+ */
1453
+ metadata: Schema.optionalKey(Schema.Record(Schema.String, Schema.String)),
1454
+ /**
1455
+ * Space-separated tools the skill asks to run without approval. Hosts must
1456
+ * ignore it for a skill served over MCP unless the user approved the grant.
1457
+ */
1458
+ "allowed-tools": Schema.optionalKey(Schema.String)
1459
+ }),
1460
+ [UnknownObject]
1461
+ )
1462
+
1463
+ /**
1464
+ * The YAML frontmatter of an Agent Skill's `SKILL.md`.
1465
+ *
1466
+ * @stability unstable
1467
+ * @category models
1468
+ * @since 4.1.1
1469
+ */
1470
+ export type SkillFrontmatter = typeof SkillFrontmatter.Type
1471
+
1472
+ /**
1473
+ * Schema for an Agent Skill served under the MCP Skills extension (SEP-2640),
1474
+ * as `skills/list` and `skills/get` describe it.
1475
+ *
1476
+ * @stability unstable
1477
+ * @category schemas
1478
+ * @since 4.1.1
1479
+ */
1480
+ export class Skill extends Schema.Class<Skill>(
1481
+ "@effect/ai/McpSchema/Skill"
1482
+ )({
1483
+ /**
1484
+ * The resource URI of the skill's `SKILL.md`.
1485
+ */
1486
+ uri: Schema.String,
1487
+ /**
1488
+ * The YAML frontmatter of the skill's `SKILL.md`, as a JSON object, with
1489
+ * every field the author wrote. See {@link SkillFrontmatter} for the fields
1490
+ * the Agent Skills specification defines.
1491
+ */
1492
+ frontmatter: Schema.StructWithRest(
1493
+ Schema.Struct({ name: Schema.String, description: Schema.String }),
1494
+ [UnknownObject]
1495
+ ),
1496
+ /**
1497
+ * Every file of the skill, `SKILL.md` included, or `"dynamic"` when the
1498
+ * server generates the skill's content and publishes no digests.
1499
+ */
1500
+ resources: Schema.Union([Schema.Array(SkillResource), Schema.Literal("dynamic")]),
1501
+ _meta: optional(UnknownObject)
1502
+ }) {}
1503
+
1385
1504
  // =============================================================================
1386
1505
  // Prompts
1387
1506
  // =============================================================================
@@ -14,6 +14,7 @@
14
14
  import * as Arr from "../Array.ts"
15
15
  import * as Cause from "../Cause.ts"
16
16
  import * as Context from "../Context.ts"
17
+ import * as Digest from "../crypto/Digest.ts"
17
18
  import * as Data from "../Data.ts"
18
19
  import * as Deferred from "../Deferred.ts"
19
20
  import * as Duration from "../Duration.ts"
@@ -53,6 +54,7 @@ import * as AiError from "./AiError.ts"
53
54
  import * as McpCore from "./internal/mcpCore.ts"
54
55
  import * as McpProtocolInternal from "./internal/mcpProtocol.ts"
55
56
  import * as McpRuntime from "./internal/mcpRuntime.ts"
57
+ import * as McpSkills from "./internal/mcpSkills.ts"
56
58
  import * as InternalStructuredOutput from "./internal/structured-output.ts"
57
59
  import type * as McpProtocol from "./McpProtocol.ts"
58
60
  import * as McpSchema from "./McpSchema.ts"
@@ -106,6 +108,8 @@ interface QueuedServerNotification {
106
108
 
107
109
  const internalState = new WeakMap<object, {
108
110
  readonly core: McpCore.McpCore
111
+ // Registers a skill's own file, skipping the check that reserves it for the skill.
112
+ readonly addSkillFile: McpServer["Service"]["addResource"]
109
113
  readonly notifications: Queue.Dequeue<QueuedServerNotification>
110
114
  readonly notificationDelivery: { consumers: number; listChangeRevision: number; requestEndpoints: number }
111
115
  readonly globalMiddleware: Array<middleware.Fn<any, any, any>>
@@ -314,6 +318,9 @@ export type MiddlewareMethod =
314
318
  | "resources/list"
315
319
  | "resources/templates/list"
316
320
  | "prompts/list"
321
+ | "skills/list"
322
+ | "skills/get"
323
+ | "resources/directory/read"
317
324
 
318
325
  /**
319
326
  * Request metadata passed to MCP middleware.
@@ -322,12 +329,17 @@ export type MiddlewareMethod =
322
329
  *
323
330
  * `target` is the tool name, the requested resource URI, or the prompt name.
324
331
  * For `completion/complete` it is the prompt name, or the URI template of the
325
- * resource template being completed. `payload` holds the request parameters.
332
+ * resource template being completed. For `skills/get` it is the URI of the
333
+ * skill's `SKILL.md`. `payload` holds the request parameters.
326
334
  *
327
335
  * On list requests, middleware runs only when it wraps a {@link visibility},
328
336
  * once for each registration it wraps. `target` is then the tool name, the
329
337
  * resource URI, the URI template or the prompt name of that registration, and
330
- * `payload` is `undefined`.
338
+ * `payload` is `undefined`. `skills/list` and `resources/directory/read` are
339
+ * list requests whose `target` is the `SKILL.md` URI of each skill; a
340
+ * directory read checks only the skills that have the directory. A skill's
341
+ * files are resources, checked as `resources/list` and `resources/read` with
342
+ * their own URIs.
331
343
  *
332
344
  * `headers` are the HTTP request headers. They are empty on stdio, on custom
333
345
  * transports and for in-process calls. `sessionId` is the `Mcp-Session-Id`
@@ -759,7 +771,7 @@ const internalError = new InternalError({ message: "Internal error" })
759
771
  const rejectWith = (error: McpErrorBase) =>
760
772
  Effect.fail(new McpCore.RequestRejected({ code: error.code, message: error.message, data: error.data, error }))
761
773
 
762
- type CallMethod = "tools/call" | "resources/read" | "prompts/get" | "completion/complete"
774
+ type CallMethod = "tools/call" | "resources/read" | "prompts/get" | "completion/complete" | "skills/get"
763
775
 
764
776
  type ListMethod = Exclude<MiddlewareMethod, CallMethod>
765
777
 
@@ -768,6 +780,7 @@ interface NotFoundErrors {
768
780
  readonly "resources/read": McpCore.ResourceNotFound
769
781
  readonly "prompts/get": McpCore.PromptNotFound
770
782
  readonly "completion/complete": InvalidParams
783
+ readonly "skills/get": McpCore.SkillNotFound
771
784
  }
772
785
 
773
786
  const notFound = (method: CallMethod, target: string): NotFoundErrors[CallMethod] => {
@@ -780,6 +793,8 @@ const notFound = (method: CallMethod, target: string): NotFoundErrors[CallMethod
780
793
  return new McpCore.PromptNotFound({ name: target })
781
794
  case "completion/complete":
782
795
  return new InvalidParams({ message: "Unknown completion reference or argument" })
796
+ case "skills/get":
797
+ return new McpCore.SkillNotFound({ uri: target })
783
798
  }
784
799
  }
785
800
 
@@ -1127,6 +1142,38 @@ export class McpServer extends Context.Service<McpServer, {
1127
1142
  ))
1128
1143
  })
1129
1144
 
1145
+ // Checking skill ownership and both writes happen in one synchronous step,
1146
+ // so a skill cannot claim the URI in between. Skill files skip the check.
1147
+ const addResource = (
1148
+ options: Parameters<McpServer["Service"]["addResource"]>[0],
1149
+ checkOwnership: boolean
1150
+ ): Effect.Effect<void> =>
1151
+ Effect.suspend(() => {
1152
+ if (checkOwnership && internalCore.skills.ownsFile(options.resource.uri)) {
1153
+ return Effect.die(
1154
+ new Cause.IllegalArgumentError(`Resource ${options.resource.uri} is a file of a registered skill`)
1155
+ )
1156
+ }
1157
+ const existingIndex = resources.findIndex(({ resource }) => resource.uri === options.resource.uri)
1158
+ if (existingIndex === -1) {
1159
+ resources.push(options)
1160
+ } else {
1161
+ resources[existingIndex] = options
1162
+ }
1163
+ const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
1164
+ internalCore.resources.register({
1165
+ descriptor: options.resource,
1166
+ visible: visible("resources/list", options.resource.uri),
1167
+ isVisible: isEnabled(options.annotations),
1168
+ read: (invocation) =>
1169
+ invoke(options.handle, invocation, {
1170
+ method: "resources/read",
1171
+ target: options.resource.uri,
1172
+ payload: { uri: options.resource.uri }
1173
+ })
1174
+ })
1175
+ return notifications.client["notifications/resources/list_changed"]({})
1176
+ })
1130
1177
  const service = McpServer.of({
1131
1178
  notifications: notifications.client,
1132
1179
  notifyElicitationComplete: ({ clientId, elicitationId }) =>
@@ -1222,27 +1269,7 @@ export class McpServer extends Context.Service<McpServer, {
1222
1269
  get resourceTemplates() {
1223
1270
  return resourceTemplates
1224
1271
  },
1225
- addResource: Effect.fnUntraced(function*(options) {
1226
- const existingIndex = resources.findIndex(({ resource }) => resource.uri === options.resource.uri)
1227
- if (existingIndex === -1) {
1228
- resources.push(options)
1229
- } else {
1230
- resources[existingIndex] = options
1231
- }
1232
- const { invoke, visible } = makeInvoke(options.services, globalMiddleware)
1233
- yield* internalCore.resources.register({
1234
- descriptor: options.resource,
1235
- visible: visible("resources/list", options.resource.uri),
1236
- isVisible: isEnabled(options.annotations),
1237
- read: (invocation) =>
1238
- invoke(options.handle, invocation, {
1239
- method: "resources/read",
1240
- target: options.resource.uri,
1241
- payload: { uri: options.resource.uri }
1242
- })
1243
- })
1244
- yield* notifications.client["notifications/resources/list_changed"]({})
1245
- }),
1272
+ addResource: (options) => addResource(options, true),
1246
1273
  addResourceTemplate: Effect.fnUntraced(
1247
1274
  function*({ annotations, completions, handle, routerPath, services, template }) {
1248
1275
  const existingIndex = resourceTemplates.findIndex(({ template: current }) =>
@@ -1400,6 +1427,7 @@ export class McpServer extends Context.Service<McpServer, {
1400
1427
  })
1401
1428
  internalState.set(service, {
1402
1429
  core: internalCore,
1430
+ addSkillFile: (options) => addResource(options, false),
1403
1431
  notifications: notificationsQueue,
1404
1432
  notificationDelivery,
1405
1433
  globalMiddleware
@@ -1467,9 +1495,11 @@ export interface CacheHint {
1467
1495
  *
1468
1496
  * Each entry sets the freshness and `cacheScope` of one result kind:
1469
1497
  * `server/discover`, `tools/list`, `resources/list`,
1470
- * `resources/templates/list` and `prompts/list`. Omitted entries, and
1471
- * `resources/read` results, keep `{ ttlMs: 0, cacheScope: "private" }`.
1472
- * Earlier protocol revisions have no cache hints and ignore this option.
1498
+ * `resources/templates/list` and `prompts/list`. `skills` covers the Skills
1499
+ * extension's `skills/list`, `skills/get` and `resources/directory/read`.
1500
+ * Omitted entries, and `resources/read` results, keep
1501
+ * `{ ttlMs: 0, cacheScope: "private" }`. Earlier protocol revisions have no
1502
+ * cache hints and ignore this option.
1473
1503
  *
1474
1504
  * These are hints for clients. The server does not cache results, and
1475
1505
  * visibility and invocation checks still run on every request.
@@ -1491,6 +1521,7 @@ export interface CacheHints {
1491
1521
  readonly resources?: CacheHint | undefined
1492
1522
  readonly resourceTemplates?: CacheHint | undefined
1493
1523
  readonly prompts?: CacheHint | undefined
1524
+ readonly skills?: CacheHint | undefined
1494
1525
  }
1495
1526
 
1496
1527
  // Converts each hint to the wire's nonnegative integer `ttlMs` once, when the server is built.
@@ -3058,6 +3089,32 @@ export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
3058
3089
  ? Effect.succeed(toolErrorResult(error.message))
3059
3090
  : Effect.map(encodeFailure(error), (encoded) =>
3060
3091
  new CallToolResult({ isError: true, content: toolResultContent(encoded) }))
3092
+ // `structuredContent` must be JSON and match the advertised `outputSchema`, which is derived from the JSON codec.
3093
+ // The Toolkit has already encoded the result, so only the JSON conversion of its encoded form runs here.
3094
+ const encodeStructured = Schema.encodeUnknownEffect(Schema.toCodecJson(Schema.toEncoded(tool.successSchema))) as (
3095
+ result: unknown
3096
+ ) => Effect.Effect<unknown, Schema.SchemaError, Tool.HandlerServices<Tools[keyof Tools]>>
3097
+ const successResult = (result: unknown) =>
3098
+ encodeStructured(result).pipe(
3099
+ Effect.mapError((cause) =>
3100
+ AiError.make({
3101
+ module: "McpServer",
3102
+ method: "registerToolkit",
3103
+ reason: new AiError.ToolResultEncodingError({
3104
+ toolName: tool.name,
3105
+ toolResult: result,
3106
+ description: cause.message
3107
+ })
3108
+ })
3109
+ ),
3110
+ Effect.map((structuredContent) =>
3111
+ new CallToolResult({
3112
+ isError: false,
3113
+ structuredContent,
3114
+ content: toolResultContent(structuredContent)
3115
+ })
3116
+ )
3117
+ )
3061
3118
  const handleCause = (cause: Cause.Cause<unknown>) => {
3062
3119
  const failure = Cause.findFail(cause)
3063
3120
  if (Result.isSuccess(failure)) {
@@ -3113,21 +3170,27 @@ export const registerToolkit: <Tools extends Record<string, Tool.Any>>(
3113
3170
  Stream.unwrap,
3114
3171
  Stream.runLast,
3115
3172
  Effect.flatMap(Effect.fromOption),
3116
- Effect.flatMap((result) =>
3173
+ Effect.flatMap((result): Effect.Effect<
3174
+ CallToolResult,
3175
+ unknown,
3176
+ Tool.HandlerServices<Tools[keyof Tools]>
3177
+ > => {
3178
+ if (!result.isFailure) {
3179
+ // An absent result, such as `Schema.Void`, has no structured form.
3180
+ return result.encodedResult === undefined
3181
+ ? Effect.succeed(new CallToolResult({ isError: false, content: [] }))
3182
+ : successResult(result.encodedResult)
3183
+ }
3117
3184
  // Declared failures return their encoded payload; anything else is classified by origin.
3118
- result.isFailure && result.failureOrigin !== "handler"
3119
- ? Effect.failCause(Cause.annotate(
3185
+ return result.failureOrigin === "handler"
3186
+ ? Effect.succeed(
3187
+ new CallToolResult({ isError: true, content: toolResultContent(result.encodedResult) })
3188
+ )
3189
+ : Effect.failCause(Cause.annotate(
3120
3190
  Cause.fail(result.result),
3121
3191
  Context.make(Toolkit.FailureOrigin, result.failureOrigin ?? "result")
3122
3192
  ))
3123
- : Effect.succeed(
3124
- new CallToolResult({
3125
- isError: result.isFailure,
3126
- structuredContent: result.isFailure ? undefined : result.encodedResult,
3127
- content: toolResultContent(result.encodedResult)
3128
- })
3129
- )
3130
- ),
3193
+ }),
3131
3194
  Effect.catchCause(handleCause)
3132
3195
  ) as Effect.Effect<CallToolResult, InvalidParams, never>
3133
3196
  },
@@ -3565,6 +3628,166 @@ export const resource: {
3565
3628
  return (options: any) => Layer.effectDiscard(register(options))
3566
3629
  } as any
3567
3630
 
3631
+ /**
3632
+ * A file of a skill: its text, its bytes, or either with an explicit MIME
3633
+ * type. Without one, the MIME type is inferred from the file extension.
3634
+ *
3635
+ * @stability unstable
3636
+ * @category models
3637
+ * @since 4.1.1
3638
+ */
3639
+ export type SkillFile = string | Uint8Array | {
3640
+ readonly content: string | Uint8Array
3641
+ readonly mimeType?: string | undefined
3642
+ }
3643
+
3644
+ /**
3645
+ * Options for {@link registerSkill} and {@link skill}.
3646
+ *
3647
+ * **Details**
3648
+ *
3649
+ * `uri` addresses the skill's `SKILL.md`, as
3650
+ * `skill://<skill-path>/SKILL.md`. Its last path segment is the skill's name,
3651
+ * which must equal the frontmatter's `name`. Other schemes work the same way.
3652
+ *
3653
+ * `files` maps paths relative to the skill root to the skill's files,
3654
+ * `SKILL.md` included. Files are served verbatim, and the skill's listed
3655
+ * frontmatter is parsed from `SKILL.md`. Quote frontmatter strings that a YAML
3656
+ * 1.1 parser reads as another type, such as `yes` or `1.0`.
3657
+ *
3658
+ * @stability unstable
3659
+ * @category models
3660
+ * @since 4.1.1
3661
+ */
3662
+ export interface SkillOptions {
3663
+ readonly uri: string
3664
+ readonly files: { readonly "SKILL.md": SkillFile } & Readonly<Record<string, SkillFile>>
3665
+ readonly annotations?: Context.Context<never> | undefined
3666
+ }
3667
+
3668
+ const skillFileEncoder = new TextEncoder()
3669
+
3670
+ // Bytes are copied so that later changes by the caller cannot alter what is
3671
+ // served under the listed digest. `Buffer#slice` shares memory, so copy with
3672
+ // the `Uint8Array` constructor.
3673
+ const toSkillSourceFile = (path: string, file: SkillFile): McpSkills.SourceFile => {
3674
+ const { content, mimeType } = typeof file === "string" || file instanceof Uint8Array ? { content: file } : file
3675
+ return {
3676
+ path,
3677
+ content: typeof content === "string" ? skillFileEncoder.encode(content) : new Uint8Array(content),
3678
+ mimeType
3679
+ }
3680
+ }
3681
+
3682
+ /**
3683
+ * Registers an Agent Skill under the MCP Skills extension (SEP-2640).
3684
+ *
3685
+ * **Details**
3686
+ *
3687
+ * Each file is registered as a resource, and the skill is listed by
3688
+ * `skills/list` and `skills/get` with a manifest of every file's SHA-256
3689
+ * digest and size, computed from the bytes served. Every directory of the
3690
+ * skill namespace is readable with `resources/directory/read`. The server
3691
+ * declares the extension once any skill is registered.
3692
+ *
3693
+ * Fails when the skill breaks the extension's rules: a name that does not
3694
+ * match the URI, a `SKILL.md` without YAML frontmatter, frontmatter that is
3695
+ * not valid {@link McpSchema.SkillFrontmatter}, a file path that leaves the
3696
+ * skill, or more than 512 files or 16 MiB in all. It also fails when the
3697
+ * skill's directory contains or lies inside another skill's, or one of its
3698
+ * files is already registered as a resource. Registering a resource at the
3699
+ * URI of a skill's file afterwards is a defect.
3700
+ *
3701
+ * SHA-256 runs on `crypto.subtle`.
3702
+ *
3703
+ * @see {@link skill} for the layer-based skill registration wrapper
3704
+ *
3705
+ * @stability unstable
3706
+ * @category handlers
3707
+ * @since 4.1.1
3708
+ */
3709
+ export const registerSkill: (options: SkillOptions) => Effect.Effect<void, Cause.IllegalArgumentError, McpServer> =
3710
+ Effect.fnUntraced(function*(options: SkillOptions) {
3711
+ const files = Object.entries(options.files).map(([path, file]) => toSkillSourceFile(path, file))
3712
+ const skill = yield* McpSkills.makeStaticSkill({ uri: options.uri, files }).pipe(
3713
+ Effect.provide(Digest.layerWebCrypto)
3714
+ )
3715
+
3716
+ const registry = yield* McpServer
3717
+ const { addSkillFile, core, globalMiddleware } = internalState.get(registry)!
3718
+ const services = yield* Effect.context<never>()
3719
+ const annotations = options.annotations ?? Context.empty()
3720
+ const { invoke, visible } = makeInvoke(services, globalMiddleware)
3721
+ // Claims the skill's file URIs before they are registered as resources.
3722
+ yield* core.skills.register({
3723
+ entry: skill.entry,
3724
+ isVisible: isEnabled(annotations),
3725
+ visible: visible("skills/list", options.uri),
3726
+ visibleInDirectories: visible("resources/directory/read", options.uri),
3727
+ get: (invocation) =>
3728
+ invoke(Effect.succeed(skill.entry), invocation, {
3729
+ method: "skills/get",
3730
+ target: options.uri,
3731
+ payload: { uri: options.uri }
3732
+ }),
3733
+ directory: (uri) => skill.directories.get(uri)
3734
+ })
3735
+ for (const { content, resource, text } of skill.files) {
3736
+ const { uri, mimeType } = resource
3737
+ yield* addSkillFile({
3738
+ resource: new Resource(resource),
3739
+ annotations,
3740
+ services,
3741
+ handle: Effect.succeed({
3742
+ contents: [text === undefined ? { uri, mimeType, blob: content } : { uri, mimeType, text }]
3743
+ })
3744
+ })
3745
+ }
3746
+ })
3747
+
3748
+ /**
3749
+ * Creates a layer that registers an Agent Skill under the MCP Skills extension
3750
+ * (SEP-2640).
3751
+ *
3752
+ * **Example** (Publishing a skill with a supporting file)
3753
+ *
3754
+ * ```ts
3755
+ * import { Layer } from "effect"
3756
+ * import { McpProtocol, McpServer } from "effect/ai"
3757
+ *
3758
+ * const RefundsSkill = McpServer.skill({
3759
+ * uri: "skill://billing/refunds/SKILL.md",
3760
+ * files: {
3761
+ * "SKILL.md": `---
3762
+ * name: refunds
3763
+ * description: Process refunds using the billing tools
3764
+ * ---
3765
+ *
3766
+ * Read references/policy.md before issuing a refund.
3767
+ * `,
3768
+ * "references/policy.md": "Refunds above CHF 500 require manager approval.\n"
3769
+ * }
3770
+ * })
3771
+ *
3772
+ * const ServerLayer = RefundsSkill.pipe(
3773
+ * Layer.provide(McpServer.layerHttp({
3774
+ * name: "billing",
3775
+ * version: "1.0.0",
3776
+ * path: "/mcp",
3777
+ * protocols: [McpProtocol.v2026_07_28, McpProtocol.v2025_11_25]
3778
+ * }))
3779
+ * )
3780
+ * ```
3781
+ *
3782
+ * @see {@link registerSkill} for the Effect-level skill registration API
3783
+ *
3784
+ * @stability unstable
3785
+ * @category layers
3786
+ * @since 4.1.1
3787
+ */
3788
+ export const skill = (options: SkillOptions): Layer.Layer<never, Cause.IllegalArgumentError, McpServer> =>
3789
+ Layer.effectDiscard(registerSkill(options))
3790
+
3568
3791
  /**
3569
3792
  * Registers an MCP prompt from an Effect program.
3570
3793
  *
package/src/ai/Prompt.ts CHANGED
@@ -486,6 +486,16 @@ export interface FilePartEncoded extends BasePartEncoded<"file", FilePartOptions
486
486
  */
487
487
  export interface FilePartOptions extends ProviderOptions {}
488
488
 
489
+ const TaggedURL = Schema.TaggedStruct("URL", { href: Schema.URLFromString })
490
+
491
+ const urlFromTaggedURL = SchemaTransformation.transform<URL, typeof TaggedURL.Type>({
492
+ decode: ({ href }) => href,
493
+ encode: (href) => (({
494
+ _tag: "URL",
495
+ href
496
+ }) as const)
497
+ })
498
+
489
499
  /**
490
500
  * Schema for validation and encoding of file parts.
491
501
  *
@@ -535,18 +545,13 @@ export const FilePart: Schema.Struct<{
535
545
  }) as const)
536
546
  })
537
547
  )),
538
- Schema.TaggedStruct("URL", { href: Schema.URLFromString }).pipe(Schema.decodeTo(
539
- Schema.URL,
540
- SchemaTransformation.transform({
541
- decode: ({ href }) => href,
542
- encode: (href) => (({
543
- _tag: "URL",
544
- href
545
- }) as const)
546
- })
547
- )),
548
+ TaggedURL.pipe(Schema.decodeTo(Schema.URL, urlFromTaggedURL)),
548
549
  Schema.Uint8Array,
549
- Schema.URL
550
+ // Under the JSON and binary codecs a raw `URL` would serialize as a plain
551
+ // string, indistinguishable from the `Schema.String` member. Serialize it
552
+ // through the same `TaggedURL` instance instead, so the codecs see a
553
+ // single `{ _tag: "URL" }` member rather than two
554
+ Schema.URL.annotate({ toCodecJson: () => Schema.link<URL>()(TaggedURL, urlFromTaggedURL) })
550
555
  ])
551
556
  }).annotate({ identifier: "FilePart" })
552
557
 
package/src/ai/Tool.ts CHANGED
@@ -2096,8 +2096,9 @@ export const getStrictMode = <T extends Any>(tool: T): boolean | undefined => Co
2096
2096
  //
2097
2097
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2098
2098
 
2099
- const suspectProtoRx = /"__proto__"\s*:/
2100
- const suspectConstructorRx = /"constructor"\s*:/
2099
+ // Text that may decode to a forbidden key. `JSON.parse` decodes Unicode
2100
+ // escapes (e.g. `"\u005f_proto__"`), so any `\u` escape also triggers the scan.
2101
+ const suspectRx = /"__proto__"\s*:|"constructor"\s*:|\\u/
2101
2102
 
2102
2103
  function _parse(text: string) {
2103
2104
  // Parse normally
@@ -2108,15 +2109,8 @@ function _parse(text: string) {
2108
2109
  return obj
2109
2110
  }
2110
2111
 
2111
- if (
2112
- suspectProtoRx.test(text) === false &&
2113
- suspectConstructorRx.test(text) === false
2114
- ) {
2115
- return obj
2116
- }
2117
-
2118
2112
  // Scan result for proto keys
2119
- return filter(obj)
2113
+ return suspectRx.test(text) ? filter(obj) : obj
2120
2114
  }
2121
2115
 
2122
2116
  function filter(obj: any) {
@@ -2133,6 +2127,7 @@ function filter(obj: any) {
2133
2127
 
2134
2128
  if (
2135
2129
  Object.hasOwn(node, "constructor") &&
2130
+ node.constructor !== null &&
2136
2131
  Object.hasOwn(node.constructor, "prototype")
2137
2132
  ) {
2138
2133
  throw new SyntaxError("Object contains forbidden prototype property")