Jelajahi Sumber

chore: generate

opencode-agent[bot] 3 bulan lalu
induk
melakukan
102c8353e0
71 mengubah file dengan 4439 tambahan dan 3174 penghapusan
  1. 37 112
      packages/core/migration/20260530232709_lovely_romulus/snapshot.json
  2. 10 3
      packages/core/script/migration.ts
  3. 1 3
      packages/core/src/aisdk.ts
  4. 25 23
      packages/core/src/database/migration.gen.ts
  5. 3 1
      packages/core/src/database/migration/20260309230000_move_org_to_state.ts
  6. 3 1
      packages/core/src/database/migration/20260312043431_session_message_cursor.ts
  7. 3 1
      packages/core/src/database/migration/20260410174513_workspace-name.ts
  8. 3 1
      packages/core/src/database/migration/20260427172553_slow_nightmare.ts
  9. 11 6
      packages/core/src/database/sqlite.bun.ts
  10. 11 6
      packages/core/src/database/sqlite.node.ts
  11. 2 1
      packages/core/src/event.ts
  12. 348 348
      packages/core/src/session/message-updater.ts
  13. 1 4
      packages/core/test/catalog.test.ts
  14. 12 4
      packages/core/test/database-migration.test.ts
  15. 36 6
      packages/core/test/event.test.ts
  16. 3 1
      packages/effect-sqlite-node/src/index.ts
  17. 2 10
      packages/opencode/src/account/repo.ts
  18. 6 1
      packages/opencode/src/permission/index.ts
  19. 106 84
      packages/opencode/src/project/project.ts
  20. 3 5
      packages/opencode/src/project/vcs.ts
  21. 3 1
      packages/opencode/src/provider/auth.ts
  22. 8 2
      packages/opencode/src/provider/provider.ts
  23. 1 2
      packages/opencode/src/server/projectors.ts
  24. 3 1
      packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts
  25. 8 2
      packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts
  26. 2 1
      packages/opencode/src/server/routes/instance/httpapi/handlers/tui.ts
  27. 3 1
      packages/opencode/src/session/compaction.ts
  28. 9 7
      packages/opencode/src/session/llm.ts
  29. 8 8
      packages/opencode/src/session/message-v2.ts
  30. 5 5
      packages/opencode/src/session/prompt.ts
  31. 1 5
      packages/opencode/src/session/session.ts
  32. 3 1
      packages/opencode/src/session/summary.ts
  33. 3 1
      packages/opencode/src/share/share-next.ts
  34. 5 1
      packages/opencode/src/skill/index.ts
  35. 5 1
      packages/opencode/src/tool/registry.ts
  36. 13 2
      packages/opencode/src/worktree/index.ts
  37. 5 1
      packages/opencode/test/cli/github-action.test.ts
  38. 3 1
      packages/opencode/test/format/format.test.ts
  39. 127 133
      packages/opencode/test/lsp/index.test.ts
  40. 6 6
      packages/opencode/test/lsp/lifecycle.test.ts
  41. 6 2
      packages/opencode/test/permission/next.test.ts
  42. 3 1
      packages/opencode/test/plugin/loader-shared.test.ts
  43. 64 64
      packages/opencode/test/plugin/workspace-adapter.test.ts
  44. 19 4
      packages/opencode/test/project/project.test.ts
  45. 7 1
      packages/opencode/test/project/vcs.test.ts
  46. 93 93
      packages/opencode/test/project/worktree-remove.test.ts
  47. 3 1
      packages/opencode/test/provider/amazon-bedrock.test.ts
  48. 25 9
      packages/opencode/test/provider/provider.test.ts
  49. 3 1
      packages/opencode/test/pty/ticket.test.ts
  50. 5 1
      packages/opencode/test/question/question.test.ts
  51. 0 1
      packages/opencode/test/server/httpapi-event.test.ts
  52. 8 2
      packages/opencode/test/server/httpapi-workspace-routing.test.ts
  53. 12 2
      packages/opencode/test/server/session-list.test.ts
  54. 8 3
      packages/opencode/test/session/compaction.test.ts
  55. 8 2
      packages/opencode/test/session/llm.test.ts
  56. 3 1
      packages/opencode/test/session/message-v2.test.ts
  57. 6 1
      packages/opencode/test/session/prompt.test.ts
  58. 25 25
      packages/opencode/test/session/retry.test.ts
  59. 8 2
      packages/opencode/test/session/session.test.ts
  60. 6 1
      packages/opencode/test/share/share-next.test.ts
  61. 7 1
      packages/opencode/test/snapshot/snapshot.test.ts
  62. 4 1
      packages/opencode/test/storage/workspace-time-migration.test.ts
  63. 51 51
      packages/opencode/test/tool/lsp.test.ts
  64. 7 1
      packages/opencode/test/tool/read.test.ts
  65. 122 122
      packages/opencode/test/tool/repo_clone.test.ts
  66. 81 81
      packages/opencode/test/tool/repo_overview.test.ts
  67. 78 78
      packages/opencode/test/tool/skill.test.ts
  68. 179 153
      packages/opencode/test/v2/session-message-updater.test.ts
  69. 5 5
      packages/sdk/js/src/v2/gen/sdk.gen.ts
  70. 1962 943
      packages/sdk/js/src/v2/gen/types.gen.ts
  71. 784 718
      packages/sdk/openapi.json

+ 37 - 112
packages/core/migration/20260530232709_lovely_romulus/snapshot.json

@@ -2,10 +2,7 @@
   "version": "7",
   "dialect": "sqlite",
   "id": "bf93c73b-5a48-4d63-9909-3c36a79b9788",
-  "prevIds": [
-    "be5eae31-b7f8-4292-8827-c36a524abd1b",
-    "fdfcccee-fb3a-481f-b801-b9835fa30d5d"
-  ],
+  "prevIds": ["be5eae31-b7f8-4292-8827-c36a524abd1b", "fdfcccee-fb3a-481f-b801-b9835fa30d5d"],
   "ddl": [
     {
       "name": "workspace",
@@ -1188,13 +1185,9 @@
       "table": "session_share"
     },
     {
-      "columns": [
-        "project_id"
-      ],
+      "columns": ["project_id"],
       "tableTo": "project",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1203,13 +1196,9 @@
       "table": "workspace"
     },
     {
-      "columns": [
-        "active_account_id"
-      ],
+      "columns": ["active_account_id"],
       "tableTo": "account",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "SET NULL",
       "nameExplicit": false,
@@ -1218,13 +1207,9 @@
       "table": "account_state"
     },
     {
-      "columns": [
-        "aggregate_id"
-      ],
+      "columns": ["aggregate_id"],
       "tableTo": "event_sequence",
-      "columnsTo": [
-        "aggregate_id"
-      ],
+      "columnsTo": ["aggregate_id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1233,13 +1218,9 @@
       "table": "event"
     },
     {
-      "columns": [
-        "session_id"
-      ],
+      "columns": ["session_id"],
       "tableTo": "session",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1248,13 +1229,9 @@
       "table": "message"
     },
     {
-      "columns": [
-        "message_id"
-      ],
+      "columns": ["message_id"],
       "tableTo": "message",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1263,13 +1240,9 @@
       "table": "part"
     },
     {
-      "columns": [
-        "project_id"
-      ],
+      "columns": ["project_id"],
       "tableTo": "project",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1278,13 +1251,9 @@
       "table": "permission"
     },
     {
-      "columns": [
-        "session_id"
-      ],
+      "columns": ["session_id"],
       "tableTo": "session",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1293,13 +1262,9 @@
       "table": "session_message"
     },
     {
-      "columns": [
-        "project_id"
-      ],
+      "columns": ["project_id"],
       "tableTo": "project",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1308,13 +1273,9 @@
       "table": "session"
     },
     {
-      "columns": [
-        "session_id"
-      ],
+      "columns": ["session_id"],
       "tableTo": "session",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1323,13 +1284,9 @@
       "table": "todo"
     },
     {
-      "columns": [
-        "session_id"
-      ],
+      "columns": ["session_id"],
       "tableTo": "session",
-      "columnsTo": [
-        "id"
-      ],
+      "columnsTo": ["id"],
       "onUpdate": "NO ACTION",
       "onDelete": "CASCADE",
       "nameExplicit": false,
@@ -1338,137 +1295,105 @@
       "table": "session_share"
     },
     {
-      "columns": [
-        "email",
-        "url"
-      ],
+      "columns": ["email", "url"],
       "nameExplicit": false,
       "name": "control_account_pk",
       "entityType": "pks",
       "table": "control_account"
     },
     {
-      "columns": [
-        "session_id",
-        "position"
-      ],
+      "columns": ["session_id", "position"],
       "nameExplicit": false,
       "name": "todo_pk",
       "entityType": "pks",
       "table": "todo"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "workspace_pk",
       "table": "workspace",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "name"
-      ],
+      "columns": ["name"],
       "nameExplicit": false,
       "name": "data_migration_pk",
       "table": "data_migration",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "account_state_pk",
       "table": "account_state",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "account_pk",
       "table": "account",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "aggregate_id"
-      ],
+      "columns": ["aggregate_id"],
       "nameExplicit": false,
       "name": "event_sequence_pk",
       "table": "event_sequence",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "event_pk",
       "table": "event",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "project_pk",
       "table": "project",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "message_pk",
       "table": "message",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "part_pk",
       "table": "part",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "project_id"
-      ],
+      "columns": ["project_id"],
       "nameExplicit": false,
       "name": "permission_pk",
       "table": "permission",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "session_message_pk",
       "table": "session_message",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "id"
-      ],
+      "columns": ["id"],
       "nameExplicit": false,
       "name": "session_pk",
       "table": "session",
       "entityType": "pks"
     },
     {
-      "columns": [
-        "session_id"
-      ],
+      "columns": ["session_id"],
       "nameExplicit": false,
       "name": "session_share_pk",
       "table": "session_share",
@@ -1632,4 +1557,4 @@
     }
   ],
   "renames": []
-}
+}

+ 10 - 3
packages/core/script/migration.ts

@@ -25,7 +25,10 @@ const sqlMigrations = (await Array.fromAsync(new Bun.Glob("*/migration.sql").sca
 
 for (const name of sqlMigrations) {
   if (await Bun.file(path.join(tsDir, `${name}.ts`)).exists()) continue
-  await Bun.write(path.join(tsDir, `${name}.ts`), renderMigration(name, await Bun.file(path.join(sqlDir, name, "migration.sql")).text()))
+  await Bun.write(
+    path.join(tsDir, `${name}.ts`),
+    renderMigration(name, await Bun.file(path.join(sqlDir, name, "migration.sql")).text()),
+  )
 }
 
 await Bun.write(registry, renderRegistry(sqlMigrations))
@@ -47,7 +50,9 @@ export default { ...config, out: ${JSON.stringify(output)} }
     await $`bun drizzle-kit generate --config ${config}`.cwd(path.join(root, "packages/core"))
     const after = await snapshot(output)
     if (JSON.stringify(after) !== JSON.stringify(before)) {
-      throw new Error("Core schema has ungenerated database migrations. Run `bun script/migration.ts` from packages/core.")
+      throw new Error(
+        "Core schema has ungenerated database migrations. Run `bun script/migration.ts` from packages/core.",
+      )
     }
 
     const migrations = before
@@ -56,7 +61,9 @@ export default { ...config, out: ${JSON.stringify(output)} }
       .sort()
     for (const name of migrations) {
       if (await Bun.file(path.join(tsDir, `${name}.ts`)).exists()) continue
-      throw new Error(`Database migration TypeScript wrapper is missing for ${name}. Run \`bun script/migration.ts\` from packages/core.`)
+      throw new Error(
+        `Database migration TypeScript wrapper is missing for ${name}. Run \`bun script/migration.ts\` from packages/core.`,
+      )
     }
     if ((await Bun.file(registry).text()) !== renderRegistry(migrations)) {
       throw new Error("Database migration registry is stale. Run `bun script/migration.ts` from packages/core.")

+ 1 - 3
packages/core/src/aisdk.ts

@@ -170,6 +170,4 @@ export const layer = Layer.effect(
   }),
 )
 
-export const defaultLayer = layer.pipe(
-  Layer.provide(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer))),
-)
+export const defaultLayer = layer.pipe(Layer.provide(PluginV2.locationLayer.pipe(Layer.provide(EventV2.defaultLayer))))

+ 25 - 23
packages/core/src/database/migration.gen.ts

@@ -1,25 +1,27 @@
 import type { DatabaseMigration } from "./migration"
 
-export const migrations = (await Promise.all([
-  import("./migration/20260127222353_familiar_lady_ursula"),
-  import("./migration/20260211171708_add_project_commands"),
-  import("./migration/20260213144116_wakeful_the_professor"),
-  import("./migration/20260225215848_workspace"),
-  import("./migration/20260227213759_add_session_workspace_id"),
-  import("./migration/20260228203230_blue_harpoon"),
-  import("./migration/20260303231226_add_workspace_fields"),
-  import("./migration/20260309230000_move_org_to_state"),
-  import("./migration/20260312043431_session_message_cursor"),
-  import("./migration/20260323234822_events"),
-  import("./migration/20260410174513_workspace-name"),
-  import("./migration/20260413175956_chief_energizer"),
-  import("./migration/20260423070820_add_icon_url_override"),
-  import("./migration/20260427172553_slow_nightmare"),
-  import("./migration/20260428004200_add_session_path"),
-  import("./migration/20260501142318_next_venus"),
-  import("./migration/20260504145000_add_sync_owner"),
-  import("./migration/20260507164347_add_workspace_time"),
-  import("./migration/20260510033149_session_usage"),
-  import("./migration/20260511000411_data_migration_state"),
-  import("./migration/20260530232709_lovely_romulus"),
-])).map((module) => module.default) satisfies DatabaseMigration.Migration[]
+export const migrations = (
+  await Promise.all([
+    import("./migration/20260127222353_familiar_lady_ursula"),
+    import("./migration/20260211171708_add_project_commands"),
+    import("./migration/20260213144116_wakeful_the_professor"),
+    import("./migration/20260225215848_workspace"),
+    import("./migration/20260227213759_add_session_workspace_id"),
+    import("./migration/20260228203230_blue_harpoon"),
+    import("./migration/20260303231226_add_workspace_fields"),
+    import("./migration/20260309230000_move_org_to_state"),
+    import("./migration/20260312043431_session_message_cursor"),
+    import("./migration/20260323234822_events"),
+    import("./migration/20260410174513_workspace-name"),
+    import("./migration/20260413175956_chief_energizer"),
+    import("./migration/20260423070820_add_icon_url_override"),
+    import("./migration/20260427172553_slow_nightmare"),
+    import("./migration/20260428004200_add_session_path"),
+    import("./migration/20260501142318_next_venus"),
+    import("./migration/20260504145000_add_sync_owner"),
+    import("./migration/20260507164347_add_workspace_time"),
+    import("./migration/20260510033149_session_usage"),
+    import("./migration/20260511000411_data_migration_state"),
+    import("./migration/20260530232709_lovely_romulus"),
+  ])
+).map((module) => module.default) satisfies DatabaseMigration.Migration[]

+ 3 - 1
packages/core/src/database/migration/20260309230000_move_org_to_state.ts

@@ -6,7 +6,9 @@ export default {
   up(tx) {
     return Effect.gen(function* () {
       yield* tx.run(`ALTER TABLE \`account_state\` ADD \`active_org_id\` text;`)
-      yield* tx.run(`UPDATE \`account_state\` SET \`active_org_id\` = (SELECT \`selected_org_id\` FROM \`account\` WHERE \`account\`.\`id\` = \`account_state\`.\`active_account_id\`);`)
+      yield* tx.run(
+        `UPDATE \`account_state\` SET \`active_org_id\` = (SELECT \`selected_org_id\` FROM \`account\` WHERE \`account\`.\`id\` = \`account_state\`.\`active_account_id\`);`,
+      )
       yield* tx.run(`ALTER TABLE \`account\` DROP COLUMN \`selected_org_id\`;`)
     })
   },

+ 3 - 1
packages/core/src/database/migration/20260312043431_session_message_cursor.ts

@@ -7,7 +7,9 @@ export default {
     return Effect.gen(function* () {
       yield* tx.run(`DROP INDEX IF EXISTS \`message_session_idx\`;`)
       yield* tx.run(`DROP INDEX IF EXISTS \`part_message_idx\`;`)
-      yield* tx.run(`CREATE INDEX \`message_session_time_created_id_idx\` ON \`message\` (\`session_id\`,\`time_created\`,\`id\`);`)
+      yield* tx.run(
+        `CREATE INDEX \`message_session_time_created_id_idx\` ON \`message\` (\`session_id\`,\`time_created\`,\`id\`);`,
+      )
       yield* tx.run(`CREATE INDEX \`part_message_id_id_idx\` ON \`part\` (\`message_id\`,\`id\`);`)
     })
   },

+ 3 - 1
packages/core/src/database/migration/20260410174513_workspace-name.ts

@@ -18,7 +18,9 @@ export default {
           CONSTRAINT \`fk_workspace_project_id_project_id_fk\` FOREIGN KEY (\`project_id\`) REFERENCES \`project\`(\`id\`) ON DELETE CASCADE
         );
       `)
-      yield* tx.run(`INSERT INTO \`__new_workspace\`(\`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\`) SELECT \`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\` FROM \`workspace\`;`)
+      yield* tx.run(
+        `INSERT INTO \`__new_workspace\`(\`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\`) SELECT \`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\` FROM \`workspace\`;`,
+      )
       yield* tx.run(`DROP TABLE \`workspace\`;`)
       yield* tx.run(`ALTER TABLE \`__new_workspace\` RENAME TO \`workspace\`;`)
       yield* tx.run(`PRAGMA foreign_keys=ON;`)

+ 3 - 1
packages/core/src/database/migration/20260427172553_slow_nightmare.ts

@@ -20,7 +20,9 @@ export default {
       yield* tx.run(`DROP INDEX IF EXISTS \`session_entry_session_type_idx\`;`)
       yield* tx.run(`DROP INDEX IF EXISTS \`session_entry_time_created_idx\`;`)
       yield* tx.run(`CREATE INDEX \`session_message_session_idx\` ON \`session_message\` (\`session_id\`);`)
-      yield* tx.run(`CREATE INDEX \`session_message_session_type_idx\` ON \`session_message\` (\`session_id\`,\`type\`);`)
+      yield* tx.run(
+        `CREATE INDEX \`session_message_session_type_idx\` ON \`session_message\` (\`session_id\`,\`type\`);`,
+      )
       yield* tx.run(`CREATE INDEX \`session_message_time_created_idx\` ON \`session_message\` (\`time_created\`);`)
       yield* tx.run(`DROP TABLE \`session_entry\`;`)
     })

+ 11 - 6
packages/core/src/database/sqlite.bun.ts

@@ -49,7 +49,9 @@ const make = (options: Config) =>
     const native = (yield* Sqlite.Native) as Database
 
     const compiler = Statement.makeCompilerSqlite(options.transformQueryNames)
-    const transformRows = options.transformResultNames ? Statement.defaultTransforms(options.transformResultNames).array : undefined
+    const transformRows = options.transformResultNames
+      ? Statement.defaultTransforms(options.transformResultNames).array
+      : undefined
 
     const run = (query: string, params: ReadonlyArray<unknown> = []) =>
       Effect.withFiber<Array<Record<string, unknown>>, SqlError>((fiber) => {
@@ -102,7 +104,9 @@ const make = (options: Config) =>
       export: Effect.try({
         try: () => native.serialize(),
         catch: (cause) =>
-          new SqlError({ reason: classifySqliteError(cause, { message: "Failed to export database", operation: "export" }) }),
+          new SqlError({
+            reason: classifySqliteError(cause, { message: "Failed to export database", operation: "export" }),
+          }),
       }),
       loadExtension: (path) =>
         Effect.try({
@@ -119,7 +123,10 @@ const make = (options: Config) =>
     const transactionAcquirer = Effect.uninterruptibleMask((restore) => {
       const fiber = Fiber.getCurrent()!
       const scope = Context.getUnsafe(fiber.context, Scope.Scope)
-      return Effect.as(Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))), connection)
+      return Effect.as(
+        Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))),
+        connection,
+      )
     })
 
     const client = Object.assign(
@@ -172,6 +179,4 @@ export const layer = (config: Config) =>
   Layer.merge(
     nativeLayer(config),
     Layer.merge(sqliteLayer(config), drizzleLayer).pipe(Layer.provide(nativeLayer(config))),
-  ).pipe(
-    Layer.provide(Reactivity.layer),
-  )
+  ).pipe(Layer.provide(Reactivity.layer))

