effect 4.1.0 → 4.1.2
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.
- package/README.md +30 -0
- package/THIRD_PARTY_NOTICES.md +62 -0
- package/ai-docs/package.json +2 -2
- package/dist/BigDecimal.d.ts +139 -7
- package/dist/BigDecimal.d.ts.map +1 -1
- package/dist/BigDecimal.js +83 -14
- package/dist/BigDecimal.js.map +1 -1
- package/dist/ByteSize.d.ts.map +1 -1
- package/dist/ByteSize.js +27 -14
- package/dist/ByteSize.js.map +1 -1
- package/dist/Equal.d.ts +2 -2
- package/dist/Equal.d.ts.map +1 -1
- package/dist/Equal.js +3 -3
- package/dist/Equal.js.map +1 -1
- package/dist/Hash.d.ts +3 -2
- package/dist/Hash.d.ts.map +1 -1
- package/dist/Hash.js +5 -4
- package/dist/Hash.js.map +1 -1
- package/dist/Iterable.d.ts.map +1 -1
- package/dist/Iterable.js +38 -32
- package/dist/Iterable.js.map +1 -1
- package/dist/JsonSchema.d.ts.map +1 -1
- package/dist/JsonSchema.js +27 -6
- package/dist/JsonSchema.js.map +1 -1
- package/dist/MutableRef.d.ts.map +1 -1
- package/dist/MutableRef.js +0 -7
- package/dist/MutableRef.js.map +1 -1
- package/dist/Path.d.ts.map +1 -1
- package/dist/Path.js +5 -1
- package/dist/Path.js.map +1 -1
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +2 -1
- package/dist/Pool.js.map +1 -1
- package/dist/Ref.d.ts.map +1 -1
- package/dist/Ref.js +0 -8
- package/dist/Ref.js.map +1 -1
- package/dist/Schedule.d.ts.map +1 -1
- package/dist/Schedule.js +8 -3
- package/dist/Schedule.js.map +1 -1
- package/dist/Schema.d.ts +2 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +2 -1
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaRepresentation.d.ts +4 -2
- package/dist/SchemaRepresentation.d.ts.map +1 -1
- package/dist/SchemaRepresentation.js +4 -2
- package/dist/SchemaRepresentation.js.map +1 -1
- package/dist/SchemaTransformation.d.ts +2 -2
- package/dist/SchemaTransformation.js +2 -2
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +6 -5
- package/dist/Stream.js.map +1 -1
- package/dist/ai/McpClient.d.ts +171 -19
- package/dist/ai/McpClient.d.ts.map +1 -1
- package/dist/ai/McpClient.js +277 -66
- package/dist/ai/McpClient.js.map +1 -1
- package/dist/ai/McpSchema.d.ts +108 -0
- package/dist/ai/McpSchema.d.ts.map +1 -1
- package/dist/ai/McpSchema.js +99 -0
- package/dist/ai/McpSchema.js.map +1 -1
- package/dist/ai/McpServer.d.ts +120 -6
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +258 -49
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Prompt.d.ts.map +1 -1
- package/dist/ai/Prompt.js +20 -11
- package/dist/ai/Prompt.js.map +1 -1
- package/dist/ai/Tool.d.ts.map +1 -1
- package/dist/ai/Tool.js +5 -7
- package/dist/ai/Tool.js.map +1 -1
- package/dist/ai/internal/mcpCore.js +72 -3
- package/dist/ai/internal/mcpCore.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js +3 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +44 -8
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js +12 -0
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +3 -3
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/ai/internal/mcpSkills.d.ts +2 -0
- package/dist/ai/internal/mcpSkills.d.ts.map +1 -0
- package/dist/ai/internal/mcpSkills.js +428 -0
- package/dist/ai/internal/mcpSkills.js.map +1 -0
- package/dist/ai/internal/structured-output.js +11 -2
- package/dist/ai/internal/structured-output.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +30 -11
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/Entity.d.ts +26 -0
- package/dist/cluster/Entity.d.ts.map +1 -1
- package/dist/cluster/Entity.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts +6 -6
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +1 -3
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Reply.d.ts +1 -1
- package/dist/cluster/Reply.d.ts.map +1 -1
- package/dist/cluster/Reply.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +13 -2
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/Sharding.d.ts +13 -0
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +4 -1
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/internal/clusterAbandon.d.ts.map +1 -1
- package/dist/cluster/internal/clusterAbandon.js +6 -4
- package/dist/cluster/internal/clusterAbandon.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +63 -11
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/cluster/internal/transactionNotifications.d.ts +5 -0
- package/dist/cluster/internal/transactionNotifications.d.ts.map +1 -0
- package/dist/cluster/internal/transactionNotifications.js +5 -0
- package/dist/cluster/internal/transactionNotifications.js.map +1 -0
- package/dist/crypto/Aead.d.ts +2 -1
- package/dist/crypto/Aead.d.ts.map +1 -1
- package/dist/crypto/Aead.js +2 -2
- package/dist/crypto/Aead.js.map +1 -1
- package/dist/crypto/Cipher.d.ts +2 -1
- package/dist/crypto/Cipher.d.ts.map +1 -1
- package/dist/crypto/Cipher.js +2 -2
- package/dist/crypto/Cipher.js.map +1 -1
- package/dist/crypto/KeyAgreement.d.ts +3 -2
- package/dist/crypto/KeyAgreement.d.ts.map +1 -1
- package/dist/crypto/KeyAgreement.js +2 -2
- package/dist/crypto/KeyAgreement.js.map +1 -1
- package/dist/crypto/KeyFormat.d.ts +33 -1
- package/dist/crypto/KeyFormat.d.ts.map +1 -1
- package/dist/crypto/KeyFormat.js.map +1 -1
- package/dist/crypto/KeyWrap.d.ts +4 -3
- package/dist/crypto/KeyWrap.d.ts.map +1 -1
- package/dist/crypto/KeyWrap.js +2 -2
- package/dist/crypto/KeyWrap.js.map +1 -1
- package/dist/crypto/Mac.d.ts +2 -1
- package/dist/crypto/Mac.d.ts.map +1 -1
- package/dist/crypto/Mac.js +2 -2
- package/dist/crypto/Mac.js.map +1 -1
- package/dist/crypto/Signature.d.ts +3 -2
- package/dist/crypto/Signature.d.ts.map +1 -1
- package/dist/crypto/Signature.js +2 -2
- package/dist/crypto/Signature.js.map +1 -1
- package/dist/crypto/WebCrypto.d.ts +19 -0
- package/dist/crypto/WebCrypto.d.ts.map +1 -1
- package/dist/crypto/WebCrypto.js.map +1 -1
- package/dist/crypto/internal/asymmetric.js.map +1 -1
- package/dist/crypto/internal/key.js +3 -2
- package/dist/crypto/internal/key.js.map +1 -1
- package/dist/encoding/Base64Url.d.ts.map +1 -1
- package/dist/encoding/Base64Url.js +26 -3
- package/dist/encoding/Base64Url.js.map +1 -1
- package/dist/encoding/Json5.d.ts +44 -0
- package/dist/encoding/Json5.d.ts.map +1 -0
- package/dist/encoding/Json5.js +56 -0
- package/dist/encoding/Json5.js.map +1 -0
- package/dist/encoding/Jsonc.d.ts +47 -0
- package/dist/encoding/Jsonc.d.ts.map +1 -0
- package/dist/encoding/Jsonc.js +120 -0
- package/dist/encoding/Jsonc.js.map +1 -0
- package/dist/encoding/Ndjson.d.ts +5 -4
- package/dist/encoding/Ndjson.d.ts.map +1 -1
- package/dist/encoding/Ndjson.js +6 -5
- package/dist/encoding/Ndjson.js.map +1 -1
- package/dist/encoding/SchemaBinary.d.ts +94 -0
- package/dist/encoding/SchemaBinary.d.ts.map +1 -1
- package/dist/encoding/SchemaBinary.js +219 -6
- package/dist/encoding/SchemaBinary.js.map +1 -1
- package/dist/encoding/Sse.d.ts +3 -1
- package/dist/encoding/Sse.d.ts.map +1 -1
- package/dist/encoding/Sse.js +5 -3
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/encoding/index.d.ts +10 -0
- package/dist/encoding/index.d.ts.map +1 -1
- package/dist/encoding/index.js +10 -0
- package/dist/encoding/index.js.map +1 -1
- package/dist/eventlog/EventLog.d.ts +7 -0
- package/dist/eventlog/EventLog.d.ts.map +1 -1
- package/dist/eventlog/EventLog.js +7 -0
- package/dist/eventlog/EventLog.js.map +1 -1
- package/dist/eventlog/EventLogServer.d.ts +7 -1
- package/dist/eventlog/EventLogServer.d.ts.map +1 -1
- package/dist/eventlog/EventLogServer.js +55 -24
- package/dist/eventlog/EventLogServer.js.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.d.ts +10 -0
- package/dist/eventlog/EventLogServerEncrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerEncrypted.js.map +1 -1
- package/dist/eventlog/EventLogServerUnencrypted.d.ts +17 -0
- package/dist/eventlog/EventLogServerUnencrypted.d.ts.map +1 -1
- package/dist/eventlog/EventLogServerUnencrypted.js +14 -2
- package/dist/eventlog/EventLogServerUnencrypted.js.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.d.ts +21 -0
- package/dist/eventlog/EventLogSessionAuth.d.ts.map +1 -1
- package/dist/eventlog/EventLogSessionAuth.js +22 -0
- package/dist/eventlog/EventLogSessionAuth.js.map +1 -1
- package/dist/graphql/GraphQLClient.d.ts +3 -1
- package/dist/graphql/GraphQLClient.d.ts.map +1 -1
- package/dist/graphql/GraphQLClient.js.map +1 -1
- package/dist/graphql/GraphQLClientError.d.ts +14 -9
- package/dist/graphql/GraphQLClientError.d.ts.map +1 -1
- package/dist/graphql/GraphQLClientError.js +14 -21
- package/dist/graphql/GraphQLClientError.js.map +1 -1
- package/dist/graphql/GraphQLProtocol.d.ts +12 -3
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
- package/dist/graphql/GraphQLProtocol.js +4 -1
- package/dist/graphql/GraphQLProtocol.js.map +1 -1
- package/dist/http/HttpClient.d.ts +204 -16
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +93 -13
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpServerRequest.d.ts.map +1 -1
- package/dist/http/HttpServerRequest.js +38 -22
- package/dist/http/HttpServerRequest.js.map +1 -1
- package/dist/http/internal/mimeTypes.d.ts +4 -1
- package/dist/http/internal/mimeTypes.d.ts.map +1 -1
- package/dist/http/internal/mimeTypes.js +4 -1
- package/dist/http/internal/mimeTypes.js.map +1 -1
- package/dist/http-api/HttpApiSchema.js +1 -1
- package/dist/http-api/HttpApiSchema.js.map +1 -1
- package/dist/internal/dateTime.js +66 -27
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +10 -0
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/glob.js +234 -56
- package/dist/internal/glob.js.map +1 -1
- package/dist/internal/graphql.js +21 -7
- package/dist/internal/graphql.js.map +1 -1
- package/dist/internal/graphqlHeaders.d.ts +7 -0
- package/dist/internal/graphqlHeaders.d.ts.map +1 -1
- package/dist/internal/graphqlHeaders.js +11 -0
- package/dist/internal/graphqlHeaders.js.map +1 -1
- package/dist/internal/jsonParser.d.ts +5 -0
- package/dist/internal/jsonParser.d.ts.map +1 -0
- package/dist/internal/jsonParser.js +280 -0
- package/dist/internal/jsonParser.js.map +1 -0
- package/dist/internal/persistence.js +8 -1
- package/dist/internal/persistence.js.map +1 -1
- package/dist/internal/request.js +29 -42
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +7 -8
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/internal/sql.js +16 -0
- package/dist/internal/sql.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/jose/JoseError.d.ts +285 -0
- package/dist/jose/JoseError.d.ts.map +1 -0
- package/dist/jose/JoseError.js +269 -0
- package/dist/jose/JoseError.js.map +1 -0
- package/dist/jose/Jwe.d.ts +128 -0
- package/dist/jose/Jwe.d.ts.map +1 -0
- package/dist/jose/Jwe.js +134 -0
- package/dist/jose/Jwe.js.map +1 -0
- package/dist/jose/Jwk.d.ts +597 -0
- package/dist/jose/Jwk.d.ts.map +1 -0
- package/dist/jose/Jwk.js +503 -0
- package/dist/jose/Jwk.js.map +1 -0
- package/dist/jose/Jwks.d.ts +236 -0
- package/dist/jose/Jwks.d.ts.map +1 -0
- package/dist/jose/Jwks.js +306 -0
- package/dist/jose/Jwks.js.map +1 -0
- package/dist/jose/Jws.d.ts +177 -0
- package/dist/jose/Jws.d.ts.map +1 -0
- package/dist/jose/Jws.js +183 -0
- package/dist/jose/Jws.js.map +1 -0
- package/dist/jose/Jwt.d.ts +270 -0
- package/dist/jose/Jwt.d.ts.map +1 -0
- package/dist/jose/Jwt.js +293 -0
- package/dist/jose/Jwt.js.map +1 -0
- package/dist/jose/index.d.ts +51 -0
- package/dist/jose/index.d.ts.map +1 -0
- package/dist/jose/index.js +52 -0
- package/dist/jose/index.js.map +1 -0
- package/dist/jose/internal/compact.d.ts +2 -0
- package/dist/jose/internal/compact.d.ts.map +1 -0
- package/dist/jose/internal/compact.js +179 -0
- package/dist/jose/internal/compact.js.map +1 -0
- package/dist/net/Host.d.ts.map +1 -1
- package/dist/net/Host.js +5 -10
- package/dist/net/Host.js.map +1 -1
- package/dist/observability/OtlpExporter.d.ts +4 -0
- package/dist/observability/OtlpExporter.d.ts.map +1 -1
- package/dist/observability/OtlpExporter.js +40 -12
- package/dist/observability/OtlpExporter.js.map +1 -1
- package/dist/observability/OtlpSerialization.d.ts +1 -1
- package/dist/observability/OtlpSerialization.d.ts.map +1 -1
- package/dist/observability/OtlpSerialization.js +10 -4
- package/dist/observability/OtlpSerialization.js.map +1 -1
- package/dist/observability/internal/otlpProtobuf.js +3 -3
- package/dist/observability/internal/otlpProtobuf.js.map +1 -1
- package/dist/observability/internal/protobuf.js +6 -0
- package/dist/observability/internal/protobuf.js.map +1 -1
- package/dist/persistence/CircuitBreaker.d.ts +98 -38
- package/dist/persistence/CircuitBreaker.d.ts.map +1 -1
- package/dist/persistence/CircuitBreaker.js +67 -29
- package/dist/persistence/CircuitBreaker.js.map +1 -1
- package/dist/persistence/RateLimiter.d.ts +15 -0
- package/dist/persistence/RateLimiter.d.ts.map +1 -1
- package/dist/persistence/RateLimiter.js +76 -16
- package/dist/persistence/RateLimiter.js.map +1 -1
- package/dist/persistence/index.d.ts +1 -1
- package/dist/persistence/index.js +1 -1
- package/dist/reactivity/AtomRegistry.js +17 -7
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/Rpc.d.ts +1 -1
- package/dist/rpc/Rpc.d.ts.map +1 -1
- package/dist/rpc/Rpc.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +14 -2
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +38 -21
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcMessage.d.ts +3 -2
- package/dist/rpc/RpcMessage.d.ts.map +1 -1
- package/dist/rpc/RpcMessage.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +10 -1
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/sql/Migrator.d.ts +9 -1
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +18 -6
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +46 -3
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +40 -15
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/sql/Statement.d.ts +46 -3
- package/dist/sql/Statement.d.ts.map +1 -1
- package/dist/sql/Statement.js +32 -26
- package/dist/sql/Statement.js.map +1 -1
- package/dist/workflow/Workflow.d.ts +4 -0
- package/dist/workflow/Workflow.d.ts.map +1 -1
- package/dist/workflow/Workflow.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +5 -0
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +7 -3
- package/src/BigDecimal.ts +170 -16
- package/src/ByteSize.ts +29 -14
- package/src/Equal.ts +3 -3
- package/src/Hash.ts +5 -4
- package/src/Iterable.ts +28 -19
- package/src/JsonSchema.ts +45 -7
- package/src/MutableRef.ts +1 -8
- package/src/Path.ts +6 -1
- package/src/Pool.ts +2 -1
- package/src/Ref.ts +0 -8
- package/src/Schedule.ts +8 -6
- package/src/Schema.ts +3 -2
- package/src/SchemaRepresentation.ts +4 -2
- package/src/SchemaTransformation.ts +2 -2
- package/src/Stream.ts +6 -5
- package/src/ai/McpClient.ts +488 -91
- package/src/ai/McpSchema.ts +119 -0
- package/src/ai/McpServer.ts +302 -56
- package/src/ai/Prompt.ts +16 -11
- package/src/ai/Tool.ts +5 -10
- package/src/ai/internal/mcpCore.ts +165 -12
- package/src/ai/internal/mcpProtocol/v2024_11_05.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_03_26.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_06_18.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2025_11_25.ts +3 -1
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +42 -9
- package/src/ai/internal/mcpProtocol.ts +15 -0
- package/src/ai/internal/mcpRuntime.ts +6 -3
- package/src/ai/internal/mcpSkills.ts +480 -0
- package/src/ai/internal/structured-output.ts +10 -4
- package/src/cluster/ClusterWorkflowEngine.ts +48 -33
- package/src/cluster/Entity.ts +52 -0
- package/src/cluster/MessageStorage.ts +3 -6
- package/src/cluster/Reply.ts +1 -1
- package/src/cluster/Runners.ts +16 -2
- package/src/cluster/Sharding.ts +17 -1
- package/src/cluster/internal/clusterAbandon.ts +7 -7
- package/src/cluster/internal/entityManager.ts +89 -15
- package/src/cluster/internal/transactionNotifications.ts +7 -0
- package/src/crypto/Aead.ts +3 -3
- package/src/crypto/Cipher.ts +3 -3
- package/src/crypto/KeyAgreement.ts +4 -4
- package/src/crypto/KeyFormat.ts +30 -1
- package/src/crypto/KeyWrap.ts +6 -6
- package/src/crypto/Mac.ts +3 -3
- package/src/crypto/Signature.ts +4 -4
- package/src/crypto/WebCrypto.ts +20 -0
- package/src/crypto/internal/asymmetric.ts +2 -1
- package/src/crypto/internal/key.ts +12 -5
- package/src/encoding/Base64Url.ts +27 -3
- package/src/encoding/Json5.ts +59 -0
- package/src/encoding/Jsonc.ts +126 -0
- package/src/encoding/Ndjson.ts +6 -5
- package/src/encoding/SchemaBinary.ts +228 -7
- package/src/encoding/Sse.ts +5 -3
- package/src/encoding/index.ts +12 -0
- package/src/eventlog/EventLog.ts +7 -0
- package/src/eventlog/EventLogServer.ts +67 -24
- package/src/eventlog/EventLogServerEncrypted.ts +5 -0
- package/src/eventlog/EventLogServerUnencrypted.ts +19 -2
- package/src/eventlog/EventLogSessionAuth.ts +26 -0
- package/src/graphql/GraphQLClient.ts +3 -1
- package/src/graphql/GraphQLClientError.ts +15 -22
- package/src/graphql/GraphQLProtocol.ts +12 -3
- package/src/http/HttpClient.ts +244 -33
- package/src/http/HttpServerRequest.ts +61 -23
- package/src/http/internal/mimeTypes.ts +4 -1
- package/src/http-api/HttpApiSchema.ts +1 -1
- package/src/internal/dateTime.ts +80 -27
- package/src/internal/effect.ts +6 -0
- package/src/internal/glob.ts +279 -63
- package/src/internal/graphql.ts +27 -9
- package/src/internal/graphqlHeaders.ts +13 -0
- package/src/internal/jsonParser.ts +307 -0
- package/src/internal/persistence.ts +9 -1
- package/src/internal/request.ts +51 -58
- package/src/internal/schema/fromJsonSchemaDocument.ts +11 -10
- package/src/internal/sql.ts +25 -0
- package/src/internal/version.ts +1 -1
- package/src/jose/JoseError.ts +329 -0
- package/src/jose/Jwe.ts +196 -0
- package/src/jose/Jwk.ts +777 -0
- package/src/jose/Jwks.ts +418 -0
- package/src/jose/Jws.ts +312 -0
- package/src/jose/Jwt.ts +493 -0
- package/src/jose/index.ts +58 -0
- package/src/jose/internal/compact.ts +195 -0
- package/src/net/Host.ts +5 -10
- package/src/observability/OtlpExporter.ts +42 -18
- package/src/observability/OtlpSerialization.ts +13 -5
- package/src/observability/internal/otlpProtobuf.ts +6 -6
- package/src/observability/internal/protobuf.ts +10 -0
- package/src/persistence/CircuitBreaker.ts +113 -48
- package/src/persistence/RateLimiter.ts +97 -19
- package/src/persistence/index.ts +1 -1
- package/src/reactivity/AtomRegistry.ts +19 -8
- package/src/rpc/Rpc.ts +3 -1
- package/src/rpc/RpcClient.ts +44 -24
- package/src/rpc/RpcMessage.ts +3 -2
- package/src/rpc/RpcServer.ts +12 -1
- package/src/sql/Migrator.ts +35 -18
- package/src/sql/SqlClient.ts +91 -22
- package/src/sql/Statement.ts +60 -35
- package/src/workflow/Workflow.ts +4 -0
- package/src/workflow/WorkflowEngine.ts +5 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Skills extension (SEP-2640): its wire vocabulary, the catalog behind a
|
|
3
|
+
* static skill, and the handlers shared by the sessionful adapters.
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
import * as Cause from "../../Cause.ts"
|
|
8
|
+
import * as Digest from "../../crypto/Digest.ts"
|
|
9
|
+
import * as Effect from "../../Effect.ts"
|
|
10
|
+
import * as Yaml from "../../encoding/Yaml.ts"
|
|
11
|
+
import * as Mime from "../../http/Mime.ts"
|
|
12
|
+
import * as Option from "../../Option.ts"
|
|
13
|
+
import * as Predicate from "../../Predicate.ts"
|
|
14
|
+
import * as Rpc from "../../rpc/Rpc.ts"
|
|
15
|
+
import * as RpcGroup from "../../rpc/RpcGroup.ts"
|
|
16
|
+
import * as Schema from "../../Schema.ts"
|
|
17
|
+
import * as PublicMcpSchema from "../McpSchema.ts"
|
|
18
|
+
import type * as McpCore from "./mcpCore.ts"
|
|
19
|
+
import * as McpProtocol from "./mcpProtocol.ts"
|
|
20
|
+
import * as Legacy from "./mcpSchema/v2024_11_05.ts"
|
|
21
|
+
import * as Stateless from "./mcpSchema/v2026_07_28.ts"
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export const EXTENSION_ID = "io.modelcontextprotocol/skills"
|
|
27
|
+
|
|
28
|
+
const DIRECTORY_MIME_TYPE = "inode/directory"
|
|
29
|
+
|
|
30
|
+
// The extension's per-skill limits, which every conforming host accepts.
|
|
31
|
+
const MAX_FILES_PER_SKILL = 512
|
|
32
|
+
const MAX_BYTES_PER_SKILL = 16 * 1024 * 1024
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adds the extension declaration while any skill is registered. Declaring it
|
|
36
|
+
* commits the server to `skills/list`, `skills/get` and, through
|
|
37
|
+
* `directoryRead`, `resources/directory/read`.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export const declareExtension = <A>(
|
|
42
|
+
extensions: Readonly<Record<string, A>> | undefined,
|
|
43
|
+
servesSkills: boolean
|
|
44
|
+
): Readonly<Record<string, A | { readonly directoryRead: true }>> | undefined =>
|
|
45
|
+
servesSkills ? { ...extensions, [EXTENSION_ID]: { directoryRead: true } } : extensions
|
|
46
|
+
|
|
47
|
+
// =============================================================================
|
|
48
|
+
// Wire schemas
|
|
49
|
+
// =============================================================================
|
|
50
|
+
|
|
51
|
+
const Skill = Schema.Struct({
|
|
52
|
+
uri: Schema.String,
|
|
53
|
+
frontmatter: Legacy.UnknownObject,
|
|
54
|
+
resources: Schema.Union([
|
|
55
|
+
Schema.Array(Schema.Struct(PublicMcpSchema.SkillResource.fields)),
|
|
56
|
+
Schema.Literal("dynamic")
|
|
57
|
+
])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Listed entries are checked one at a time, so that a client can leave out an
|
|
61
|
+
// invalid entry instead of rejecting the whole page.
|
|
62
|
+
const ListedSkills = Schema.Array(Schema.Unknown)
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
export class LegacyListSkills extends Rpc.make("skills/list", {
|
|
68
|
+
success: Schema.Struct({ ...Legacy.PaginatedResult.fields, skills: ListedSkills }),
|
|
69
|
+
error: Legacy.McpError,
|
|
70
|
+
payload: Schema.UndefinedOr(Legacy.PaginatedRequest)
|
|
71
|
+
}) {}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @internal
|
|
75
|
+
*/
|
|
76
|
+
export class LegacyGetSkill extends Rpc.make("skills/get", {
|
|
77
|
+
success: Schema.Struct({ ...Legacy.ResultMeta.fields, skill: Skill }),
|
|
78
|
+
error: Legacy.McpError,
|
|
79
|
+
payload: { ...Legacy.RequestMeta.fields, uri: Schema.String }
|
|
80
|
+
}) {}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
*/
|
|
85
|
+
export class LegacyReadResourceDirectory extends Rpc.make("resources/directory/read", {
|
|
86
|
+
success: Schema.Struct({ ...Legacy.PaginatedResult.fields, resources: Schema.Array(Legacy.Resource) }),
|
|
87
|
+
error: Legacy.McpError,
|
|
88
|
+
payload: { ...Legacy.PaginatedRequest.fields, uri: Schema.String }
|
|
89
|
+
}) {}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The extension's methods on revisions before 2026-07-28, which have no
|
|
93
|
+
* result caching attributes.
|
|
94
|
+
*
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
export class LegacyRpcs extends RpcGroup.make(LegacyListSkills, LegacyGetSkill, LegacyReadResourceDirectory) {}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
export const ListSkillsResult = Schema.StructWithRest(
|
|
103
|
+
Schema.Struct({ ...Stateless.PaginatedResult, ...Stateless.CacheableResult, skills: ListedSkills }),
|
|
104
|
+
[Stateless.UnknownObject]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
export class ListSkills extends Rpc.make("skills/list", {
|
|
111
|
+
success: ListSkillsResult,
|
|
112
|
+
error: Stateless.McpError,
|
|
113
|
+
payload: Stateless.PaginatedRequestParams
|
|
114
|
+
}) {}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
export const GetSkillResult = Schema.StructWithRest(
|
|
120
|
+
Schema.Struct({ ...Stateless.CacheableResult, skill: Skill }),
|
|
121
|
+
[Stateless.UnknownObject]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
export class GetSkill extends Rpc.make("skills/get", {
|
|
128
|
+
success: GetSkillResult,
|
|
129
|
+
error: Stateless.McpError,
|
|
130
|
+
payload: { ...Stateless.RequestParams.fields, uri: Schema.String }
|
|
131
|
+
}) {}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @internal
|
|
135
|
+
*/
|
|
136
|
+
export const ReadResourceDirectoryResult = Schema.StructWithRest(
|
|
137
|
+
Schema.Struct({
|
|
138
|
+
...Stateless.PaginatedResult,
|
|
139
|
+
...Stateless.CacheableResult,
|
|
140
|
+
resources: Schema.Array(Stateless.Resource)
|
|
141
|
+
}),
|
|
142
|
+
[Stateless.UnknownObject]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @internal
|
|
147
|
+
*/
|
|
148
|
+
export class ReadResourceDirectory extends Rpc.make("resources/directory/read", {
|
|
149
|
+
success: ReadResourceDirectoryResult,
|
|
150
|
+
error: Stateless.McpError,
|
|
151
|
+
payload: { ...Stateless.PaginatedRequestParams, uri: Schema.String }
|
|
152
|
+
}) {}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* The extension's methods on revision 2026-07-28.
|
|
156
|
+
*
|
|
157
|
+
* @internal
|
|
158
|
+
*/
|
|
159
|
+
export class StatelessRpcs extends RpcGroup.make(ListSkills, GetSkill, ReadResourceDirectory) {}
|
|
160
|
+
|
|
161
|
+
// =============================================================================
|
|
162
|
+
// Handlers
|
|
163
|
+
// =============================================================================
|
|
164
|
+
|
|
165
|
+
const methodNotFound = (method: string) =>
|
|
166
|
+
new McpProtocol.ProtocolError({
|
|
167
|
+
code: PublicMcpSchema.METHOD_NOT_FOUND_ERROR_CODE,
|
|
168
|
+
message: `Method not found: ${method}`
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// Every skill fits on one page, so the server never issues a cursor.
|
|
172
|
+
const unknownCursor = new McpProtocol.ProtocolError({
|
|
173
|
+
code: PublicMcpSchema.INVALID_PARAMS_ERROR_CODE,
|
|
174
|
+
message: "Unknown cursor"
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Fails with `Method not found` while no skill is registered, as a server
|
|
179
|
+
* that does not declare the extension.
|
|
180
|
+
*
|
|
181
|
+
* @internal
|
|
182
|
+
*/
|
|
183
|
+
export const requireSkills = (core: McpCore.McpCore, method: string) =>
|
|
184
|
+
Effect.flatMap(
|
|
185
|
+
core.registrationPresence,
|
|
186
|
+
(presence) => presence.skills ? Effect.void : Effect.fail(methodNotFound(method))
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @internal
|
|
191
|
+
*/
|
|
192
|
+
export const rejectCursor = (cursor: string | undefined) =>
|
|
193
|
+
cursor === undefined ? Effect.void : Effect.fail(unknownCursor)
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Handlers for {@link LegacyRpcs}, for the sessionful adapters.
|
|
197
|
+
*
|
|
198
|
+
* @internal
|
|
199
|
+
*/
|
|
200
|
+
export const legacyHandlers = (core: McpCore.McpCore) => ({
|
|
201
|
+
"skills/list": Effect.fnUntraced(function*(request: typeof LegacyListSkills.payloadSchema.Type) {
|
|
202
|
+
yield* requireSkills(core, "skills/list")
|
|
203
|
+
yield* rejectCursor(request?.cursor)
|
|
204
|
+
const skills = yield* McpProtocol.listForClient(core.skills.list)
|
|
205
|
+
return { skills }
|
|
206
|
+
}),
|
|
207
|
+
"skills/get": Effect.fnUntraced(function*(request: typeof LegacyGetSkill.payloadSchema.Type) {
|
|
208
|
+
yield* requireSkills(core, "skills/get")
|
|
209
|
+
const client = yield* PublicMcpSchema.McpServerClient
|
|
210
|
+
const skill = yield* core.skills.get(request.uri, McpProtocol.invocationFromClient(client)).pipe(
|
|
211
|
+
Effect.mapError(McpProtocol.ProtocolError.fromFeature)
|
|
212
|
+
)
|
|
213
|
+
return { skill }
|
|
214
|
+
}),
|
|
215
|
+
"resources/directory/read": Effect.fnUntraced(function*(
|
|
216
|
+
request: typeof LegacyReadResourceDirectory.payloadSchema.Type
|
|
217
|
+
) {
|
|
218
|
+
yield* requireSkills(core, "resources/directory/read")
|
|
219
|
+
yield* rejectCursor(request.cursor)
|
|
220
|
+
const client = yield* PublicMcpSchema.McpServerClient
|
|
221
|
+
const resources = yield* core.skills.readDirectory(request.uri, McpProtocol.invocationFromClient(client)).pipe(
|
|
222
|
+
Effect.mapError(McpProtocol.ProtocolError.fromFeature)
|
|
223
|
+
)
|
|
224
|
+
return { resources }
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// =============================================================================
|
|
229
|
+
// Static skills
|
|
230
|
+
// =============================================================================
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* A file of a static skill as handed in, `SKILL.md` included, with its path
|
|
234
|
+
* relative to the skill root.
|
|
235
|
+
*
|
|
236
|
+
* @internal
|
|
237
|
+
*/
|
|
238
|
+
export interface SourceFile {
|
|
239
|
+
readonly path: string
|
|
240
|
+
readonly content: Uint8Array
|
|
241
|
+
readonly mimeType?: string | undefined
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A file of a static skill as served through `resources/read`.
|
|
246
|
+
*
|
|
247
|
+
* @internal
|
|
248
|
+
*/
|
|
249
|
+
export interface ServedFile {
|
|
250
|
+
// Listed by `resources/list` and as a child on `resources/directory/read`.
|
|
251
|
+
readonly resource: McpCore.DirectoryChild
|
|
252
|
+
readonly content: Uint8Array
|
|
253
|
+
// Set when the file is textual and valid UTF-8; otherwise it is served as a blob.
|
|
254
|
+
readonly text: string | undefined
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @internal
|
|
259
|
+
*/
|
|
260
|
+
export interface StaticSkill {
|
|
261
|
+
readonly entry: McpCore.Skill
|
|
262
|
+
readonly files: ReadonlyArray<ServedFile>
|
|
263
|
+
// Directory URI to its direct children.
|
|
264
|
+
readonly directories: ReadonlyMap<string, ReadonlyArray<McpCore.DirectoryChild>>
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const TEXTUAL_APPLICATION_TYPES: ReadonlySet<string> = new Set([
|
|
268
|
+
"application/json",
|
|
269
|
+
"application/yaml",
|
|
270
|
+
"image/svg+xml"
|
|
271
|
+
])
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* @internal
|
|
275
|
+
*/
|
|
276
|
+
export const mimeTypeFor = (path: string): string => {
|
|
277
|
+
// The standard table maps `.ts` to MPEG transport streams, but a skill's
|
|
278
|
+
// `.ts` files are TypeScript scripts.
|
|
279
|
+
if (/\.ts$/i.test(path)) return "text/x-typescript"
|
|
280
|
+
return Option.getOrElse(Mime.getType(path), () => "application/octet-stream")
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// `ignoreBOM` keeps a leading BOM in the decoded text, so re-encoding the text
|
|
284
|
+
// reproduces exactly the bytes the manifest digest covers.
|
|
285
|
+
const utf8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true })
|
|
286
|
+
|
|
287
|
+
const decodeText = (mimeType: string, content: Uint8Array): string | undefined => {
|
|
288
|
+
if (!mimeType.startsWith("text/") && !TEXTUAL_APPLICATION_TYPES.has(mimeType)) {
|
|
289
|
+
return undefined
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
292
|
+
return utf8.decode(content)
|
|
293
|
+
} catch {
|
|
294
|
+
return undefined
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const isSafeRelativePath = (path: string): boolean =>
|
|
299
|
+
path !== "" &&
|
|
300
|
+
!path.includes("\\") &&
|
|
301
|
+
path.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..")
|
|
302
|
+
|
|
303
|
+
// The Agent Skills `name` rule: lowercase letters, digits and single hyphens,
|
|
304
|
+
// neither leading nor trailing, at most 64 characters.
|
|
305
|
+
const isSkillName = (name: string): boolean => name.length <= 64 && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)
|
|
306
|
+
|
|
307
|
+
const FRONTMATTER = /^\uFEFF?---\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The YAML frontmatter of a `SKILL.md`, or `undefined` when the file does not
|
|
311
|
+
* begin with a frontmatter block holding a YAML mapping.
|
|
312
|
+
*
|
|
313
|
+
* @internal
|
|
314
|
+
*/
|
|
315
|
+
export const parseFrontmatter = (markdown: string): Record<string, unknown> | undefined => {
|
|
316
|
+
const block = FRONTMATTER.exec(markdown)?.[1]
|
|
317
|
+
if (block === undefined) return undefined
|
|
318
|
+
try {
|
|
319
|
+
const parsed = Yaml.parse(block)
|
|
320
|
+
// The frontmatter is listed as JSON, so keep only what JSON carries:
|
|
321
|
+
// `.inf` and `.nan` become `null`.
|
|
322
|
+
return Predicate.isObject(parsed) ? JSON.parse(JSON.stringify(parsed)) : undefined
|
|
323
|
+
} catch {
|
|
324
|
+
return undefined
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// The directory holding a skill's `SKILL.md`, given the `SKILL.md` URI.
|
|
329
|
+
const skillRoot = (uri: string): string => uri.slice(0, -"/SKILL.md".length)
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Whether `uri` addresses a file inside the skill whose `SKILL.md` is at
|
|
333
|
+
* `skillUri`.
|
|
334
|
+
*
|
|
335
|
+
* @internal
|
|
336
|
+
*/
|
|
337
|
+
export const isSkillFile = (skillUri: string, uri: string): boolean => {
|
|
338
|
+
const root = skillRoot(skillUri)
|
|
339
|
+
return uri.startsWith(`${root}/`) && isSafeRelativePath(uri.slice(root.length + 1))
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Why a skill entry received from a server breaks the extension's rules, or
|
|
344
|
+
* `undefined` when it is valid. Hosts must not load an invalid entry. The
|
|
345
|
+
* digest and size formats are checked by {@link PublicMcpSchema.SkillResource}.
|
|
346
|
+
*
|
|
347
|
+
* @internal
|
|
348
|
+
*/
|
|
349
|
+
export const entryProblem = (entry: McpCore.Skill): string | undefined => {
|
|
350
|
+
const schemeEnd = entry.uri.indexOf("://")
|
|
351
|
+
if (schemeEnd <= 0 || !entry.uri.endsWith("/SKILL.md")) return "the URI does not address a SKILL.md"
|
|
352
|
+
const root = skillRoot(entry.uri)
|
|
353
|
+
const skillPath = root.slice(schemeEnd + 3)
|
|
354
|
+
if (!isSafeRelativePath(skillPath)) return "the skill path has an empty, '.' or '..' segment"
|
|
355
|
+
const name = skillPath.split("/").at(-1)!
|
|
356
|
+
if (entry.frontmatter.name !== name || !isSkillName(name)) {
|
|
357
|
+
return `the frontmatter name must equal "${name}" and follow the Agent Skills naming rules`
|
|
358
|
+
}
|
|
359
|
+
const description = entry.frontmatter.description
|
|
360
|
+
if (typeof description !== "string" || description === "" || description.length > 1024) {
|
|
361
|
+
return "the frontmatter description must be a string of 1 to 1024 characters"
|
|
362
|
+
}
|
|
363
|
+
if (entry.resources === "dynamic") return undefined
|
|
364
|
+
const uris = new Set<string>()
|
|
365
|
+
for (const resource of entry.resources) {
|
|
366
|
+
if (!isSkillFile(entry.uri, resource.uri)) return `${resource.uri} is not a file of the skill`
|
|
367
|
+
if (uris.has(resource.uri)) return `${resource.uri} is listed twice`
|
|
368
|
+
uris.add(resource.uri)
|
|
369
|
+
}
|
|
370
|
+
return uris.has(entry.uri) ? undefined : "the manifest does not list SKILL.md"
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Whether `child` is a direct child of the directory at `directory`.
|
|
375
|
+
*
|
|
376
|
+
* @internal
|
|
377
|
+
*/
|
|
378
|
+
export const isDirectChild = (directory: string, child: string): boolean => {
|
|
379
|
+
if (!child.startsWith(`${directory}/`)) return false
|
|
380
|
+
const name = child.slice(directory.length + 1)
|
|
381
|
+
return name !== "" && name !== "." && name !== ".." && !name.includes("/")
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const decodeFrontmatter = Schema.decodeUnknownEffect(PublicMcpSchema.SkillFrontmatter)
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Validates a static skill against the extension's server requirements and
|
|
388
|
+
* precomputes what the protocol serves: the manifest, with digests over the
|
|
389
|
+
* exact bytes served, each file's resource metadata, and the directory tree
|
|
390
|
+
* from the namespace root down.
|
|
391
|
+
*
|
|
392
|
+
* @internal
|
|
393
|
+
*/
|
|
394
|
+
export const makeStaticSkill = (options: {
|
|
395
|
+
readonly uri: string
|
|
396
|
+
readonly files: ReadonlyArray<SourceFile>
|
|
397
|
+
}): Effect.Effect<StaticSkill, Cause.IllegalArgumentError, Digest.Digest> =>
|
|
398
|
+
Effect.gen(function*() {
|
|
399
|
+
const { uri } = options
|
|
400
|
+
const fail = (reason: string) => Effect.fail(new Cause.IllegalArgumentError(`Skill ${uri}: ${reason}`))
|
|
401
|
+
|
|
402
|
+
const schemeEnd = uri.indexOf("://")
|
|
403
|
+
if (schemeEnd <= 0 || !uri.endsWith("/SKILL.md")) {
|
|
404
|
+
return yield* fail("the URI must have the form <scheme>://<skill-path>/SKILL.md")
|
|
405
|
+
}
|
|
406
|
+
const scheme = uri.slice(0, schemeEnd + 3)
|
|
407
|
+
const skillPath = uri.slice(scheme.length, -"/SKILL.md".length)
|
|
408
|
+
if (!isSafeRelativePath(skillPath)) {
|
|
409
|
+
return yield* fail("the skill path must have no empty, '.' or '..' segment")
|
|
410
|
+
}
|
|
411
|
+
const name = skillPath.split("/").at(-1)!
|
|
412
|
+
if (options.files.length > MAX_FILES_PER_SKILL) {
|
|
413
|
+
return yield* fail(`it has ${options.files.length} files; the limit is ${MAX_FILES_PER_SKILL}`)
|
|
414
|
+
}
|
|
415
|
+
const totalBytes = options.files.reduce((sum, file) => sum + file.content.byteLength, 0)
|
|
416
|
+
if (totalBytes > MAX_BYTES_PER_SKILL) {
|
|
417
|
+
return yield* fail(`it has ${totalBytes} bytes; the limit is ${MAX_BYTES_PER_SKILL}`)
|
|
418
|
+
}
|
|
419
|
+
const paths = new Set<string>()
|
|
420
|
+
for (const file of options.files) {
|
|
421
|
+
if (!isSafeRelativePath(file.path)) return yield* fail(`the file path "${file.path}" escapes the skill`)
|
|
422
|
+
if (paths.has(file.path)) return yield* fail(`the file "${file.path}" is given twice`)
|
|
423
|
+
paths.add(file.path)
|
|
424
|
+
}
|
|
425
|
+
const skillMd = options.files.find((file) => file.path === "SKILL.md")
|
|
426
|
+
if (skillMd === undefined) return yield* fail("SKILL.md is missing")
|
|
427
|
+
|
|
428
|
+
// The listed frontmatter is parsed from the served SKILL.md, so the two
|
|
429
|
+
// cannot differ.
|
|
430
|
+
const frontmatter = parseFrontmatter(decodeText("text/markdown", skillMd.content) ?? "")
|
|
431
|
+
if (frontmatter === undefined) return yield* fail("SKILL.md must begin with YAML frontmatter holding a mapping")
|
|
432
|
+
if (frontmatter.name !== name) {
|
|
433
|
+
return yield* fail(`the frontmatter name ${JSON.stringify(frontmatter.name)} must equal "${name}"`)
|
|
434
|
+
}
|
|
435
|
+
// A skill served over MCP must conform to the Agent Skills specification.
|
|
436
|
+
const { description } = yield* decodeFrontmatter(frontmatter).pipe(
|
|
437
|
+
Effect.catch((error) => fail(`the frontmatter is not valid Agent Skills frontmatter: ${error.message}`))
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
const digest = yield* Digest.Digest
|
|
441
|
+
const sorted = [...options.files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0)
|
|
442
|
+
const resources: Array<McpCore.SkillResource> = []
|
|
443
|
+
const files: Array<ServedFile> = []
|
|
444
|
+
const children = new Map<string, Map<string, McpCore.DirectoryChild>>()
|
|
445
|
+
for (const file of sorted) {
|
|
446
|
+
const fileUri = `${scheme}${skillPath}/${file.path}`
|
|
447
|
+
const fileDigest = `sha256:${yield* digest.digestHex(Digest.sha256, file.content)}`
|
|
448
|
+
const mimeType = file.mimeType ?? mimeTypeFor(file.path)
|
|
449
|
+
const size = file.content.byteLength
|
|
450
|
+
const resource: McpCore.DirectoryChild = file.path === "SKILL.md"
|
|
451
|
+
? { uri: fileUri, name, description, mimeType, size }
|
|
452
|
+
: { uri: fileUri, name: file.path.split("/").at(-1)!, mimeType, size }
|
|
453
|
+
files.push({ resource, content: file.content, text: decodeText(mimeType, file.content) })
|
|
454
|
+
resources.push({ uri: fileUri, digest: fileDigest, size })
|
|
455
|
+
|
|
456
|
+
// Every directory from the namespace root down is readable, including
|
|
457
|
+
// the prefix directories above the skill root.
|
|
458
|
+
const segments = `${skillPath}/${file.path}`.split("/")
|
|
459
|
+
for (let depth = 1; depth < segments.length; depth++) {
|
|
460
|
+
const directory = `${scheme}${segments.slice(0, depth).join("/")}`
|
|
461
|
+
const child: McpCore.DirectoryChild = depth === segments.length - 1 ? resource : {
|
|
462
|
+
uri: `${scheme}${segments.slice(0, depth + 1).join("/")}`,
|
|
463
|
+
name: segments[depth]!,
|
|
464
|
+
mimeType: DIRECTORY_MIME_TYPE
|
|
465
|
+
}
|
|
466
|
+
let siblings = children.get(directory)
|
|
467
|
+
if (siblings === undefined) {
|
|
468
|
+
siblings = new Map()
|
|
469
|
+
children.set(directory, siblings)
|
|
470
|
+
}
|
|
471
|
+
siblings.set(child.uri, child)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const directories = new Map<string, ReadonlyArray<McpCore.DirectoryChild>>()
|
|
476
|
+
for (const [directory, siblings] of children) {
|
|
477
|
+
directories.set(directory, Array.from(siblings.values()))
|
|
478
|
+
}
|
|
479
|
+
return { entry: { uri, frontmatter, resources }, files, directories }
|
|
480
|
+
})
|
|
@@ -38,11 +38,17 @@ export function resolveReference($ref: string, definitions: JsonSchema.Definitio
|
|
|
38
38
|
export function resolveTopLevelReference(
|
|
39
39
|
document: JsonSchema.Document<"draft-2020-12">
|
|
40
40
|
): JsonSchema.Document<"draft-2020-12"> {
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
let schema = document.schema
|
|
42
|
+
if (typeof schema.$ref !== "string") return document
|
|
43
|
+
const visited = new Set<string>()
|
|
44
|
+
while (typeof schema.$ref === "string") {
|
|
45
|
+
if (visited.has(schema.$ref)) {
|
|
46
|
+
throw new Error(`Circular reference ${JSON.stringify(schema.$ref)}`)
|
|
47
|
+
}
|
|
48
|
+
visited.add(schema.$ref)
|
|
49
|
+
schema = resolveReference(schema.$ref, document.definitions)
|
|
45
50
|
}
|
|
51
|
+
return { ...document, schema }
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
/**
|
|
@@ -267,7 +267,11 @@ export const make = Effect.gen(function*() {
|
|
|
267
267
|
id: activityPrimaryKey(options.activity.name, options.attempt)
|
|
268
268
|
})
|
|
269
269
|
if (Option.isNone(requestId)) return
|
|
270
|
-
|
|
270
|
+
// Reset only the Suspended reply this runner saw. If another runner has
|
|
271
|
+
// stored a newer reply, storage skips the reset and the caller reads it.
|
|
272
|
+
const reply = yield* replyForRequestId(requestId.value)
|
|
273
|
+
if (Option.isNone(reply) || !isSuspendedReply(reply.value)) return
|
|
274
|
+
yield* sharding.reset(requestId.value, { expectedReplyId: Snowflake.Snowflake(reply.value.id) })
|
|
271
275
|
},
|
|
272
276
|
Effect.retry({
|
|
273
277
|
times: 3,
|
|
@@ -344,7 +348,7 @@ export const make = Effect.gen(function*() {
|
|
|
344
348
|
}
|
|
345
349
|
const reply = yield* replyForRequestId(requestId.value)
|
|
346
350
|
if (Option.isNone(reply)) return
|
|
347
|
-
yield* sharding.reset(requestId.value)
|
|
351
|
+
yield* sharding.reset(requestId.value, { expectedReplyId: Snowflake.Snowflake(reply.value.id) })
|
|
348
352
|
}, Effect.scoped)
|
|
349
353
|
|
|
350
354
|
// Subscribe to the run's next reply before reading storage, so a reply
|
|
@@ -535,14 +539,15 @@ export const make = Effect.gen(function*() {
|
|
|
535
539
|
if (instance.abandoned) {
|
|
536
540
|
instance.suspended = false
|
|
537
541
|
}
|
|
542
|
+
// Abandoned runs never wake the parent; another owner replays them.
|
|
543
|
+
const resumeParent = parent && !parent.detached && !instance.abandoned
|
|
544
|
+
? ensureSuccess(sendResumeParent(parent, executionId))
|
|
545
|
+
: Effect.void
|
|
538
546
|
if (
|
|
539
547
|
!instance.suspended &&
|
|
540
548
|
!instance.abandoned &&
|
|
541
549
|
!(suspendOnFailure && exit._tag === "Failure")
|
|
542
550
|
) {
|
|
543
|
-
const resumeParent = parent && !parent.detached
|
|
544
|
-
? ensureSuccess(sendResumeParent(parent, executionId))
|
|
545
|
-
: Effect.void
|
|
546
551
|
if (exit._tag === "Success" || !Cause.hasInterruptsOnly(exit.cause)) {
|
|
547
552
|
return resumeParent
|
|
548
553
|
}
|
|
@@ -568,9 +573,13 @@ export const make = Effect.gen(function*() {
|
|
|
568
573
|
}
|
|
569
574
|
instance.suspended = false
|
|
570
575
|
instance.interrupted = true
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
576
|
+
// The interrupt completes the run, so wake the parent
|
|
577
|
+
// to observe it.
|
|
578
|
+
return Effect.ignore(clearClock({ workflow, executionId })).pipe(
|
|
579
|
+
Effect.andThen(resumeParent),
|
|
580
|
+
Effect.andThen(
|
|
581
|
+
Effect.withFiber<void>((fiber) => Effect.interruptible(Fiber.interrupt(fiber)))
|
|
582
|
+
)
|
|
574
583
|
)
|
|
575
584
|
}),
|
|
576
585
|
Effect.orDie
|
|
@@ -760,7 +769,7 @@ export const make = Effect.gen(function*() {
|
|
|
760
769
|
// A pending interrupt replays only to rebuild compensations, so it must
|
|
761
770
|
// not run new activities. Finalizers run once `interrupted` is set.
|
|
762
771
|
if (!instance.interrupted && (yield* interruptPending(instance))) {
|
|
763
|
-
const reply = yield*
|
|
772
|
+
const reply = yield* retryPersistence(requestReply({
|
|
764
773
|
workflow: instance.workflow,
|
|
765
774
|
entityType: `Workflow/${instance.workflow._tag}`,
|
|
766
775
|
executionId: instance.executionId,
|
|
@@ -777,7 +786,7 @@ export const make = Effect.gen(function*() {
|
|
|
777
786
|
if (!activities.has(activityId)) {
|
|
778
787
|
activities.set(activityId, { activity, context: services })
|
|
779
788
|
}
|
|
780
|
-
const result = yield*
|
|
789
|
+
const result = yield* retryPersistence(
|
|
781
790
|
client.activity({
|
|
782
791
|
name: activity.name,
|
|
783
792
|
attempt,
|
|
@@ -860,7 +869,7 @@ export const make = Effect.gen(function*() {
|
|
|
860
869
|
}
|
|
861
870
|
const workflow = workflows.get(workflowName)
|
|
862
871
|
if (workflow) {
|
|
863
|
-
return yield*
|
|
872
|
+
return yield* retryPersistence(sendDiscard({
|
|
864
873
|
rpc: DeferredRpc,
|
|
865
874
|
address: entityAddressFor({
|
|
866
875
|
workflow,
|
|
@@ -874,7 +883,7 @@ export const make = Effect.gen(function*() {
|
|
|
874
883
|
}))
|
|
875
884
|
}
|
|
876
885
|
const client = yield* RcMap.get(clientsPartial, workflowName)
|
|
877
|
-
return yield*
|
|
886
|
+
return yield* retryPersistence(
|
|
878
887
|
client(executionId).deferred({
|
|
879
888
|
name: deferredName,
|
|
880
889
|
exit
|
|
@@ -885,27 +894,24 @@ export const make = Effect.gen(function*() {
|
|
|
885
894
|
),
|
|
886
895
|
|
|
887
896
|
scheduleClock(workflow, options) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
),
|
|
907
|
-
Effect.orDie
|
|
908
|
-
)
|
|
897
|
+
// Compute the deadline once, so a retried save keeps the original wake-up.
|
|
898
|
+
return Effect.flatMap(DateTime.now, (now) =>
|
|
899
|
+
retryPersistence(sendDiscard({
|
|
900
|
+
rpc: ClockRpc,
|
|
901
|
+
address: entityAddressFor({
|
|
902
|
+
workflow,
|
|
903
|
+
entityType: ClockEntity.type,
|
|
904
|
+
executionId: options.executionId
|
|
905
|
+
}),
|
|
906
|
+
payload: {
|
|
907
|
+
name: options.clock.name,
|
|
908
|
+
workflowName: workflow._tag,
|
|
909
|
+
wakeUp: DateTime.mapEpochMillis(
|
|
910
|
+
DateTime.addDuration(now, options.clock.duration),
|
|
911
|
+
Math.ceil
|
|
912
|
+
)
|
|
913
|
+
}
|
|
914
|
+
})))
|
|
909
915
|
}
|
|
910
916
|
})
|
|
911
917
|
|
|
@@ -917,6 +923,15 @@ const retryPolicy = Schedule.min([
|
|
|
917
923
|
Schedule.spaced("1 minute")
|
|
918
924
|
])
|
|
919
925
|
|
|
926
|
+
// A transient storage failure must not become the workflow result. Activity,
|
|
927
|
+
// clock and deferred requests are saved under a primary key, so a retry after
|
|
928
|
+
// a lost acknowledgement is deduplicated instead of stored twice.
|
|
929
|
+
const retryPersistence = <A, E extends { readonly _tag: string }, R>(effect: Effect.Effect<A, E, R>) =>
|
|
930
|
+
effect.pipe(
|
|
931
|
+
Effect.retry({ schedule: retryPolicy, while: (e) => e._tag === "PersistenceError" }),
|
|
932
|
+
Effect.orDie
|
|
933
|
+
)
|
|
934
|
+
|
|
920
935
|
// A cluster abandonment means this owner can no longer complete the effect, so
|
|
921
936
|
// retrying it would only wait on a retired run. Re-raise it as an annotated
|
|
922
937
|
// interrupt, so the activation can unwind and its shard can be released.
|