From 44f7bb71c14d79b61d5c5d067dd6de143ccce8fd Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 17 Jul 2026 10:31:27 -0400 Subject: [PATCH] chore: upgrade Effect to beta.98 (#37498) --- bun.lock | 56 +++++++---- package.json | 10 +- packages/cli/test/service.test.ts | 6 +- packages/client/package.json | 2 +- packages/client/src/effect/service.ts | 17 +--- packages/core/src/config/plugin/agent.ts | 2 +- packages/core/src/config/plugin/command.ts | 2 +- packages/core/src/database/sqlite.bun.ts | 3 + packages/core/src/database/sqlite.node.ts | 3 + packages/core/src/fs-util.ts | 10 +- packages/core/src/plugin/skill.ts | 2 +- packages/core/src/plugin/supervisor.ts | 2 +- packages/core/src/session/runner/retry.ts | 5 +- packages/core/src/skill.ts | 2 +- packages/core/src/tool/skill.ts | 2 +- packages/core/src/util/effect-flock.ts | 13 ++- .../core/test/filesystem/filesystem.test.ts | 4 +- packages/effect-sqlite-node/src/index.ts | 3 + packages/http-recorder/package.json | 2 +- packages/httpapi-codegen/src/index.ts | 95 +++++++++++------- .../httpapi-codegen/test/generate.test.ts | 21 ++-- .../httpapi-codegen/test/generated/event.ts | 2 +- .../httpapi-codegen/test/generated/session.ts | 2 +- packages/opencode/src/agent/agent.ts | 2 +- packages/opencode/src/project/project.ts | 2 +- .../instance/httpapi/groups/experimental.ts | 2 +- packages/opencode/src/session/instruction.ts | 2 +- packages/opencode/src/session/llm.ts | 2 +- packages/opencode/src/storage/storage.ts | 14 +-- .../test/filesystem/filesystem.test.ts | 4 +- packages/opencode/test/lib/cli-process.ts | 2 +- .../opencode/test/storage/storage.test.ts | 4 +- packages/protocol/src/api.ts | 10 +- packages/sdk/js/script/build.ts | 2 +- packages/sdk/js/src/v2/gen/sdk.gen.ts | 37 +++++++ packages/sdk/js/src/v2/gen/types.gen.ts | 97 ++++++++++++++++++- ...ta.83.patch => effect@4.0.0-beta.98.patch} | 8 +- 37 files changed, 316 insertions(+), 138 deletions(-) rename patches/{effect@4.0.0-beta.83.patch => effect@4.0.0-beta.98.patch} (88%) diff --git a/bun.lock b/bun.lock index 7d2f7ea4536..1d50a2d541b 100644 --- a/bun.lock +++ b/bun.lock @@ -185,7 +185,7 @@ "effect": "catalog:", }, "peerDependencies": { - "effect": "4.0.0-beta.83", + "effect": "4.0.0-beta.98", }, "optionalPeers": [ "effect", @@ -569,7 +569,7 @@ "name": "@opencode-ai/http-recorder", "version": "1.18.3", "dependencies": { - "@effect/platform-node-shared": "4.0.0-beta.83", + "@effect/platform-node-shared": "4.0.0-beta.98", }, "devDependencies": { "@effect/platform-node": "catalog:", @@ -1180,12 +1180,12 @@ "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", - "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", "pacote@21.5.0": "patches/pacote@21.5.0.patch", + "effect@4.0.0-beta.98": "patches/effect@4.0.0-beta.98.patch", }, "overrides": { "@opentui/core": "catalog:", @@ -1197,9 +1197,9 @@ "catalog": { "@cloudflare/workers-types": "4.20251008.0", "@corvu/drawer": "0.2.4", - "@effect/opentelemetry": "4.0.0-beta.83", - "@effect/platform-node": "4.0.0-beta.83", - "@effect/sql-sqlite-bun": "4.0.0-beta.83", + "@effect/opentelemetry": "4.0.0-beta.98", + "@effect/platform-node": "4.0.0-beta.98", + "@effect/sql-sqlite-bun": "4.0.0-beta.98", "@hono/standard-validator": "0.2.0", "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", @@ -1235,7 +1235,7 @@ "dompurify": "3.3.1", "drizzle-kit": "1.0.0-rc.2", "drizzle-orm": "1.0.0-rc.2", - "effect": "4.0.0-beta.83", + "effect": "4.0.0-beta.98", "fuzzysort": "3.1.0", "get-east-asian-width": "1.6.0", "hono": "4.10.7", @@ -1639,13 +1639,13 @@ "@drizzle-team/brocli": ["@drizzle-team/brocli@0.11.0", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="], - "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.83", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.83" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/api-logs", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-cPfCfp/ghu0itbX6Dqjdr4N0rbjng5ON4sUpnLHV5JJySG8zZpWmuOZLWIrfrNKT2ctYR1BYmp1aYCgkItaJLw=="], + "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.98", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.98" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/api-logs", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-ITfK8xhcl+9GXOvPwzADWkOQ+dgUGZrJNefT3r2+uLFmzjyKRLtHzhLOl6lZaLSsf5io13+nmt8adfMRQPq+oA=="], - "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.83", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.83", "mime": "^4.1.0", "undici": "^8.2.0" }, "peerDependencies": { "effect": "^4.0.0-beta.83", "ioredis": "^5.7.0" } }, "sha512-RmpVGu/+X/Bif3/g1Rzj8oFzTOknoVB3yHCa0b179vytPpKe+Kj9ZwKNcAnKWqHUDkbSPBq1Ca60mvOHr2/+LQ=="], + "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.98", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.98", "mime": "^4.1.0", "undici": "^8.7.0" }, "peerDependencies": { "effect": "^4.0.0-beta.98", "ioredis": "^5.7.0" } }, "sha512-IQu1TiLXQEDSGkDBllyYjVadf+UqdjptryqX4mmktVTTbGDq7X4uVxe7cSgXuqZvyfG6kagTzwj2lfynxOaKQg=="], - "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.83", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.83" } }, "sha512-+yr/+PJmKTgmJq1QOINSBPgLu7Cjc4CZcotBXnGjyDEizOmimFgTkN2B8PBJAKIKUWYWfobjXqC+58/VhhPKAw=="], + "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.98", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.21.0" }, "peerDependencies": { "effect": "^4.0.0-beta.98" } }, "sha512-iySXaffnCJX1sNAIp79ghhIeui9E5qwUQyqd1VLPkB9UNO4vdpd9B5fTEXwe7S/GusL4jsk9vSvX38XJgRFG1w=="], - "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.83", "", { "peerDependencies": { "effect": "^4.0.0-beta.83" } }, "sha512-6OaxLsWffxkh9pXYUSyj/AxjVb9URY2rG9U6atjxClWy30Jx77R9Pm3Rrc7cQ63kQurePavEw1bQbzQ/SILiQQ=="], + "@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.98", "", { "peerDependencies": { "effect": "^4.0.0-beta.98" } }, "sha512-cc41uLhYBqexdbTNu4dlui+31E8hcVLEapLySa0C8d60FmBY8IEAV/RD3oF+6pqPslKEZ9p1+XVLdDm0iflw5Q=="], "@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="], @@ -4037,7 +4037,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "effect": ["effect@4.0.0-beta.83", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-0wsak8RtgGAr9UWSbVDgJHZcUqMSvicHcvaZv1MbMM7MCGgW4Rn/137J1MHQbwYPcwYGxT/IqehFd+UbYuj78w=="], + "effect": ["effect@4.0.0-beta.98", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.9.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.4", "multipasta": "^0.2.8", "toml": "^4.1.2", "uuid": "^14.0.1", "yaml": "^2.9.0" } }, "sha512-oz+bsG5h+6RNrw4t5GMfQrk/xBS8ROoqkYsuvRhBr5O7mCOrpvH/hbw+QrDzvKIpX4HJClwm86F94c87W0sJxg=="], "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], @@ -4213,7 +4213,7 @@ "extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="], - "fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + "fast-check": ["fast-check@4.9.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg=="], "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], @@ -5077,7 +5077,7 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "msgpackr": ["msgpackr@2.0.2", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ=="], + "msgpackr": ["msgpackr@2.0.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-o1C5KRmuRt+apqMr1HuGSqWStZoRBUpEsCsl15uM9VdAF1qHLtvMOU2En747EnTyEl6c4pzPewRMFF31s1CNbA=="], "msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="], @@ -5085,7 +5085,7 @@ "multicast-dns": ["multicast-dns@7.2.5", "", { "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" }, "bin": { "multicast-dns": "cli.js" } }, "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg=="], - "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + "multipasta": ["multipasta@0.2.8", "", {}, "sha512-ZPWuMKyv0cSO29f7hozp+k6+crZbQijV8ipMvxNxRf2SwtYGTX1ZX89Kd20VV4H9Znonx+EQn+iy1wGQsJ+b+Q=="], "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], @@ -5985,7 +5985,7 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], + "toml": ["toml@4.3.0", "", {}, "sha512-lVb8X9BsPVuH0M4BKeS91tXAmJvCjQ5UIyAbQFaxkKGyUFK2RPkhwaFSQH8vbpl1d23eu/IBH+dwVMHWaq9A5A=="], "toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.8", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-b+5ynEFp4Woe5a22hzNQm42lD23t13ZMihVxHbzjA50zdcM9aOSJTIjdJ0PDSd4/50HbBXcpHiQsz6rM4N88ww=="], @@ -6071,7 +6071,7 @@ "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], - "undici": ["undici@8.3.0", "", {}, "sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q=="], + "undici": ["undici@8.7.0", "", {}, "sha512-N7iQtfyLhIMOFgQubvmLV26svHpO0bqKnAiWotTQCVKCmWrcGbBotPuW1x+xwYZ2VHdSTVUfPQQnlEt1/LouTQ=="], "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], @@ -6153,7 +6153,7 @@ "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], - "uuid": ["uuid@14.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg=="], + "uuid": ["uuid@14.0.1", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew=="], "valibot": ["valibot@1.4.1", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-klCmFTz2jeDluy9RwX+F884TCiogtdBJ/YaxSx1EOBYXa3NXNWj8kR1jjN8rzluwojJVWWaHJ4r1U5LfICnM3g=="], @@ -8363,6 +8363,26 @@ "@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="], + "@standard-community/standard-json/effect/fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + + "@standard-community/standard-json/effect/msgpackr": ["msgpackr@2.0.2", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ=="], + + "@standard-community/standard-json/effect/multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + + "@standard-community/standard-json/effect/toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], + + "@standard-community/standard-json/effect/uuid": ["uuid@14.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg=="], + + "@standard-community/standard-openapi/effect/fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="], + + "@standard-community/standard-openapi/effect/msgpackr": ["msgpackr@2.0.2", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ=="], + + "@standard-community/standard-openapi/effect/multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], + + "@standard-community/standard-openapi/effect/toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="], + + "@standard-community/standard-openapi/effect/uuid": ["uuid@14.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg=="], + "@stoplight/spectral-core/minimatch/brace-expansion": ["brace-expansion@1.1.15", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg=="], "@storybook/addon-docs/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], diff --git a/package.json b/package.json index 45f424a363e..c8b778f207f 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,9 @@ "packages/slack" ], "catalog": { - "@effect/opentelemetry": "4.0.0-beta.83", - "@effect/platform-node": "4.0.0-beta.83", - "@effect/sql-sqlite-bun": "4.0.0-beta.83", + "@effect/opentelemetry": "4.0.0-beta.98", + "@effect/platform-node": "4.0.0-beta.98", + "@effect/sql-sqlite-bun": "4.0.0-beta.98", "@npmcli/arborist": "9.4.0", "@types/bun": "1.3.13", "@types/cross-spawn": "6.0.6", @@ -69,7 +69,7 @@ "dompurify": "3.3.1", "drizzle-kit": "1.0.0-rc.2", "drizzle-orm": "1.0.0-rc.2", - "effect": "4.0.0-beta.83", + "effect": "4.0.0-beta.98", "ai": "6.0.168", "cross-spawn": "7.0.6", "hono": "4.10.7", @@ -166,7 +166,7 @@ "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", "@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch", "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", - "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", + "effect@4.0.0-beta.98": "patches/effect@4.0.0-beta.98.patch", "@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch" } } diff --git a/packages/cli/test/service.test.ts b/packages/cli/test/service.test.ts index d887e5b2b34..3f6225b383b 100644 --- a/packages/cli/test/service.test.ts +++ b/packages/cli/test/service.test.ts @@ -191,9 +191,7 @@ test("concurrent service processes elect one server", async () => { ), ).toEqual({ timeSuspended: null }) expect(await waitForExecutionStart(database, sessionID)).toBe(1) - await Effect.runPromise( - Service.stop({ file: registration }).pipe(Effect.provide(NodeFileSystem.layer)), - ) + await Effect.runPromise(Service.stop({ file: registration }).pipe(Effect.provide(NodeFileSystem.layer))) await winner?.exited } finally { processes.forEach((process) => process.kill("SIGTERM")) @@ -271,7 +269,7 @@ function waitForExecutionStart(file: string, sessionID: SessionV2.ID) { ), Effect.filterOrFail((rows) => rows.length > 0), Effect.map((rows) => rows.length), - Effect.retry(Schedule.spaced("50 millis").pipe(Schedule.both(Schedule.recurs(200)))), + Effect.retry(Schedule.max([Schedule.spaced("50 millis"), Schedule.recurs(200)])), ) }), ) diff --git a/packages/client/package.json b/packages/client/package.json index d7b4c9b24e9..ffb6458d849 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -36,7 +36,7 @@ "@opencode-ai/protocol": "workspace:*" }, "peerDependencies": { - "effect": "4.0.0-beta.83" + "effect": "4.0.0-beta.98" }, "peerDependenciesMeta": { "effect": { diff --git a/packages/client/src/effect/service.ts b/packages/client/src/effect/service.ts index e4bf5bbefba..ffd36bff77d 100644 --- a/packages/client/src/effect/service.ts +++ b/packages/client/src/effect/service.ts @@ -3,12 +3,7 @@ import { Effect, FileSystem, Option, Schedule, Schema } from "effect" import { spawn, type ChildProcess } from "node:child_process" import { homedir } from "node:os" import { join } from "node:path" -import type { - DiscoverOptions, - Endpoint, - EnsureOptions, - StopOptions, -} from "../service.js" +import type { DiscoverOptions, Endpoint, EnsureOptions, StopOptions } from "../service.js" export * from "../service.js" /** Contents of the local service registration file. */ @@ -82,7 +77,8 @@ export const ensure = Effect.fn("service.ensure")(function* (options: EnsureOpti spawnDelay = 5_000 const compatible = !service.legacy && (options.version === undefined || service.version === options.version) if (compatible && service.state === "ready") return Option.some(service) - if (compatible && service.state === "failed") return yield* Effect.fail(new Error("Background service failed to start")) + if (compatible && service.state === "failed") + return yield* Effect.fail(new Error("Background service failed to start")) if (compatible) return Option.none() yield* announce("version-mismatch", service.version) yield* kill(service, options).pipe(Effect.ignore) @@ -221,7 +217,7 @@ const find = Effect.fnUntraced(function* (options: { readonly file?: string }) { // 50ms cadence bounded at ~5s, shared by stop escalation and each ensure // discovery window. -const poll = Schedule.spaced("50 millis").pipe(Schedule.both(Schedule.recurs(100))) +const poll = Schedule.max([Schedule.spaced("50 millis"), Schedule.recurs(100)]) const signal = (pid: number, name: NodeJS.Signals) => Effect.try({ try: () => process.kill(pid, name), catch: (cause) => cause }).pipe(Effect.ignore) @@ -238,10 +234,7 @@ function same(left: Info, right: Info) { return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid } -const kill = Effect.fnUntraced(function* ( - service: LocalService, - options: { readonly file?: string }, -) { +const kill = Effect.fnUntraced(function* (service: LocalService, options: { readonly file?: string }) { const requested = yield* requestStop(service) if (requested === "rejected") return if (requested === "unsupported") { diff --git a/packages/core/src/config/plugin/agent.ts b/packages/core/src/config/plugin/agent.ts index 0be04bf85ab..8ec2c15a635 100644 --- a/packages/core/src/config/plugin/agent.ts +++ b/packages/core/src/config/plugin/agent.ts @@ -150,7 +150,7 @@ function expandHome(resource: string, home: string) { function discover(fs: FSUtil.Interface, directory: string) { return Effect.forEach(legacySources, (source) => fs - .glob(source.pattern, { cwd: directory, absolute: true, dot: true, symlink: true }) + .scan(source.pattern, { cwd: directory, absolute: true, dot: true, symlink: true }) .pipe( Effect.map((files) => files.toSorted().map((filepath) => ({ directory, filepath, primary: source.primary }))), ), diff --git a/packages/core/src/config/plugin/command.ts b/packages/core/src/config/plugin/command.ts index 744f58f7a74..90b1920ee49 100644 --- a/packages/core/src/config/plugin/command.ts +++ b/packages/core/src/config/plugin/command.ts @@ -62,7 +62,7 @@ export const Plugin = define({ function loadDirectory(fs: FSUtil.Interface, directory: string) { return Effect.gen(function* () { const files = yield* fs - .glob("{command,commands}/**/*.md", { cwd: directory, absolute: true, dot: true, symlink: true }) + .scan("{command,commands}/**/*.md", { cwd: directory, absolute: true, dot: true, symlink: true }) .pipe(Effect.catch(() => Effect.succeed([] as string[]))) return yield* Effect.forEach(files.toSorted(), (filepath) => fs.readFileStringSafe(filepath).pipe( diff --git a/packages/core/src/database/sqlite.bun.ts b/packages/core/src/database/sqlite.bun.ts index bbb3ad6143e..c1fa349fcd7 100644 --- a/packages/core/src/database/sqlite.bun.ts +++ b/packages/core/src/database/sqlite.bun.ts @@ -88,6 +88,9 @@ const make = (options: Config) => executeValues(query, params) { return runValues(query, params) }, + executeValuesUnprepared(query, params) { + return runValues(query, params) + }, executeUnprepared(query, params, transformRows) { return this.execute(query, params, transformRows) }, diff --git a/packages/core/src/database/sqlite.node.ts b/packages/core/src/database/sqlite.node.ts index 6fcbd76e4e7..a9e0f04660b 100644 --- a/packages/core/src/database/sqlite.node.ts +++ b/packages/core/src/database/sqlite.node.ts @@ -89,6 +89,9 @@ const make = (options: Config) => executeValues(query, params) { return runValues(query, params) }, + executeValuesUnprepared(query, params) { + return runValues(query, params) + }, executeUnprepared(query, params, transformRows) { return this.execute(query, params, transformRows) }, diff --git a/packages/core/src/fs-util.ts b/packages/core/src/fs-util.ts index b9f5bb8a474..0c74fbbc35d 100644 --- a/packages/core/src/fs-util.ts +++ b/packages/core/src/fs-util.ts @@ -42,7 +42,7 @@ export namespace FSUtil { readonly findUp: (target: string, start: string, stop?: string) => Effect.Effect readonly up: (options: { targets: string[]; start: string; stop?: string }) => Effect.Effect readonly globUp: (pattern: string, start: string, stop?: string) => Effect.Effect - readonly glob: (pattern: string, options?: Glob.Options) => Effect.Effect + readonly scan: (pattern: string, options?: Glob.Options) => Effect.Effect readonly globMatch: (pattern: string, filepath: string) => boolean } @@ -51,7 +51,7 @@ export namespace FSUtil { export const use = serviceUse(Service) // Exported so simulation can wrap this layer and override the methods that - // bypass the injected FileSystem (readDirectoryEntries, glob, globUp). + // bypass the injected FileSystem (readDirectoryEntries, scan, globUp). export const layer = Layer.effect( Service, Effect.gen(function* () { @@ -146,7 +146,7 @@ export namespace FSUtil { if (mode) yield* fs.chmod(path, mode) }) - const glob = Effect.fn("FileSystem.glob")(function* (pattern: string, options?: Glob.Options) { + const scan = Effect.fn("FileSystem.scan")(function* (pattern: string, options?: Glob.Options) { return yield* Effect.tryPromise({ try: () => Glob.scan(pattern, options), catch: (cause) => new FileSystemError({ method: "glob", cause }), @@ -187,7 +187,7 @@ export namespace FSUtil { const result: string[] = [] let current = start while (true) { - const matches = yield* glob(pattern, { cwd: current, absolute: true, include: "file", dot: true }).pipe( + const matches = yield* scan(pattern, { cwd: current, absolute: true, include: "file", dot: true }).pipe( Effect.catch(() => Effect.succeed([] as string[])), ) result.push(...matches) @@ -214,7 +214,7 @@ export namespace FSUtil { findUp, up, globUp, - glob, + scan, globMatch: Glob.match, }) }), diff --git a/packages/core/src/plugin/skill.ts b/packages/core/src/plugin/skill.ts index 0729ac86849..f5fa0a4ea5d 100644 --- a/packages/core/src/plugin/skill.ts +++ b/packages/core/src/plugin/skill.ts @@ -94,7 +94,7 @@ const configuredPlugins = Effect.fn("SkillPlugin.configuredPlugins")(function* ( } if (entry.type !== "directory") return Effect.succeed([]) return fs - .glob("{plugin,plugins}/*.{ts,js}", { + .scan("{plugin,plugins}/*.{ts,js}", { cwd: entry.path, absolute: true, include: "file", diff --git a/packages/core/src/plugin/supervisor.ts b/packages/core/src/plugin/supervisor.ts index ad02bb33edc..382474cdd15 100644 --- a/packages/core/src/plugin/supervisor.ts +++ b/packages/core/src/plugin/supervisor.ts @@ -184,7 +184,7 @@ const load = Effect.fn("PluginSupervisor.load")(function* (operation: Extract { } export const schedule = (events: EventV2.Interface, sessionID: SessionSchema.ID) => - Schedule.exponential("2 seconds").pipe( - Schedule.take(4), + Schedule.max([Schedule.exponential("2 seconds"), Schedule.recurs(4)]).pipe( Schedule.setInputType(), Schedule.passthrough, Schedule.while(({ input }) => input instanceof RetryableFailure), - Schedule.modifyDelay((failure, delay) => { + Schedule.modifyDelay(({ input: failure, duration: delay }) => { const minimum = failure instanceof RetryableFailure ? retryAfter(failure) : undefined return Effect.succeed(minimum === undefined ? delay : Duration.max(delay, Duration.millis(minimum))) }), diff --git a/packages/core/src/skill.ts b/packages/core/src/skill.ts index 2bdc478c248..8708d84fda3 100644 --- a/packages/core/src/skill.ts +++ b/packages/core/src/skill.ts @@ -109,7 +109,7 @@ const layer = Layer.effect( const directories = source.type === "directory" ? [source.path] : yield* discovery.pull(source.url) for (const directory of directories) { const files = yield* fs - .glob("{*.md,**/SKILL.md}", { cwd: directory, absolute: true, include: "file", symlink: true, dot: true }) + .scan("{*.md,**/SKILL.md}", { cwd: directory, absolute: true, include: "file", symlink: true, dot: true }) .pipe(Effect.catch(() => Effect.succeed([] as string[]))) for (const filepath of files.toSorted()) { const content = yield* fs.readFileStringSafe(filepath).pipe(Effect.catch(() => Effect.succeed(undefined))) diff --git a/packages/core/src/tool/skill.ts b/packages/core/src/tool/skill.ts index bf2ac0540df..ac7e675e461 100644 --- a/packages/core/src/tool/skill.ts +++ b/packages/core/src/tool/skill.ts @@ -84,7 +84,7 @@ export const Plugin = { const directory = path.dirname(skill.location) const files = path.basename(skill.location) === "SKILL.md" - ? (yield* fs.glob("**/*", { cwd: directory, absolute: true, include: "file", dot: true })) + ? (yield* fs.scan("**/*", { cwd: directory, absolute: true, include: "file", dot: true })) .filter((file) => path.basename(file) !== "SKILL.md") .toSorted() .slice(0, FILE_LIMIT) diff --git a/packages/core/src/util/effect-flock.ts b/packages/core/src/util/effect-flock.ts index 4b528d26022..c190d37393e 100644 --- a/packages/core/src/util/effect-flock.ts +++ b/packages/core/src/util/effect-flock.ts @@ -50,11 +50,14 @@ export namespace EffectFlock { const BASE_DELAY_MS = 100 const MAX_DELAY_MS = 2_000 - const retrySchedule = (timeoutMs: number) => Schedule.exponential(BASE_DELAY_MS, 1.7).pipe( - Schedule.either(Schedule.spaced(Math.min(MAX_DELAY_MS, Math.max(BASE_DELAY_MS, Math.floor(timeoutMs / 10))))), - Schedule.jittered, - Schedule.while((meta) => meta.elapsed < timeoutMs), - ) + const retrySchedule = (timeoutMs: number) => + Schedule.min([ + Schedule.exponential(BASE_DELAY_MS, 1.7), + Schedule.spaced(Math.min(MAX_DELAY_MS, Math.max(BASE_DELAY_MS, Math.floor(timeoutMs / 10)))), + ]).pipe( + Schedule.jittered, + Schedule.while((meta) => meta.elapsed < timeoutMs), + ) // --------------------------------------------------------------------------- // Lock metadata schema diff --git a/packages/core/test/filesystem/filesystem.test.ts b/packages/core/test/filesystem/filesystem.test.ts index fdce1b44764..907fcb77395 100644 --- a/packages/core/test/filesystem/filesystem.test.ts +++ b/packages/core/test/filesystem/filesystem.test.ts @@ -280,7 +280,7 @@ describe("FSUtil", () => { yield* filesys.writeFileString(path.join(tmp, "b.ts"), "b") yield* filesys.writeFileString(path.join(tmp, "c.json"), "c") - const result = yield* fs.glob("*.ts", { cwd: tmp }) + const result = yield* fs.scan("*.ts", { cwd: tmp }) expect(result.sort()).toEqual(["a.ts", "b.ts"]) }), ) @@ -293,7 +293,7 @@ describe("FSUtil", () => { const tmp = yield* filesys.makeTempDirectoryScoped() yield* filesys.writeFileString(path.join(tmp, "file.txt"), "hello") - const result = yield* fs.glob("*.txt", { cwd: tmp, absolute: true }) + const result = yield* fs.scan("*.txt", { cwd: tmp, absolute: true }) expect(result).toEqual([path.join(tmp, "file.txt")]) }), ) diff --git a/packages/effect-sqlite-node/src/index.ts b/packages/effect-sqlite-node/src/index.ts index 37e255391da..95fcd4fd745 100644 --- a/packages/effect-sqlite-node/src/index.ts +++ b/packages/effect-sqlite-node/src/index.ts @@ -112,6 +112,9 @@ export const make = ( executeValues(sql, params) { return runValues(sql, params) }, + executeValuesUnprepared(sql, params) { + return runValues(sql, params) + }, executeUnprepared(sql, params, transformRows) { return this.execute(sql, params, transformRows) }, diff --git a/packages/http-recorder/package.json b/packages/http-recorder/package.json index 4d6bbbb0c30..213ea720abc 100644 --- a/packages/http-recorder/package.json +++ b/packages/http-recorder/package.json @@ -52,7 +52,7 @@ "typescript": "catalog:" }, "dependencies": { - "@effect/platform-node-shared": "4.0.0-beta.83" + "@effect/platform-node-shared": "4.0.0-beta.98" }, "peerDependencies": { "effect": "catalog:" diff --git a/packages/httpapi-codegen/src/index.ts b/packages/httpapi-codegen/src/index.ts index 88204e771a3..5cefabd59f3 100644 --- a/packages/httpapi-codegen/src/index.ts +++ b/packages/httpapi-codegen/src/index.ts @@ -47,7 +47,7 @@ export type Endpoint = { readonly group: string readonly sourceGroup: string readonly topLevel: boolean - readonly endpoint: HttpApiEndpoint.AnyWithProps + readonly endpoint: HttpApiEndpoint.Top readonly params: Schema.Top | undefined readonly query: Schema.Top | undefined readonly headers: Schema.Top | undefined @@ -83,7 +83,7 @@ const resolveContentSchema = SchemaAST.resolveAt("contentSchema") const Manifest = Schema.fromJsonString(Schema.Array(Schema.String)) const manifestName = ".httpapi-codegen.json" -export function compile( +export function compile( api: HttpApi.HttpApi, options?: { readonly groupNames?: Readonly> @@ -96,9 +96,9 @@ export function compile( HttpApi.reflect(api, { onGroup() {}, onEndpoint({ endpoint, errors, group, middleware }) { - if (options?.omitEndpoints?.has(endpoint.name)) return + if (options?.omitEndpoints?.has(endpoint.identifier)) return const groupName = options?.groupNames?.[group.identifier] ?? group.identifier - const name = `${groupName}.${endpoint.name}` + const name = `${groupName}.${endpoint.identifier}` const required = Array.from(middleware).find((item) => item.requiredForClient) if (required !== undefined) { throw new GenerationError({ reason: `Client middleware requires adapter: ${required.key}` }) @@ -151,7 +151,7 @@ export function compile( const clientPath = clientEndpointPath( group.identifier, Context.getOrElse(endpoint.annotations, OpenApi.Identifier, () => - group.topLevel ? endpoint.name : `${group.identifier}.${endpoint.name}`, + group.topLevel ? endpoint.identifier : `${group.identifier}.${endpoint.identifier}`, ), ) endpoints.push({ @@ -263,7 +263,7 @@ export function emitEffect(contract: Contract): Output { const endpoint = contract.groups.flatMap((group) => group.endpoints).find((endpoint) => !endpoint.effectPortable) if (endpoint !== undefined) { throw new GenerationError({ - reason: `Effect schema requires authoritative import: ${endpoint.group}.${endpoint.endpoint.name}`, + reason: `Effect schema requires authoritative import: ${endpoint.group}.${endpoint.endpoint.identifier}`, }) } return { operations: operations(contract.groups), files: renderEffectFiles(contract.groups) } @@ -332,7 +332,7 @@ function renderEffectShape(groups: ReadonlyArray, options: { readonly mod const request = endpoint.operation.inputMode === "none" ? "" - : `type ${prefix}Request = Parameters<${rawGroup}[${JSON.stringify(endpoint.endpoint.name)}]>[0]` + : `type ${prefix}Request = Parameters<${rawGroup}[${JSON.stringify(endpoint.endpoint.identifier)}]>[0]` const input = endpoint.input .map( (field) => @@ -340,7 +340,7 @@ function renderEffectShape(groups: ReadonlyArray, options: { readonly mod ) .join("; ") const inputType = endpoint.operation.inputMode === "none" ? "" : `export type ${prefix}Input = { ${input} }` - const rawOutput = `EffectValue>` + const rawOutput = `EffectValue>` const outputType = isStreamSchema(endpoint.successes[0]) ? `export type ${prefix}Output = StreamValue<${rawOutput}>` : `export type ${prefix}Output = ${endpoint.unwrapData ? `(${rawOutput})["data"]` : rawOutput}` @@ -399,7 +399,7 @@ function groupShapeTypeName(group: Group, endpoint: Endpoint) { } function assertPromiseEndpoint(endpoint: Endpoint) { - const name = `${endpoint.group}.${endpoint.endpoint.name}` + const name = `${endpoint.group}.${endpoint.endpoint.identifier}` const payload = endpoint.payloads[0] const payloadEncoding = payload === undefined ? undefined : resolveHttpApiEncoding(payload.ast) if ( @@ -493,9 +493,9 @@ function renderImportedEffectFiles( item.operation.inputMode === "none" ? "" : `input${item.operation.inputMode === "optional" ? "?" : ""}: ${prefix}Input` - const rawCall = `raw[${JSON.stringify(item.endpoint.name)}]({ ${request} })` + const rawCall = `raw[${JSON.stringify(item.endpoint.identifier)}]({ ${request} })` const mapped = `${rawCall}.pipe(Effect.mapError(mapClientError)${item.unwrapData ? ", Effect.map((value) => value.data)" : ""})` - return `${item.operation.inputMode === "none" ? "" : `type ${prefix}Request = Parameters<${rawGroup}[${JSON.stringify(item.endpoint.name)}]>[0]\ntype ${prefix}Input = { ${input} }\n`}const ${prefix} = (raw: ${rawGroup}) => (${argument}) => ${item.operation.success === "stream" ? `Stream.unwrap(${rawCall}.pipe(Effect.mapError(mapClientError), Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError)))))` : mapped}` + return `${item.operation.inputMode === "none" ? "" : `type ${prefix}Request = Parameters<${rawGroup}[${JSON.stringify(item.endpoint.identifier)}]>[0]\ntype ${prefix}Input = { ${input} }\n`}const ${prefix} = (raw: ${rawGroup}) => (${argument}) => ${item.operation.success === "stream" ? `Stream.unwrap(${rawCall}.pipe(Effect.mapError(mapClientError), Effect.map((stream) => stream.pipe(Stream.mapError(mapClientError)))))` : mapped}` }) const fields = renderClientTree( group.endpoints, @@ -548,10 +548,10 @@ function renderImportedGroup(group: string) { function renderImportedProjection(groups: ReadonlyArray, endpoints: Readonly>) { const imports = groups.flatMap((group) => group.endpoints.map((endpoint) => { - const name = endpoints[`${group.identifier}.${endpoint.endpoint.name}`] + const name = endpoints[`${group.identifier}.${endpoint.endpoint.identifier}`] if (name === undefined) { throw new GenerationError({ - reason: `Missing imported endpoint: ${group.identifier}.${endpoint.endpoint.name}`, + reason: `Missing imported endpoint: ${group.identifier}.${endpoint.endpoint.identifier}`, }) } return name @@ -560,7 +560,7 @@ function renderImportedProjection(groups: ReadonlyArray, endpoints: Reado const source = `const Api = HttpApi.make("generated").${groups .map((group) => { const options = group.endpoints[0]?.topLevel ? ", { topLevel: true }" : "" - return `add(HttpApiGroup.make(${JSON.stringify(group.identifier)}${options})${group.endpoints.map((endpoint) => `.add(${endpoints[`${group.identifier}.${endpoint.endpoint.name}`]})`).join("")})` + return `add(HttpApiGroup.make(${JSON.stringify(group.identifier)}${options})${group.endpoints.map((endpoint) => `.add(${endpoints[`${group.identifier}.${endpoint.endpoint.identifier}`]})`).join("")})` }) .join(".")}\n\n` return { imports: [...new Set(imports)], source } @@ -633,7 +633,7 @@ function renderPromiseTypes( isStreamSchema(successSchema) && successSchema._tag === "StreamSse" ? successSchema.sseMode === "data" ? streamEncodedDataSchema(successSchema) - : successSchema.events + : Schema.make(successSchema.events.ast) : successSchema, ) return [ @@ -660,7 +660,9 @@ function renderPromiseTypes( rendered.types.reduce((result, type, index) => result.replaceAll(`__PROMISE_TYPE_${index}__`, type), source) const resolvedErrors = errorTypes.map(resolve) const resolvedOperations = resolve(operations) - const json = [...rendered.definitions, ...resolvedErrors, resolvedOperations].some((type) => type.includes("JsonValue")) + const json = [...rendered.definitions, ...resolvedErrors, resolvedOperations].some((type) => + type.includes("JsonValue"), + ) ? `export type JsonValue = null | boolean | number | string | ${mutableOutputs ? "Array | { [key: string]: JsonValue }" : "ReadonlyArray | { readonly [key: string]: JsonValue }"}` : "" const imports = [...new Set(Object.values(outputTypes ?? {}).map((override) => override.import))] @@ -705,7 +707,7 @@ function renderPromiseClient(groups: ReadonlyArray) { const success = endpoint.successes[0] if (!isStreamSchema(success) || success._tag !== "StreamSse" || success.sseMode !== "data") { throw new GenerationError({ - reason: `Promise stream emission is not implemented: ${group.identifier}.${endpoint.endpoint.name}`, + reason: `Promise stream emission is not implemented: ${group.identifier}.${endpoint.endpoint.identifier}`, }) } return `(${argument}): AsyncIterable<${prefix}Output> => sse<${prefix}Output>(${descriptor}, requestOptions)` @@ -956,7 +958,7 @@ function uniqueModule(base: string, index: number, modules: ReadonlySet) function normalizeTransport( schema: Schema.Top | undefined, source: InputField["source"] | "success" | "error", - endpoint: HttpApiEndpoint.AnyWithProps, + endpoint: HttpApiEndpoint.Top, operation: string, ) { if (schema === undefined) return undefined @@ -968,14 +970,33 @@ function normalizeTransport( if (!isPathInput(endpoint.path)) { throw new GenerationError({ reason: `Invalid endpoint path: ${operation}` }) } - const rebuilt = HttpApiEndpoint.make(endpoint.method)(endpoint.name, endpoint.path, { - ...(source === "params" ? { params: decoded } : undefined), - ...(source === "query" ? { query: decoded } : undefined), - ...(source === "headers" ? { headers: decoded } : undefined), - ...(source === "payload" ? { payload: decoded } : undefined), - ...(source === "success" ? { success: decoded } : { success: Schema.String }), - ...(source === "error" ? { error: decoded } : undefined), - }) + const rebuilt = + source === "params" + ? HttpApiEndpoint.make(endpoint.method)(endpoint.identifier, endpoint.path, { + params: decoded, + success: Schema.String, + }) + : source === "query" + ? HttpApiEndpoint.make(endpoint.method)(endpoint.identifier, endpoint.path, { + query: decoded, + success: Schema.String, + }) + : source === "headers" + ? HttpApiEndpoint.make(endpoint.method)(endpoint.identifier, endpoint.path, { + headers: decoded, + success: Schema.String, + }) + : source === "payload" + ? HttpApiEndpoint.make(endpoint.method)(endpoint.identifier, endpoint.path, { + payload: decoded, + success: Schema.String, + }) + : source === "success" + ? HttpApiEndpoint.make(endpoint.method)(endpoint.identifier, endpoint.path, { success: decoded }) + : HttpApiEndpoint.make(endpoint.method)(endpoint.identifier, endpoint.path, { + success: Schema.String, + error: decoded, + }) const normalized = source === "params" ? rebuilt.params @@ -1115,7 +1136,7 @@ function isSafeOutputPath(path: string) { return path !== manifestName && !isAbsolute(path) && path !== "." && path !== ".." && !/[\\/]/.test(path) } -export function generate( +export function generate( api: HttpApi.HttpApi, options: { readonly directory: string }, ): Effect.Effect { @@ -1147,7 +1168,7 @@ function responseSchemas(schema: Schema.Top, path: string): Array(schema.events.ast) return [ [`${path}.${schema.sseMode}`, value], [`${path}.error`, schema.error], @@ -1306,7 +1327,7 @@ function declaredErrorFields(schema: Schema.Top) { fields: fields.propertySignatures.flatMap((field) => field.name === key || typeof field.name !== "string" ? [] - : [[field.name, Schema.make(field.type), SchemaAST.isOptional(field.type)] as const], + : [[field.name, Schema.make(field.type), SchemaAST.isOptional(field.type)] as const], ), } } @@ -1327,7 +1348,7 @@ function isStreamSchema(schema: Schema.Top): schema is HttpApiSchema.StreamSchem } function streamDataSchema(schema: Extract) { - return Schema.make(streamDataAst(Schema.toType(schema.events).ast)) + return Schema.make(streamDataAst(Schema.toType(schema.events).ast)) } function streamEncodedDataSchema(schema: Extract) { @@ -1336,7 +1357,7 @@ function streamEncodedDataSchema(schema: Extract(encoded) } function streamDataAst(ast: SchemaAST.AST) { @@ -1393,7 +1414,7 @@ function renderGroup(group: Group, groupIndex: number) { .map((field) => { const slot = schemaBySource[field.source] if (slot === undefined) { - throw new GenerationError({ reason: `Missing input schema: ${group.identifier}.${endpoint.name}` }) + throw new GenerationError({ reason: `Missing input schema: ${group.identifier}.${endpoint.identifier}` }) } return `readonly ${JSON.stringify(field.name)}${field.optional ? "?" : ""}: (typeof ${slot.name}.Type)[${JSON.stringify(field.name)}]` }) @@ -1418,13 +1439,13 @@ function renderGroup(group: Group, groupIndex: number) { const declared = [...errorSlots, ...(success.streamError === undefined ? [] : [success.streamError])] const declaredSchema = declared.length === 0 ? "Schema.Never" : `Schema.Union([${declared.map((slot) => slot.name).join(", ")}])` - const rawCall = `raw[${JSON.stringify(endpoint.name)}]({ ${request} })` + const rawCall = `raw[${JSON.stringify(endpoint.identifier)}]({ ${request} })` const mapped = `${rawCall}.pipe(Effect.mapError(map${prefix}Error)${operation.unwrapData ? ", Effect.map((value) => value.data)" : ""})` const inputDeclaration = operation.operation.inputMode === "none" ? "" : `type ${prefix}Input = { ${inputType} }\n` adapters.push( `${inputDeclaration}const ${prefix}DeclaredError = ${declaredSchema}\nconst map${prefix}Error = (error: unknown) => HttpClientError.isHttpClientError(error) || Schema.isSchemaError(error) || Sse.Retry.is(error) ? new ClientError({ cause: error }) : Schema.is(${prefix}DeclaredError)(error) ? error : new ClientError({ cause: error })\nconst ${prefix} = (raw: RawGroup) => (${argument}) => ${operation.operation.success === "stream" ? `Stream.unwrap(${rawCall}.pipe(Effect.mapError(map${prefix}Error), Effect.map((stream) => stream.pipe(Stream.mapError(map${prefix}Error)))))` : mapped}`, ) - return `HttpApiEndpoint.make(${JSON.stringify(endpoint.method)})(${JSON.stringify(endpoint.name)}, ${JSON.stringify(endpoint.path)}, { ${options.join(", ")} })` + return `HttpApiEndpoint.make(${JSON.stringify(endpoint.method)})(${JSON.stringify(endpoint.identifier)}, ${JSON.stringify(endpoint.path)}, { ${options.join(", ")} })` }) function addSlot(schema: Schema.Top | undefined, name: string) { @@ -1444,7 +1465,7 @@ function renderGroup(group: Group, groupIndex: number) { } } const value = addSlot( - schema.sseMode === "data" ? streamDataSchema(schema) : schema.events, + schema.sseMode === "data" ? streamDataSchema(schema) : Schema.make(schema.events.ast), `${name}${schema.sseMode === "data" ? "Data" : "Events"}`, )! const error = addSlot(schema.error, `${name}Error`)! @@ -1465,7 +1486,7 @@ function renderGroup(group: Group, groupIndex: number) { ) const rawGroup = group.endpoints[0]?.topLevel ? `HttpApiClient.Client` - : `HttpApiClient.Client.Group` + : `HttpApiClient.Client.Group` const usesStream = group.endpoints.some((item) => item.operation.success === "stream") return `// Generated by @opencode-ai/httpapi-codegen. Do not edit.\nimport { Effect, Schema${usesStream ? ", Stream" : ""} } from "effect"\nimport { Sse } from "effect/unstable/encoding"\nimport { HttpClientError } from "effect/unstable/http"\nimport { HttpApiClient, HttpApiEndpoint, HttpApiGroup${usesHttpApiSchema ? ", HttpApiSchema" : ""} } from "effect/unstable/httpapi"\nimport { ClientError } from "./client-error"\n\n${declarations}\n\nexport const Group${groupIndex} = ${groupSource}\n\ntype RawGroup = ${rawGroup}\n\n${adapters.join("\n\n")}\n\nexport const adaptGroup${groupIndex} = (raw: RawGroup) => ({ ${methods} })\n` } @@ -1528,7 +1549,7 @@ function renderClient(groups: ReadonlyArray) { if (!group.endpoints[0]?.topLevel) { return [`${JSON.stringify(group.identifier)}: adaptGroup${index}(raw[${JSON.stringify(group.identifier)}])`] } - const raw = `{ ${group.endpoints.map((item) => `${JSON.stringify(item.endpoint.name)}: raw[${JSON.stringify(item.endpoint.name)}]`).join(", ")} }` + const raw = `{ ${group.endpoints.map((item) => `${JSON.stringify(item.endpoint.identifier)}: raw[${JSON.stringify(item.endpoint.identifier)}]`).join(", ")} }` return [`...adaptGroup${index}(${raw})`] }) return `// Generated by @opencode-ai/httpapi-codegen. Do not edit.\nimport { Effect } from "effect"\nimport { HttpApi, HttpApiClient } from "effect/unstable/httpapi"\n${imports}\n\nconst Api = ${api}\nconst adaptClient = (raw: HttpApiClient.ForApi) => ({ ${fields.join(", ")} })\n\nexport const make = (options?: { readonly baseUrl?: URL | string }) =>\n HttpApiClient.make(Api, options).pipe(Effect.map(adaptClient))\n` diff --git a/packages/httpapi-codegen/test/generate.test.ts b/packages/httpapi-codegen/test/generate.test.ts index 1404f4884d7..7797b509535 100644 --- a/packages/httpapi-codegen/test/generate.test.ts +++ b/packages/httpapi-codegen/test/generate.test.ts @@ -3,7 +3,14 @@ import { mkdtemp, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" import { Effect, FileSystem, Schema, SchemaAST, SchemaGetter } from "effect" -import { HttpApi, HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, OpenApi } from "effect/unstable/httpapi" +import { + HttpApi, + HttpApiEndpoint, + HttpApiGroup, + HttpApiMiddleware, + HttpApiSchema, + OpenApi, +} from "effect/unstable/httpapi" import { format } from "prettier" import { compile as compileContract, @@ -17,11 +24,11 @@ import { import { it } from "./effect" import { Api as FixtureApi, Missing } from "./fixture" -function api(endpoint: HttpApiEndpoint.Any) { +function api(endpoint: HttpApiEndpoint.Constraint) { return HttpApi.make("test").add(HttpApiGroup.make("session").add(endpoint)) } -function compile(source: HttpApi.HttpApi) { +function compile(source: HttpApi.HttpApi) { return emitEffect(compileContract(source)) } @@ -385,7 +392,7 @@ describe("HttpApiCodegen.generate", () => { ) const contract = compileContract(source, { omitEndpoints: new Set(["pty.connect"]) }) - expect(contract.groups[0]?.endpoints.map((endpoint) => endpoint.endpoint.name)).toEqual(["pty.get"]) + expect(contract.groups[0]?.endpoints.map((endpoint) => endpoint.endpoint.identifier)).toEqual(["pty.get"]) }) test("uses bracket access for input field names", () => { @@ -528,7 +535,9 @@ describe("HttpApiCodegen.generate", () => { const types = output.files.find((file) => file.path === "types.ts")?.content expect(types).toContain('readonly "values": ReadonlyArray') - expect(types).toContain('export type SessionCreateOutput = ({ "data": Array<{ "values": Array }> })["data"]') + expect(types).toContain( + 'export type SessionCreateOutput = ({ "data": Array<{ "values": Array }> })["data"]', + ) }) test("retains distinct Promise references at identifier boundaries", () => { @@ -1193,7 +1202,7 @@ describe("HttpApiCodegen.generate", () => { new SchemaAST.Link(Schema.String.check(Schema.isMinLength(2)).ast, link.transformation), ]) if (!SchemaAST.isAST(ast)) throw new Error("Expected altered schema AST") - const Altered = Schema.make(ast) + const Altered = Schema.make(ast) expect(() => compile(api(HttpApiEndpoint.get("get", "/session", { success: Altered })))).toThrow( "Effect schema requires authoritative import: session.get", diff --git a/packages/httpapi-codegen/test/generated/event.ts b/packages/httpapi-codegen/test/generated/event.ts index 764f2fd1c70..af34a065a50 100644 --- a/packages/httpapi-codegen/test/generated/event.ts +++ b/packages/httpapi-codegen/test/generated/event.ts @@ -19,7 +19,7 @@ export const Group1 = HttpApiGroup.make("event", { topLevel: false }).add( }), ) -type RawGroup = HttpApiClient.Client.Group +type RawGroup = HttpApiClient.Client.Group const Endpoint0DeclaredError = Schema.Union([Endpoint0SuccessError]) const mapEndpoint0Error = (error: unknown) => diff --git a/packages/httpapi-codegen/test/generated/session.ts b/packages/httpapi-codegen/test/generated/session.ts index 6a1937c49e2..7217d5fd18a 100644 --- a/packages/httpapi-codegen/test/generated/session.ts +++ b/packages/httpapi-codegen/test/generated/session.ts @@ -41,7 +41,7 @@ export const Group0 = HttpApiGroup.make("session", { topLevel: false }) }), ) -type RawGroup = HttpApiClient.Client.Group +type RawGroup = HttpApiClient.Client.Group const Endpoint0DeclaredError = Schema.Never const mapEndpoint0Error = (error: unknown) => diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 6d4a7599d93..ae42af1dffe 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -23,7 +23,7 @@ import { Skill } from "../skill" import { Effect, Context, Layer, Schema } from "effect" import { InstanceState } from "@/effect/instance-state" import * as Option from "effect/Option" -import * as OtelTracer from "@effect/opentelemetry/Tracer" +import * as OtelTracer from "@effect/opentelemetry/OtelTracer" import { AbsolutePath, type DeepMutable } from "@opencode-ai/core/schema" import { ProviderV2 } from "@opencode-ai/core/provider" import { ModelV2 } from "@opencode-ai/core/model" diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 9870377b216..19d79445f77 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -315,7 +315,7 @@ const layer = Layer.effect( if (input.icon?.url) return const matches = yield* fs - .glob("**/favicon.{ico,png,svg,jpg,jpeg,webp}", { + .scan("**/favicon.{ico,png,svg,jpg,jpeg,webp}", { cwd: input.worktree, absolute: true, include: "file", diff --git a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts index 52c714a5ae6..3900010f279 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/groups/experimental.ts @@ -186,7 +186,7 @@ export const ExperimentalApi = HttpApi.make("experimental") ), HttpApiEndpoint.post("worktreeCreate", ExperimentalPaths.worktree, { disableCodecs: true, - query: WorkspaceRoutingQuery, + query: WorkspaceRoutingQueryFields, payload: [HttpApiSchema.NoContent, Worktree.CreateInput], success: described(Worktree.Info, "Worktree created"), error: WorktreeApiError, diff --git a/packages/opencode/src/session/instruction.ts b/packages/opencode/src/session/instruction.ts index 7f593550d46..ebdae0dbe37 100644 --- a/packages/opencode/src/session/instruction.ts +++ b/packages/opencode/src/session/instruction.ts @@ -138,7 +138,7 @@ const layer: Layer.Layer< const instruction = raw.startsWith("~/") ? path.join(global.home, raw.slice(2)) : raw const matches = yield* ( path.isAbsolute(instruction) - ? fs.glob(path.basename(instruction), { + ? fs.scan(path.basename(instruction), { cwd: path.dirname(instruction), absolute: true, include: "file", diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index 9d652f8aaba..d8e0bdc831c 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -25,7 +25,7 @@ import { Auth } from "@/auth" import { EffectBridge } from "@/effect/bridge" import { RuntimeFlags } from "@/effect/runtime-flags" import * as Option from "effect/Option" -import * as OtelTracer from "@effect/opentelemetry/Tracer" +import * as OtelTracer from "@effect/opentelemetry/OtelTracer" import { LLMAISDK } from "./llm/ai-sdk" import { LLMNativeRuntime } from "./llm/native-runtime" import { LLMRequestPrep } from "./llm/request" diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts index ddbd83d7931..353a6b1e8bc 100644 --- a/packages/opencode/src/storage/storage.ts +++ b/packages/opencode/src/storage/storage.ts @@ -82,7 +82,7 @@ const MIGRATIONS: Migration[] = [ Effect.fn("Storage.migration.1")(function* (dir: string, fs: FSUtil.Interface, git: Git.Interface) { const project = path.resolve(dir, "../project") if (!(yield* fs.isDir(project))) return - const projectDirs = yield* fs.glob("*", { + const projectDirs = yield* fs.scan("*", { cwd: project, include: "all", }) @@ -94,7 +94,7 @@ const MIGRATIONS: Migration[] = [ let worktree = "/" if (projectID !== "global") { - for (const msgFile of yield* fs.glob("storage/session/message/*/*.json", { + for (const msgFile of yield* fs.scan("storage/session/message/*/*.json", { cwd: full, absolute: true, })) { @@ -136,7 +136,7 @@ const MIGRATIONS: Migration[] = [ ) yield* Effect.logInfo(`migrating sessions for project ${projectID}`) - for (const sessionFile of yield* fs.glob("storage/session/info/*.json", { + for (const sessionFile of yield* fs.scan("storage/session/info/*.json", { cwd: full, absolute: true, })) { @@ -147,7 +147,7 @@ const MIGRATIONS: Migration[] = [ yield* fs.writeWithDirs(dest, JSON.stringify(session, null, 2)) if (Option.isNone(info)) continue yield* Effect.logInfo(`migrating messages for session ${info.value.id}`) - for (const msgFile of yield* fs.glob(`storage/session/message/${info.value.id}/*.json`, { + for (const msgFile of yield* fs.scan(`storage/session/message/${info.value.id}/*.json`, { cwd: full, absolute: true, })) { @@ -162,7 +162,7 @@ const MIGRATIONS: Migration[] = [ if (Option.isNone(item)) continue yield* Effect.logInfo(`migrating parts for message ${item.value.id}`) - for (const partFile of yield* fs.glob(`storage/session/part/${info.value.id}/${item.value.id}/*.json`, { + for (const partFile of yield* fs.scan(`storage/session/part/${info.value.id}/${item.value.id}/*.json`, { cwd: full, absolute: true, })) { @@ -180,7 +180,7 @@ const MIGRATIONS: Migration[] = [ } }), Effect.fn("Storage.migration.2")(function* (dir: string, fs: FSUtil.Interface) { - for (const item of yield* fs.glob("session/*/*.json", { + for (const item of yield* fs.scan("session/*/*.json", { cwd: dir, absolute: true, })) { @@ -302,7 +302,7 @@ const layer = Layer.effect( const dir = (yield* state).dir const cwd = path.join(dir, ...prefix) const result = yield* fs - .glob("**/*", { + .scan("**/*", { cwd, include: "file", }) diff --git a/packages/opencode/test/filesystem/filesystem.test.ts b/packages/opencode/test/filesystem/filesystem.test.ts index d1ccc1f3eea..86488331bd1 100644 --- a/packages/opencode/test/filesystem/filesystem.test.ts +++ b/packages/opencode/test/filesystem/filesystem.test.ts @@ -220,7 +220,7 @@ describe("FSUtil", () => { yield* fs.writeFileString(path.join(tmp, "b.ts"), "b") yield* fs.writeFileString(path.join(tmp, "c.json"), "c") - const result = yield* fs.glob("*.ts", { cwd: tmp }) + const result = yield* fs.scan("*.ts", { cwd: tmp }) expect(result.sort()).toEqual(["a.ts", "b.ts"]) }), ) @@ -232,7 +232,7 @@ describe("FSUtil", () => { const tmp = yield* fs.makeTempDirectoryScoped() yield* fs.writeFileString(path.join(tmp, "file.txt"), "hello") - const result = yield* fs.glob("*.txt", { cwd: tmp, absolute: true }) + const result = yield* fs.scan("*.txt", { cwd: tmp, absolute: true }) expect(result).toEqual([path.join(tmp, "file.txt")]) }), ) diff --git a/packages/opencode/test/lib/cli-process.ts b/packages/opencode/test/lib/cli-process.ts index 77684d5e4f3..9f539421a59 100644 --- a/packages/opencode/test/lib/cli-process.ts +++ b/packages/opencode/test/lib/cli-process.ts @@ -202,7 +202,7 @@ export function withCliFixture( yield* Effect.addFinalizer(() => fs .remove(home, { recursive: true }) - .pipe(Effect.retry(Schedule.spaced("50 millis").pipe(Schedule.both(Schedule.recurs(20)))), Effect.ignore), + .pipe(Effect.retry(Schedule.max([Schedule.spaced("50 millis"), Schedule.recurs(20)])), Effect.ignore), ) const env = isolatedEnv(home) diff --git a/packages/opencode/test/storage/storage.test.ts b/packages/opencode/test/storage/storage.test.ts index a3f3aef4464..c143429b7bd 100644 --- a/packages/opencode/test/storage/storage.test.ts +++ b/packages/opencode/test/storage/storage.test.ts @@ -47,8 +47,8 @@ function remappedFs(root: string) { writeWithDirs: (file, content, mode) => fs.writeWithDirs(remap(root, file), content, mode), readFileString: (file) => fs.readFileString(remap(root, file)), remove: (file) => fs.remove(remap(root, file)), - glob: (pattern, options) => - fs.glob(pattern, options?.cwd ? { ...options, cwd: remap(root, options.cwd) } : options), + scan: (pattern, options) => + fs.scan(pattern, options?.cwd ? { ...options, cwd: remap(root, options.cwd) } : options), }) }), ).pipe(Layer.provide(LayerNode.compile(FSUtil.node))) diff --git a/packages/protocol/src/api.ts b/packages/protocol/src/api.ts index a7a6ef9fd2b..df48ecb3a26 100644 --- a/packages/protocol/src/api.ts +++ b/packages/protocol/src/api.ts @@ -68,9 +68,7 @@ type MixedMiddlewareGroups< SessionLocationId extends HttpApiMiddleware.AnyId, SessionLocationService, > = - | ReturnType< - typeof makePermissionGroup - > + | ReturnType> | ReturnType> type ApiGroups< @@ -80,7 +78,7 @@ type ApiGroups< FormLocationService, SessionLocationId extends HttpApiMiddleware.AnyId, SessionLocationService, - Event extends HttpApiGroup.Any, + Event extends HttpApiGroup.Constraint, > = | typeof HealthGroup | typeof ServerGroup @@ -100,7 +98,7 @@ export type Api< FormLocationService, SessionLocationId extends HttpApiMiddleware.AnyId, SessionLocationService, - Event extends HttpApiGroup.Any, + Event extends HttpApiGroup.Constraint, > = HttpApi.HttpApi< "server", HttpApiGroup.AddMiddleware< @@ -122,7 +120,7 @@ export type Api< // Protocol owns middleware placement, while Server injects concrete keys so Core service identities stay downstream. const makeApiFromGroup = < - const Group extends HttpApiGroup.Any, + const Group extends HttpApiGroup.Constraint, LocationId extends HttpApiMiddleware.AnyId, LocationService, FormLocationId extends HttpApiMiddleware.AnyId, diff --git a/packages/sdk/js/script/build.ts b/packages/sdk/js/script/build.ts index 8999db05903..927422cc9d8 100755 --- a/packages/sdk/js/script/build.ts +++ b/packages/sdk/js/script/build.ts @@ -141,7 +141,7 @@ if (sessionMessagesTypesPatched === sessionListTypesPatched) { throw new Error("Session messages numeric query patch did not apply") } const eventSubscribeTypesPatched = sessionMessagesTypesPatched.replace( - /(export type V2EventSubscribeResponses = \{\s*\/\*\*[\s\S]*?\*\/\s*200: )\{\s*id: string \| null;?\s*event: string;?\s*data: V2EventStream(?:V2)?;?\s*\};?/, + /(export type V2EventSubscribeResponses = \{\s*\/\*\*[\s\S]*?\*\/\s*200: )\{\s*id: string \| null;?\s*event: string;?\s*data: (?:V2EventStream(?:V2)?|V2EventJsonString);?\s*\};?/, "$1V2Event", ) if (eventSubscribeTypesPatched === sessionMessagesTypesPatched) { diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index a7c84c38150..35dc600e5df 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -398,6 +398,8 @@ import type { V2SessionFormReplyResponses, V2SessionFormStateErrors, V2SessionFormStateResponses, + V2SessionGenerateErrors, + V2SessionGenerateResponses, V2SessionGetErrors, V2SessionGetResponses, V2SessionInstructionsEntryListErrors, @@ -6475,6 +6477,41 @@ export class Session3 extends HeyApiClient { }) } + /** + * Generate text from session context + * + * Generate transient text from the current session context without mutating session history. + */ + public generate( + parameters: { + sessionID: string + prompt?: string + }, + options?: Options, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "sessionID" }, + { in: "body", key: "prompt" }, + ], + }, + ], + ) + return (options?.client ?? this.client).post({ + url: "/api/session/{sessionID}/generate", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } + /** * Read the session log * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index b24a95f5d4c..b742ed2f407 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -2089,6 +2089,7 @@ export type Config = { batch_tool?: boolean openTelemetry?: boolean primary_tools?: Array + subagent_depth?: number continue_loop_on_deny?: boolean mcp_timeout?: number } @@ -2888,9 +2889,15 @@ export type InstructionEntryValueTooLargeError = { message: string } +export type SessionGenerateResponse = { + data: { + text: string + } +} + export type SessionLogItem = SessionEventDurable | EventLogSynced -export type SessionLogItemStream = string +export type SessionLogItemJsonString = string export type SessionMessagesResponse = { data: Array @@ -3102,7 +3109,7 @@ export type V2Event = | ServerConnected | GlobalDisposed -export type V2EventStream = string +export type V2EventJsonString = string export type ForbiddenError = { _tag: "ForbiddenError" @@ -5552,6 +5559,27 @@ export type SessionRevertCommitted = { } } +export type SessionUsageRecorded = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "session.usage.recorded" + durable: { + aggregateID: string + seq: number + version: 1 + } + location?: LocationRef + data: { + sessionID: string + source: "title" | "compaction" + cost: MoneyUsd + tokens: TokenUsageInfo + } +} + export type SessionEventDurable = | SessionAgentSelected | SessionModelSelected @@ -5591,6 +5619,7 @@ export type SessionEventDurable = | SessionRevertStaged | SessionRevertCleared | SessionRevertCommitted + | SessionUsageRecorded export type EventLogSynced = { type: "log.synced" @@ -9892,6 +9921,27 @@ export type SessionRevertCommittedV2 = { } } +export type SessionUsageRecordedV2 = { + id: string + created: number + metadata?: { + [key: string]: unknown + } + type: "session.usage.recorded" + durable: { + aggregateID: string + seq: number + version: 1 + } + location?: LocationRefV2 + data: { + sessionID: string + source: "title" | "compaction" + cost: MoneyUsd + tokens: TokenUsageInfo + } +} + /** * Marker emitted once when a log read reaches its captured watermark. The reader holds every event committed at or below seq. */ @@ -16373,6 +16423,47 @@ export type V2SessionInstructionsEntryPutResponses = { export type V2SessionInstructionsEntryPutResponse = V2SessionInstructionsEntryPutResponses[keyof V2SessionInstructionsEntryPutResponses] +export type V2SessionGenerateData = { + body: { + prompt: string + } + path: { + sessionID: string + } + query?: never + url: "/api/session/{sessionID}/generate" +} + +export type V2SessionGenerateErrors = { + /** + * InvalidRequestError + */ + 400: InvalidRequestErrorV2 + /** + * UnauthorizedError + */ + 401: UnauthorizedError + /** + * SessionNotFoundError + */ + 404: SessionNotFoundError + /** + * ServiceUnavailableError + */ + 503: ServiceUnavailableErrorV2 +} + +export type V2SessionGenerateError = V2SessionGenerateErrors[keyof V2SessionGenerateErrors] + +export type V2SessionGenerateResponses = { + /** + * SessionGenerateResponse + */ + 200: SessionGenerateResponse +} + +export type V2SessionGenerateResponse = V2SessionGenerateResponses[keyof V2SessionGenerateResponses] + export type V2SessionLogData = { body?: never path: { @@ -16409,7 +16500,7 @@ export type V2SessionLogResponses = { 200: { id: string | null event: string - data: SessionLogItemStream + data: SessionLogItemJsonString } } diff --git a/patches/effect@4.0.0-beta.83.patch b/patches/effect@4.0.0-beta.98.patch similarity index 88% rename from patches/effect@4.0.0-beta.83.patch rename to patches/effect@4.0.0-beta.98.patch index 11030bc6ab7..cf14b1f195e 100644 --- a/patches/effect@4.0.0-beta.83.patch +++ b/patches/effect@4.0.0-beta.98.patch @@ -1,5 +1,5 @@ diff --git a/dist/unstable/httpapi/HttpApiSchema.js b/dist/unstable/httpapi/HttpApiSchema.js -index ecb6603d1ee4e89e92174b3a1100e8c9c720b3f3..08b832b6b3e9e4d10df156eca36b16f571f67f6e 100644 +index c51851b..2100420 100644 --- a/dist/unstable/httpapi/HttpApiSchema.js +++ b/dist/unstable/httpapi/HttpApiSchema.js @@ -151,7 +151,7 @@ export const StreamSse = options => { @@ -27,10 +27,10 @@ index ecb6603d1ee4e89e92174b3a1100e8c9c720b3f3..08b832b6b3e9e4d10df156eca36b16f5 * Creates a streaming `Uint8Array` success response schema. * diff --git a/src/unstable/httpapi/HttpApiSchema.ts b/src/unstable/httpapi/HttpApiSchema.ts -index f2920365cf0328146523ce2e2dba04d6a8809a4a..5482d635cdf01a72063ff683d244fab891910211 100644 +index aae6cd5..f05e3ed 100644 --- a/src/unstable/httpapi/HttpApiSchema.ts +++ b/src/unstable/httpapi/HttpApiSchema.ts -@@ -433,7 +433,7 @@ export const StreamSse: { +@@ -407,7 +407,7 @@ export const StreamSse: { const events = options.events ?? (options.data === undefined ? undefined : Schema.Struct({ id: Schema.UndefinedOr(Schema.String), event: Schema.String, @@ -39,7 +39,7 @@ index f2920365cf0328146523ce2e2dba04d6a8809a4a..5482d635cdf01a72063ff683d244fab8 })) if (events === undefined) { throw new Error("StreamSse requires either an events schema or a data schema") -@@ -449,6 +449,15 @@ export const StreamSse: { +@@ -423,6 +423,15 @@ export const StreamSse: { }) }