+ 11 - 6
packages/core/src/database/sqlite.node.ts

@@ -49,7 +49,9 @@ const make = (options: Config) =>
     const native = (yield* Sqlite.Native) as DatabaseSync
 
     const compiler = Statement.makeCompilerSqlite(options.transformQueryNames)
-    const transformRows = options.transformResultNames ? Statement.defaultTransforms(options.transformResultNames).array : undefined
+    const transformRows = options.transformResultNames
+      ? Statement.defaultTransforms(options.transformResultNames).array
+      : undefined
 
     const run = (query: string, params: ReadonlyArray<unknown> = []) =>
       Effect.withFiber<Array<Record<string, unknown>>, SqlError>((fiber) => {
@@ -72,7 +74,9 @@ const make = (options: Config) =>
         statement.setReadBigInts(Context.get(fiber.context, Client.SafeIntegers))
         statement.setReturnArrays(true)
         try {
-          return Effect.succeed(statement.all(...(params as SQLInputValue[])) as unknown as ReadonlyArray<ReadonlyArray<unknown>>)
+          return Effect.succeed(
+            statement.all(...(params as SQLInputValue[])) as unknown as ReadonlyArray<ReadonlyArray<unknown>>,
+          )
         } catch (cause) {
           return Effect.fail(
             new SqlError({
@@ -113,7 +117,10 @@ const make = (options: Config) =>
     const transactionAcquirer = Effect.uninterruptibleMask((restore) => {
       const fiber = Fiber.getCurrent()!
       const scope = Context.getUnsafe(fiber.context, Scope.Scope)
-      return Effect.as(Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))), connection)
+      return Effect.as(
+        Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))),
+        connection,
+      )
     })
 
     const client = Object.assign(
@@ -167,6 +174,4 @@ export const layer = (config: Config) =>
   Layer.merge(
     nativeLayer(config),
     Layer.merge(sqliteLayer(config), drizzleLayer).pipe(Layer.provide(nativeLayer(config))),
-  ).pipe(
-    Layer.provide(Reactivity.layer),
-  )
+  ).pipe(Layer.provide(Reactivity.layer))

+ 2 - 1
packages/core/src/event.ts

@@ -261,7 +261,8 @@ export const layer = Layer.effect(
     function publish<D extends Definition>(definition: D, data: Data<D>, options?: PublishOptions) {
       return Effect.gen(function* () {
         const serviceLocation = Option.getOrUndefined(yield* Effect.serviceOption(Location.Service))
-        const location = options?.location ??
+        const location =
+          options?.location ??
           (serviceLocation
             ? { directory: serviceLocation.directory, workspaceID: serviceLocation.workspaceID }
             : undefined)

+ 348 - 348
packages/core/src/session/message-updater.ts

@@ -103,371 +103,371 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
 
   return Effect.gen(function* () {
     yield* SessionEvent.All.match(event, {
-    "session.next.agent.switched": (event) => {
-      return adapter.appendMessage(
-        new SessionMessage.AgentSwitched({
-          id: event.id,
-          type: "agent-switched",
-          metadata: event.metadata,
-          agent: event.data.agent,
-          time: { created: event.data.timestamp },
-        }),
-      )
-    },
-    "session.next.model.switched": (event) => {
-      return adapter.appendMessage(
-        new SessionMessage.ModelSwitched({
-          id: event.id,
-          type: "model-switched",
-          metadata: event.metadata,
-          model: event.data.model,
-          time: { created: event.data.timestamp },
-        }),
-      )
-    },
-    "session.next.prompted": (event) => {
-      return adapter.appendMessage(
-        new SessionMessage.User({
-          id: event.id,
-          type: "user",
-          metadata: event.metadata,
-          text: event.data.prompt.text,
-          files: event.data.prompt.files,
-          agents: event.data.prompt.agents,
-          references: event.data.prompt.references,
-          time: { created: event.data.timestamp },
-        }),
-      )
-    },
-    "session.next.synthetic": (event) => {
-      return adapter.appendMessage(
-        new SessionMessage.Synthetic({
-          sessionID: event.data.sessionID,
-          text: event.data.text,
-          id: event.id,
-          type: "synthetic",
-          time: { created: event.data.timestamp },
-        }),
-      )
-    },
-    "session.next.shell.started": (event) => {
-      return adapter.appendMessage(
-        new SessionMessage.Shell({
-          id: event.id,
-          type: "shell",
-          metadata: event.metadata,
-          callID: event.data.callID,
-          command: event.data.command,
-          output: "",
-          time: { created: event.data.timestamp },
-        }),
-      )
-    },
-    "session.next.shell.ended": (event) => {
-      return Effect.gen(function* () {
-      const currentShell = yield* adapter.getCurrentShell(event.data.callID)
-      if (currentShell) {
-        yield* adapter.updateShell(
-          produce(currentShell, (draft) => {
-            draft.output = event.data.output
-            draft.time.completed = event.data.timestamp
-          }),
-        )
-      }
-      })
-    },
-    "session.next.step.started": (event) => {
-      return Effect.gen(function* () {
-        const currentAssistant = yield* adapter.getCurrentAssistant()
-        if (currentAssistant) {
-          yield* adapter.updateAssistant(
-            produce(currentAssistant, (draft) => {
-              draft.time.completed = event.data.timestamp
-            }),
-          )
-        }
-        yield* adapter.appendMessage(
-          new SessionMessage.Assistant({
+      "session.next.agent.switched": (event) => {
+        return adapter.appendMessage(
+          new SessionMessage.AgentSwitched({
             id: event.id,
-            type: "assistant",
+            type: "agent-switched",
+            metadata: event.metadata,
             agent: event.data.agent,
-            model: event.data.model,
             time: { created: event.data.timestamp },
-            content: [],
-            snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
           }),
         )
-      })
-    },
-    "session.next.step.ended": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            draft.time.completed = event.data.timestamp
-            draft.finish = event.data.finish
-            draft.cost = event.data.cost
-            draft.tokens = event.data.tokens
-            if (event.data.snapshot) draft.snapshot = { ...draft.snapshot, end: event.data.snapshot }
-          }),
-        )
-      }
-      })
-    },
-    "session.next.step.failed": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            draft.time.completed = event.data.timestamp
-            draft.finish = "error"
-            draft.error = event.data.error
+      },
+      "session.next.model.switched": (event) => {
+        return adapter.appendMessage(
+          new SessionMessage.ModelSwitched({
+            id: event.id,
+            type: "model-switched",
+            metadata: event.metadata,
+            model: event.data.model,
+            time: { created: event.data.timestamp },
           }),
         )
-      }
-      })
-    },
-    "session.next.text.started": () => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            draft.content.push(new SessionMessage.AssistantText({ type: "text", text: "" }) as DraftText)
+      },
+      "session.next.prompted": (event) => {
+        return adapter.appendMessage(
+          new SessionMessage.User({
+            id: event.id,
+            type: "user",
+            metadata: event.metadata,
+            text: event.data.prompt.text,
+            files: event.data.prompt.files,
+            agents: event.data.prompt.agents,
+            references: event.data.prompt.references,
+            time: { created: event.data.timestamp },
           }),
         )
-      }
-      })
-    },
-    "session.next.text.delta": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestText(draft)
-            if (match) match.text += event.data.delta
+      },
+      "session.next.synthetic": (event) => {
+        return adapter.appendMessage(
+          new SessionMessage.Synthetic({
+            sessionID: event.data.sessionID,
+            text: event.data.text,
+            id: event.id,
+            type: "synthetic",
+            time: { created: event.data.timestamp },
           }),
         )
-      }
-      })
-    },
-    "session.next.text.ended": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestText(draft)
-            if (match) match.text = event.data.text
+      },
+      "session.next.shell.started": (event) => {
+        return adapter.appendMessage(
+          new SessionMessage.Shell({
+            id: event.id,
+            type: "shell",
+            metadata: event.metadata,
+            callID: event.data.callID,
+            command: event.data.command,
+            output: "",
+            time: { created: event.data.timestamp },
           }),
         )
-      }
-      })
-    },
-    "session.next.tool.input.started": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            draft.content.push(
-              new SessionMessage.AssistantTool({
-                type: "tool",
-                id: event.data.callID,
-                name: event.data.name,
-                time: { created: event.data.timestamp },
-                state: new SessionMessage.ToolStatePending({ status: "pending", input: "" }),
-              }) as DraftTool,
+      },
+      "session.next.shell.ended": (event) => {
+        return Effect.gen(function* () {
+          const currentShell = yield* adapter.getCurrentShell(event.data.callID)
+          if (currentShell) {
+            yield* adapter.updateShell(
+              produce(currentShell, (draft) => {
+                draft.output = event.data.output
+                draft.time.completed = event.data.timestamp
+              }),
             )
-          }),
-        )
-      }
-      })
-    },
-    "session.next.tool.input.delta": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestTool(draft, event.data.callID)
-            // oxlint-disable-next-line no-base-to-string -- event.delta is a Schema.String (runtime string)
-            if (match && match.state.status === "pending") match.state.input += event.data.delta
-          }),
-        )
-      }
-      })
-    },
-    "session.next.tool.input.ended": () => Effect.void,
-    "session.next.tool.called": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestTool(draft, event.data.callID)
-            if (match) {
-              match.provider = event.data.provider
-              match.time.ran = event.data.timestamp
-              match.state = {
-                status: "running",
-                input: event.data.input,
-                structured: {},
-                content: [],
-              }
-            }
-          }),
-        )
-      }
-      })
-    },
-    "session.next.tool.progress": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestTool(draft, event.data.callID)
-            if (match && match.state.status === "running") {
-              match.state.structured = event.data.structured
-              match.state.content = [...event.data.content]
-            }
-          }),
-        )
-      }
-      })
-    },
-    "session.next.tool.success": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestTool(draft, event.data.callID)
-            if (match && match.state.status === "running") {
-              match.provider = event.data.provider
-              match.time.completed = event.data.timestamp
-              match.state = {
-                status: "completed",
-                input: match.state.input,
-                structured: event.data.structured,
-                content: [...event.data.content],
-              }
-            }
-          }),
-        )
-      }
-      })
-    },
-    "session.next.tool.failed": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestTool(draft, event.data.callID)
-            if (match && match.state.status === "running") {
-              match.provider = event.data.provider
-              match.time.completed = event.data.timestamp
-              match.state = {
-                status: "error",
-                error: event.data.error,
-                input: match.state.input,
-                structured: match.state.structured,
-                content: match.state.content,
-              }
-            }
-          }),
-        )
-      }
-      })
-    },
-    "session.next.reasoning.started": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            draft.content.push(
-              new SessionMessage.AssistantReasoning({
-                type: "reasoning",
-                id: event.data.reasoningID,
-                text: "",
-              }) as DraftReasoning,
+          }
+        })
+      },
+      "session.next.step.started": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                draft.time.completed = event.data.timestamp
+              }),
             )
+          }
+          yield* adapter.appendMessage(
+            new SessionMessage.Assistant({
+              id: event.id,
+              type: "assistant",
+              agent: event.data.agent,
+              model: event.data.model,
+              time: { created: event.data.timestamp },
+              content: [],
+              snapshot: event.data.snapshot ? { start: event.data.snapshot } : undefined,
+            }),
+          )
+        })
+      },
+      "session.next.step.ended": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                draft.time.completed = event.data.timestamp
+                draft.finish = event.data.finish
+                draft.cost = event.data.cost
+                draft.tokens = event.data.tokens
+                if (event.data.snapshot) draft.snapshot = { ...draft.snapshot, end: event.data.snapshot }
+              }),
+            )
+          }
+        })
+      },
+      "session.next.step.failed": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                draft.time.completed = event.data.timestamp
+                draft.finish = "error"
+                draft.error = event.data.error
+              }),
+            )
+          }
+        })
+      },
+      "session.next.text.started": () => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                draft.content.push(new SessionMessage.AssistantText({ type: "text", text: "" }) as DraftText)
+              }),
+            )
+          }
+        })
+      },
+      "session.next.text.delta": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestText(draft)
+                if (match) match.text += event.data.delta
+              }),
+            )
+          }
+        })
+      },
+      "session.next.text.ended": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestText(draft)
+                if (match) match.text = event.data.text
+              }),
+            )
+          }
+        })
+      },
+      "session.next.tool.input.started": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                draft.content.push(
+                  new SessionMessage.AssistantTool({
+                    type: "tool",
+                    id: event.data.callID,
+                    name: event.data.name,
+                    time: { created: event.data.timestamp },
+                    state: new SessionMessage.ToolStatePending({ status: "pending", input: "" }),
+                  }) as DraftTool,
+                )
+              }),
+            )
+          }
+        })
+      },
+      "session.next.tool.input.delta": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestTool(draft, event.data.callID)
+                // oxlint-disable-next-line no-base-to-string -- event.delta is a Schema.String (runtime string)
+                if (match && match.state.status === "pending") match.state.input += event.data.delta
+              }),
+            )
+          }
+        })
+      },
+      "session.next.tool.input.ended": () => Effect.void,
+      "session.next.tool.called": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestTool(draft, event.data.callID)
+                if (match) {
+                  match.provider = event.data.provider
+                  match.time.ran = event.data.timestamp
+                  match.state = {
+                    status: "running",
+                    input: event.data.input,
+                    structured: {},
+                    content: [],
+                  }
+                }
+              }),
+            )
+          }
+        })
+      },
+      "session.next.tool.progress": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestTool(draft, event.data.callID)
+                if (match && match.state.status === "running") {
+                  match.state.structured = event.data.structured
+                  match.state.content = [...event.data.content]
+                }
+              }),
+            )
+          }
+        })
+      },
+      "session.next.tool.success": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestTool(draft, event.data.callID)
+                if (match && match.state.status === "running") {
+                  match.provider = event.data.provider
+                  match.time.completed = event.data.timestamp
+                  match.state = {
+                    status: "completed",
+                    input: match.state.input,
+                    structured: event.data.structured,
+                    content: [...event.data.content],
+                  }
+                }
+              }),
+            )
+          }
+        })
+      },
+      "session.next.tool.failed": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestTool(draft, event.data.callID)
+                if (match && match.state.status === "running") {
+                  match.provider = event.data.provider
+                  match.time.completed = event.data.timestamp
+                  match.state = {
+                    status: "error",
+                    error: event.data.error,
+                    input: match.state.input,
+                    structured: match.state.structured,
+                    content: match.state.content,
+                  }
+                }
+              }),
+            )
+          }
+        })
+      },
+      "session.next.reasoning.started": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                draft.content.push(
+                  new SessionMessage.AssistantReasoning({
+                    type: "reasoning",
+                    id: event.data.reasoningID,
+                    text: "",
+                  }) as DraftReasoning,
+                )
+              }),
+            )
+          }
+        })
+      },
+      "session.next.reasoning.delta": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestReasoning(draft, event.data.reasoningID)
+                if (match) match.text += event.data.delta
+              }),
+            )
+          }
+        })
+      },
+      "session.next.reasoning.ended": (event) => {
+        return Effect.gen(function* () {
+          const currentAssistant = yield* adapter.getCurrentAssistant()
+          if (currentAssistant) {
+            yield* adapter.updateAssistant(
+              produce(currentAssistant, (draft) => {
+                const match = latestReasoning(draft, event.data.reasoningID)
+                if (match) match.text = event.data.text
+              }),
+            )
+          }
+        })
+      },
+      "session.next.retried": () => Effect.void,
+      "session.next.compaction.started": (event) => {
+        return adapter.appendMessage(
+          new SessionMessage.Compaction({
+            id: event.id,
+            type: "compaction",
+            metadata: event.metadata,
+            reason: event.data.reason,
+            summary: "",
+            time: { created: event.data.timestamp },
           }),
         )
-      }
-      })
-    },
-    "session.next.reasoning.delta": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestReasoning(draft, event.data.reasoningID)
-            if (match) match.text += event.data.delta
-          }),
-        )
-      }
-      })
-    },
-    "session.next.reasoning.ended": (event) => {
-      return Effect.gen(function* () {
-      const currentAssistant = yield* adapter.getCurrentAssistant()
-      if (currentAssistant) {
-        yield* adapter.updateAssistant(
-          produce(currentAssistant, (draft) => {
-            const match = latestReasoning(draft, event.data.reasoningID)
-            if (match) match.text = event.data.text
-          }),
-        )
-      }
-      })
-    },
-    "session.next.retried": () => Effect.void,
-    "session.next.compaction.started": (event) => {
-      return adapter.appendMessage(
-        new SessionMessage.Compaction({
-          id: event.id,
-          type: "compaction",
-          metadata: event.metadata,
-          reason: event.data.reason,
-          summary: "",
-          time: { created: event.data.timestamp },
-        }),
-      )
-    },
-    "session.next.compaction.delta": (event) => {
-      return Effect.gen(function* () {
-      const currentCompaction = yield* adapter.getCurrentCompaction()
-      if (currentCompaction) {
-        yield* adapter.updateCompaction(
-          produce(currentCompaction, (draft) => {
-            draft.summary += event.data.text
-          }),
-        )
-      }
-      })
-    },
-    "session.next.compaction.ended": (event) => {
-      return Effect.gen(function* () {
-      const currentCompaction = yield* adapter.getCurrentCompaction()
-      if (currentCompaction) {
-        yield* adapter.updateCompaction(
-          produce(currentCompaction, (draft) => {
-            draft.summary = event.data.text
-            draft.include = event.data.include
-          }),
-        )
-      }
-      })
-    },
-  })
+      },
+      "session.next.compaction.delta": (event) => {
+        return Effect.gen(function* () {
+          const currentCompaction = yield* adapter.getCurrentCompaction()
+          if (currentCompaction) {
+            yield* adapter.updateCompaction(
+              produce(currentCompaction, (draft) => {
+                draft.summary += event.data.text
+              }),
+            )
+          }
+        })
+      },
+      "session.next.compaction.ended": (event) => {
+        return Effect.gen(function* () {
+          const currentCompaction = yield* adapter.getCurrentCompaction()
+          if (currentCompaction) {
+            yield* adapter.updateCompaction(
+              produce(currentCompaction, (draft) => {
+                draft.summary = event.data.text
+                draft.include = event.data.include
+              }),
+            )
+          }
+        })
+      },
+    })
   })
 }
 

+ 1 - 4
packages/core/test/catalog.test.ts

