Преглед изворни кода

refactor(effect): upgrade opencode to beta.46 context APIs (#21977)

Kit Langton пре 1 недеља
родитељ
комит
9581bf0670
75 измењених фајлова са 195 додато и 209 уклоњено
  1. 15 7
      bun.lock
  2. 2 2
      package.json
  3. 1 1
      packages/opencode/package.json
  4. 1 1
      packages/opencode/specs/effect-migration.md
  5. 2 2
      packages/opencode/src/account/index.ts
  6. 2 2
      packages/opencode/src/account/repo.ts
  7. 6 26
      packages/opencode/src/account/schema.ts
  8. 2 2
      packages/opencode/src/agent/agent.ts
  9. 2 2
      packages/opencode/src/auth/index.ts
  10. 2 2
      packages/opencode/src/bus/index.ts
  11. 4 3
      packages/opencode/src/command/index.ts
  12. 2 2
      packages/opencode/src/config/config.ts
  13. 1 2
      packages/opencode/src/control-plane/schema.ts
  14. 2 2
      packages/opencode/src/control-plane/workspace-context.ts
  15. 13 0
      packages/opencode/src/effect/cross-spawn-spawner.ts
  16. 3 3
      packages/opencode/src/effect/instance-ref.ts
  17. 4 4
      packages/opencode/src/effect/instance-state.ts
  18. 4 4
      packages/opencode/src/effect/run-service.ts
  19. 2 2
      packages/opencode/src/file/index.ts
  20. 2 2
      packages/opencode/src/file/ripgrep.ts
  21. 2 2
      packages/opencode/src/file/time.ts
  22. 2 2
      packages/opencode/src/file/watcher.ts
  23. 2 2
      packages/opencode/src/filesystem/index.ts
  24. 2 2
      packages/opencode/src/format/index.ts
  25. 2 2
      packages/opencode/src/git/index.ts
  26. 2 2
      packages/opencode/src/installation/index.ts
  27. 2 2
      packages/opencode/src/lsp/index.ts
  28. 2 2
      packages/opencode/src/mcp/auth.ts
  29. 2 2
      packages/opencode/src/mcp/index.ts
  30. 2 2
      packages/opencode/src/permission/index.ts
  31. 1 5
      packages/opencode/src/permission/schema.ts
  32. 2 2
      packages/opencode/src/plugin/index.ts
  33. 2 2
      packages/opencode/src/project/instance.ts
  34. 2 2
      packages/opencode/src/project/project.ts
  35. 1 2
      packages/opencode/src/project/schema.ts
  36. 2 2
      packages/opencode/src/project/vcs.ts
  37. 2 2
      packages/opencode/src/provider/auth.ts
  38. 2 2
      packages/opencode/src/provider/provider.ts
  39. 11 13
      packages/opencode/src/provider/schema.ts
  40. 2 2
      packages/opencode/src/pty/index.ts
  41. 1 2
      packages/opencode/src/pty/schema.ts
  42. 2 2
      packages/opencode/src/question/index.ts
  43. 1 5
      packages/opencode/src/question/schema.ts
  44. 2 2
      packages/opencode/src/session/compaction.ts
  45. 2 2
      packages/opencode/src/session/index.ts
  46. 2 2
      packages/opencode/src/session/instruction.ts
  47. 2 2
      packages/opencode/src/session/llm.ts
  48. 2 2
      packages/opencode/src/session/processor.ts
  49. 2 2
      packages/opencode/src/session/prompt.ts
  50. 2 2
      packages/opencode/src/session/revert.ts
  51. 2 2
      packages/opencode/src/session/run-state.ts
  52. 3 6
      packages/opencode/src/session/schema.ts
  53. 2 2
      packages/opencode/src/session/status.ts
  54. 2 2
      packages/opencode/src/session/summary.ts
  55. 2 2
      packages/opencode/src/session/todo.ts
  56. 2 2
      packages/opencode/src/share/session.ts
  57. 2 2
      packages/opencode/src/share/share-next.ts
  58. 2 2
      packages/opencode/src/skill/discovery.ts
  59. 2 2
      packages/opencode/src/skill/index.ts
  60. 2 2
      packages/opencode/src/snapshot/index.ts
  61. 4 4
      packages/opencode/src/storage/db.ts
  62. 2 2
      packages/opencode/src/storage/storage.ts
  63. 1 2
      packages/opencode/src/sync/schema.ts
  64. 2 2
      packages/opencode/src/tool/registry.ts
  65. 1 2
      packages/opencode/src/tool/schema.ts
  66. 2 2
      packages/opencode/src/tool/truncate.ts
  67. 1 1
      packages/opencode/src/util/local-context.ts
  68. 5 5
      packages/opencode/src/util/schema.ts
  69. 2 2
      packages/opencode/src/worktree/index.ts
  70. 7 7
      packages/opencode/test/effect/instance-state.test.ts
  71. 4 4
      packages/opencode/test/effect/run-service.test.ts
  72. 2 2
      packages/opencode/test/fixture/fixture.ts
  73. 1 0
      packages/opencode/test/installation/installation.test.ts
  74. 2 2
      packages/opencode/test/lib/llm-server.ts
  75. 1 0
      packages/opencode/test/project/project.test.ts

+ 15 - 7
bun.lock

@@ -413,7 +413,7 @@
       },
       "devDependencies": {
         "@babel/core": "7.28.4",
-        "@effect/language-service": "0.79.0",
+        "@effect/language-service": "0.84.2",
         "@octokit/webhooks-types": "7.6.1",
         "@opencode-ai/script": "workspace:*",
         "@parcel/watcher-darwin-arm64": "2.5.1",
@@ -641,7 +641,7 @@
   },
   "catalog": {
     "@cloudflare/workers-types": "4.20251008.0",
-    "@effect/platform-node": "4.0.0-beta.43",
+    "@effect/platform-node": "4.0.0-beta.46",
     "@hono/zod-validator": "0.4.2",
     "@kobalte/core": "0.13.11",
     "@lydell/node-pty": "1.2.0-beta.10",
@@ -668,7 +668,7 @@
     "dompurify": "3.3.1",
     "drizzle-kit": "1.0.0-beta.19-d95b7a4",
     "drizzle-orm": "1.0.0-beta.19-d95b7a4",
-    "effect": "4.0.0-beta.43",
+    "effect": "4.0.0-beta.46",
     "fuzzysort": "3.1.0",
     "hono": "4.10.7",
     "hono-openapi": "1.1.2",
@@ -1025,11 +1025,11 @@
 
     "@drizzle-team/brocli": ["@drizzle-team/[email protected]", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="],
 
-    "@effect/language-service": ["@effect/language-service@0.79.0", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-DEmIOsg1GjjP6s9HXH1oJrW+gDmzkhVv9WOZl6to5eNyyCrjz1S2PDqQ7aYrW/HuifhfwI5Bik1pK4pj7Z+lrg=="],
+    "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="],
 
-    "@effect/platform-node": ["@effect/[email protected]3", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.43", "mime": "^4.1.0", "undici": "^7.24.0" }, "peerDependencies": { "effect": "^4.0.0-beta.43", "ioredis": "^5.7.0" } }, "sha512-Uq6E1rjaIpjHauzjwoB2HzAg3battYt2Boy8XO50GoHiWCXKE6WapYZ0/AnaBx5v5qg2sOfqpuiLsUf9ZgxOkA=="],
+    "@effect/platform-node": ["@effect/[email protected]6", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.46", "mime": "^4.1.0", "undici": "^7.24.0" }, "peerDependencies": { "effect": "^4.0.0-beta.46", "ioredis": "^5.7.0" } }, "sha512-6AFRKjJO95dFl5lK/YnJi04uePjQDFi3+K1aXwcz/EfVlRwJ4+lg5O4vbievfKL/hnfcShVp3/eXnNS9tvlMZQ=="],
 
-    "@effect/platform-node-shared": ["@effect/[email protected]3", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.43" } }, "sha512-A9q0GEb61pYcQ06Dr6gXj1nKlDI3KHsar1sk3qb1ZY+kVSR64tBAylI8zGon23KY+NPtTUj/sEIToB7jc3Qt5w=="],
+    "@effect/platform-node-shared": ["@effect/[email protected]6", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.19.0" }, "peerDependencies": { "effect": "^4.0.0-beta.46" } }, "sha512-Yzci82XbZ1W3tuiownsJawrJZTGeTrTZKLD0uxdBWCBzlVyqDwoSwRwO5qh33DurJj9B7iS8MDf14fpGRBPNGQ=="],
 
     "@electron/asar": ["@electron/[email protected]", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
 
@@ -2889,7 +2889,7 @@
 
     "ee-first": ["[email protected]", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
 
-    "effect": ["[email protected]3", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-AJYyDimIwJOn87uUz/JzmgDc5GfjxJbXvEbTvNzMa+M3Uer344bLo/O5mMRkqc1vBleA+Ygs4+dbE3QsqOkKTQ=="],
+    "effect": ["[email protected]6", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-3f6gXvvUMtEueCRY0tU76Vq2Pej1SAwwE+s0Owd5nD53yS5n4RZhUA1rlCGFuSbQFA225pGy8vO72+lpvu7u5A=="],
 
     "ejs": ["[email protected]", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
 
@@ -5509,6 +5509,10 @@
 
     "@solidjs/start/vite-plugin-solid": ["[email protected]", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-YMZCXsLw9kyuvQFEdwLP27fuTQJLmjNoHy90AOJnbRuJ6DwShUxKFo38gdFrWn9v11hnGicKCZEaeI/TFs6JKw=="],
 
+    "@standard-community/standard-json/effect": ["[email protected]", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-AJYyDimIwJOn87uUz/JzmgDc5GfjxJbXvEbTvNzMa+M3Uer344bLo/O5mMRkqc1vBleA+Ygs4+dbE3QsqOkKTQ=="],
+
+    "@standard-community/standard-openapi/effect": ["[email protected]", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.5.3", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.8", "multipasta": "^0.2.7", "toml": "^3.0.0", "uuid": "^13.0.0", "yaml": "^2.8.2" } }, "sha512-AJYyDimIwJOn87uUz/JzmgDc5GfjxJbXvEbTvNzMa+M3Uer344bLo/O5mMRkqc1vBleA+Ygs4+dbE3QsqOkKTQ=="],
+
     "@tailwindcss/oxide/detect-libc": ["[email protected]", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
 
     "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/[email protected]", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA=="],
@@ -6435,6 +6439,10 @@
 
     "@solidjs/start/shiki/@shikijs/types": ["@shikijs/[email protected]", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="],
 
+    "@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/[email protected]", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
+
+    "@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/[email protected]", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
+
     "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/[email protected]", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
 
     "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/[email protected]", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],

+ 2 - 2
package.json

@@ -26,7 +26,7 @@
       "packages/slack"
     ],
     "catalog": {
-      "@effect/platform-node": "4.0.0-beta.43",
+      "@effect/platform-node": "4.0.0-beta.46",
       "@types/bun": "1.3.11",
       "@types/cross-spawn": "6.0.6",
       "@octokit/rest": "22.0.0",
@@ -47,7 +47,7 @@
       "dompurify": "3.3.1",
       "drizzle-kit": "1.0.0-beta.19-d95b7a4",
       "drizzle-orm": "1.0.0-beta.19-d95b7a4",
-      "effect": "4.0.0-beta.43",
+      "effect": "4.0.0-beta.46",
       "ai": "6.0.149",
       "cross-spawn": "7.0.6",
       "hono": "4.10.7",

+ 1 - 1
packages/opencode/package.json

@@ -43,7 +43,7 @@
   },
   "devDependencies": {
     "@babel/core": "7.28.4",
-    "@effect/language-service": "0.79.0",
+    "@effect/language-service": "0.84.2",
     "@octokit/webhooks-types": "7.6.1",
     "@opencode-ai/script": "workspace:*",
     "@parcel/watcher-darwin-arm64": "2.5.1",

+ 1 - 1
packages/opencode/specs/effect-migration.md

@@ -23,7 +23,7 @@ export namespace Foo {
     readonly get: (id: FooID) => Effect.Effect<FooInfo, FooError>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Foo") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Foo") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/account/index.ts

@@ -1,4 +1,4 @@
-import { Cache, Clock, Duration, Effect, Layer, Option, Schema, SchemaGetter, ServiceMap } from "effect"
+import { Cache, Clock, Duration, Effect, Layer, Option, Schema, SchemaGetter, Context } from "effect"
 import {
   FetchHttpClient,
   HttpClient,
@@ -181,7 +181,7 @@ export namespace Account {
     readonly poll: (input: Login) => Effect.Effect<PollResult, AccountError>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Account") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Account") {}
 
   export const layer: Layer.Layer<Service, never, AccountRepo | HttpClient.HttpClient> = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/account/repo.ts

@@ -1,5 +1,5 @@
 import { eq } from "drizzle-orm"
-import { Effect, Layer, Option, Schema, ServiceMap } from "effect"
+import { Effect, Layer, Option, Schema, Context } from "effect"
 
 import { Database } from "@/storage/db"
 import { AccountStateTable, AccountTable } from "./account.sql"
@@ -38,7 +38,7 @@ export namespace AccountRepo {
   }
 }
 
-export class AccountRepo extends ServiceMap.Service<AccountRepo, AccountRepo.Service>()("@opencode/AccountRepo") {
+export class AccountRepo extends Context.Service<AccountRepo, AccountRepo.Service>()("@opencode/AccountRepo") {
   static readonly layer: Layer.Layer<AccountRepo> = Layer.effect(
     AccountRepo,
     Effect.gen(function* () {

+ 6 - 26
packages/opencode/src/account/schema.ts

@@ -1,42 +1,22 @@
 import { Schema } from "effect"
 import type * as HttpClientError from "effect/unstable/http/HttpClientError"
 
-import { withStatics } from "@/util/schema"
-
-export const AccountID = Schema.String.pipe(
-  Schema.brand("AccountID"),
-  withStatics((s) => ({ make: (id: string) => s.makeUnsafe(id) })),
-)
+export const AccountID = Schema.String.pipe(Schema.brand("AccountID"))
 export type AccountID = Schema.Schema.Type<typeof AccountID>
 
-export const OrgID = Schema.String.pipe(
-  Schema.brand("OrgID"),
-  withStatics((s) => ({ make: (id: string) => s.makeUnsafe(id) })),
-)
+export const OrgID = Schema.String.pipe(Schema.brand("OrgID"))
 export type OrgID = Schema.Schema.Type<typeof OrgID>
 
-export const AccessToken = Schema.String.pipe(
-  Schema.brand("AccessToken"),
-  withStatics((s) => ({ make: (token: string) => s.makeUnsafe(token) })),
-)
+export const AccessToken = Schema.String.pipe(Schema.brand("AccessToken"))
 export type AccessToken = Schema.Schema.Type<typeof AccessToken>
 
-export const RefreshToken = Schema.String.pipe(
-  Schema.brand("RefreshToken"),
-  withStatics((s) => ({ make: (token: string) => s.makeUnsafe(token) })),
-)
+export const RefreshToken = Schema.String.pipe(Schema.brand("RefreshToken"))
 export type RefreshToken = Schema.Schema.Type<typeof RefreshToken>
 
-export const DeviceCode = Schema.String.pipe(
-  Schema.brand("DeviceCode"),
-  withStatics((s) => ({ make: (code: string) => s.makeUnsafe(code) })),
-)
+export const DeviceCode = Schema.String.pipe(Schema.brand("DeviceCode"))
 export type DeviceCode = Schema.Schema.Type<typeof DeviceCode>
 
-export const UserCode = Schema.String.pipe(
-  Schema.brand("UserCode"),
-  withStatics((s) => ({ make: (code: string) => s.makeUnsafe(code) })),
-)
+export const UserCode = Schema.String.pipe(Schema.brand("UserCode"))
 export type UserCode = Schema.Schema.Type<typeof UserCode>
 
 export class Info extends Schema.Class<Info>("Account")({

+ 2 - 2
packages/opencode/src/agent/agent.ts

@@ -19,7 +19,7 @@ import { Global } from "@/global"
 import path from "path"
 import { Plugin } from "@/plugin"
 import { Skill } from "../skill"
-import { Effect, ServiceMap, Layer } from "effect"
+import { Effect, Context, Layer } from "effect"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
 
@@ -67,7 +67,7 @@ export namespace Agent {
 
   type State = Omit<Interface, "generate">
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Agent") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Agent") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/auth/index.ts

@@ -1,5 +1,5 @@
 import path from "path"
-import { Effect, Layer, Record, Result, Schema, ServiceMap } from "effect"
+import { Effect, Layer, Record, Result, Schema, Context } from "effect"
 import { makeRuntime } from "@/effect/run-service"
 import { zod } from "@/util/effect-zod"
 import { Global } from "../global"
@@ -49,7 +49,7 @@ export namespace Auth {
     readonly remove: (key: string) => Effect.Effect<void, AuthError>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Auth") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Auth") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/bus/index.ts

@@ -1,5 +1,5 @@
 import z from "zod"
-import { Effect, Exit, Layer, PubSub, Scope, ServiceMap, Stream } from "effect"
+import { Effect, Exit, Layer, PubSub, Scope, Context, Stream } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { Log } from "../util/log"
 import { BusEvent } from "./bus-event"
@@ -42,7 +42,7 @@ export namespace Bus {
     readonly subscribeAllCallback: (callback: (event: any) => unknown) => Effect.Effect<() => void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Bus") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Bus") {}
 
   export const layer = Layer.effect(
     Service,

+ 4 - 3
packages/opencode/src/command/index.ts

@@ -1,8 +1,9 @@
 import { BusEvent } from "@/bus/bus-event"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
+import type { InstanceContext } from "@/project/instance"
 import { SessionID, MessageID } from "@/session/schema"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import z from "zod"
 import { Config } from "../config/config"
@@ -71,7 +72,7 @@ export namespace Command {
     readonly list: () => Effect.Effect<Info[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Command") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Command") {}
 
   export const layer = Layer.effect(
     Service,
@@ -80,7 +81,7 @@ export namespace Command {
       const mcp = yield* MCP.Service
       const skill = yield* Skill.Service
 
-      const init = Effect.fn("Command.state")(function* (ctx) {
+      const init = Effect.fn("Command.state")(function* (ctx: InstanceContext) {
         const cfg = yield* config.get()
         const commands: Record<string, Info> = {}
 

+ 2 - 2
packages/opencode/src/config/config.ts

@@ -37,7 +37,7 @@ import type { ConsoleState } from "./console-state"
 import { AppFileSystem } from "@/filesystem"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
-import { Duration, Effect, Layer, Option, ServiceMap } from "effect"
+import { Duration, Effect, Layer, Option, Context } from "effect"
 import { Flock } from "@/util/flock"
 import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared"
 import { Npm } from "@/npm"
@@ -1127,7 +1127,7 @@ export namespace Config {
     readonly waitForDependencies: () => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Config") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Config") {}
 
   function globalConfigFile() {
     const candidates = ["opencode.jsonc", "opencode.json", "config.json"].map((file) =>

+ 1 - 2
packages/opencode/src/control-plane/schema.ts

@@ -10,8 +10,7 @@ export type WorkspaceID = typeof workspaceIdSchema.Type
 
 export const WorkspaceID = workspaceIdSchema.pipe(
   withStatics((schema: typeof workspaceIdSchema) => ({
-    make: (id: string) => schema.makeUnsafe(id),
-    ascending: (id?: string) => schema.makeUnsafe(Identifier.ascending("workspace", id)),
+    ascending: (id?: string) => schema.make(Identifier.ascending("workspace", id)),
     zod: Identifier.schema("workspace").pipe(z.custom<WorkspaceID>()),
   })),
 )

+ 2 - 2
packages/opencode/src/control-plane/workspace-context.ts

@@ -1,11 +1,11 @@
-import { Context } from "../util/context"
+import { LocalContext } from "../util/local-context"
 import type { WorkspaceID } from "../control-plane/schema"
 
 export interface WorkspaceContext {
   workspaceID: string
 }
 
-const context = Context.create<WorkspaceContext>("instance")
+const context = LocalContext.create<WorkspaceContext>("instance")
 
 export const WorkspaceContext = {
   async provide<R>(input: { workspaceID: WorkspaceID; fn: () => R }): Promise<R> {

+ 13 - 0
packages/opencode/src/effect/cross-spawn-spawner.ts

@@ -402,6 +402,7 @@ export const make = Effect.gen(function* () {
 
           const fd = yield* setupFds(command, proc, extra)
           const out = setupOutput(command, proc, sout, serr)
+          let ref = true
           return makeHandle({
             pid: ProcessId(proc.pid!),
             stdin: yield* setupStdin(command, proc, sin),
@@ -432,6 +433,18 @@ export const make = Effect.gen(function* () {
                 orElse: () => send("SIGKILL").pipe(Effect.andThen(Deferred.await(signal)), Effect.asVoid),
               })
             },
+            unref: Effect.sync(() => {
+              if (ref) {
+                proc.unref()
+                ref = false
+              }
+              return Effect.sync(() => {
+                if (!ref) {
+                  proc.ref()
+                  ref = true
+                }
+              })
+            }),
           })
         }
         case "PipedCommand": {

+ 3 - 3
packages/opencode/src/effect/instance-ref.ts

@@ -1,10 +1,10 @@
-import { ServiceMap } from "effect"
+import { Context } from "effect"
 import type { InstanceContext } from "@/project/instance"
 
-export const InstanceRef = ServiceMap.Reference<InstanceContext | undefined>("~opencode/InstanceRef", {
+export const InstanceRef = Context.Reference<InstanceContext | undefined>("~opencode/InstanceRef", {
   defaultValue: () => undefined,
 })
 
-export const WorkspaceRef = ServiceMap.Reference<string | undefined>("~opencode/WorkspaceRef", {
+export const WorkspaceRef = Context.Reference<string | undefined>("~opencode/WorkspaceRef", {
   defaultValue: () => undefined,
 })

+ 4 - 4
packages/opencode/src/effect/instance-state.ts

@@ -1,7 +1,7 @@
-import { Effect, Fiber, ScopedCache, Scope, ServiceMap } from "effect"
+import { Effect, Fiber, ScopedCache, Scope, Context } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { Instance, type InstanceContext } from "@/project/instance"
-import { Context } from "@/util/context"
+import { LocalContext } from "@/util/local-context"
 import { InstanceRef, WorkspaceRef } from "./instance-ref"
 import { registerDisposer } from "./instance-registry"
 import { WorkspaceContext } from "@/control-plane/workspace-context"
@@ -18,10 +18,10 @@ export namespace InstanceState {
     try {
       return Instance.bind(fn)
     } catch (err) {
-      if (!(err instanceof Context.NotFound)) throw err
+      if (!(err instanceof LocalContext.NotFound)) throw err
     }
     const fiber = Fiber.getCurrent()
-    const ctx = fiber ? ServiceMap.getReferenceUnsafe(fiber.services, InstanceRef) : undefined
+    const ctx = fiber ? Context.getReferenceUnsafe(fiber.context, InstanceRef) : undefined
     if (!ctx) return fn
     return ((...args: any[]) => Instance.restore(ctx, () => fn(...args))) as F
   }

+ 4 - 4
packages/opencode/src/effect/run-service.ts

@@ -1,7 +1,7 @@
 import { Effect, Layer, ManagedRuntime } from "effect"
-import * as ServiceMap from "effect/ServiceMap"
+import * as Context from "effect/Context"
 import { Instance } from "@/project/instance"
-import { Context } from "@/util/context"
+import { LocalContext } from "@/util/local-context"
 import { InstanceRef, WorkspaceRef } from "./instance-ref"
 import { Observability } from "./oltp"
 import { WorkspaceContext } from "@/control-plane/workspace-context"
@@ -14,12 +14,12 @@ export function attach<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A
     const workspaceID = WorkspaceContext.workspaceID
     return effect.pipe(Effect.provideService(InstanceRef, ctx), Effect.provideService(WorkspaceRef, workspaceID))
   } catch (err) {
-    if (!(err instanceof Context.NotFound)) throw err
+    if (!(err instanceof LocalContext.NotFound)) throw err
   }
   return effect
 }
 
-export function makeRuntime<I, S, E>(service: ServiceMap.Service<I, S>, layer: Layer.Layer<I, E>) {
+export function makeRuntime<I, S, E>(service: Context.Service<I, S>, layer: Layer.Layer<I, E>) {
   let rt: ManagedRuntime.ManagedRuntime<I, E> | undefined
   const getRuntime = () => (rt ??= ManagedRuntime.make(Layer.merge(layer, Observability.layer), { memoMap }))
 

+ 2 - 2
packages/opencode/src/file/index.ts

@@ -3,7 +3,7 @@ import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
 import { AppFileSystem } from "@/filesystem"
 import { Git } from "@/git"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { formatPatch, structuredPatch } from "diff"
 import fuzzysort from "fuzzysort"
 import ignore from "ignore"
@@ -337,7 +337,7 @@ export namespace File {
     }) => Effect.Effect<string[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/File") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/File") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/file/ripgrep.ts

@@ -3,7 +3,7 @@ import path from "path"
 import { Global } from "../global"
 import fs from "fs/promises"
 import z from "zod"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import * as Stream from "effect/Stream"
 import { ChildProcess } from "effect/unstable/process"
 import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
@@ -291,7 +291,7 @@ export namespace Ripgrep {
     }) => Stream.Stream<string, PlatformError>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Ripgrep") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Ripgrep") {}
 
   export const layer: Layer.Layer<Service, never, ChildProcessSpawner | AppFileSystem.Service> = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/file/time.ts

@@ -1,4 +1,4 @@
-import { DateTime, Effect, Layer, Option, Semaphore, ServiceMap } from "effect"
+import { DateTime, Effect, Layer, Option, Semaphore, Context } from "effect"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
 import { AppFileSystem } from "@/filesystem"
@@ -37,7 +37,7 @@ export namespace FileTime {
     readonly withLock: <T>(filepath: string, fn: () => Effect.Effect<T>) => Effect.Effect<T>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/FileTime") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/FileTime") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/file/watcher.ts

@@ -1,4 +1,4 @@
-import { Cause, Effect, Layer, Scope, ServiceMap } from "effect"
+import { Cause, Effect, Layer, Scope, Context } from "effect"
 // @ts-ignore
 import { createWrapper } from "@parcel/watcher/wrapper"
 import type ParcelWatcher from "@parcel/watcher"
@@ -65,7 +65,7 @@ export namespace FileWatcher {
     readonly init: () => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/FileWatcher") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/FileWatcher") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/filesystem/index.ts

@@ -3,7 +3,7 @@ import { dirname, join, relative, resolve as pathResolve } from "path"
 import { realpathSync } from "fs"
 import * as NFS from "fs/promises"
 import { lookup } from "mime-types"
-import { Effect, FileSystem, Layer, Schema, ServiceMap } from "effect"
+import { Effect, FileSystem, Layer, Schema, Context } from "effect"
 import type { PlatformError } from "effect/PlatformError"
 import { Glob } from "../util/glob"
 
@@ -36,7 +36,7 @@ export namespace AppFileSystem {
     readonly globMatch: (pattern: string, filepath: string) => boolean
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/FileSystem") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/FileSystem") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/format/index.ts

@@ -1,4 +1,4 @@
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
 import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
 import { InstanceState } from "@/effect/instance-state"
@@ -31,7 +31,7 @@ export namespace Format {
     readonly file: (filepath: string) => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Format") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Format") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/git/index.ts

@@ -1,5 +1,5 @@
 import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
-import { Effect, Layer, ServiceMap, Stream } from "effect"
+import { Effect, Layer, Context, Stream } from "effect"
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
 import { makeRuntime } from "@/effect/run-service"
 
@@ -80,7 +80,7 @@ export namespace Git {
     return "modified"
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Git") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Git") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/installation/index.ts

@@ -1,4 +1,4 @@
-import { Effect, Layer, Schema, ServiceMap, Stream } from "effect"
+import { Effect, Layer, Schema, Context, Stream } from "effect"
 import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
 import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
 import { makeRuntime } from "@/effect/run-service"
@@ -91,7 +91,7 @@ export namespace Installation {
     readonly upgrade: (method: Method, target: string) => Effect.Effect<void, UpgradeFailedError>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Installation") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Installation") {}
 
   export const layer: Layer.Layer<Service, never, HttpClient.HttpClient | ChildProcessSpawner.ChildProcessSpawner> =
     Layer.effect(

+ 2 - 2
packages/opencode/src/lsp/index.ts

@@ -11,7 +11,7 @@ import { Instance } from "../project/instance"
 import { Flag } from "@/flag/flag"
 import { Process } from "../util/process"
 import { spawn as lspspawn } from "./launch"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
 
@@ -156,7 +156,7 @@ export namespace LSP {
     readonly outgoingCalls: (input: LocInput) => Effect.Effect<any[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/LSP") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/LSP") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/mcp/auth.ts

@@ -1,7 +1,7 @@
 import path from "path"
 import z from "zod"
 import { Global } from "../global"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { AppFileSystem } from "@/filesystem"
 import { makeRuntime } from "@/effect/run-service"
 
@@ -49,7 +49,7 @@ export namespace McpAuth {
     readonly isTokenExpired: (mcpName: string) => Effect.Effect<boolean | null>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/McpAuth") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/mcp/index.ts

@@ -24,7 +24,7 @@ import { BusEvent } from "../bus/bus-event"
 import { Bus } from "@/bus"
 import { TuiEvent } from "@/cli/cmd/tui/event"
 import open from "open"
-import { Effect, Exit, Layer, Option, ServiceMap, Stream } from "effect"
+import { Effect, Exit, Layer, Option, Context, Stream } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
@@ -240,7 +240,7 @@ export namespace MCP {
     readonly getAuthStatus: (mcpName: string) => Effect.Effect<AuthStatus>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/MCP") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/MCP") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/permission/index.ts

@@ -10,7 +10,7 @@ import { PermissionTable } from "@/session/session.sql"
 import { Database, eq } from "@/storage/db"
 import { Log } from "@/util/log"
 import { Wildcard } from "@/util/wildcard"
-import { Deferred, Effect, Layer, Schema, ServiceMap } from "effect"
+import { Deferred, Effect, Layer, Schema, Context } from "effect"
 import os from "os"
 import z from "zod"
 import { evaluate as evalRule } from "./evaluate"
@@ -135,7 +135,7 @@ export namespace Permission {
     return evalRule(permission, pattern, ...rulesets)
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Permission") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Permission") {}
 
   export const layer = Layer.effect(
     Service,

+ 1 - 5
packages/opencode/src/permission/schema.ts

@@ -5,12 +5,8 @@ import { Identifier } from "@/id/id"
 import { Newtype } from "@/util/schema"
 
 export class PermissionID extends Newtype<PermissionID>()("PermissionID", Schema.String) {
-  static make(id: string): PermissionID {
-    return this.makeUnsafe(id)
-  }
-
   static ascending(id?: string): PermissionID {
-    return this.makeUnsafe(Identifier.ascending("permission", id))
+    return this.make(Identifier.ascending("permission", id))
   }
 
   static readonly zod = Identifier.schema("permission") as unknown as z.ZodType<PermissionID>

+ 2 - 2
packages/opencode/src/plugin/index.ts

@@ -11,7 +11,7 @@ import { CopilotAuthPlugin } from "./github-copilot/copilot"
 import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth"
 import { PoeAuthPlugin } from "opencode-poe-auth"
 import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare"
-import { Effect, Layer, ServiceMap, Stream } from "effect"
+import { Effect, Layer, Context, Stream } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
@@ -45,7 +45,7 @@ export namespace Plugin {
     readonly init: () => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Plugin") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Plugin") {}
 
   // Built-in plugins that are directly imported (not installed from npm)
   const INTERNAL_PLUGINS: PluginInstance[] = [

+ 2 - 2
packages/opencode/src/project/instance.ts

@@ -3,7 +3,7 @@ import { disposeInstance } from "@/effect/instance-registry"
 import { Filesystem } from "@/util/filesystem"
 import { iife } from "@/util/iife"
 import { Log } from "@/util/log"
-import { Context } from "../util/context"
+import { LocalContext } from "../util/local-context"
 import { Project } from "./project"
 import { WorkspaceContext } from "@/control-plane/workspace-context"
 import { State } from "./state"
@@ -14,7 +14,7 @@ export interface InstanceContext {
   project: Project.Info
 }
 
-const context = Context.create<InstanceContext>("instance")
+const context = LocalContext.create<InstanceContext>("instance")
 const cache = new Map<string, Promise<InstanceContext>>()
 
 const disposal = {

+ 2 - 2
packages/opencode/src/project/project.ts

@@ -8,7 +8,7 @@ import { BusEvent } from "@/bus/bus-event"
 import { GlobalBus } from "@/bus/global"
 import { which } from "../util/which"
 import { ProjectID } from "./schema"
-import { Effect, Layer, Path, Scope, ServiceMap, Stream } from "effect"
+import { Effect, Layer, Path, Scope, Context, Stream } from "effect"
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
 import { NodeFileSystem, NodePath } from "@effect/platform-node"
 import { makeRuntime } from "@/effect/run-service"
@@ -100,7 +100,7 @@ export namespace Project {
     readonly removeSandbox: (id: ProjectID, directory: string) => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Project") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Project") {}
 
   type GitResult = { code: number; text: string; stderr: string }
 

+ 1 - 2
packages/opencode/src/project/schema.ts

@@ -9,8 +9,7 @@ export type ProjectID = typeof projectIdSchema.Type
 
 export const ProjectID = projectIdSchema.pipe(
   withStatics((schema: typeof projectIdSchema) => ({
-    global: schema.makeUnsafe("global"),
-    make: (id: string) => schema.makeUnsafe(id),
+    global: schema.make("global"),
     zod: z.string().pipe(z.custom<ProjectID>()),
   })),
 )

+ 2 - 2
packages/opencode/src/project/vcs.ts

@@ -1,4 +1,4 @@
-import { Effect, Layer, ServiceMap, Stream } from "effect"
+import { Effect, Layer, Context, Stream } from "effect"
 import { formatPatch, structuredPatch } from "diff"
 import path from "path"
 import { Bus } from "@/bus"
@@ -151,7 +151,7 @@ export namespace Vcs {
     root: Git.Base | undefined
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Vcs") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Vcs") {}
 
   export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Git.Service | Bus.Service> = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/provider/auth.ts

@@ -5,7 +5,7 @@ import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
 import { Plugin } from "../plugin"
 import { ProviderID } from "./schema"
-import { Array as Arr, Effect, Layer, Record, Result, ServiceMap } from "effect"
+import { Array as Arr, Effect, Layer, Record, Result, Context } from "effect"
 import z from "zod"
 
 export namespace ProviderAuth {
@@ -109,7 +109,7 @@ export namespace ProviderAuth {
     pending: Map<ProviderID, AuthOAuthResult>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/ProviderAuth") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/ProviderAuth") {}
 
   export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/provider/provider.ts

@@ -19,7 +19,7 @@ import { iife } from "@/util/iife"
 import { Global } from "../global"
 import path from "path"
 import { Filesystem } from "../util/filesystem"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
@@ -925,7 +925,7 @@ export namespace Provider {
     varsLoaders: Record<string, CustomVarsLoader>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Provider") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Provider") {}
 
   function cost(c: ModelsDev.Model["cost"]): Model["cost"] {
     const result: Model["cost"] = {

+ 11 - 13
packages/opencode/src/provider/schema.ts

@@ -9,20 +9,19 @@ export type ProviderID = typeof providerIdSchema.Type
 
 export const ProviderID = providerIdSchema.pipe(
   withStatics((schema: typeof providerIdSchema) => ({
-    make: (id: string) => schema.makeUnsafe(id),
     zod: z.string().pipe(z.custom<ProviderID>()),
     // Well-known providers
-    opencode: schema.makeUnsafe("opencode"),
-    anthropic: schema.makeUnsafe("anthropic"),
-    openai: schema.makeUnsafe("openai"),
-    google: schema.makeUnsafe("google"),
-    googleVertex: schema.makeUnsafe("google-vertex"),
-    githubCopilot: schema.makeUnsafe("github-copilot"),
-    amazonBedrock: schema.makeUnsafe("amazon-bedrock"),
-    azure: schema.makeUnsafe("azure"),
-    openrouter: schema.makeUnsafe("openrouter"),
-    mistral: schema.makeUnsafe("mistral"),
-    gitlab: schema.makeUnsafe("gitlab"),
+    opencode: schema.make("opencode"),
+    anthropic: schema.make("anthropic"),
+    openai: schema.make("openai"),
+    google: schema.make("google"),
+    googleVertex: schema.make("google-vertex"),
+    githubCopilot: schema.make("github-copilot"),
+    amazonBedrock: schema.make("amazon-bedrock"),
+    azure: schema.make("azure"),
+    openrouter: schema.make("openrouter"),
+    mistral: schema.make("mistral"),
+    gitlab: schema.make("gitlab"),
   })),
 )
 
@@ -32,7 +31,6 @@ export type ModelID = typeof modelIdSchema.Type
 
 export const ModelID = modelIdSchema.pipe(
   withStatics((schema: typeof modelIdSchema) => ({
-    make: (id: string) => schema.makeUnsafe(id),
     zod: z.string().pipe(z.custom<ModelID>()),
   })),
 )

+ 2 - 2
packages/opencode/src/pty/index.ts

@@ -10,7 +10,7 @@ import { lazy } from "@opencode-ai/util/lazy"
 import { Shell } from "@/shell/shell"
 import { Plugin } from "@/plugin"
 import { PtyID } from "./schema"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { EffectLogger } from "@/effect/logger"
 
 export namespace Pty {
@@ -113,7 +113,7 @@ export namespace Pty {
     ) => Effect.Effect<{ onMessage: (message: string | ArrayBuffer) => void; onClose: () => void } | undefined>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Pty") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Pty") {}
 
   export const layer = Layer.effect(
     Service,

+ 1 - 2
packages/opencode/src/pty/schema.ts

@@ -10,8 +10,7 @@ export type PtyID = typeof ptyIdSchema.Type
 
 export const PtyID = ptyIdSchema.pipe(
   withStatics((schema: typeof ptyIdSchema) => ({
-    make: (id: string) => schema.makeUnsafe(id),
-    ascending: (id?: string) => schema.makeUnsafe(Identifier.ascending("pty", id)),
+    ascending: (id?: string) => schema.make(Identifier.ascending("pty", id)),
     zod: Identifier.schema("pty").pipe(z.custom<PtyID>()),
   })),
 )

+ 2 - 2
packages/opencode/src/question/index.ts

@@ -1,4 +1,4 @@
-import { Deferred, Effect, Layer, Schema, ServiceMap } from "effect"
+import { Deferred, Effect, Layer, Schema, Context } from "effect"
 import { Bus } from "@/bus"
 import { BusEvent } from "@/bus/bus-event"
 import { InstanceState } from "@/effect/instance-state"
@@ -104,7 +104,7 @@ export namespace Question {
     readonly list: () => Effect.Effect<Request[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Question") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Question") {}
 
   export const layer = Layer.effect(
     Service,

+ 1 - 5
packages/opencode/src/question/schema.ts

@@ -5,12 +5,8 @@ import { Identifier } from "@/id/id"
 import { Newtype } from "@/util/schema"
 
 export class QuestionID extends Newtype<QuestionID>()("QuestionID", Schema.String) {
-  static make(id: string): QuestionID {
-    return this.makeUnsafe(id)
-  }
-
   static ascending(id?: string): QuestionID {
-    return this.makeUnsafe(Identifier.ascending("question", id))
+    return this.make(Identifier.ascending("question", id))
   }
 
   static readonly zod = Identifier.schema("question") as unknown as z.ZodType<QuestionID>

+ 2 - 2
packages/opencode/src/session/compaction.ts

@@ -15,7 +15,7 @@ import { Plugin } from "@/plugin"
 import { Config } from "@/config/config"
 import { NotFoundError } from "@/storage/db"
 import { ModelID, ProviderID } from "@/provider/schema"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { makeRuntime } from "@/effect/run-service"
 import { InstanceState } from "@/effect/instance-state"
 import { isOverflow as overflow } from "./overflow"
@@ -58,7 +58,7 @@ export namespace SessionCompaction {
     }) => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionCompaction") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionCompaction") {}
 
   export const layer: Layer.Layer<
     Service,

+ 2 - 2
packages/opencode/src/session/index.ts

@@ -29,7 +29,7 @@ import type { Provider } from "@/provider/provider"
 import { Permission } from "@/permission"
 import { Global } from "@/global"
 import type { LanguageModelV2Usage } from "@ai-sdk/provider"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { makeRuntime } from "@/effect/run-service"
 
 export namespace Session {
@@ -354,7 +354,7 @@ export namespace Session {
     }) => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Session") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Session") {}
 
   type Patch = z.infer<typeof Event.Updated.schema>["info"]
 

+ 2 - 2
packages/opencode/src/session/instruction.ts

@@ -1,6 +1,6 @@
 import os from "os"
 import path from "path"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
 import { Config } from "@/config/config"
 import { InstanceState } from "@/effect/instance-state"
@@ -64,7 +64,7 @@ export namespace Instruction {
     ) => Effect.Effect<{ filepath: string; content: string }[], AppFileSystem.Error>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Instruction") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Instruction") {}
 
   export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Config.Service | HttpClient.HttpClient> =
     Layer.effect(

+ 2 - 2
packages/opencode/src/session/llm.ts

@@ -1,6 +1,6 @@
 import { Provider } from "@/provider/provider"
 import { Log } from "@/util/log"
-import { Cause, Effect, Layer, Record, ServiceMap } from "effect"
+import { Cause, Effect, Layer, Record, Context } from "effect"
 import * as Queue from "effect/Queue"
 import * as Stream from "effect/Stream"
 import { streamText, wrapLanguageModel, type ModelMessage, type Tool, tool, jsonSchema } from "ai"
@@ -51,7 +51,7 @@ export namespace LLM {
     readonly stream: (input: StreamInput) => Stream.Stream<Event, unknown>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/LLM") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/LLM") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/session/processor.ts

@@ -1,4 +1,4 @@
-import { Cause, Deferred, Effect, Layer, ServiceMap } from "effect"
+import { Cause, Deferred, Effect, Layer, Context } from "effect"
 import * as Stream from "effect/Stream"
 import { Agent } from "@/agent/agent"
 import { Bus } from "@/bus"
@@ -76,7 +76,7 @@ export namespace SessionProcessor {
 
   type StreamEvent = Event
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionProcessor") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionProcessor") {}
 
   export const layer: Layer.Layer<
     Service,

+ 2 - 2
packages/opencode/src/session/prompt.ts

@@ -43,7 +43,7 @@ import { AppFileSystem } from "@/filesystem"
 import { Truncate } from "@/tool/truncate"
 import { decodeDataUrl } from "@/util/data-url"
 import { Process } from "@/util/process"
-import { Cause, Effect, Exit, Layer, Option, Scope, ServiceMap } from "effect"
+import { Cause, Effect, Exit, Layer, Option, Scope, Context } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { InstanceState } from "@/effect/instance-state"
 import { makeRuntime } from "@/effect/run-service"
@@ -76,7 +76,7 @@ export namespace SessionPrompt {
     readonly resolvePromptParts: (template: string) => Effect.Effect<PromptInput["parts"]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionPrompt") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionPrompt") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/session/revert.ts

@@ -1,5 +1,5 @@
 import z from "zod"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { makeRuntime } from "@/effect/run-service"
 import { Bus } from "../bus"
 import { Snapshot } from "../snapshot"
@@ -29,7 +29,7 @@ export namespace SessionRevert {
     readonly cleanup: (session: Session.Info) => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionRevert") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionRevert") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/session/run-state.ts

@@ -1,7 +1,7 @@
 import { InstanceState } from "@/effect/instance-state"
 import { Runner } from "@/effect/runner"
 import { makeRuntime } from "@/effect/run-service"
-import { Effect, Layer, Scope, ServiceMap } from "effect"
+import { Effect, Layer, Scope, Context } from "effect"
 import { Session } from "."
 import { MessageV2 } from "./message-v2"
 import { SessionID } from "./schema"
@@ -23,7 +23,7 @@ export namespace SessionRunState {
     ) => Effect.Effect<MessageV2.WithParts>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionRunState") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionRunState") {}
 
   export const layer = Layer.effect(
     Service,

+ 3 - 6
packages/opencode/src/session/schema.ts

@@ -7,8 +7,7 @@ import { withStatics } from "@/util/schema"
 export const SessionID = Schema.String.pipe(
   Schema.brand("SessionID"),
   withStatics((s) => ({
-    make: (id: string) => s.makeUnsafe(id),
-    descending: (id?: string) => s.makeUnsafe(Identifier.descending("session", id)),
+    descending: (id?: string) => s.make(Identifier.descending("session", id)),
     zod: Identifier.schema("session").pipe(z.custom<Schema.Schema.Type<typeof s>>()),
   })),
 )
@@ -18,8 +17,7 @@ export type SessionID = Schema.Schema.Type<typeof SessionID>
 export const MessageID = Schema.String.pipe(
   Schema.brand("MessageID"),
   withStatics((s) => ({
-    make: (id: string) => s.makeUnsafe(id),
-    ascending: (id?: string) => s.makeUnsafe(Identifier.ascending("message", id)),
+    ascending: (id?: string) => s.make(Identifier.ascending("message", id)),
     zod: Identifier.schema("message").pipe(z.custom<Schema.Schema.Type<typeof s>>()),
   })),
 )
@@ -29,8 +27,7 @@ export type MessageID = Schema.Schema.Type<typeof MessageID>
 export const PartID = Schema.String.pipe(
   Schema.brand("PartID"),
   withStatics((s) => ({
-    make: (id: string) => s.makeUnsafe(id),
-    ascending: (id?: string) => s.makeUnsafe(Identifier.ascending("part", id)),
+    ascending: (id?: string) => s.make(Identifier.ascending("part", id)),
     zod: Identifier.schema("part").pipe(z.custom<Schema.Schema.Type<typeof s>>()),
   })),
 )

+ 2 - 2
packages/opencode/src/session/status.ts

@@ -2,7 +2,7 @@ import { BusEvent } from "@/bus/bus-event"
 import { Bus } from "@/bus"
 import { InstanceState } from "@/effect/instance-state"
 import { SessionID } from "./schema"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import z from "zod"
 
 export namespace SessionStatus {
@@ -49,7 +49,7 @@ export namespace SessionStatus {
     readonly set: (sessionID: SessionID, status: Info) => Effect.Effect<void>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionStatus") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionStatus") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/session/summary.ts

@@ -1,5 +1,5 @@
 import z from "zod"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { makeRuntime } from "@/effect/run-service"
 import { Bus } from "@/bus"
 import { Snapshot } from "@/snapshot"
@@ -71,7 +71,7 @@ export namespace SessionSummary {
     readonly computeDiff: (input: { messages: MessageV2.WithParts[] }) => Effect.Effect<Snapshot.FileDiff[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionSummary") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionSummary") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/session/todo.ts

@@ -1,7 +1,7 @@
 import { BusEvent } from "@/bus/bus-event"
 import { Bus } from "@/bus"
 import { SessionID } from "./schema"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import z from "zod"
 import { Database, eq, asc } from "../storage/db"
 import { TodoTable } from "./session.sql"
@@ -31,7 +31,7 @@ export namespace Todo {
     readonly get: (sessionID: SessionID) => Effect.Effect<Info[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionTodo") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionTodo") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/share/session.ts

@@ -3,7 +3,7 @@ import { Session } from "@/session"
 import { SessionID } from "@/session/schema"
 import { SyncEvent } from "@/sync"
 import { fn } from "@/util/fn"
-import { Effect, Layer, Scope, ServiceMap } from "effect"
+import { Effect, Layer, Scope, Context } from "effect"
 import { Config } from "../config/config"
 import { Flag } from "../flag/flag"
 import { ShareNext } from "./share-next"
@@ -15,7 +15,7 @@ export namespace SessionShare {
     readonly unshare: (sessionID: SessionID) => Effect.Effect<void, unknown>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SessionShare") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SessionShare") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/share/share-next.ts

@@ -1,5 +1,5 @@
 import type * as SDK from "@opencode-ai/sdk/v2"
-import { Effect, Exit, Layer, Option, Schema, Scope, ServiceMap, Stream } from "effect"
+import { Effect, Exit, Layer, Option, Schema, Scope, Context, Stream } from "effect"
 import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
 import { Account } from "@/account"
 import { Bus } from "@/bus"
@@ -73,7 +73,7 @@ export namespace ShareNext {
     readonly remove: (sessionID: SessionID) => Effect.Effect<void, unknown>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/ShareNext") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/ShareNext") {}
 
   const db = <T>(fn: (d: Parameters<typeof Database.use>[0] extends (trx: infer D) => any ? D : never) => T) =>
     Effect.sync(() => Database.use(fn))

+ 2 - 2
packages/opencode/src/skill/discovery.ts

@@ -1,5 +1,5 @@
 import { NodePath } from "@effect/platform-node"
-import { Effect, Layer, Path, Schema, ServiceMap } from "effect"
+import { Effect, Layer, Path, Schema, Context } from "effect"
 import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
 import { withTransientReadRetry } from "@/util/effect-http-client"
 import { AppFileSystem } from "@/filesystem"
@@ -23,7 +23,7 @@ export namespace Discovery {
     readonly pull: (url: string) => Effect.Effect<string[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/SkillDiscovery") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/SkillDiscovery") {}
 
   export const layer: Layer.Layer<Service, never, AppFileSystem.Service | Path.Path | HttpClient.HttpClient> =
     Layer.effect(

+ 2 - 2
packages/opencode/src/skill/index.ts

@@ -2,7 +2,7 @@ import os from "os"
 import path from "path"
 import { pathToFileURL } from "url"
 import z from "zod"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { NamedError } from "@opencode-ai/util/error"
 import type { Agent } from "@/agent/agent"
 import { Bus } from "@/bus"
@@ -187,7 +187,7 @@ export namespace Skill {
     log.info("init", { count: Object.keys(state.skills).length })
   })
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Skill") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Skill") {}
 
   export const layer = Layer.effect(
     Service,

+ 2 - 2
packages/opencode/src/snapshot/index.ts

@@ -1,4 +1,4 @@
-import { Cause, Duration, Effect, Layer, Schedule, Semaphore, ServiceMap, Stream } from "effect"
+import { Cause, Duration, Effect, Layer, Schedule, Semaphore, Context, Stream } from "effect"
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
 import { formatPatch, structuredPatch } from "diff"
 import path from "path"
@@ -57,7 +57,7 @@ export namespace Snapshot {
     readonly diffFull: (from: string, to: string) => Effect.Effect<Snapshot.FileDiff[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Snapshot") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Snapshot") {}
 
   export const layer: Layer.Layer<
     Service,

+ 4 - 4
packages/opencode/src/storage/db.ts

@@ -2,7 +2,7 @@ import { type SQLiteBunDatabase } from "drizzle-orm/bun-sqlite"
 import { migrate } from "drizzle-orm/bun-sqlite/migrator"
 import { type SQLiteTransaction } from "drizzle-orm/sqlite-core"
 export * from "drizzle-orm"
-import { Context } from "../util/context"
+import { LocalContext } from "../util/local-context"
 import { lazy } from "../util/lazy"
 import { Global } from "../global"
 import { Log } from "../util/log"
@@ -122,7 +122,7 @@ export namespace Database {
 
   export type TxOrDb = Transaction | Client
 
-  const ctx = Context.create<{
+  const ctx = LocalContext.create<{
     tx: TxOrDb
     effects: (() => void | Promise<void>)[]
   }>("database")
@@ -131,7 +131,7 @@ export namespace Database {
     try {
       return callback(ctx.use().tx)
     } catch (err) {
-      if (err instanceof Context.NotFound) {
+      if (err instanceof LocalContext.NotFound) {
         const effects: (() => void | Promise<void>)[] = []
         const result = ctx.provide({ effects, tx: Client() }, () => callback(Client()))
         for (const effect of effects) effect()
@@ -161,7 +161,7 @@ export namespace Database {
     try {
       return callback(ctx.use().tx)
     } catch (err) {
-      if (err instanceof Context.NotFound) {
+      if (err instanceof LocalContext.NotFound) {
         const effects: (() => void | Promise<void>)[] = []
         const txCallback = InstanceState.bind((tx: TxOrDb) => ctx.provide({ tx, effects }, () => callback(tx)))
         const result = Client().transaction(txCallback, { behavior: options?.behavior })

+ 2 - 2
packages/opencode/src/storage/storage.ts

@@ -4,7 +4,7 @@ import { Global } from "../global"
 import { NamedError } from "@opencode-ai/util/error"
 import z from "zod"
 import { AppFileSystem } from "@/filesystem"
-import { Effect, Exit, Layer, Option, RcMap, Schema, ServiceMap, TxReentrantLock } from "effect"
+import { Effect, Exit, Layer, Option, RcMap, Schema, Context, TxReentrantLock } from "effect"
 import { Git } from "@/git"
 
 export namespace Storage {
@@ -65,7 +65,7 @@ export namespace Storage {
     readonly list: (prefix: string[]) => Effect.Effect<string[][], AppFileSystem.Error>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Storage") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Storage") {}
 
   function file(dir: string, key: string[]) {
     return path.join(dir, ...key) + ".json"

+ 1 - 2
packages/opencode/src/sync/schema.ts

@@ -7,8 +7,7 @@ import { withStatics } from "@/util/schema"
 export const EventID = Schema.String.pipe(
   Schema.brand("EventID"),
   withStatics((s) => ({
-    make: (id: string) => s.makeUnsafe(id),
-    ascending: (id?: string) => s.makeUnsafe(Identifier.ascending("event", id)),
+    ascending: (id?: string) => s.make(Identifier.ascending("event", id)),
     zod: Identifier.schema("event").pipe(z.custom<Schema.Schema.Type<typeof s>>()),
   })),
 )

+ 2 - 2
packages/opencode/src/tool/registry.ts

@@ -29,7 +29,7 @@ import { ApplyPatchTool } from "./apply_patch"
 import { Glob } from "../util/glob"
 import path from "path"
 import { pathToFileURL } from "url"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { EffectLogger } from "@/effect/logger"
 import { FetchHttpClient, HttpClient } from "effect/unstable/http"
 import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
@@ -74,7 +74,7 @@ export namespace ToolRegistry {
     }) => Effect.Effect<Tool.Def[]>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/ToolRegistry") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/ToolRegistry") {}
 
   export const layer: Layer.Layer<
     Service,

+ 1 - 2
packages/opencode/src/tool/schema.ts

@@ -10,8 +10,7 @@ export type ToolID = typeof toolIdSchema.Type
 
 export const ToolID = toolIdSchema.pipe(
   withStatics((schema: typeof toolIdSchema) => ({
-    make: (id: string) => schema.makeUnsafe(id),
-    ascending: (id?: string) => schema.makeUnsafe(Identifier.ascending("tool", id)),
+    ascending: (id?: string) => schema.make(Identifier.ascending("tool", id)),
     zod: Identifier.schema("tool").pipe(z.custom<ToolID>()),
   })),
 )

+ 2 - 2
packages/opencode/src/tool/truncate.ts

@@ -1,5 +1,5 @@
 import { NodePath } from "@effect/platform-node"
-import { Cause, Duration, Effect, Layer, Schedule, ServiceMap } from "effect"
+import { Cause, Duration, Effect, Layer, Schedule, Context } from "effect"
 import path from "path"
 import type { Agent } from "../agent/agent"
 import { makeRuntime } from "@/effect/run-service"
@@ -41,7 +41,7 @@ export namespace Truncate {
     readonly output: (text: string, options?: Options, agent?: Agent.Info) => Effect.Effect<Result>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Truncate") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Truncate") {}
 
   export const layer = Layer.effect(
     Service,

+ 1 - 1
packages/opencode/src/util/context.ts → packages/opencode/src/util/local-context.ts

@@ -1,6 +1,6 @@
 import { AsyncLocalStorage } from "async_hooks"
 
-export namespace Context {
+export namespace LocalContext {
   export class NotFound extends Error {
     constructor(public override readonly name: string) {
       super(`No context found for ${name}`)

+ 5 - 5
packages/opencode/src/util/schema.ts

@@ -6,7 +6,7 @@ import { Schema } from "effect"
  * @example
  *   export const Foo = fooSchema.pipe(
  *     withStatics((schema) => ({
- *       zero: schema.makeUnsafe(0),
+ *       zero: schema.make(0),
  *       from: Schema.decodeUnknownOption(schema),
  *     }))
  *   )
@@ -26,7 +26,7 @@ type NewtypeBrand<Tag extends string> = { readonly [NewtypeBrand]: Tag }
  * @example
  *   class QuestionID extends Newtype<QuestionID>()("QuestionID", Schema.String) {
  *     static make(id: string): QuestionID {
- *       return this.makeUnsafe(id)
+ *       return this.make(id)
  *     }
  *   }
  *
@@ -39,7 +39,7 @@ export function Newtype<Self>() {
     abstract class Base {
       declare readonly [NewtypeBrand]: Tag
 
-      static makeUnsafe(value: Schema.Schema.Type<S>): Self {
+      static make(value: Schema.Schema.Type<S>): Self {
         return value as unknown as Self
       }
     }
@@ -47,7 +47,7 @@ export function Newtype<Self>() {
     Object.setPrototypeOf(Base, schema)
 
     return Base as unknown as (abstract new (_: never) => Branded) & {
-      readonly makeUnsafe: (value: Schema.Schema.Type<S>) => Self
-    } & Omit<Schema.Opaque<Self, S, {}>, "makeUnsafe">
+      readonly make: (value: Schema.Schema.Type<S>) => Self
+    } & Omit<Schema.Opaque<Self, S, {}>, "make">
   }
 }

+ 2 - 2
packages/opencode/src/worktree/index.ts

@@ -13,7 +13,7 @@ import { errorMessage } from "../util/error"
 import { BusEvent } from "@/bus/bus-event"
 import { GlobalBus } from "@/bus/global"
 import { Git } from "@/git"
-import { Effect, Layer, Path, Scope, ServiceMap, Stream } from "effect"
+import { Effect, Layer, Path, Scope, Context, Stream } from "effect"
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
 import { NodePath } from "@effect/platform-node"
 import { AppFileSystem } from "@/filesystem"
@@ -164,7 +164,7 @@ export namespace Worktree {
     readonly reset: (input: ResetInput) => Effect.Effect<boolean>
   }
 
-  export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Worktree") {}
+  export class Service extends Context.Service<Service, Interface>()("@opencode/Worktree") {}
 
   type GitResult = { code: number; text: string; stderr: string }
 

+ 7 - 7
packages/opencode/test/effect/instance-state.test.ts

@@ -1,5 +1,5 @@
 import { afterEach, expect, test } from "bun:test"
-import { Cause, Deferred, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, ServiceMap } from "effect"
+import { Cause, Deferred, Duration, Effect, Exit, Fiber, Layer, ManagedRuntime, Context } from "effect"
 import { InstanceState } from "../../src/effect/instance-state"
 import { InstanceRef } from "../../src/effect/instance-ref"
 import { Instance } from "../../src/project/instance"
@@ -122,7 +122,7 @@ test("InstanceState.get reads the current directory lazily", async () => {
     readonly get: () => Effect.Effect<string>
   }
 
-  class Test extends ServiceMap.Service<Test, Api>()("@test/InstanceStateLazy") {
+  class Test extends Context.Service<Test, Api>()("@test/InstanceStateLazy") {
     static readonly layer = Layer.effect(
       Test,
       Effect.gen(function* () {
@@ -166,7 +166,7 @@ test("InstanceState preserves directory across async boundaries", async () => {
     readonly get: () => Effect.Effect<{ directory: string; worktree: string; project: string }>
   }
 
-  class Test extends ServiceMap.Service<Test, Api>()("@test/InstanceStateAsync") {
+  class Test extends Context.Service<Test, Api>()("@test/InstanceStateAsync") {
     static readonly layer = Layer.effect(
       Test,
       Effect.gen(function* () {
@@ -234,7 +234,7 @@ test("InstanceState survives high-contention concurrent access", async () => {
     readonly get: () => Effect.Effect<string>
   }
 
-  class Test extends ServiceMap.Service<Test, Api>()("@test/HighContention") {
+  class Test extends Context.Service<Test, Api>()("@test/HighContention") {
     static readonly layer = Layer.effect(
       Test,
       Effect.gen(function* () {
@@ -284,7 +284,7 @@ test("InstanceState correct after interleaved init and dispose", async () => {
     readonly get: () => Effect.Effect<string>
   }
 
-  class Test extends ServiceMap.Service<Test, Api>()("@test/InterleavedDispose") {
+  class Test extends Context.Service<Test, Api>()("@test/InterleavedDispose") {
     static readonly layer = Layer.effect(
       Test,
       Effect.gen(function* () {
@@ -391,7 +391,7 @@ test("InstanceState survives deferred resume from the same instance context", as
     readonly get: (gate: Deferred.Deferred<void>) => Effect.Effect<string>
   }
 
-  class Test extends ServiceMap.Service<Test, Api>()("@test/DeferredResume") {
+  class Test extends Context.Service<Test, Api>()("@test/DeferredResume") {
     static readonly layer = Layer.effect(
       Test,
       Effect.gen(function* () {
@@ -438,7 +438,7 @@ test("InstanceState survives deferred resume outside ALS when InstanceRef is set
     readonly get: (gate: Deferred.Deferred<void>) => Effect.Effect<string>
   }
 
-  class Test extends ServiceMap.Service<Test, Api>()("@test/DeferredResumeOutside") {
+  class Test extends Context.Service<Test, Api>()("@test/DeferredResumeOutside") {
     static readonly layer = Layer.effect(
       Test,
       Effect.gen(function* () {

+ 4 - 4
packages/opencode/test/effect/run-service.test.ts

@@ -1,8 +1,8 @@
 import { expect, test } from "bun:test"
-import { Effect, Layer, ServiceMap } from "effect"
+import { Effect, Layer, Context } from "effect"
 import { makeRuntime } from "../../src/effect/run-service"
 
-class Shared extends ServiceMap.Service<Shared, { readonly id: number }>()("@test/Shared") {}
+class Shared extends Context.Service<Shared, { readonly id: number }>()("@test/Shared") {}
 
 test("makeRuntime shares dependent layers through the shared memo map", async () => {
   let n = 0
@@ -15,7 +15,7 @@ test("makeRuntime shares dependent layers through the shared memo map", async ()
     }),
   )
 
-  class One extends ServiceMap.Service<One, { readonly get: () => Effect.Effect<number> }>()("@test/One") {}
+  class One extends Context.Service<One, { readonly get: () => Effect.Effect<number> }>()("@test/One") {}
   const one = Layer.effect(
     One,
     Effect.gen(function* () {
@@ -26,7 +26,7 @@ test("makeRuntime shares dependent layers through the shared memo map", async ()
     }),
   ).pipe(Layer.provide(shared))
 
-  class Two extends ServiceMap.Service<Two, { readonly get: () => Effect.Effect<number> }>()("@test/Two") {}
+  class Two extends Context.Service<Two, { readonly get: () => Effect.Effect<number> }>()("@test/Two") {}
   const two = Layer.effect(
     Two,
     Effect.gen(function* () {

+ 2 - 2
packages/opencode/test/fixture/fixture.ts

@@ -2,7 +2,7 @@ import { $ } from "bun"
 import * as fs from "fs/promises"
 import os from "os"
 import path from "path"
-import { Effect, ServiceMap } from "effect"
+import { Effect, Context } from "effect"
 import type * as PlatformError from "effect/PlatformError"
 import type * as Scope from "effect/Scope"
 import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
@@ -123,7 +123,7 @@ export function tmpdirScoped(options?: { git?: boolean; config?: Partial<Config.
 export const provideInstance =
   (directory: string) =>
   <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, E, R> =>
-    Effect.servicesWith((services: ServiceMap.ServiceMap<R>) =>
+    Effect.contextWith((services: Context.Context<R>) =>
       Effect.promise<A>(async () =>
         Instance.provide({
           directory,

+ 1 - 0
packages/opencode/test/installation/installation.test.ts

@@ -27,6 +27,7 @@ function mockSpawner(handler: (cmd: string, args: readonly string[]) => string =
         all: Stream.empty,
         getInputFd: () => ({ [Symbol.for("effect/Sink/TypeId")]: Symbol.for("effect/Sink/TypeId") }) as any,
         getOutputFd: () => Stream.empty,
+        unref: Effect.succeed(Effect.void),
       }),
     )
   })

+ 2 - 2
packages/opencode/test/lib/llm-server.ts

@@ -1,6 +1,6 @@
 import { NodeHttpServer, NodeHttpServerRequest } from "@effect/platform-node"
 import * as Http from "node:http"
-import { Deferred, Effect, Layer, ServiceMap, Stream } from "effect"
+import { Deferred, Effect, Layer, Context, Stream } from "effect"
 import * as HttpServer from "effect/unstable/http/HttpServer"
 import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
 
@@ -650,7 +650,7 @@ namespace TestLLMServer {
   }
 }
 
-export class TestLLMServer extends ServiceMap.Service<TestLLMServer, TestLLMServer.Service>()("@test/LLMServer") {
+export class TestLLMServer extends Context.Service<TestLLMServer, TestLLMServer.Service>()("@test/LLMServer") {
   static readonly layer = Layer.effect(
     TestLLMServer,
     Effect.gen(function* () {

+ 1 - 0
packages/opencode/test/project/project.test.ts

@@ -41,6 +41,7 @@ function mockGitFailure(failArg: string) {
               all: Stream.empty,
               getInputFd: () => ({ [Symbol.for("effect/Sink/TypeId")]: Symbol.for("effect/Sink/TypeId") }) as any,
               getOutputFd: () => Stream.empty,
+              unref: Effect.succeed(Effect.void),
             })
           }
           return yield* real.spawn(command)