@@ -16,10 +16,7 @@ const locationLayer = Layer.succeed(
   Location.Service.of(location({ directory: AbsolutePath.make("test") })),
 )
 const it = testEffect(
-  Catalog.locationLayer.pipe(
-    Layer.provideMerge(EventV2.defaultLayer),
-    Layer.provideMerge(locationLayer),
-  ),
+  Catalog.locationLayer.pipe(Layer.provideMerge(EventV2.defaultLayer), Layer.provideMerge(locationLayer)),
 )
 
 describe("CatalogV2", () => {

+ 12 - 4
packages/core/test/database-migration.test.ts

@@ -10,14 +10,18 @@ import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510
 import type { SqlClient as SqlClientService } from "effect/unstable/sql/SqlClient"
 
 const run = <A, E>(effect: Effect.Effect<A, E, SqlClientService>) =>
-  Effect.runPromise(effect.pipe(Effect.provide(SqliteClient.layer({ filename: ":memory:", disableWAL: true })), Effect.scoped))
+  Effect.runPromise(
+    effect.pipe(Effect.provide(SqliteClient.layer({ filename: ":memory:", disableWAL: true })), Effect.scoped),
+  )
 
 const makeDb = EffectDrizzleSqlite.makeWithDefaults()
 
 describe("DatabaseMigration", () => {
   if (process.platform === "linux") {
     test("declared schema has no ungenerated migrations", async () => {
-      const result = await $`bun ${fileURLToPath(new URL("../script/migration.ts", import.meta.url))} --check`.quiet().nothrow()
+      const result = await $`bun ${fileURLToPath(new URL("../script/migration.ts", import.meta.url))} --check`
+        .quiet()
+        .nothrow()
       expect(result.exitCode, result.stderr.toString()).toBe(0)
       expect(result.stdout.toString()).toContain("No schema changes, nothing to migrate")
     }, 30_000)
@@ -70,7 +74,9 @@ describe("DatabaseMigration", () => {
     await run(
       Effect.gen(function* () {
         const db = yield* makeDb
-        yield* db.run(sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`)
+        yield* db.run(
+          sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`,
+        )
         yield* db.run(sql`
           INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at)
           VALUES ('hash', 1, '20260127222353_familiar_lady_ursula', ${new Date().toISOString()})
@@ -89,7 +95,9 @@ describe("DatabaseMigration", () => {
         const db = yield* makeDb
         yield* db.run(sql`CREATE TABLE migration (id TEXT PRIMARY KEY, time_completed INTEGER NOT NULL)`)
         yield* db.run(sql`INSERT INTO migration (id, time_completed) VALUES ('existing', 1)`)
-        yield* db.run(sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`)
+        yield* db.run(
+          sql`CREATE TABLE __drizzle_migrations (id INTEGER PRIMARY KEY, hash text NOT NULL, created_at numeric, name text, applied_at TEXT)`,
+        )
         yield* db.run(sql`
           INSERT INTO __drizzle_migrations (hash, created_at, name, applied_at)
           VALUES ('hash', 1, '20260127222353_familiar_lady_ursula', ${new Date().toISOString()})

+ 36 - 6
packages/core/test/event.test.ts

@@ -211,7 +211,12 @@ describe("EventV2", () => {
       const aggregateID = EventV2.ID.create()
 
       yield* events.publish(SyncMessage, { id: aggregateID, text: "first" })
-      const rows = yield* db.select().from(EventTable).where(eq(EventTable.aggregate_id, aggregateID)).all().pipe(Effect.orDie)
+      const rows = yield* db
+        .select()
+        .from(EventTable)
+        .where(eq(EventTable.aggregate_id, aggregateID))
+        .all()
+        .pipe(Effect.orDie)
 
       expect(rows).toHaveLength(1)
       expect(rows[0]?.type).toBe(EventV2.versionedType(SyncMessage.type, 1))
@@ -227,7 +232,12 @@ describe("EventV2", () => {
 
       yield* events.publish(SyncMessage, { id: aggregateID, text: "first" })
       yield* events.publish(SyncMessage, { id: aggregateID, text: "second" })
-      const rows = yield* db.select().from(EventTable).where(eq(EventTable.aggregate_id, aggregateID)).all().pipe(Effect.orDie)
+      const rows = yield* db
+        .select()
+        .from(EventTable)
+        .where(eq(EventTable.aggregate_id, aggregateID))
+        .all()
+        .pipe(Effect.orDie)
 
       expect(rows.map((row) => row.seq)).toEqual([0, 1])
     }),
@@ -240,7 +250,12 @@ describe("EventV2", () => {
       const aggregateID = EventV2.ID.create()
 
       yield* events.publish(SyncSent, { messageID: aggregateID, text: "sent" })
-      const rows = yield* db.select().from(EventTable).where(eq(EventTable.aggregate_id, aggregateID)).all().pipe(Effect.orDie)
+      const rows = yield* db
+        .select()
+        .from(EventTable)
+        .where(eq(EventTable.aggregate_id, aggregateID))
+        .all()
+        .pipe(Effect.orDie)
 
       expect(rows).toHaveLength(1)
       expect(rows[0]?.aggregate_id).toBe(aggregateID)
@@ -284,7 +299,12 @@ describe("EventV2", () => {
         aggregateID,
         data: { id: aggregateID, text: "replayed" },
       })
-      const rows = yield* db.select().from(EventTable).where(eq(EventTable.aggregate_id, aggregateID)).all().pipe(Effect.orDie)
+      const rows = yield* db
+        .select()
+        .from(EventTable)
+        .where(eq(EventTable.aggregate_id, aggregateID))
+        .all()
+        .pipe(Effect.orDie)
 
       expect(rows).toHaveLength(1)
       expect(rows[0]?.aggregate_id).toBe(aggregateID)
@@ -397,7 +417,12 @@ describe("EventV2", () => {
           data: { id: aggregateID, text: "four" },
         },
       ])
-      const rows = yield* db.select().from(EventTable).where(eq(EventTable.aggregate_id, aggregateID)).all().pipe(Effect.orDie)
+      const rows = yield* db
+        .select()
+        .from(EventTable)
+        .where(eq(EventTable.aggregate_id, aggregateID))
+        .all()
+        .pipe(Effect.orDie)
 
       expect(one).toBe(aggregateID)
       expect(two).toBe(aggregateID)
@@ -486,7 +511,12 @@ describe("EventV2", () => {
         },
         { ownerID: "owner-2" },
       )
-      const rows = yield* db.select().from(EventTable).where(eq(EventTable.aggregate_id, aggregateID)).all().pipe(Effect.orDie)
+      const rows = yield* db
+        .select()
+        .from(EventTable)
+        .where(eq(EventTable.aggregate_id, aggregateID))
+        .all()
+        .pipe(Effect.orDie)
       const sequence = yield* db
         .select({ seq: EventSequenceTable.seq, ownerID: EventSequenceTable.owner_id })
         .from(EventSequenceTable)

+ 3 - 1
packages/effect-sqlite-node/src/index.ts

@@ -162,5 +162,7 @@ export const make = (
 
 export const layer = (config: SqliteClientConfig): Layer.Layer<SqliteClient | Client.SqlClient> =>
   Layer.effectContext(
-    Effect.map(make(config), (client) => Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client))),
+    Effect.map(make(config), (client) =>
+      Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),
+    ),
   ).pipe(Layer.provide(Reactivity.layer))

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

@@ -48,17 +48,9 @@ export const layer = Layer.effect(
       effect.pipe(Effect.mapError((cause) => new AccountRepoError({ message: "Database operation failed", cause })))
 
     const current = Effect.fnUntraced(function* () {
-      const state = yield* db
-        .select()
-        .from(AccountStateTable)
-        .where(eq(AccountStateTable.id, ACCOUNT_STATE_ID))
-        .get()
+      const state = yield* db.select().from(AccountStateTable).where(eq(AccountStateTable.id, ACCOUNT_STATE_ID)).get()
       if (!state?.active_account_id) return
-      const account = yield* db
-        .select()
-        .from(AccountTable)
-        .where(eq(AccountTable.id, state.active_account_id))
-        .get()
+      const account = yield* db.select().from(AccountTable).where(eq(AccountTable.id, state.active_account_id)).get()
       if (!account) return
       return { ...account, active_org_id: state.active_org_id ?? null }
     })

+ 6 - 1
packages/opencode/src/permission/index.ts

@@ -148,7 +148,12 @@ export const layer = Layer.effect(
     const { db } = yield* Database.Service
     const state = yield* InstanceState.make<State>(
       Effect.fn("Permission.state")(function* (ctx) {
-        const row = yield* db.select().from(PermissionTable).where(eq(PermissionTable.project_id, ctx.project.id)).get().pipe(Effect.orDie)
+        const row = yield* db
+          .select()
+          .from(PermissionTable)
+          .where(eq(PermissionTable.project_id, ctx.project.id))
+          .get()
+          .pipe(Effect.orDie)
         const state = {
           pending: new Map<PermissionID, PendingEntry>(),
           approved: [...(row?.data ?? [])],

+ 106 - 84
packages/opencode/src/project/project.ts

@@ -190,41 +190,57 @@ export const layer = Layer.effect(
             Effect.gen(function* () {
               const oldProject = yield* d.select().from(ProjectTable).where(eq(ProjectTable.id, oldID)).get()
               const newProject = yield* d.select().from(ProjectTable).where(eq(ProjectTable.id, newID)).get()
-            if (oldProject && !newProject) {
-              yield* d
-                .insert(ProjectTable)
-                .values({
-                  ...oldProject,
-                  id: newID,
-                  time_updated: Date.now(),
-                })
-                .run()
-            }
-
-              const oldPermission = yield* d.select().from(PermissionTable).where(eq(PermissionTable.project_id, oldID)).get()
-              const newPermission = yield* d.select().from(PermissionTable).where(eq(PermissionTable.project_id, newID)).get()
-            if (oldPermission && newPermission) {
-              yield* d
-                .update(PermissionTable)
-                .set({
-                  data: mergePermissionRules(oldPermission.data, newPermission.data),
-                  time_created: Math.min(oldPermission.time_created, newPermission.time_created),
-                  time_updated: Date.now(),
-                })
+              if (oldProject && !newProject) {
+                yield* d
+                  .insert(ProjectTable)
+                  .values({
+                    ...oldProject,
+                    id: newID,
+                    time_updated: Date.now(),
+                  })
+                  .run()
+              }
+
+              const oldPermission = yield* d
+                .select()
+                .from(PermissionTable)
+                .where(eq(PermissionTable.project_id, oldID))
+                .get()
+              const newPermission = yield* d
+                .select()
+                .from(PermissionTable)
                 .where(eq(PermissionTable.project_id, newID))
-                .run()
+                .get()
+              if (oldPermission && newPermission) {
+                yield* d
+                  .update(PermissionTable)
+                  .set({
+                    data: mergePermissionRules(oldPermission.data, newPermission.data),
+                    time_created: Math.min(oldPermission.time_created, newPermission.time_created),
+                    time_updated: Date.now(),
+                  })
+                  .where(eq(PermissionTable.project_id, newID))
+                  .run()
                 yield* d.delete(PermissionTable).where(eq(PermissionTable.project_id, oldID)).run()
-            }
-            if (oldPermission && !newPermission) {
-                yield* d.update(PermissionTable).set({ project_id: newID }).where(eq(PermissionTable.project_id, oldID)).run()
-            }
+              }
+              if (oldPermission && !newPermission) {
+                yield* d
+                  .update(PermissionTable)
+                  .set({ project_id: newID })
+                  .where(eq(PermissionTable.project_id, oldID))
+                  .run()
+              }
 
               yield* d
                 .update(SessionTable)
-              .set({ project_id: newID, time_updated: sql`${SessionTable.time_updated}` })
-              .where(eq(SessionTable.project_id, oldID))
-              .run()
-              yield* d.update(WorkspaceTable).set({ project_id: newID }).where(eq(WorkspaceTable.project_id, oldID)).run()
+                .set({ project_id: newID, time_updated: sql`${SessionTable.time_updated}` })
+                .where(eq(SessionTable.project_id, oldID))
+                .run()
+              yield* d
+                .update(WorkspaceTable)
+                .set({ project_id: newID })
+                .where(eq(WorkspaceTable.project_id, oldID))
+                .run()
 
               if (oldProject) yield* d.delete(ProjectTable).where(eq(ProjectTable.id, oldID)).run()
             }),
@@ -278,46 +294,46 @@ export const layer = Layer.effect(
       ).pipe(Effect.map((arr) => arr.filter((x): x is string => x !== undefined)))
 
       yield* db
-          .insert(ProjectTable)
-          .values({
-            id: result.id,
+        .insert(ProjectTable)
+        .values({
+          id: result.id,
+          worktree: result.worktree,
+          vcs: result.vcs ?? null,
+          name: result.name,
+          icon_url: result.icon?.url,
+          icon_url_override: result.icon?.override,
+          icon_color: result.icon?.color,
+          time_created: result.time.created,
+          time_updated: result.time.updated,
+          time_initialized: result.time.initialized,
+          sandboxes: result.sandboxes,
+          commands: result.commands,
+        })
+        .onConflictDoUpdate({
+          target: ProjectTable.id,
+          set: {
             worktree: result.worktree,
             vcs: result.vcs ?? null,
             name: result.name,
             icon_url: result.icon?.url,
             icon_url_override: result.icon?.override,
             icon_color: result.icon?.color,
-            time_created: result.time.created,
             time_updated: result.time.updated,
             time_initialized: result.time.initialized,
             sandboxes: result.sandboxes,
             commands: result.commands,
-          })
-          .onConflictDoUpdate({
-            target: ProjectTable.id,
-            set: {
-              worktree: result.worktree,
-              vcs: result.vcs ?? null,
-              name: result.name,
-              icon_url: result.icon?.url,
-              icon_url_override: result.icon?.override,
-              icon_color: result.icon?.color,
-              time_updated: result.time.updated,
-              time_initialized: result.time.initialized,
-              sandboxes: result.sandboxes,
-              commands: result.commands,
-            },
-          })
-          .run()
-          .pipe(Effect.orDie)
+          },
+        })
+        .run()
+        .pipe(Effect.orDie)
 
       if (projectID !== ProjectV2.ID.global) {
         yield* db
-            .update(SessionTable)
-            .set({ project_id: projectID })
-            .where(and(eq(SessionTable.project_id, ProjectV2.ID.global), eq(SessionTable.directory, data.directory)))
-            .run()
-            .pipe(Effect.orDie)
+          .update(SessionTable)
+          .set({ project_id: projectID })
+          .where(and(eq(SessionTable.project_id, ProjectV2.ID.global), eq(SessionTable.directory, data.directory)))
+          .run()
+          .pipe(Effect.orDie)
       }
 
       yield* emitUpdated(result)
@@ -362,19 +378,19 @@ export const layer = Layer.effect(
 
     const update = Effect.fn("Project.update")(function* (input: UpdateInput) {
       const result = yield* db
-          .update(ProjectTable)
-          .set({
-            name: input.name,
-            icon_url: input.icon?.url,
-            icon_url_override: input.icon?.override,
-            icon_color: input.icon?.color,
-            commands: input.commands,
-            time_updated: Date.now(),
-          })
-          .where(eq(ProjectTable.id, input.projectID))
-          .returning()
-          .get()
-          .pipe(Effect.orDie)
+        .update(ProjectTable)
+        .set({
+          name: input.name,
+          icon_url: input.icon?.url,
+          icon_url_override: input.icon?.override,
+          icon_color: input.icon?.color,
+          commands: input.commands,
+          time_updated: Date.now(),
+        })
+        .where(eq(ProjectTable.id, input.projectID))
+        .returning()
+        .get()
+        .pipe(Effect.orDie)
       if (!result) return yield* new NotFoundError({ projectID: input.projectID })
       const data = fromRow(result)
       yield* emitUpdated(data)
@@ -393,13 +409,19 @@ export const layer = Layer.effect(
     })
 
     const setInitialized = Effect.fn("Project.setInitialized")(function* (id: ProjectV2.ID) {
-      yield* db.update(ProjectTable).set({ time_initialized: Date.now() }).where(eq(ProjectTable.id, id)).run().pipe(Effect.orDie)
+      yield* db
+        .update(ProjectTable)
+        .set({ time_initialized: Date.now() })
+        .where(eq(ProjectTable.id, id))
+        .run()
+        .pipe(Effect.orDie)
     })
 
     const initState = yield* InstanceState.make(
       Effect.fn("Project.initState")(function* (ctx) {
         const unsubscribe = yield* events.listen((event) => {
-          if (event.type !== Command.Event.Executed.type || event.location?.directory !== ctx.directory) return Effect.void
+          if (event.type !== Command.Event.Executed.type || event.location?.directory !== ctx.directory)
+            return Effect.void
           const data = event.data as EventV2.Data<typeof Command.Event.Executed>
           return data.name === Command.Default.INIT ? setInitialized(ctx.project.id) : Effect.void
         })
@@ -432,12 +454,12 @@ export const layer = Layer.effect(
       const sboxes = [...row.sandboxes]
       if (!sboxes.includes(directory)) sboxes.push(directory)
       const result = yield* db
-          .update(ProjectTable)
-          .set({ sandboxes: sboxes, time_updated: Date.now() })
-          .where(eq(ProjectTable.id, id))
-          .returning()
-          .get()
-          .pipe(Effect.orDie)
+        .update(ProjectTable)
+        .set({ sandboxes: sboxes, time_updated: Date.now() })
+        .where(eq(ProjectTable.id, id))
+        .returning()
+        .get()
+        .pipe(Effect.orDie)
       if (!result) throw new Error(`Project not found: ${id}`)
       yield* emitUpdated(fromRow(result))
     })
@@ -447,12 +469,12 @@ export const layer = Layer.effect(
       if (!row) throw new Error(`Project not found: ${id}`)
       const sboxes = row.sandboxes.filter((s) => s !== directory)
       const result = yield* db
-          .update(ProjectTable)
-          .set({ sandboxes: sboxes, time_updated: Date.now() })
-          .where(eq(ProjectTable.id, id))
-          .returning()
-          .get()
-          .pipe(Effect.orDie)
+        .update(ProjectTable)
+        .set({ sandboxes: sboxes, time_updated: Date.now() })
+        .where(eq(ProjectTable.id, id))
+        .returning()
+        .get()
+        .pipe(Effect.orDie)
       if (!result) throw new Error(`Project not found: ${id}`)
       yield* emitUpdated(fromRow(result))
     })

+ 3 - 5
packages/opencode/src/project/vcs.ts

@@ -328,7 +328,8 @@ export const layer: Layer.Layer<Service, never, Git.Service | EventV2Bridge.Serv
         log.info("initialized", { branch: value.current, default_branch: value.root?.name })
 
         const unsubscribe = yield* events.listen((event) => {
-          if (event.type !== FileWatcher.Event.Updated.type || event.location?.directory !== ctx.directory) return Effect.void
+          if (event.type !== FileWatcher.Event.Updated.type || event.location?.directory !== ctx.directory)
+            return Effect.void
           const data = event.data as EventV2.Data<typeof FileWatcher.Event.Updated>
           if (!data.file.endsWith("HEAD")) return Effect.void
           return Effect.gen(function* () {
@@ -429,9 +430,6 @@ export const layer: Layer.Layer<Service, never, Git.Service | EventV2Bridge.Serv
   }),
 )
 
-export const defaultLayer = layer.pipe(
-  Layer.provide(Git.defaultLayer),
-  Layer.provide(EventV2Bridge.defaultLayer),
-)
+export const defaultLayer = layer.pipe(Layer.provide(Git.defaultLayer), Layer.provide(EventV2Bridge.defaultLayer))
 
 export * as Vcs from "./vcs"

+ 3 - 1
packages/opencode/src/provider/auth.ts

@@ -184,7 +184,9 @@ export const layer: Layer.Layer<Service, never, Auth.Service | Plugin.Service> =
       }
     })
 
-    const callback = Effect.fn("ProviderAuth.callback")(function* (input: { providerID: ProviderV2.ID } & CallbackInput) {
+    const callback = Effect.fn("ProviderAuth.callback")(function* (
+      input: { providerID: ProviderV2.ID } & CallbackInput,
+    ) {
       const pending = (yield* InstanceState.get(state)).pending
       const match = pending.get(input.providerID)
       if (!match) return yield* new OauthMissing({ providerID: input.providerID })

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

@@ -1024,14 +1024,20 @@ export type Error = ModelNotFoundError | InitError | NoProvidersError | NoModels
 export interface Interface {
   readonly list: () => Effect.Effect<Record<ProviderV2.ID, Info>>
   readonly getProvider: (providerID: ProviderV2.ID) => Effect.Effect<Info>
-  readonly getModel: (providerID: ProviderV2.ID, modelID: ProviderV2.ModelID) => Effect.Effect<Model, ModelNotFoundError>
+  readonly getModel: (
+    providerID: ProviderV2.ID,
+    modelID: ProviderV2.ModelID,
+  ) => Effect.Effect<Model, ModelNotFoundError>
   readonly getLanguage: (model: Model) => Effect.Effect<LanguageModelV3, ModelNotFoundError>
   readonly closest: (
     providerID: ProviderV2.ID,
     query: string[],
   ) => Effect.Effect<{ providerID: ProviderV2.ID; modelID: string } | undefined>
   readonly getSmallModel: (providerID: ProviderV2.ID) => Effect.Effect<Model | undefined>
-  readonly defaultModel: () => Effect.Effect<{ providerID: ProviderV2.ID; modelID: ProviderV2.ModelID }, DefaultModelError>
+  readonly defaultModel: () => Effect.Effect<
+    { providerID: ProviderV2.ID; modelID: ProviderV2.ModelID },
+    DefaultModelError
+  >
 }
 
 interface State {

+ 1 - 2
packages/opencode/src/server/projectors.ts

@@ -1,2 +1 @@
-export function initProjectors() {
-}
+export function initProjectors() {}

+ 3 - 1
packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts

@@ -19,7 +19,9 @@ export const controlHandlers = HttpApiBuilder.group(RootHttpApi, "control", (han
       return true
     })
 
-    const authRemove = Effect.fn("ControlHttpApi.authRemove")(function* (ctx: { params: { providerID: ProviderV2.ID } }) {
+    const authRemove = Effect.fn("ControlHttpApi.authRemove")(function* (ctx: {
+      params: { providerID: ProviderV2.ID }
+    }) {
       yield* auth.remove(ctx.params.providerID).pipe(Effect.orDie)
       return true
     })

+ 8 - 2
packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts

@@ -43,7 +43,10 @@ function eventResponse(events: EventV2.Interface) {
       Stream.map((event) => ({ id: event.id, type: event.type, properties: event.data })),
     )
     const disposed = Stream.callback<{ id: string; type: string; properties: unknown }>((queue) => {
-      const listener = (event: { directory?: string; payload: { id?: string; type?: string; properties?: unknown } }) => {
+      const listener = (event: {
+        directory?: string
+        payload: { id?: string; type?: string; properties?: unknown }
+      }) => {
         if (event.directory !== instance.directory || event.payload.type !== "server.instance.disposed") return
         Queue.offerUnsafe(queue, {
           id: event.payload.id ?? eventID(),
@@ -56,7 +59,10 @@ function eventResponse(events: EventV2.Interface) {
         () => Effect.sync(() => GlobalBus.off("event", listener)),
       )
     })
-    const output = stream.pipe(Stream.merge(disposed, { haltStrategy: "left" }), Stream.takeUntil((event) => event.type === "server.instance.disposed"))
+    const output = stream.pipe(
+      Stream.merge(disposed, { haltStrategy: "left" }),
+      Stream.takeUntil((event) => event.type === "server.instance.disposed"),
+    )
     const heartbeat = Stream.tick("10 seconds").pipe(
       Stream.drop(1),
       Stream.map(() => ({ id: eventID(), type: "server.heartbeat", properties: {} })),

+ 2 - 1
packages/opencode/src/server/routes/instance/httpapi/handlers/tui.ts

@@ -88,7 +88,8 @@ export const tuiHandlers = HttpApiBuilder.group(InstanceHttpApi, "tui", (handler
         yield* events.publish(TuiEvent.PromptAppend, ctx.payload.properties)
       if (ctx.payload.type === TuiEvent.CommandExecute.type)
         yield* events.publish(TuiEvent.CommandExecute, ctx.payload.properties)
-      if (ctx.payload.type === TuiEvent.ToastShow.type) yield* events.publish(TuiEvent.ToastShow, ctx.payload.properties)
+      if (ctx.payload.type === TuiEvent.ToastShow.type)
+        yield* events.publish(TuiEvent.ToastShow, ctx.payload.properties)
       if (ctx.payload.type === TuiEvent.SessionSelect.type)
         yield* events.publish(TuiEvent.SessionSelect, ctx.payload.properties)
       return true

+ 3 - 1
packages/opencode/src/session/compaction.ts

@@ -353,7 +353,9 @@ export const layer = Layer.effect(
         throw new Error(`Compaction parent must be a user message: ${input.parentID}`)
       }
       const userMessage = parent.info
-      const compactionPart = parent.parts.find((part): part is SessionLegacy.CompactionPart => part.type === "compaction")
+      const compactionPart = parent.parts.find(
+        (part): part is SessionLegacy.CompactionPart => part.type === "compaction",
+      )
 
       let messages = input.messages
       let replay:

+ 9 - 7
packages/opencode/src/session/llm.ts

@@ -168,13 +168,15 @@ const live: Layer.Layer<
           const id = PermissionID.ascending()
           let unsub: EventV2.Unsubscribe | undefined
           try {
-            unsub = await bridge.promise(events.listen((event) => {
-              if (event.type !== Permission.Event.Replied.type) return Effect.void
-              const data = event.data as EventV2.Data<typeof Permission.Event.Replied>
-              if (data.requestID !== id) return Effect.void
-              void data.reply
-              return Effect.void
-            }))
+            unsub = await bridge.promise(
+              events.listen((event) => {
+                if (event.type !== Permission.Event.Replied.type) return Effect.void
+                const data = event.data as EventV2.Data<typeof Permission.Event.Replied>
+                if (data.requestID !== id) return Effect.void
+                void data.reply
+                return Effect.void
+              }),
+            )
             const toolPatterns = approvalTools.map((t: { name: string; args: string }) => {
               try {
                 const parsed = JSON.parse(t.args) as Record<string, unknown>

+ 8 - 8
packages/opencode/src/session/message-v2.ts

@@ -503,14 +503,14 @@ export function stream(sessionID: SessionID) {
 export function parts(messageID: MessageID) {
   return Effect.gen(function* () {
     const { db } = yield* Database.Service
-      const rows = yield* db
-        .select()
-        .from(PartTable)
-        .where(eq(PartTable.message_id, messageID))
-        .orderBy(PartTable.id)
-        .all()
-        .pipe(Effect.orDie)
-      return rows.map(part)
+    const rows = yield* db
+      .select()
+      .from(PartTable)
+      .where(eq(PartTable.message_id, messageID))
+      .orderBy(PartTable.id)
+      .all()
+      .pipe(Effect.orDie)
+    return rows.map(part)
   })
 }
 

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

@@ -1502,11 +1502,11 @@ export const layer = Layer.effect(
       },
     )
 
-    const loop: (input: LoopInput) => Effect.Effect<SessionLegacy.WithParts> = Effect.fn("SessionPrompt.loop")(function* (
-      input: LoopInput,
-    ) {
-      return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID))
-    })
+    const loop: (input: LoopInput) => Effect.Effect<SessionLegacy.WithParts> = Effect.fn("SessionPrompt.loop")(
+      function* (input: LoopInput) {
+        return yield* state.ensureRunning(input.sessionID, lastAssistant(input.sessionID), runLoop(input.sessionID))
+      },
+    )
 
     const shell: (input: ShellInput) => Effect.Effect<SessionLegacy.WithParts, Session.BusyError> = Effect.fn(
       "SessionPrompt.shell",

+ 1 - 5
packages/opencode/src/session/session.ts

@@ -536,11 +536,7 @@ export type Patch = Omit<Partial<Info>, "time" | "share" | "summary" | "revert"
 export const layer: Layer.Layer<
   Service,
   never,
-  | BackgroundJob.Service
-  | Storage.Service
-  | RuntimeFlags.Service
-  | Database.Service
-  | EventV2Bridge.Service
+  BackgroundJob.Service | Storage.Service | RuntimeFlags.Service | Database.Service | EventV2Bridge.Service
 > = Layer.effect(
   Service,
   Effect.gen(function* () {

+ 3 - 1
packages/opencode/src/session/summary.ts

@@ -79,7 +79,9 @@ export const layer = Layer.effect(
     const storage = yield* Storage.Service
     const events = yield* EventV2Bridge.Service
 
-    const computeDiff = Effect.fn("SessionSummary.computeDiff")(function* (input: { messages: SessionLegacy.WithParts[] }) {
+    const computeDiff = Effect.fn("SessionSummary.computeDiff")(function* (input: {
+      messages: SessionLegacy.WithParts[]
+    }) {
       let from: string | undefined
       let to: string | undefined
       for (const item of input.messages) {

+ 3 - 1
packages/opencode/src/share/share-next.ts

@@ -173,7 +173,9 @@ export const layer = Layer.effect(
           events.listen((event) => {
             if (event.type !== def.type || event.location?.directory !== _ctx.directory) return Effect.void
             return fn(event.data as EventV2.Data<D>).pipe(
-              Effect.catchCause((cause) => Effect.sync(() => log.error("share subscriber failed", { type: def.type, cause }))),
+              Effect.catchCause((cause) =>
+                Effect.sync(() => log.error("share subscriber failed", { type: def.type, cause })),
+              ),
             )
           })
 

+ 5 - 1
packages/opencode/src/skill/index.ts

@@ -232,7 +232,11 @@ const discoverSkills = Effect.fnUntraced(function* (
   }
 })
 
-const loadSkills = Effect.fnUntraced(function* (state: State, discovered: DiscoveryState, events: EventV2Bridge.Service["Service"]) {
+const loadSkills = Effect.fnUntraced(function* (
+  state: State,
+  discovered: DiscoveryState,
+  events: EventV2Bridge.Service["Service"],
+) {
   yield* Effect.forEach(discovered.matches, (match) => add(state, match, events), {
     concurrency: "unbounded",
     discard: true,

+ 5 - 1
packages/opencode/src/tool/registry.ts

@@ -76,7 +76,11 @@ export interface Interface {
   readonly ids: () => Effect.Effect<string[]>
   readonly all: () => Effect.Effect<Tool.Def[]>
   readonly named: () => Effect.Effect<{ task: TaskDef; read: ReadDef }>
-  readonly tools: (model: { providerID: ProviderV2.ID; modelID: ProviderV2.ModelID; agent: Agent.Info }) => Effect.Effect<Tool.Def[]>
+  readonly tools: (model: {
+    providerID: ProviderV2.ID
+    modelID: ProviderV2.ModelID
+    agent: Agent.Info
+  }) => Effect.Effect<Tool.Def[]>
 }
 
 export class Service extends Context.Service<Service, Interface>()("@opencode/ToolRegistry") {}

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

@@ -149,7 +149,13 @@ type GitResult = { code: number; text: string; stderr: string }
 export const layer: Layer.Layer<
   Service,
   never,
-  AppFileSystem.Service | Path.Path | AppProcess.Service | Git.Service | Project.Service | InstanceStore.Service | Database.Service
+  | AppFileSystem.Service
+  | Path.Path
+  | AppProcess.Service
+  | Git.Service
+  | Project.Service
+  | InstanceStore.Service
+  | Database.Service
 > = Layer.effect(
   Service,
   Effect.gen(function* () {
@@ -484,7 +490,12 @@ export const layer: Layer.Layer<
       directory: string,
       input: { projectID: ProjectV2.ID; extra?: string },
     ) {
-      const row = yield* db.select().from(ProjectTable).where(eq(ProjectTable.id, input.projectID)).get().pipe(Effect.orDie)
+      const row = yield* db
+        .select()
+        .from(ProjectTable)
+        .where(eq(ProjectTable.id, input.projectID))
+        .get()
+        .pipe(Effect.orDie)
       const project = row ? Project.fromRow(row) : undefined
       const startup = project?.commands?.start?.trim() ?? ""
       const ok = yield* runStartScript(directory, startup, "project")

+ 5 - 1
packages/opencode/test/cli/github-action.test.ts

@@ -26,7 +26,11 @@ function createReasoningPart(text: string): SessionLegacy.Part {
   }
 }
 
-function createToolPart(tool: string, title: string, status: "completed" | "running" = "completed"): SessionLegacy.Part {
+function createToolPart(
+  tool: string,
+  title: string,
+  status: "completed" | "running" = "completed",
+): SessionLegacy.Part {
   if (status === "completed") {
     return {
       id: PartID.ascending(),

+ 3 - 1
packages/opencode/test/format/format.test.ts

@@ -105,7 +105,9 @@ describe("Format", () => {
     { config: { formatter: false } },
   )
 
-  testEffect(Layer.mergeAll(Format.defaultLayer, CrossSpawnSpawner.defaultLayer, NodeFileSystem.layer, testInstanceStoreLayer)).live("status() initializes formatter state per directory", () =>
+  testEffect(
+    Layer.mergeAll(Format.defaultLayer, CrossSpawnSpawner.defaultLayer, NodeFileSystem.layer, testInstanceStoreLayer),
+  ).live("status() initializes formatter state per directory", () =>
     Effect.gen(function* () {
       const a = yield* provideTmpdirInstance(() => Format.use.status(), {
         config: { formatter: false },

+ 127 - 133
packages/opencode/test/lsp/index.test.ts

@@ -19,53 +19,26 @@ const lspLayer = (flags: Parameters<typeof RuntimeFlags.layer>[0] = {}) =>
 
 const it = testEffect(Layer.mergeAll(lspLayer(), CrossSpawnSpawner.defaultLayer))
 const experimentalTyIt = testEffect(
-  Layer.mergeAll(
-    lspLayer({ experimentalLspTy: true }),
-    CrossSpawnSpawner.defaultLayer,
-  ),
+  Layer.mergeAll(lspLayer({ experimentalLspTy: true }), CrossSpawnSpawner.defaultLayer),
 )
 const fakeServerPath = path.join(__dirname, "../fixture/lsp/fake-lsp-server.js")
 const disabledDownloadIt = testEffect(
-  Layer.mergeAll(
-    lspLayer({ disableLspDownload: true }),
-    CrossSpawnSpawner.defaultLayer,
-  ),
+  Layer.mergeAll(lspLayer({ disableLspDownload: true }), CrossSpawnSpawner.defaultLayer),
 )
 
 describe("lsp.spawn", () => {
   it.instance(
     "does not spawn builtin LSP for files outside instance",
     () =>
-      LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
-
-            try {
-              yield* lsp.touchFile(path.join(dir, "..", "outside.ts"))
-              yield* lsp.hover({
-                file: path.join(dir, "..", "hover.ts"),
-                line: 0,
-                character: 0,
-              })
-              expect(spy).toHaveBeenCalledTimes(0)
-            } finally {
-              spy.mockRestore()
-            }
-          }),
-        ),
-    { config: { lsp: true } },
-  )
-
-  it.instance("does not spawn builtin LSP for files inside instance when LSP is unset", () =>
       LSP.Service.use((lsp) =>
         Effect.gen(function* () {
           const dir = (yield* TestInstance).directory
           const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
 
           try {
+            yield* lsp.touchFile(path.join(dir, "..", "outside.ts"))
             yield* lsp.hover({
-              file: path.join(dir, "src", "inside.ts"),
+              file: path.join(dir, "..", "hover.ts"),
               line: 0,
               character: 0,
             })
@@ -75,28 +48,49 @@ describe("lsp.spawn", () => {
           }
         }),
       ),
+    { config: { lsp: true } },
+  )
+
+  it.instance("does not spawn builtin LSP for files inside instance when LSP is unset", () =>
+    LSP.Service.use((lsp) =>
+      Effect.gen(function* () {
+        const dir = (yield* TestInstance).directory
+        const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
+
+        try {
+          yield* lsp.hover({
+            file: path.join(dir, "src", "inside.ts"),
+            line: 0,
+            character: 0,
+          })
+          expect(spy).toHaveBeenCalledTimes(0)
+        } finally {
+          spy.mockRestore()
+        }
+      }),
+    ),
   )
 
   it.instance(
     "would spawn builtin LSP for files inside instance when lsp is true",
     () =>
       LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
-
-            try {
-              yield* lsp.hover({
-                file: path.join(dir, "src", "inside.ts"),
-                line: 0,
-                character: 0,
-              })
-              expect(spy).toHaveBeenCalledTimes(1)
-            } finally {
-              spy.mockRestore()
-            }
-          }),
-        ),
+        Effect.gen(function* () {
+          const dir = (yield* TestInstance).directory
+          const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
+
+          try {
+            yield* lsp.hover({
+              file: path.join(dir, "src", "inside.ts"),
+              line: 0,
+              character: 0,
+            })
+            expect(spy).toHaveBeenCalledTimes(1)
+          } finally {
+            spy.mockRestore()
+          }
+        }),
+      ),
     { config: { lsp: true } },
   )
 
@@ -104,21 +98,21 @@ describe("lsp.spawn", () => {
     "publishes lsp.updated after custom LSP initialization",
     () =>
       Effect.gen(function* () {
-          const dir = (yield* TestInstance).directory
-          const lsp = yield* LSP.Service
-          const updated = yield* Deferred.make<void>()
-          const events = yield* EventV2Bridge.Service
-          const unsubscribe = yield* events.listen((event) => {
-            if (event.type === LSP.Event.Updated.type) Deferred.doneUnsafe(updated, Effect.void)
-            return Effect.void
-          })
-          yield* Effect.addFinalizer(() => unsubscribe)
-
-          const file = path.join(dir, "sample.repro")
-          yield* Effect.promise(() => Bun.write(file, "sample\n"))
-          yield* lsp.touchFile(file)
-          yield* awaitWithTimeout(Deferred.await(updated), "lsp.updated event was not published")
-        }),
+        const dir = (yield* TestInstance).directory
+        const lsp = yield* LSP.Service
+        const updated = yield* Deferred.make<void>()
+        const events = yield* EventV2Bridge.Service
+        const unsubscribe = yield* events.listen((event) => {
+          if (event.type === LSP.Event.Updated.type) Deferred.doneUnsafe(updated, Effect.void)
+          return Effect.void
+        })
+        yield* Effect.addFinalizer(() => unsubscribe)
+
+        const file = path.join(dir, "sample.repro")
+        yield* Effect.promise(() => Bun.write(file, "sample\n"))
+        yield* lsp.touchFile(file)
+        yield* awaitWithTimeout(Deferred.await(updated), "lsp.updated event was not published")
+      }),
     {
       config: {
         lsp: {
@@ -135,22 +129,22 @@ describe("lsp.spawn", () => {
     "would spawn builtin LSP for files inside instance when config object is provided",
     () =>
       LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
-
-            try {
-              yield* lsp.hover({
-                file: path.join(dir, "src", "inside.ts"),
-                line: 0,
-                character: 0,
-              })
-              expect(spy).toHaveBeenCalledTimes(1)
-            } finally {
-              spy.mockRestore()
-            }
-          }),
-        ),
+        Effect.gen(function* () {
+          const dir = (yield* TestInstance).directory
+          const spy = spyOn(LSPServer.Typescript, "spawn").mockResolvedValue(undefined)
+
+          try {
+            yield* lsp.hover({
+              file: path.join(dir, "src", "inside.ts"),
+              line: 0,
+              character: 0,
+            })
+            expect(spy).toHaveBeenCalledTimes(1)
+          } finally {
+            spy.mockRestore()
+          }
+        }),
+      ),
     {
       config: {
         lsp: {
@@ -164,25 +158,25 @@ describe("lsp.spawn", () => {
     "uses pyright instead of ty by default",
     () =>
       LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined)
-            const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
-
-            try {
-              yield* lsp.hover({
-                file: path.join(dir, "src", "inside.py"),
-                line: 0,
-                character: 0,
-              })
-              expect(ty).toHaveBeenCalledTimes(0)
-              expect(pyright).toHaveBeenCalledTimes(1)
-            } finally {
-              ty.mockRestore()
-              pyright.mockRestore()
-            }
-          }),
-        ),
+        Effect.gen(function* () {
+          const dir = (yield* TestInstance).directory
+          const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined)
+          const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
+
+          try {
+            yield* lsp.hover({
+              file: path.join(dir, "src", "inside.py"),
+              line: 0,
+              character: 0,
+            })
+            expect(ty).toHaveBeenCalledTimes(0)
+            expect(pyright).toHaveBeenCalledTimes(1)
+          } finally {
+            ty.mockRestore()
+            pyright.mockRestore()
+          }
+        }),
+      ),
     { config: { lsp: true } },
   )
 
@@ -190,25 +184,25 @@ describe("lsp.spawn", () => {
     "uses ty instead of pyright when experimentalLspTy is enabled",
     () =>
       LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined)
-            const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
-
-            try {
-              yield* lsp.hover({
-                file: path.join(dir, "src", "inside.py"),
-                line: 0,
-                character: 0,
-              })
-              expect(ty).toHaveBeenCalledTimes(1)
-              expect(pyright).toHaveBeenCalledTimes(0)
-            } finally {
-              ty.mockRestore()
-              pyright.mockRestore()
-            }
-          }),
-        ),
+        Effect.gen(function* () {
+          const dir = (yield* TestInstance).directory
+          const ty = spyOn(LSPServer.Ty, "spawn").mockResolvedValue(undefined)
+          const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
+
+          try {
+            yield* lsp.hover({
+              file: path.join(dir, "src", "inside.py"),
+              line: 0,
+              character: 0,
+            })
+            expect(ty).toHaveBeenCalledTimes(1)
+            expect(pyright).toHaveBeenCalledTimes(0)
+          } finally {
+            ty.mockRestore()
+            pyright.mockRestore()
+          }
+        }),
+      ),
     { config: { lsp: true } },
   )
 
@@ -216,23 +210,23 @@ describe("lsp.spawn", () => {
     "passes disableLspDownload to builtin LSP spawn",
     () =>
       LSP.Service.use((lsp) =>
-          Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
-
-            try {
-              yield* lsp.hover({
-                file: path.join(dir, "src", "inside.py"),
-                line: 0,
-                character: 0,
-              })
-              expect(pyright).toHaveBeenCalledTimes(1)
-              expect(pyright.mock.calls[0]?.[2]).toMatchObject({ disableLspDownload: true })
-            } finally {
-              pyright.mockRestore()
-            }
-          }),
-        ),
+        Effect.gen(function* () {
+          const dir = (yield* TestInstance).directory
+          const pyright = spyOn(LSPServer.Pyright, "spawn").mockResolvedValue(undefined)
+
+          try {
+            yield* lsp.hover({
+              file: path.join(dir, "src", "inside.py"),
+              line: 0,
+              character: 0,
+            })
+            expect(pyright).toHaveBeenCalledTimes(1)
+            expect(pyright.mock.calls[0]?.[2]).toMatchObject({ disableLspDownload: true })
+          } finally {
+            pyright.mockRestore()
+          }
+        }),
+      ),
     { config: { lsp: true } },
   )
 })

+ 6 - 6
packages/opencode/test/lsp/lifecycle.test.ts

@@ -43,12 +43,12 @@ describe("LSP service lifecycle", () => {
   )
 
   it.instance("hasClients() returns false for .ts files in instance when LSP is unset", () =>
-      LSP.Service.use((lsp) =>
-        Effect.gen(function* () {
-          const result = yield* lsp.hasClients(path.join((yield* TestInstance).directory, "test.ts"))
-          expect(result).toBe(false)
-        }),
-      ),
+    LSP.Service.use((lsp) =>
+      Effect.gen(function* () {
+        const result = yield* lsp.hasClients(path.join((yield* TestInstance).directory, "test.ts"))
+        expect(result).toBe(false)
+      }),
+    ),
   )
 
   it.instance(

+ 6 - 2
packages/opencode/test/permission/next.test.ts

@@ -657,7 +657,8 @@ it.instance(
       const events = yield* EventV2Bridge.Service
       const seen = yield* Deferred.make<Permission.Request>()
       const unsub = yield* events.listen((event) => {
-        if (event.type === Permission.Event.Asked.type) Deferred.doneUnsafe(seen, Effect.succeed(event.data as Permission.Request))
+        if (event.type === Permission.Event.Asked.type)
+          Deferred.doneUnsafe(seen, Effect.succeed(event.data as Permission.Request))
         return Effect.void
       })
       yield* Effect.addFinalizer(() => unsub)
@@ -932,7 +933,10 @@ it.instance(
 
       const unsub = yield* events.listen((event) => {
         if (event.type === Permission.Event.Replied.type)
-          Deferred.doneUnsafe(seen, Effect.succeed(event.data as { sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }))
+          Deferred.doneUnsafe(
+            seen,
+            Effect.succeed(event.data as { sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }),
+          )
         return Effect.void
       })
       yield* Effect.addFinalizer(() => unsub)

+ 3 - 1
packages/opencode/test/plugin/loader-shared.test.ts

@@ -20,7 +20,9 @@ afterEach(async () => {
   await disposeAllInstances()
 })
 
-const it = testEffect(Layer.mergeAll(CrossSpawnSpawner.defaultLayer, AppFileSystem.defaultLayer, testInstanceStoreLayer))
+const it = testEffect(
+  Layer.mergeAll(CrossSpawnSpawner.defaultLayer, AppFileSystem.defaultLayer, testInstanceStoreLayer),
+)
 
 function withTmp<T, A, E, R>(
   init: (dir: string) => Promise<T>,

+ 64 - 64
packages/opencode/test/plugin/workspace-adapter.test.ts

@@ -64,74 +64,74 @@ afterEach(async () => {
 describe("plugin.workspace", () => {
   it.instance("plugin can install a workspace adapter", () =>
     Effect.gen(function* () {
-        const dir = (yield* TestInstance).directory
-        const type = `plug-${Math.random().toString(36).slice(2)}`
-        const file = path.join(dir, "plugin.ts")
-        const mark = path.join(dir, "created.json")
-        const space = path.join(dir, "space")
-        yield* Effect.promise(() =>
-          Bun.write(
-            file,
-            [
-              "export default async ({ experimental_workspace }) => {",
-              `  experimental_workspace.register(${JSON.stringify(type)}, {`,
-              '    name: "plug",',
-              '    description: "plugin workspace adapter",',
-              "    configure(input) {",
-              `      return { ...input, name: "plug", branch: "plug/main", directory: ${JSON.stringify(space)} }`,
-              "    },",
-              "    async create(input) {",
-              `      await Bun.write(${JSON.stringify(mark)}, JSON.stringify(input))`,
-              "    },",
-              "    async remove() {},",
-              "    target(input) {",
-              '      return { type: "local", directory: input.directory }',
-              "    },",
-              "  })",
-              "  return {}",
-              "}",
-              "",
-            ].join("\n"),
-          ),
-        )
+      const dir = (yield* TestInstance).directory
+      const type = `plug-${Math.random().toString(36).slice(2)}`
+      const file = path.join(dir, "plugin.ts")
+      const mark = path.join(dir, "created.json")
+      const space = path.join(dir, "space")
+      yield* Effect.promise(() =>
+        Bun.write(
+          file,
+          [
+            "export default async ({ experimental_workspace }) => {",
+            `  experimental_workspace.register(${JSON.stringify(type)}, {`,
+            '    name: "plug",',
+            '    description: "plugin workspace adapter",',
+            "    configure(input) {",
+            `      return { ...input, name: "plug", branch: "plug/main", directory: ${JSON.stringify(space)} }`,
+            "    },",
+            "    async create(input) {",
+            `      await Bun.write(${JSON.stringify(mark)}, JSON.stringify(input))`,
+            "    },",
+            "    async remove() {},",
+            "    target(input) {",
+            '      return { type: "local", directory: input.directory }',
+            "    },",
+            "  })",
+            "  return {}",
+            "}",
+            "",
+          ].join("\n"),
+        ),
+      )
 
-        yield* Effect.promise(() =>
-          Bun.write(
-            path.join(dir, "opencode.json"),
-            JSON.stringify(
-              {
-                $schema: "https://opencode.ai/config.json",
-                plugin: [pathToFileURL(file).href],
-              },
-              null,
-              2,
-            ),
+      yield* Effect.promise(() =>
+        Bun.write(
+          path.join(dir, "opencode.json"),
+          JSON.stringify(
+            {
+              $schema: "https://opencode.ai/config.json",
+              plugin: [pathToFileURL(file).href],
+            },
+            null,
+            2,
           ),
-        )
+        ),
+      )
 
-        const plugin = yield* Plugin.Service
-        yield* plugin.init()
-        const workspace = yield* Workspace.Service
-        const ctx = yield* InstanceState.context
-        const info = yield* workspace.create({
-          type,
-          branch: null,
-          extra: { key: "value" },
-          projectID: ctx.project.id,
-        })
+      const plugin = yield* Plugin.Service
+      yield* plugin.init()
+      const workspace = yield* Workspace.Service
+      const ctx = yield* InstanceState.context
+      const info = yield* workspace.create({
+        type,
+        branch: null,
+        extra: { key: "value" },
+        projectID: ctx.project.id,
+      })
 
-        expect(info.type).toBe(type)
-        expect(info.name).toBe("plug")
-        expect(info.branch).toBe("plug/main")
-        expect(info.directory).toBe(space)
-        expect(info.extra).toEqual({ key: "value" })
-        expect(JSON.parse(yield* Effect.promise(() => Bun.file(mark).text()))).toMatchObject({
-          type,
-          name: "plug",
-          branch: "plug/main",
-          directory: space,
-          extra: { key: "value" },
-        })
+      expect(info.type).toBe(type)
+      expect(info.name).toBe("plug")
+      expect(info.branch).toBe("plug/main")
+      expect(info.directory).toBe(space)
+      expect(info.extra).toEqual({ key: "value" })
+      expect(JSON.parse(yield* Effect.promise(() => Bun.file(mark).text()))).toMatchObject({
+        type,
+        name: "plug",
+        branch: "plug/main",
+        directory: space,
+        extra: { key: "value" },
+      })
     }),
   )
 })

+ 19 - 4
packages/opencode/test/project/project.test.ts

@@ -238,10 +238,25 @@ describe("Project.fromDirectory", () => {
       const result = yield* projects.fromDirectory(tmp)
 
       expect(result.project.id).toBe(remoteID)
-      expect(yield* db.select().from(ProjectTable).where(eq(ProjectTable.id, rootProject.id)).get().pipe(Effect.orDie)).toBeUndefined()
-      expect((yield* db.select().from(SessionTable).where(eq(SessionTable.id, sessionID)).get().pipe(Effect.orDie))?.project_id).toBe(remoteID)
-      expect(yield* db.select().from(PermissionTable).where(eq(PermissionTable.project_id, remoteID)).get().pipe(Effect.orDie)).toBeDefined()
-      expect((yield* db.select().from(WorkspaceTable).where(eq(WorkspaceTable.id, workspaceID)).get().pipe(Effect.orDie))?.project_id).toBe(remoteID)
+      expect(
+        yield* db.select().from(ProjectTable).where(eq(ProjectTable.id, rootProject.id)).get().pipe(Effect.orDie),
+      ).toBeUndefined()
+      expect(
+        (yield* db.select().from(SessionTable).where(eq(SessionTable.id, sessionID)).get().pipe(Effect.orDie))
+          ?.project_id,
+      ).toBe(remoteID)
+      expect(
+        yield* db
+          .select()
+          .from(PermissionTable)
+          .where(eq(PermissionTable.project_id, remoteID))
+          .get()
+          .pipe(Effect.orDie),
+      ).toBeDefined()
+      expect(
+        (yield* db.select().from(WorkspaceTable).where(eq(WorkspaceTable.id, workspaceID)).get().pipe(Effect.orDie))
+          ?.project_id,
+      ).toBe(remoteID)
     }),
   )
 })

+ 7 - 1
packages/opencode/test/project/vcs.test.ts

@@ -5,7 +5,13 @@ import { Deferred, Effect, Layer } from "effect"
 import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
 import fs from "fs/promises"
 import path from "path"
-import { disposeAllInstances, provideInstance, testInstanceStoreLayer, TestInstance, tmpdirScoped } from "../fixture/fixture"
+import {
+  disposeAllInstances,
+  provideInstance,
+  testInstanceStoreLayer,
+  TestInstance,
+  tmpdirScoped,
+} from "../fixture/fixture"
 import { EventV2Bridge } from "../../src/event-v2-bridge"
 import { FileWatcher } from "../../src/file/watcher"
 import { Git } from "../../src/git"

+ 93 - 93
packages/opencode/test/project/worktree-remove.test.ts

@@ -16,70 +16,70 @@ describe("Worktree.remove", () => {
     "continues when git remove exits non-zero after detaching",
     () =>
       Effect.gen(function* () {
-          const root = (yield* TestInstance).directory
-          const svc = yield* Worktree.Service
-          const name = `remove-regression-${Date.now().toString(36)}`
-          const branch = `opencode/${name}`
-          const dir = path.join(root, "..", name)
-
-          yield* Effect.promise(() => $`git worktree add --no-checkout -b ${branch} ${dir}`.cwd(root).quiet())
-          yield* Effect.promise(() => $`git reset --hard`.cwd(dir).quiet())
+        const root = (yield* TestInstance).directory
+        const svc = yield* Worktree.Service
+        const name = `remove-regression-${Date.now().toString(36)}`
+        const branch = `opencode/${name}`
+        const dir = path.join(root, "..", name)
+
+        yield* Effect.promise(() => $`git worktree add --no-checkout -b ${branch} ${dir}`.cwd(root).quiet())
+        yield* Effect.promise(() => $`git reset --hard`.cwd(dir).quiet())
+
+        const real = (yield* Effect.promise(() => $`which git`.quiet().text())).trim()
+        expect(real).toBeTruthy()
+
+        const bin = path.join(root, "bin")
+        const shim = path.join(bin, "git")
+        yield* Effect.promise(() => fs.mkdir(bin, { recursive: true }))
+        yield* Effect.promise(() =>
+          Bun.write(
+            shim,
+            [
+              "#!/bin/bash",
+              `REAL_GIT=${JSON.stringify(real)}`,
+              'if [ "$1" = "worktree" ] && [ "$2" = "remove" ]; then',
+              '  "$REAL_GIT" "$@" >/dev/null 2>&1',
+              '  echo "fatal: failed to remove worktree: Directory not empty" >&2',
+              "  exit 1",
+              "fi",
+              'exec "$REAL_GIT" "$@"',
+            ].join("\n"),
+          ),
+        )
+        yield* Effect.promise(() => fs.chmod(shim, 0o755))
+
+        const prev = yield* Effect.acquireRelease(
+          Effect.sync(() => {
+            const prev = process.env.PATH ?? ""
+            process.env.PATH = `${bin}${path.delimiter}${prev}`
+            return prev
+          }),
+          (prev) =>
+            Effect.sync(() => {
+              process.env.PATH = prev
+            }),
+        )
+        void prev
 
-          const real = (yield* Effect.promise(() => $`which git`.quiet().text())).trim()
-          expect(real).toBeTruthy()
+        const ok = yield* svc.remove({ directory: dir })
 
-          const bin = path.join(root, "bin")
-          const shim = path.join(bin, "git")
-          yield* Effect.promise(() => fs.mkdir(bin, { recursive: true }))
+        expect(ok).toBe(true)
+        expect(
           yield* Effect.promise(() =>
-            Bun.write(
-              shim,
-              [
-                "#!/bin/bash",
-                `REAL_GIT=${JSON.stringify(real)}`,
-                'if [ "$1" = "worktree" ] && [ "$2" = "remove" ]; then',
-                '  "$REAL_GIT" "$@" >/dev/null 2>&1',
-                '  echo "fatal: failed to remove worktree: Directory not empty" >&2',
-                "  exit 1",
-                "fi",
-                'exec "$REAL_GIT" "$@"',
-              ].join("\n"),
-            ),
-          )
-          yield* Effect.promise(() => fs.chmod(shim, 0o755))
-
-          const prev = yield* Effect.acquireRelease(
-            Effect.sync(() => {
-              const prev = process.env.PATH ?? ""
-              process.env.PATH = `${bin}${path.delimiter}${prev}`
-              return prev
-            }),
-            (prev) =>
-              Effect.sync(() => {
-                process.env.PATH = prev
-              }),
-          )
-          void prev
-
-          const ok = yield* svc.remove({ directory: dir })
-
-          expect(ok).toBe(true)
-          expect(
-            yield* Effect.promise(() =>
-              fs
-                .stat(dir)
-                .then(() => true)
-                .catch(() => false),
-            ),
-          ).toBe(false)
-
-          const list = yield* Effect.promise(() => $`git worktree list --porcelain`.cwd(root).quiet().text())
-          expect(list).not.toContain(`worktree ${dir}`)
-
-          const ref = yield* Effect.promise(() =>
-            $`git show-ref --verify --quiet refs/heads/${branch}`.cwd(root).quiet().nothrow(),
-          )
-          expect(ref.exitCode).not.toBe(0)
+            fs
+              .stat(dir)
+              .then(() => true)
+              .catch(() => false),
+          ),
+        ).toBe(false)
+
+        const list = yield* Effect.promise(() => $`git worktree list --porcelain`.cwd(root).quiet().text())
+        expect(list).not.toContain(`worktree ${dir}`)
+
+        const ref = yield* Effect.promise(() =>
+          $`git show-ref --verify --quiet refs/heads/${branch}`.cwd(root).quiet().nothrow(),
+        )
+        expect(ref.exitCode).not.toBe(0)
       }),
     { git: true },
   )
@@ -88,38 +88,38 @@ describe("Worktree.remove", () => {
     "stops fsmonitor before removing a worktree",
     () =>
       Effect.gen(function* () {
-          const root = (yield* TestInstance).directory
-          const svc = yield* Worktree.Service
-          const name = `remove-fsmonitor-${Date.now().toString(36)}`
-          const branch = `opencode/${name}`
-          const dir = path.join(root, "..", name)
-
-          yield* Effect.promise(() => $`git worktree add --no-checkout -b ${branch} ${dir}`.cwd(root).quiet())
-          yield* Effect.promise(() => $`git reset --hard`.cwd(dir).quiet())
-          yield* Effect.promise(() => $`git config core.fsmonitor true`.cwd(dir).quiet())
-          yield* Effect.promise(() => $`git fsmonitor--daemon stop`.cwd(dir).quiet().nothrow())
-          yield* Effect.promise(() => Bun.write(path.join(dir, "tracked.txt"), "next\n"))
-          yield* Effect.promise(() => $`git diff`.cwd(dir).quiet())
-
-          const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(dir).quiet().nothrow())
-          expect(before.exitCode).toBe(0)
-
-          const ok = yield* svc.remove({ directory: dir })
-
-          expect(ok).toBe(true)
-          expect(
-            yield* Effect.promise(() =>
-              fs
-                .stat(dir)
-                .then(() => true)
-                .catch(() => false),
-            ),
-          ).toBe(false)
-
-          const ref = yield* Effect.promise(() =>
-            $`git show-ref --verify --quiet refs/heads/${branch}`.cwd(root).quiet().nothrow(),
-          )
-          expect(ref.exitCode).not.toBe(0)
+        const root = (yield* TestInstance).directory
+        const svc = yield* Worktree.Service
+        const name = `remove-fsmonitor-${Date.now().toString(36)}`
+        const branch = `opencode/${name}`
+        const dir = path.join(root, "..", name)
+
+        yield* Effect.promise(() => $`git worktree add --no-checkout -b ${branch} ${dir}`.cwd(root).quiet())
+        yield* Effect.promise(() => $`git reset --hard`.cwd(dir).quiet())
+        yield* Effect.promise(() => $`git config core.fsmonitor true`.cwd(dir).quiet())
+        yield* Effect.promise(() => $`git fsmonitor--daemon stop`.cwd(dir).quiet().nothrow())
+        yield* Effect.promise(() => Bun.write(path.join(dir, "tracked.txt"), "next\n"))
+        yield* Effect.promise(() => $`git diff`.cwd(dir).quiet())
+
+        const before = yield* Effect.promise(() => $`git fsmonitor--daemon status`.cwd(dir).quiet().nothrow())
+        expect(before.exitCode).toBe(0)
+
+        const ok = yield* svc.remove({ directory: dir })
+
+        expect(ok).toBe(true)
+        expect(
+          yield* Effect.promise(() =>
+            fs
+              .stat(dir)
+              .then(() => true)
+              .catch(() => false),
+          ),
+        ).toBe(false)
+
+        const ref = yield* Effect.promise(() =>
+          $`git show-ref --verify --quiet refs/heads/${branch}`.cwd(root).quiet().nothrow(),
+        )
+        expect(ref.exitCode).not.toBe(0)
       }),
     { git: true },
   )

+ 3 - 1
packages/opencode/test/provider/amazon-bedrock.test.ts

@@ -180,7 +180,9 @@ it.instance(
       yield* set("AWS_PROFILE", "default")
       const providers = yield* list
       expect(providers[ProviderV2.ID.amazonBedrock]).toBeDefined()
-      expect(providers[ProviderV2.ID.amazonBedrock].models["global.anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
+      expect(
+        providers[ProviderV2.ID.amazonBedrock].models["global.anthropic.claude-opus-4-5-20251101-v1:0"],
+      ).toBeDefined()
     }),
   {
     config: {

+ 25 - 9
packages/opencode/test/provider/provider.test.ts

@@ -245,9 +245,9 @@ it.instance(
     expect(provider.models["deepseek-r1"].capabilities.interleaved).toEqual({ field: "reasoning_content" })
     expect(provider.models["deepseek-details"].capabilities.interleaved).toEqual({ field: "reasoning_details" })
     expect(provider.models["custom-model"].capabilities.interleaved).toBe(false)
-    expect(providers[ProviderV2.ID.make("custom-anthropic-provider")].models["deepseek-r1"].capabilities.interleaved).toBe(
-      false,
-    )
+    expect(
+      providers[ProviderV2.ID.make("custom-anthropic-provider")].models["deepseek-r1"].capabilities.interleaved,
+    ).toBe(false)
   }),
   {
     config: {
@@ -305,7 +305,9 @@ it.instance("getModel returns model for valid provider/model", () =>
 it.instance("getModel throws ModelNotFoundError for invalid model", () =>
   Effect.gen(function* () {
     yield* set("ANTHROPIC_API_KEY", "test-api-key")
-    const exit = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ProviderV2.ModelID.make("nonexistent-model")).pipe(Effect.exit)
+    const exit = yield* Provider.use
+      .getModel(ProviderV2.ID.anthropic, ProviderV2.ModelID.make("nonexistent-model"))
+      .pipe(Effect.exit)
     expect(exit._tag).toBe("Failure")
   }),
 )
@@ -977,8 +979,14 @@ it.instance(
 it.instance("getModel returns consistent results", () =>
   Effect.gen(function* () {
     yield* set("ANTHROPIC_API_KEY", "test-api-key")
-    const model1 = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ProviderV2.ModelID.make("claude-sonnet-4-20250514"))
-    const model2 = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ProviderV2.ModelID.make("claude-sonnet-4-20250514"))
+    const model1 = yield* Provider.use.getModel(
+      ProviderV2.ID.anthropic,
+      ProviderV2.ModelID.make("claude-sonnet-4-20250514"),
+    )
+    const model2 = yield* Provider.use.getModel(
+      ProviderV2.ID.anthropic,
+      ProviderV2.ModelID.make("claude-sonnet-4-20250514"),
+    )
     expect(model1.providerID).toEqual(model2.providerID)
     expect(model1.id).toEqual(model2.id)
     expect(model1).toEqual(model2)
@@ -1008,7 +1016,9 @@ it.instance(
 it.instance("ModelNotFoundError includes suggestions for typos", () =>
   Effect.gen(function* () {
     yield* set("ANTHROPIC_API_KEY", "test-api-key")
-    const error = yield* Provider.use.getModel(ProviderV2.ID.anthropic, ProviderV2.ModelID.make("claude-sonet-4")).pipe(Effect.flip)
+    const error = yield* Provider.use
+      .getModel(ProviderV2.ID.anthropic, ProviderV2.ModelID.make("claude-sonet-4"))
+      .pipe(Effect.flip)
     expect(error.suggestions).toBeDefined()
     expect((error.suggestions ?? []).length).toBeGreaterThan(0)
   }),
@@ -1565,7 +1575,10 @@ it.instance("Google Vertex: uses REP endpoint for Claude continental multi-regio
     yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
     yield* set("VERTEX_LOCATION", "eu")
     const provider = yield* Provider.Service
-    const model = yield* provider.getModel(ProviderV2.ID.make("google-vertex"), ProviderV2.ModelID.make("claude-sonnet-4-6@default"))
+    const model = yield* provider.getModel(
+      ProviderV2.ID.make("google-vertex"),
+      ProviderV2.ModelID.make("claude-sonnet-4-6@default"),
+    )
     const language = yield* provider.getLanguage(model)
     expect(languageBaseURL(language)).toBe(
       "https://aiplatform.eu.rep.googleapis.com/v1/projects/test-project/locations/eu/publishers/anthropic/models",
@@ -1594,7 +1607,10 @@ it.instance("Google Vertex: keeps regional Claude endpoints unchanged", () =>
     yield* set("GOOGLE_CLOUD_PROJECT", "test-project")
     yield* set("VERTEX_LOCATION", "europe-west1")
     const provider = yield* Provider.Service
-    const model = yield* provider.getModel(ProviderV2.ID.make("google-vertex"), ProviderV2.ModelID.make("claude-sonnet-4-6@default"))
+    const model = yield* provider.getModel(
+      ProviderV2.ID.make("google-vertex"),
+      ProviderV2.ModelID.make("claude-sonnet-4-6@default"),
+    )
     const language = yield* provider.getLanguage(model)
     expect(languageBaseURL(language)).toBe(
       "https://europe-west1-aiplatform.googleapis.com/v1/projects/test-project/locations/europe-west1/publishers/anthropic/models",

+ 3 - 1
packages/opencode/test/pty/ticket.test.ts

@@ -50,7 +50,9 @@ describe("PTY websocket tickets", () => {
       const workspaceID = WorkspaceV2.ID.ascending()
       const issued = yield* tickets.issue({ ptyID, workspaceID })
 
-      expect(yield* tickets.consume({ ptyID, workspaceID: WorkspaceV2.ID.ascending(), ticket: issued.ticket })).toBe(false)
+      expect(yield* tickets.consume({ ptyID, workspaceID: WorkspaceV2.ID.ascending(), ticket: issued.ticket })).toBe(
+        false,
+      )
       expect(yield* tickets.consume({ ptyID, workspaceID, ticket: issued.ticket })).toBe(true)
     }),
   )

+ 5 - 1
packages/opencode/test/question/question.test.ts

@@ -14,7 +14,11 @@ const it = testEffect(
   Layer.mergeAll(Question.layer.pipe(Layer.provideMerge(EventV2Bridge.defaultLayer)), CrossSpawnSpawner.defaultLayer),
 )
 const lifecycle = testEffect(
-  Layer.mergeAll(Question.layer.pipe(Layer.provideMerge(EventV2Bridge.defaultLayer)), CrossSpawnSpawner.defaultLayer, testInstanceStoreLayer),
+  Layer.mergeAll(
+    Question.layer.pipe(Layer.provideMerge(EventV2Bridge.defaultLayer)),
+    CrossSpawnSpawner.defaultLayer,
+    testInstanceStoreLayer,
+  ),
 )
 
 const askEffect = Effect.fn("QuestionTest.ask")(function* (input: {

+ 0 - 1
packages/opencode/test/server/httpapi-event.test.ts

@@ -94,5 +94,4 @@ describe("event HttpApi", () => {
       }),
     { git: true, config: { formatter: false, lsp: false } },
   )
-
 })

+ 8 - 2
packages/opencode/test/server/httpapi-workspace-routing.test.ts

@@ -170,7 +170,11 @@ const insertRemoteWorkspaceWithoutSync = (input: {
     const id = WorkspaceV2.ID.ascending()
     registerAdapter(input.projectID, input.type, remoteAdapter(path.join(input.dir, `.${input.type}`), input.url))
     const { db } = yield* Database.Service
-    yield* db.insert(WorkspaceTable).values({ id, type: input.type, project_id: input.projectID }).run().pipe(Effect.orDie)
+    yield* db
+      .insert(WorkspaceTable)
+      .values({ id, type: input.type, project_id: input.projectID })
+      .run()
+      .pipe(Effect.orDie)
     return id
   })
 
@@ -331,7 +335,9 @@ describe("HttpApi workspace routing middleware", () => {
       const project = yield* Project.use.fromDirectory(dir)
       const workspaceID = WorkspaceV2.ID.ascending()
       const type = "remote-http-fence-target"
-      const waited = yield* Ref.make<{ workspaceID: WorkspaceV2.ID; state: Record<string, number> } | undefined>(undefined)
+      const waited = yield* Ref.make<{ workspaceID: WorkspaceV2.ID; state: Record<string, number> } | undefined>(
+        undefined,
+      )
 
       const remoteUrl = yield* startRemoteWorkspaceHttpServer(() =>
         HttpServerResponse.json(

+ 12 - 2
packages/opencode/test/server/session-list.test.ts

@@ -154,8 +154,18 @@ describe("session.list", () => {
         )
 
         const { db } = yield* Database.Service
-        yield* db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, current.id)).run().pipe(Effect.orDie)
-        yield* db.update(SessionTable).set({ path: null }).where(eq(SessionTable.id, sibling.id)).run().pipe(Effect.orDie)
+        yield* db
+          .update(SessionTable)
+          .set({ path: null })
+          .where(eq(SessionTable.id, current.id))
+          .run()
+          .pipe(Effect.orDie)
+        yield* db
+          .update(SessionTable)
+          .set({ path: null })
+          .where(eq(SessionTable.id, sibling.id))
+          .run()
+          .pipe(Effect.orDie)
 
         const pathIDs = (yield* SessionNs.Service.use((session) =>
           session.list({

+ 8 - 3
packages/opencode/test/session/compaction.test.ts

@@ -247,7 +247,12 @@ const env = Layer.mergeAll(
 
 const it = testEffect(env)
 
-const compactionEnv = Layer.mergeAll(SessionNs.defaultLayer, Database.defaultLayer, EventV2Bridge.defaultLayer, CrossSpawnSpawner.defaultLayer)
+const compactionEnv = Layer.mergeAll(
+  SessionNs.defaultLayer,
+  Database.defaultLayer,
+  EventV2Bridge.defaultLayer,
+  CrossSpawnSpawner.defaultLayer,
+)
 const itCompaction = testEffect(compactionEnv)
 
 type CompactionProcessOptions = {
@@ -587,7 +592,6 @@ describe("session.compaction.create", () => {
           auto: true,
           overflow: true,
         })
-
       }),
     ),
   )
@@ -852,7 +856,8 @@ describe("session.compaction.process", () => {
       let seen = false
       const unsub = yield* events.listen((evt) => {
         if (evt.type !== SessionCompaction.Event.Compacted.type) return Effect.void
-        if ((evt.data as typeof SessionCompaction.Event.Compacted.data.Type).sessionID !== session.id) return Effect.void
+        if ((evt.data as typeof SessionCompaction.Event.Compacted.data.Type).sessionID !== session.id)
+          return Effect.void
         seen = true
         Deferred.doneUnsafe(done, Effect.void)
         return Effect.void

+ 8 - 2
packages/opencode/test/session/llm.test.ts

@@ -1617,7 +1617,10 @@ describe("session.llm.stream", () => {
         ]
         const request = waitRequest("/messages", createEventResponse(chunks))
 
-        const resolved = yield* Provider.use.getModel(ProviderV2.ID.make("anthropic"), ProviderV2.ModelID.make(model.id))
+        const resolved = yield* Provider.use.getModel(
+          ProviderV2.ID.make("anthropic"),
+          ProviderV2.ModelID.make(model.id),
+        )
         const sessionID = SessionID.make("session-test-anthropic-tools")
         const agent = {
           name: "test",
@@ -1816,7 +1819,10 @@ describe("session.llm.stream", () => {
         ]
         const request = waitRequest(pathSuffix, createEventResponse(chunks))
 
-        const resolved = yield* Provider.use.getModel(ProviderV2.ID.make(geminiFixture.providerID), ProviderV2.ModelID.make(model.id))
+        const resolved = yield* Provider.use.getModel(
+          ProviderV2.ID.make(geminiFixture.providerID),
+          ProviderV2.ModelID.make(model.id),
+        )
         const sessionID = SessionID.make("session-test-4")
         const agent = {
           name: "test",

+ 3 - 1
packages/opencode/test/session/message-v2.test.ts

@@ -991,7 +991,9 @@ describe("session.message-v2.toModelMessage", () => {
     const assistantID1 = "m-assistant-1"
     const assistantID2 = "m-assistant-2"
 
-    const aborted = new SessionLegacy.AbortedError({ message: "aborted" }).toObject() as SessionLegacy.Assistant["error"]
+    const aborted = new SessionLegacy.AbortedError({
+      message: "aborted",
+    }).toObject() as SessionLegacy.Assistant["error"]
 
     const input: SessionLegacy.WithParts[] = [
       {

+ 6 - 1
packages/opencode/test/session/prompt.test.ts

@@ -539,7 +539,12 @@ noLLMServer.instance.skip(
         Effect.provide(SessionV2.defaultLayer),
       )
       const { db } = yield* Database.Service
-      const row = yield* db.select().from(SessionMessageTable).where(eq(SessionMessageTable.session_id, chat.id)).get().pipe(Effect.orDie)
+      const row = yield* db
+        .select()
+        .from(SessionMessageTable)
+        .where(eq(SessionMessageTable.session_id, chat.id))
+        .get()
+        .pipe(Effect.orDie)
       expect(messages.find((message) => message.type === "user")).toMatchObject({ type: "user", text: "hello v2" })
       expect(typeof row?.data.time.created).toBe("number")
       expect(messages).toEqual(

+ 25 - 25
packages/opencode/test/session/retry.test.ts

@@ -87,31 +87,31 @@ describe("session.retry.delay", () => {
 
   it.instance("policy updates retry status and increments attempts", () =>
     Effect.gen(function* () {
-        const sessionID = SessionID.make("session-retry-test")
-        const error = apiError({ "retry-after-ms": "0" })
-        const status = yield* SessionStatus.Service
-
-        const step = yield* Schedule.toStepWithMetadata(
-          SessionRetry.policy({
-            provider: "test",
-            parse: Schema.decodeUnknownSync(SessionLegacy.APIError.Schema),
-            set: (info) =>
-              status.set(sessionID, {
-                type: "retry",
-                attempt: info.attempt,
-                message: info.message,
-                next: info.next,
-              }),
-          }),
-        )
-        yield* step(error)
-        yield* step(error)
-
-        expect(yield* status.get(sessionID)).toMatchObject({
-          type: "retry",
-          attempt: 2,
-          message: "boom",
-        })
+      const sessionID = SessionID.make("session-retry-test")
+      const error = apiError({ "retry-after-ms": "0" })
+      const status = yield* SessionStatus.Service
+
+      const step = yield* Schedule.toStepWithMetadata(
+        SessionRetry.policy({
+          provider: "test",
+          parse: Schema.decodeUnknownSync(SessionLegacy.APIError.Schema),
+          set: (info) =>
+            status.set(sessionID, {
+              type: "retry",
+              attempt: info.attempt,
+              message: info.message,
+              next: info.next,
+            }),
+        }),
+      )
+      yield* step(error)
+      yield* step(error)
+
+      expect(yield* status.get(sessionID)).toMatchObject({
+        type: "retry",
+        attempt: 2,
+        message: "boom",
+      })
     }),
   )
 })

+ 8 - 2
packages/opencode/test/session/session.test.ts

@@ -49,7 +49,10 @@ describe("session.created event", () => {
 
       const unsub = yield* events.listen((event) => {
         if (event.type === SessionNs.Event.Created.type)
-          Deferred.doneUnsafe(received, Effect.succeed((event.data as typeof SessionNs.Event.Created.data.Type).info as SessionNs.Info))
+          Deferred.doneUnsafe(
+            received,
+            Effect.succeed((event.data as typeof SessionNs.Event.Created.data.Type).info as SessionNs.Info),
+          )
         return Effect.void
       })
       yield* Effect.addFinalizer(() => unsub)
@@ -127,7 +130,10 @@ describe("step-finish token propagation via event", () => {
         const received = yield* Deferred.make<SessionLegacy.Part>()
         const unsub = yield* events.listen((event) => {
           if (event.type === MessageV2.Event.PartUpdated.type)
-            Deferred.doneUnsafe(received, Effect.succeed((event.data as typeof MessageV2.Event.PartUpdated.data.Type).part as SessionLegacy.Part))
+            Deferred.doneUnsafe(
+              received,
+              Effect.succeed((event.data as typeof MessageV2.Event.PartUpdated.data.Type).part as SessionLegacy.Part),
+            )
           return Effect.void
         })
         yield* Effect.addFinalizer(() => unsub)

+ 6 - 1
packages/opencode/test/share/share-next.test.ts

@@ -75,7 +75,12 @@ function wired(client: HttpClient.HttpClient) {
 const share = (id: SessionID) =>
   Effect.gen(function* () {
     const { db } = yield* Database.Service
-    return yield* db.select().from(SessionShareTable).where(eq(SessionShareTable.session_id, id)).get().pipe(Effect.orDie)
+    return yield* db
+      .select()
+      .from(SessionShareTable)
+      .where(eq(SessionShareTable.session_id, id))
+      .get()
+      .pipe(Effect.orDie)
   })
 
 const seed = (url: string, org?: string) =>

+ 7 - 1
packages/opencode/test/snapshot/snapshot.test.ts

@@ -6,7 +6,13 @@ import fs from "fs/promises"
 import path from "path"
 import { Effect, Fiber, Layer } from "effect"
 import { Snapshot } from "../../src/snapshot"
-import { disposeAllInstances, provideInstance, testInstanceStoreLayer, TestInstance, tmpdirScoped } from "../fixture/fixture"
+import {
+  disposeAllInstances,
+  provideInstance,
+  testInstanceStoreLayer,
+  TestInstance,
+  tmpdirScoped,
+} from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
 
 const it = testEffect(Layer.mergeAll(Snapshot.defaultLayer, AppFileSystem.defaultLayer, testInstanceStoreLayer))

+ 4 - 1
packages/opencode/test/storage/workspace-time-migration.test.ts

@@ -17,7 +17,10 @@ function migrations() {
     .map((entry) => ({
       name: entry.name,
       timestamp: Number(entry.name.split("_")[0]),
-      sql: readFileSync(path.join(import.meta.dirname, "../../../core/migration", entry.name, "migration.sql"), "utf-8"),
+      sql: readFileSync(
+        path.join(import.meta.dirname, "../../../core/migration", entry.name, "migration.sql"),
+        "utf-8",
+      ),
     }))
     .sort((a, b) => a.timestamp - b.timestamp)
 }

+ 51 - 51
packages/opencode/test/tool/lsp.test.ts

@@ -102,22 +102,22 @@ describe("tool.lsp", () => {
       "keeps cursor details for position-based operations",
       () =>
         Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const file = path.join(dir, "test.ts")
-            yield* put(file)
-
-            const { items, next } = asks()
-            const result = yield* run({ operation: "goToDefinition", filePath: file, line: 3, character: 7 }, next)
-            const req = items.find((item) => item.permission === "lsp")
-
-            expect(req).toBeDefined()
-            expect(req!.metadata).toEqual({
-              operation: "goToDefinition",
-              filePath: file,
-              line: 3,
-              character: 7,
-            })
-            expect(result.title).toBe("goToDefinition test.ts:3:7")
+          const dir = (yield* TestInstance).directory
+          const file = path.join(dir, "test.ts")
+          yield* put(file)
+
+          const { items, next } = asks()
+          const result = yield* run({ operation: "goToDefinition", filePath: file, line: 3, character: 7 }, next)
+          const req = items.find((item) => item.permission === "lsp")
+
+          expect(req).toBeDefined()
+          expect(req!.metadata).toEqual({
+            operation: "goToDefinition",
+            filePath: file,
+            line: 3,
+            character: 7,
+          })
+          expect(result.title).toBe("goToDefinition test.ts:3:7")
         }),
       { git: true },
     )
@@ -126,20 +126,20 @@ describe("tool.lsp", () => {
       "omits cursor details for documentSymbol",
       () =>
         Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            const file = path.join(dir, "test.ts")
-            yield* put(file)
-
-            const { items, next } = asks()
-            const result = yield* run({ operation: "documentSymbol", filePath: file, line: 3, character: 7 }, next)
-            const req = items.find((item) => item.permission === "lsp")
-
-            expect(req).toBeDefined()
-            expect(req!.metadata).toEqual({
-              operation: "documentSymbol",
-              filePath: file,
-            })
-            expect(result.title).toBe("documentSymbol test.ts")
+          const dir = (yield* TestInstance).directory
+          const file = path.join(dir, "test.ts")
+          yield* put(file)
+
+          const { items, next } = asks()
+          const result = yield* run({ operation: "documentSymbol", filePath: file, line: 3, character: 7 }, next)
+          const req = items.find((item) => item.permission === "lsp")
+
+          expect(req).toBeDefined()
+          expect(req!.metadata).toEqual({
+            operation: "documentSymbol",
+            filePath: file,
+          })
+          expect(result.title).toBe("documentSymbol test.ts")
         }),
       { git: true },
     )
@@ -148,20 +148,20 @@ describe("tool.lsp", () => {
       "omits file and cursor details for workspaceSymbol",
       () =>
         Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            workspaceSymbolQueries.length = 0
-            const file = path.join(dir, "test.ts")
-            yield* put(file)
-
-            const { items, next } = asks()
-            const result = yield* run({ operation: "workspaceSymbol", filePath: file, line: 3, character: 7 }, next)
-            const req = items.find((item) => item.permission === "lsp")
-
-            expect(req).toBeDefined()
-            expect(req!.metadata).toEqual({
-              operation: "workspaceSymbol",
-            })
-            expect(result.title).toBe("workspaceSymbol")
+          const dir = (yield* TestInstance).directory
+          workspaceSymbolQueries.length = 0
+          const file = path.join(dir, "test.ts")
+          yield* put(file)
+
+          const { items, next } = asks()
+          const result = yield* run({ operation: "workspaceSymbol", filePath: file, line: 3, character: 7 }, next)
+          const req = items.find((item) => item.permission === "lsp")
+
+          expect(req).toBeDefined()
+          expect(req!.metadata).toEqual({
+            operation: "workspaceSymbol",
+          })
+          expect(result.title).toBe("workspaceSymbol")
         }),
       { git: true },
     )
@@ -170,15 +170,15 @@ describe("tool.lsp", () => {
       "passes workspaceSymbol query to LSP",
       () =>
         Effect.gen(function* () {
-            const dir = (yield* TestInstance).directory
-            workspaceSymbolQueries.length = 0
-            const file = path.join(dir, "test.ts")
-            yield* put(file)
+          const dir = (yield* TestInstance).directory
+          workspaceSymbolQueries.length = 0
+          const file = path.join(dir, "test.ts")
+          yield* put(file)
 
-            yield* run({ operation: "workspaceSymbol", filePath: file, line: 3, character: 7, query: "TestSymbol" })
-            yield* run({ operation: "workspaceSymbol", filePath: file, line: 3, character: 7 })
+          yield* run({ operation: "workspaceSymbol", filePath: file, line: 3, character: 7, query: "TestSymbol" })
+          yield* run({ operation: "workspaceSymbol", filePath: file, line: 3, character: 7 })
 
-            expect(workspaceSymbolQueries).toEqual(["TestSymbol", ""])
+          expect(workspaceSymbolQueries).toEqual(["TestSymbol", ""])
         }),
       { git: true },
     )

+ 7 - 1
packages/opencode/test/tool/read.test.ts

@@ -15,7 +15,13 @@ import { ReadTool } from "../../src/tool/read"
 import { Truncate } from "@/tool/truncate"
 import { Tool } from "@/tool/tool"
 import { Filesystem } from "@/util/filesystem"
-import { disposeAllInstances, provideInstance, testInstanceStoreLayer, TestInstance, tmpdirScoped } from "../fixture/fixture"
+import {
+  disposeAllInstances,
+  provideInstance,
+  testInstanceStoreLayer,
+  TestInstance,
+  tmpdirScoped,
+} from "../fixture/fixture"
 import { testEffect } from "../lib/effect"
 import { Reference } from "@/reference/reference"
 import { RepositoryCache } from "@/reference/repository-cache"

+ 122 - 122
packages/opencode/test/tool/repo_clone.test.ts

@@ -82,153 +82,153 @@ const githubBase = <A, E, R>(url: string, self: Effect.Effect<A, E, R>) =>
 describe("tool.repo_clone", () => {
   it.instance("clones a repo into the managed cache and reuses it on subsequent calls", () =>
     Effect.gen(function* () {
-        const fs = yield* AppFileSystem.Service
-        const source = yield* tmpdirScoped({ git: true })
-        const remoteRoot = yield* tmpdirScoped()
-        const remoteDir = path.join(remoteRoot, "owner")
-        const remoteRepo = path.join(remoteDir, "repo.git")
-
-        yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v1\n"))
-        yield* git(source, ["add", "."])
-        yield* git(source, ["commit", "-m", "add readme"])
-        yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
-        yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
-
-        const tool = yield* init()
-        const cloned = yield* githubBase(`file://${remoteRoot}/`, tool.execute({ repository: "owner/repo" }, ctx))
-        const cached = yield* githubBase(
-          `file://${remoteRoot}/`,
-          tool.execute({ repository: "https://github.com/owner/repo.git" }, ctx),
-        )
-
-        expect(cloned.metadata.status).toBe("cloned")
-        expect(cloned.metadata.localPath).toBe(path.join(Global.Path.repos, "github.com", "owner", "repo"))
-        expect(cached.metadata.status).toBe("cached")
-        expect(yield* fs.readFileString(path.join(cloned.metadata.localPath, "README.md"))).toBe("v1\n")
+      const fs = yield* AppFileSystem.Service
+      const source = yield* tmpdirScoped({ git: true })
+      const remoteRoot = yield* tmpdirScoped()
+      const remoteDir = path.join(remoteRoot, "owner")
+      const remoteRepo = path.join(remoteDir, "repo.git")
+
+      yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v1\n"))
+      yield* git(source, ["add", "."])
+      yield* git(source, ["commit", "-m", "add readme"])
+      yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
+      yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
+
+      const tool = yield* init()
+      const cloned = yield* githubBase(`file://${remoteRoot}/`, tool.execute({ repository: "owner/repo" }, ctx))
+      const cached = yield* githubBase(
+        `file://${remoteRoot}/`,
+        tool.execute({ repository: "https://github.com/owner/repo.git" }, ctx),
+      )
+
+      expect(cloned.metadata.status).toBe("cloned")
+      expect(cloned.metadata.localPath).toBe(path.join(Global.Path.repos, "github.com", "owner", "repo"))
+      expect(cached.metadata.status).toBe("cached")
+      expect(yield* fs.readFileString(path.join(cloned.metadata.localPath, "README.md"))).toBe("v1\n")
     }),
   )
 
   it.instance("refresh updates an existing cached clone", () =>
     Effect.gen(function* () {
-        const fs = yield* AppFileSystem.Service
-        const source = yield* tmpdirScoped({ git: true })
-        const remoteRoot = yield* tmpdirScoped()
-        const remoteDir = path.join(remoteRoot, "owner")
-        const remoteRepo = path.join(remoteDir, "repo.git")
-
-        yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v1\n"))
-        yield* git(source, ["add", "."])
-        yield* git(source, ["commit", "-m", "add readme"])
-        yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
-        yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
-
-        const branch = yield* git(source, ["branch", "--show-current"])
-        yield* git(source, ["remote", "add", "origin", remoteRepo])
-        yield* git(source, ["push", "-u", "origin", `${branch}:${branch}`])
-
-        const tool = yield* init()
-        const first = yield* githubBase(`file://${remoteRoot}/`, tool.execute({ repository: "owner/repo" }, ctx))
-
-        yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v2\n"))
-        yield* git(source, ["add", "."])
-        yield* git(source, ["commit", "-m", "update readme"])
-        yield* git(source, ["push", "origin", `${branch}:${branch}`])
-
-        const refreshed = yield* githubBase(
-          `file://${remoteRoot}/`,
-          tool.execute({ repository: "owner/repo", refresh: true }, ctx),
-        )
-
-        expect(first.metadata.status).toBe("cloned")
-        expect(refreshed.metadata.status).toBe("refreshed")
-        expect(yield* fs.readFileString(path.join(first.metadata.localPath, "README.md"))).toBe("v2\n")
+      const fs = yield* AppFileSystem.Service
+      const source = yield* tmpdirScoped({ git: true })
+      const remoteRoot = yield* tmpdirScoped()
+      const remoteDir = path.join(remoteRoot, "owner")
+      const remoteRepo = path.join(remoteDir, "repo.git")
+
+      yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v1\n"))
+      yield* git(source, ["add", "."])
+      yield* git(source, ["commit", "-m", "add readme"])
+      yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
+      yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
+
+      const branch = yield* git(source, ["branch", "--show-current"])
+      yield* git(source, ["remote", "add", "origin", remoteRepo])
+      yield* git(source, ["push", "-u", "origin", `${branch}:${branch}`])
+
+      const tool = yield* init()
+      const first = yield* githubBase(`file://${remoteRoot}/`, tool.execute({ repository: "owner/repo" }, ctx))
+
+      yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "v2\n"))
+      yield* git(source, ["add", "."])
+      yield* git(source, ["commit", "-m", "update readme"])
+      yield* git(source, ["push", "origin", `${branch}:${branch}`])
+
+      const refreshed = yield* githubBase(
+        `file://${remoteRoot}/`,
+        tool.execute({ repository: "owner/repo", refresh: true }, ctx),
+      )
+
+      expect(first.metadata.status).toBe("cloned")
+      expect(refreshed.metadata.status).toBe("refreshed")
+      expect(yield* fs.readFileString(path.join(first.metadata.localPath, "README.md"))).toBe("v2\n")
     }),
   )
 
   it.instance("clones a configured branch", () =>
     Effect.gen(function* () {
-        const fs = yield* AppFileSystem.Service
-        const source = yield* tmpdirScoped({ git: true })
-        const remoteRoot = yield* tmpdirScoped()
-        const remoteDir = path.join(remoteRoot, "owner")
-        const remoteRepo = path.join(remoteDir, "repo.git")
-
-        yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "main\n"))
-        yield* git(source, ["add", "."])
-        yield* git(source, ["commit", "-m", "add readme"])
-        yield* git(source, ["checkout", "-b", "docs"])
-        yield* Effect.promise(() => Bun.write(path.join(source, "DOCS.md"), "docs\n"))
-        yield* git(source, ["add", "."])
-        yield* git(source, ["commit", "-m", "add docs"])
-        yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
-        yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
-
-        const tool = yield* init()
-        const result = yield* githubBase(
-          `file://${remoteRoot}/`,
-          tool.execute({ repository: "owner/repo", branch: "docs" }, ctx),
-        )
-
-        expect(result.metadata.status).toBe("cloned")
-        expect(result.metadata.branch).toBe("docs")
-        expect(yield* fs.readFileString(path.join(result.metadata.localPath, "DOCS.md"))).toBe("docs\n")
+      const fs = yield* AppFileSystem.Service
+      const source = yield* tmpdirScoped({ git: true })
+      const remoteRoot = yield* tmpdirScoped()
+      const remoteDir = path.join(remoteRoot, "owner")
+      const remoteRepo = path.join(remoteDir, "repo.git")
+
+      yield* Effect.promise(() => Bun.write(path.join(source, "README.md"), "main\n"))
+      yield* git(source, ["add", "."])
+      yield* git(source, ["commit", "-m", "add readme"])
+      yield* git(source, ["checkout", "-b", "docs"])
+      yield* Effect.promise(() => Bun.write(path.join(source, "DOCS.md"), "docs\n"))
+      yield* git(source, ["add", "."])
+      yield* git(source, ["commit", "-m", "add docs"])
+      yield* fs.makeDirectory(remoteDir, { recursive: true }).pipe(Effect.orDie)
+      yield* git(remoteRoot, ["clone", "--bare", source, remoteRepo])
+
+      const tool = yield* init()
+      const result = yield* githubBase(
+        `file://${remoteRoot}/`,
+        tool.execute({ repository: "owner/repo", branch: "docs" }, ctx),
+      )
+
+      expect(result.metadata.status).toBe("cloned")
+      expect(result.metadata.branch).toBe("docs")
+      expect(yield* fs.readFileString(path.join(result.metadata.localPath, "DOCS.md"))).toBe("docs\n")
     }),
   )
 
   it.instance("rejects invalid repository inputs", () =>
     Effect.gen(function* () {
-        const dir = (yield* TestInstance).directory
-        const tool = yield* init()
-        const inputs = [
-          { repository: "not-a-repo", message: "git URL" },
-          { repository: "git@github.com:../../../etc/passwd", message: "git URL" },
-          { repository: "-u:foo/bar", message: "git URL" },
-          { repository: pathToFileURL(path.join(dir, "local.git")).href, message: "Local file" },
-        ]
-
-        yield* Effect.forEach(
-          inputs,
-          (input) =>
-            Effect.gen(function* () {
-              const result = yield* tool.execute({ repository: input.repository }, ctx).pipe(Effect.exit)
-
-              expect(Exit.isFailure(result)).toBe(true)
-              if (Exit.isFailure(result)) {
-                const error = Cause.squash(result.cause)
-                expect(error instanceof Error ? error.message : String(error)).toContain(input.message)
-              }
-            }),
-          { discard: true },
-        )
+      const dir = (yield* TestInstance).directory
+      const tool = yield* init()
+      const inputs = [
+        { repository: "not-a-repo", message: "git URL" },
+        { repository: "git@github.com:../../../etc/passwd", message: "git URL" },
+        { repository: "-u:foo/bar", message: "git URL" },
+        { repository: pathToFileURL(path.join(dir, "local.git")).href, message: "Local file" },
+      ]
+
+      yield* Effect.forEach(
+        inputs,
+        (input) =>
+          Effect.gen(function* () {
+            const result = yield* tool.execute({ repository: input.repository }, ctx).pipe(Effect.exit)
+
+            expect(Exit.isFailure(result)).toBe(true)
+            if (Exit.isFailure(result)) {
+              const error = Cause.squash(result.cause)
+              expect(error instanceof Error ? error.message : String(error)).toContain(input.message)
+            }
+          }),
+        { discard: true },
+      )
     }),
   )
 
   it.instance("rejects local file repository URLs", () =>
     Effect.gen(function* () {
-        const source = yield* tmpdirScoped({ git: true })
-        const tool = yield* init()
-        const result = yield* tool.execute({ repository: pathToFileURL(source).href }, ctx).pipe(Effect.exit)
-
-        expect(Exit.isFailure(result)).toBe(true)
-        if (Exit.isFailure(result)) {
-          const error = Cause.squash(result.cause)
-          expect(error instanceof Error ? error.message : String(error)).toContain("Local file")
-        }
+      const source = yield* tmpdirScoped({ git: true })
+      const tool = yield* init()
+      const result = yield* tool.execute({ repository: pathToFileURL(source).href }, ctx).pipe(Effect.exit)
+
+      expect(Exit.isFailure(result)).toBe(true)
+      if (Exit.isFailure(result)) {
+        const error = Cause.squash(result.cause)
+        expect(error instanceof Error ? error.message : String(error)).toContain("Local file")
+      }
     }),
   )
 
   it.instance("rejects invalid branch inputs", () =>
     Effect.gen(function* () {
-        const tool = yield* init()
-        const result = yield* tool.execute({ repository: "owner/repo", branch: "bad..branch" }, ctx).pipe(Effect.exit)
-
-        expect(Exit.isFailure(result)).toBe(true)
-        if (Exit.isFailure(result)) {
-          const error = Cause.squash(result.cause)
-          expect(error instanceof Error ? error.message : String(error)).toContain(
-            "Branch must contain only alphanumeric characters",
-          )
-        }
+      const tool = yield* init()
+      const result = yield* tool.execute({ repository: "owner/repo", branch: "bad..branch" }, ctx).pipe(Effect.exit)
+
+      expect(Exit.isFailure(result)).toBe(true)
+      if (Exit.isFailure(result)) {
+        const error = Cause.squash(result.cause)
+        expect(error instanceof Error ? error.message : String(error)).toContain(
+          "Branch must contain only alphanumeric characters",
+        )
+      }
     }),
   )
 })

+ 81 - 81
packages/opencode/test/tool/repo_overview.test.ts

@@ -45,112 +45,112 @@ const init = Effect.fn("RepoOverviewToolTest.init")(function* () {
 describe("tool.repo_overview", () => {
   it.instance("summarizes a local repository path", () =>
     Effect.gen(function* () {
-        const repo = yield* tmpdirScoped({ git: true })
-        const fs = yield* AppFileSystem.Service
-        yield* fs.writeWithDirs(
-          path.join(repo, "package.json"),
-          JSON.stringify(
-            {
-              name: "example-repo",
-              main: "dist/index.js",
-              module: "dist/index.mjs",
-              types: "dist/index.d.ts",
-              exports: {
-                ".": "./dist/index.js",
-                "./server": "./dist/server.js",
-              },
-              bin: {
-                example: "./bin/example.js",
-              },
+      const repo = yield* tmpdirScoped({ git: true })
+      const fs = yield* AppFileSystem.Service
+      yield* fs.writeWithDirs(
+        path.join(repo, "package.json"),
+        JSON.stringify(
+          {
+            name: "example-repo",
+            main: "dist/index.js",
+            module: "dist/index.mjs",
+            types: "dist/index.d.ts",
+            exports: {
+              ".": "./dist/index.js",
+              "./server": "./dist/server.js",
             },
-            null,
-            2,
-          ),
-        )
-        yield* fs.writeWithDirs(path.join(repo, "bun.lock"), "")
-        yield* fs.writeWithDirs(path.join(repo, "README.md"), "# Example\n")
-        yield* fs.writeWithDirs(path.join(repo, "src", "index.ts"), "export const value = 1\n")
-
-        const tool = yield* init()
-        const result = yield* tool.execute({ path: repo }, ctx)
-
-        expect(result.metadata.path).toBe(repo)
-        expect(result.metadata.ecosystems).toContain("Node.js")
-        expect(result.metadata.package_manager).toBe("bun")
-        expect(result.metadata.dependency_files).toEqual(expect.arrayContaining(["package.json", "bun.lock"]))
-        expect(result.metadata.entrypoints).toEqual(
-          expect.arrayContaining([
-            "main: dist/index.js",
-            "module: dist/index.mjs",
-            "types: dist/index.d.ts",
-            "exports: .",
-            "exports: ./server",
-            "bin: example",
-            "file: src/index.ts",
-          ]),
-        )
-        expect(result.output).toContain("Top-level structure:")
-        expect(result.output).toContain("src/")
-        expect(result.output).toContain("README.md")
+            bin: {
+              example: "./bin/example.js",
+            },
+          },
+          null,
+          2,
+        ),
+      )
+      yield* fs.writeWithDirs(path.join(repo, "bun.lock"), "")
+      yield* fs.writeWithDirs(path.join(repo, "README.md"), "# Example\n")
+      yield* fs.writeWithDirs(path.join(repo, "src", "index.ts"), "export const value = 1\n")
+
+      const tool = yield* init()
+      const result = yield* tool.execute({ path: repo }, ctx)
+
+      expect(result.metadata.path).toBe(repo)
+      expect(result.metadata.ecosystems).toContain("Node.js")
+      expect(result.metadata.package_manager).toBe("bun")
+      expect(result.metadata.dependency_files).toEqual(expect.arrayContaining(["package.json", "bun.lock"]))
+      expect(result.metadata.entrypoints).toEqual(
+        expect.arrayContaining([
+          "main: dist/index.js",
+          "module: dist/index.mjs",
+          "types: dist/index.d.ts",
+          "exports: .",
+          "exports: ./server",
+          "bin: example",
+          "file: src/index.ts",
+        ]),
+      )
+      expect(result.output).toContain("Top-level structure:")
+      expect(result.output).toContain("src/")
+      expect(result.output).toContain("README.md")
     }),
   )
 
   it.instance("resolves relative paths from the instance directory", () =>
     Effect.gen(function* () {
-        const dir = (yield* TestInstance).directory
-        const fs = yield* AppFileSystem.Service
-        yield* fs.writeWithDirs(path.join(dir, "nested", "README.md"), "# Nested\n")
+      const dir = (yield* TestInstance).directory
+      const fs = yield* AppFileSystem.Service
+      yield* fs.writeWithDirs(path.join(dir, "nested", "README.md"), "# Nested\n")
 
-        const tool = yield* init()
-        const result = yield* tool.execute({ path: "nested" }, ctx)
+      const tool = yield* init()
+      const result = yield* tool.execute({ path: "nested" }, ctx)
 
-        expect(result.metadata.path).toBe(path.join(dir, "nested"))
-        expect(result.output).toContain("README.md")
+      expect(result.metadata.path).toBe(path.join(dir, "nested"))
+      expect(result.output).toContain("README.md")
     }),
   )
 
   it.instance("resolves a cached repository from repository shorthand", () =>
     Effect.gen(function* () {
-        const fs = yield* AppFileSystem.Service
-        const cached = path.join(Global.Path.repos, "github.com", "owner", "repo")
-        yield* fs.writeWithDirs(path.join(cached, "package.json"), JSON.stringify({ name: "cached-repo" }, null, 2))
-        yield* fs.writeWithDirs(path.join(cached, "README.md"), "cached\n")
-
-        const tool = yield* init()
-        const result = yield* tool.execute({ repository: "owner/repo" }, ctx)
-
-        expect(result.metadata.path).toBe(cached)
-        expect(result.metadata.repository).toBe("owner/repo")
-        expect(result.output).toContain("Repository: owner/repo")
-        expect(result.output).toContain(`Path: ${cached}`)
+      const fs = yield* AppFileSystem.Service
+      const cached = path.join(Global.Path.repos, "github.com", "owner", "repo")
+      yield* fs.writeWithDirs(path.join(cached, "package.json"), JSON.stringify({ name: "cached-repo" }, null, 2))
+      yield* fs.writeWithDirs(path.join(cached, "README.md"), "cached\n")
+
+      const tool = yield* init()
+      const result = yield* tool.execute({ repository: "owner/repo" }, ctx)
+
+      expect(result.metadata.path).toBe(cached)
+      expect(result.metadata.repository).toBe("owner/repo")
+      expect(result.output).toContain("Repository: owner/repo")
+      expect(result.output).toContain(`Path: ${cached}`)
     }),
   )
 
   it.instance("fails clearly when a repository is not cloned", () =>
     Effect.gen(function* () {
-        const tool = yield* init()
-        const result = yield* tool.execute({ repository: "missing/repo" }, ctx).pipe(Effect.exit)
-
-        expect(Exit.isFailure(result)).toBe(true)
-        if (Exit.isFailure(result)) {
-          const error = Cause.squash(result.cause)
-          expect(error instanceof Error ? error.message : String(error)).toContain("Use repo_clone first")
-        }
+      const tool = yield* init()
+      const result = yield* tool.execute({ repository: "missing/repo" }, ctx).pipe(Effect.exit)
+
+      expect(Exit.isFailure(result)).toBe(true)
+      if (Exit.isFailure(result)) {
+        const error = Cause.squash(result.cause)
+        expect(error instanceof Error ? error.message : String(error)).toContain("Use repo_clone first")
+      }
     }),
   )
 
   it.instance("resolves cached repositories from host/path references", () =>
     Effect.gen(function* () {
-        const fs = yield* AppFileSystem.Service
-        const cached = path.join(Global.Path.repos, "gitlab.com", "group", "repo")
-        yield* fs.writeWithDirs(path.join(cached, "README.md"), "cached\n")
+      const fs = yield* AppFileSystem.Service
+      const cached = path.join(Global.Path.repos, "gitlab.com", "group", "repo")
+      yield* fs.writeWithDirs(path.join(cached, "README.md"), "cached\n")
 
-        const tool = yield* init()
-        const result = yield* tool.execute({ repository: "gitlab.com/group/repo" }, ctx)
+      const tool = yield* init()
+      const result = yield* tool.execute({ repository: "gitlab.com/group/repo" }, ctx)
 
-        expect(result.metadata.path).toBe(cached)
-        expect(result.metadata.repository).toBe("gitlab.com/group/repo")
-        expect(result.output).toContain("Repository: gitlab.com/group/repo")
+      expect(result.metadata.path).toBe(cached)
+      expect(result.metadata.repository).toBe("gitlab.com/group/repo")
+      expect(result.output).toContain("Repository: gitlab.com/group/repo")
     }),
   )
 })

+ 78 - 78
packages/opencode/test/tool/skill.test.ts

@@ -32,12 +32,12 @@ const it = testEffect(Layer.mergeAll(ToolRegistry.defaultLayer, node))
 describe("tool.skill", () => {
   it.instance("execute returns skill content block with files", () =>
     Effect.gen(function* () {
-        const dir = (yield* TestInstance).directory
-        const skill = path.join(dir, ".opencode", "skill", "tool-skill")
-        yield* Effect.promise(() =>
-          Bun.write(
-            path.join(skill, "SKILL.md"),
-            `---
+      const dir = (yield* TestInstance).directory
+      const skill = path.join(dir, ".opencode", "skill", "tool-skill")
+      yield* Effect.promise(() =>
+        Bun.write(
+          path.join(skill, "SKILL.md"),
+          `---
 name: tool-skill
 description: Skill for tool tests.
 ---
@@ -46,86 +46,86 @@ description: Skill for tool tests.
 
 Use this skill.
 `,
-          ),
-        )
-        yield* Effect.promise(() => Bun.write(path.join(skill, "scripts", "demo.txt"), "demo"))
-
-        const home = process.env.OPENCODE_TEST_HOME
-        process.env.OPENCODE_TEST_HOME = dir
-        yield* Effect.addFinalizer(() =>
+        ),
+      )
+      yield* Effect.promise(() => Bun.write(path.join(skill, "scripts", "demo.txt"), "demo"))
+
+      const home = process.env.OPENCODE_TEST_HOME
+      process.env.OPENCODE_TEST_HOME = dir
+      yield* Effect.addFinalizer(() =>
+        Effect.sync(() => {
+          process.env.OPENCODE_TEST_HOME = home
+        }),
+      )
+
+      const registry = yield* ToolRegistry.Service
+      const agent = { name: "build", mode: "primary" as const, permission: [], options: {} }
+      const tool = (yield* registry.tools({
+        providerID: "opencode" as any,
+        modelID: "gpt-5" as any,
+        agent,
+      })).find((tool) => tool.id === SkillTool.id)
+      if (!tool) throw new Error("Skill tool not found")
+
+      const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
+      const ctx: Tool.Context = {
+        ...baseCtx,
+        ask: (req) =>
           Effect.sync(() => {
-            process.env.OPENCODE_TEST_HOME = home
+            requests.push(req)
           }),
-        )
-
-        const registry = yield* ToolRegistry.Service
-        const agent = { name: "build", mode: "primary" as const, permission: [], options: {} }
-        const tool = (yield* registry.tools({
-          providerID: "opencode" as any,
-          modelID: "gpt-5" as any,
-          agent,
-        })).find((tool) => tool.id === SkillTool.id)
-        if (!tool) throw new Error("Skill tool not found")
-
-        const requests: Array<Omit<Permission.Request, "id" | "sessionID" | "tool">> = []
-        const ctx: Tool.Context = {
-          ...baseCtx,
-          ask: (req) =>
-            Effect.sync(() => {
-              requests.push(req)
-            }),
-        }
-
-        const result = yield* tool.execute({ name: "tool-skill" }, ctx)
-        const file = path.resolve(skill, "scripts", "demo.txt")
-
-        expect(requests.length).toBe(1)
-        expect(requests[0].permission).toBe("skill")
-        expect(requests[0].patterns).toContain("tool-skill")
-        expect(requests[0].always).toContain("tool-skill")
-        expect(result.metadata.dir).toBe(skill)
-        expect(result.output).toContain(`<skill_content name="tool-skill">`)
-        expect(result.output).toContain(`Base directory for this skill: ${pathToFileURL(skill).href}`)
-        expect(result.output).toContain(`<file>${file}</file>`)
+      }
+
+      const result = yield* tool.execute({ name: "tool-skill" }, ctx)
+      const file = path.resolve(skill, "scripts", "demo.txt")
+
+      expect(requests.length).toBe(1)
+      expect(requests[0].permission).toBe("skill")
+      expect(requests[0].patterns).toContain("tool-skill")
+      expect(requests[0].always).toContain("tool-skill")
+      expect(result.metadata.dir).toBe(skill)
+      expect(result.output).toContain(`<skill_content name="tool-skill">`)
+      expect(result.output).toContain(`Base directory for this skill: ${pathToFileURL(skill).href}`)
+      expect(result.output).toContain(`<file>${file}</file>`)
     }),
   )
 
   it.instance("execute preserves not found message", () =>
     Effect.gen(function* () {
-        const dir = (yield* TestInstance).directory
-        const home = process.env.OPENCODE_TEST_HOME
-        process.env.OPENCODE_TEST_HOME = dir
-        yield* Effect.addFinalizer(() =>
-          Effect.sync(() => {
-            process.env.OPENCODE_TEST_HOME = home
-          }),
+      const dir = (yield* TestInstance).directory
+      const home = process.env.OPENCODE_TEST_HOME
+      process.env.OPENCODE_TEST_HOME = dir
+      yield* Effect.addFinalizer(() =>
+        Effect.sync(() => {
+          process.env.OPENCODE_TEST_HOME = home
+        }),
+      )
+
+      const registry = yield* ToolRegistry.Service
+      const agent = { name: "build", mode: "primary" as const, permission: [], options: {} }
+      const tool = (yield* registry.tools({
+        providerID: "opencode" as any,
+        modelID: "gpt-5" as any,
+        agent,
+      })).find((tool) => tool.id === SkillTool.id)
+      if (!tool) throw new Error("Skill tool not found")
+
+      const exit = yield* tool
+        .execute(
+          { name: "missing-skill" },
+          {
+            ...baseCtx,
+            ask: () => Effect.void,
+          },
         )
-
-        const registry = yield* ToolRegistry.Service
-        const agent = { name: "build", mode: "primary" as const, permission: [], options: {} }
-        const tool = (yield* registry.tools({
-          providerID: "opencode" as any,
-          modelID: "gpt-5" as any,
-          agent,
-        })).find((tool) => tool.id === SkillTool.id)
-        if (!tool) throw new Error("Skill tool not found")
-
-        const exit = yield* tool
-          .execute(
-            { name: "missing-skill" },
-            {
-              ...baseCtx,
-              ask: () => Effect.void,
-            },
-          )
-          .pipe(Effect.exit)
-
-        expect(Exit.isFailure(exit)).toBe(true)
-        if (Exit.isFailure(exit)) {
-          const error = Cause.squash(exit.cause)
-          expect(error).toBeInstanceOf(Error)
-          if (error instanceof Error) expect(error.message).toContain('Skill "missing-skill" not found.')
-        }
+        .pipe(Effect.exit)
+
+      expect(Exit.isFailure(exit)).toBe(true)
+      if (Exit.isFailure(exit)) {
+        const error = Cause.squash(exit.cause)
+        expect(error).toBeInstanceOf(Error)
+        if (error instanceof Error) expect(error.message).toContain('Skill "missing-skill" not found.')
+      }
     }),
   )
 })

+ 179 - 153
packages/opencode/test/v2/session-message-updater.test.ts

@@ -12,39 +12,43 @@ test.skip("step snapshots carry over to assistant messages", () => {
   const state: SessionMessageUpdater.MemoryState = { messages: [] }
   const sessionID = SessionID.make("session")
 
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.step.started",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(1),
-      agent: "build",
-      model: {
-        id: ModelV2.ID.make("model"),
-        providerID: ProviderV2.ID.make("provider"),
-        variant: ModelV2.VariantID.make("default"),
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.step.started",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(1),
+        agent: "build",
+        model: {
+          id: ModelV2.ID.make("model"),
+          providerID: ProviderV2.ID.make("provider"),
+          variant: ModelV2.VariantID.make("default"),
+        },
+        snapshot: "before",
       },
-      snapshot: "before",
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.step.ended",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(2),
-      finish: "stop",
-      cost: 0,
-      tokens: {
-        input: 1,
-        output: 2,
-        reasoning: 0,
-        cache: { read: 0, write: 0 },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.step.ended",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(2),
+        finish: "stop",
+        cost: 0,
+        tokens: {
+          input: 1,
+          output: 2,
+          reasoning: 0,
+          cache: { read: 0, write: 0 },
+        },
+        snapshot: "after",
       },
-      snapshot: "after",
-    },
-  } satisfies SessionEvent.Event))
+    } satisfies SessionEvent.Event),
+  )
 
   expect(state.messages[0]?.type).toBe("assistant")
   if (state.messages[0]?.type !== "assistant") return
@@ -56,39 +60,45 @@ test.skip("text ended populates assistant text content", () => {
   const state: SessionMessageUpdater.MemoryState = { messages: [] }
   const sessionID = SessionID.make("session")
 
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.step.started",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(1),
-      agent: "build",
-      model: {
-        id: ModelV2.ID.make("model"),
-        providerID: ProviderV2.ID.make("provider"),
-        variant: ModelV2.VariantID.make("default"),
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.step.started",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(1),
+        agent: "build",
+        model: {
+          id: ModelV2.ID.make("model"),
+          providerID: ProviderV2.ID.make("provider"),
+          variant: ModelV2.VariantID.make("default"),
+        },
       },
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.text.started",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(2),
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.text.ended",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(3),
-      text: "hello assistant",
-    },
-  } satisfies SessionEvent.Event))
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.text.started",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(2),
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.text.ended",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(3),
+        text: "hello assistant",
+      },
+    } satisfies SessionEvent.Event),
+  )
 
   expect(state.messages[0]?.type).toBe("assistant")
   if (state.messages[0]?.type !== "assistant") return
@@ -100,57 +110,65 @@ test.skip("tool completion stores completed timestamp", () => {
   const sessionID = SessionID.make("session")
   const callID = "call"
 
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.step.started",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(1),
-      agent: "build",
-      model: {
-        id: ModelV2.ID.make("model"),
-        providerID: ProviderV2.ID.make("provider"),
-        variant: ModelV2.VariantID.make("default"),
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.step.started",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(1),
+        agent: "build",
+        model: {
+          id: ModelV2.ID.make("model"),
+          providerID: ProviderV2.ID.make("provider"),
+          variant: ModelV2.VariantID.make("default"),
+        },
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.tool.input.started",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(2),
+        callID,
+        name: "bash",
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.tool.called",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(3),
+        callID,
+        tool: "bash",
+        input: { command: "pwd" },
+        provider: { executed: true, metadata: { source: "provider" } },
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.tool.success",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(4),
+        callID,
+        structured: {},
+        content: [{ type: "text", text: "/tmp" }],
+        provider: { executed: true, metadata: { status: "done" } },
       },
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.tool.input.started",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(2),
-      callID,
-      name: "bash",
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.tool.called",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(3),
-      callID,
-      tool: "bash",
-      input: { command: "pwd" },
-      provider: { executed: true, metadata: { source: "provider" } },
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.tool.success",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(4),
-      callID,
-      structured: {},
-      content: [{ type: "text", text: "/tmp" }],
-      provider: { executed: true, metadata: { status: "done" } },
-    },
-  } satisfies SessionEvent.Event))
+    } satisfies SessionEvent.Event),
+  )
 
   expect(state.messages[0]?.type).toBe("assistant")
   if (state.messages[0]?.type !== "assistant") return
@@ -165,46 +183,54 @@ test.skip("compaction events reduce to compaction message", () => {
   const sessionID = SessionID.make("session")
   const id = EventV2.ID.create()
 
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id,
-    type: "session.next.compaction.started",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(1),
-      reason: "auto",
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.compaction.delta",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(2),
-      text: "hello ",
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.compaction.delta",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(3),
-      text: "summary",
-    },
-  } satisfies SessionEvent.Event))
-
-  Effect.runSync(SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
-    id: EventV2.ID.create(),
-    type: "session.next.compaction.ended",
-    data: {
-      sessionID,
-      timestamp: DateTime.makeUnsafe(4),
-      text: "final summary",
-      include: "recent context",
-    },
-  } satisfies SessionEvent.Event))
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id,
+      type: "session.next.compaction.started",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(1),
+        reason: "auto",
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.compaction.delta",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(2),
+        text: "hello ",
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.compaction.delta",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(3),
+        text: "summary",
+      },
+    } satisfies SessionEvent.Event),
+  )
+
+  Effect.runSync(
+    SessionMessageUpdater.update(SessionMessageUpdater.memory(state), {
+      id: EventV2.ID.create(),
+      type: "session.next.compaction.ended",
+      data: {
+        sessionID,
+        timestamp: DateTime.makeUnsafe(4),
+        text: "final summary",
+        include: "recent context",
+      },
+    } satisfies SessionEvent.Event),
+  )
 
   expect(state.messages).toHaveLength(1)
   expect(state.messages[0]).toMatchObject({

+ 5 - 5
packages/sdk/js/src/v2/gen/sdk.gen.ts

@@ -25,10 +25,10 @@ import type {
   ConfigUpdateErrors,
   ConfigUpdateResponses,
   EventSubscribeResponses,
-  EventTuiCommandExecute2,
-  EventTuiPromptAppend2,
-  EventTuiSessionSelect2,
-  EventTuiToastShow2,
+  EventTuiCommandExecute,
+  EventTuiPromptAppend,
+  EventTuiSessionSelect,
+  EventTuiToastShow,
   ExperimentalConsoleGetErrors,
   ExperimentalConsoleGetResponses,
   ExperimentalConsoleListOrgsErrors,
@@ -4950,7 +4950,7 @@ export class Tui extends HeyApiClient {
     parameters?: {
       directory?: string
       workspace?: string
-      body?: EventTuiPromptAppend2 | EventTuiCommandExecute2 | EventTuiToastShow2 | EventTuiSessionSelect2
+      body?: EventTuiPromptAppend | EventTuiCommandExecute | EventTuiToastShow | EventTuiSessionSelect
     },
     options?: Options<never, ThrowOnError>,
   ) {

File diff ditekan karena terlalu besar
+ 1962 - 943
packages/sdk/js/src/v2/gen/types.gen.ts


File diff ditekan karena terlalu besar
+ 784 - 718
packages/sdk/openapi.json


Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini