Quellcode durchsuchen

refactor(schema): normalize module patterns (#33770)

Kit Langton vor 1 Monat
Ursprung
Commit
7854f5b9f7
50 geänderte Dateien mit 204 neuen und 202 gelöschten Zeilen
  1. 7 8
      packages/core/src/plugin.ts
  2. 2 2
      packages/core/src/project/copy.ts
  3. 2 2
      packages/core/src/project/directories.ts
  4. 4 5
      packages/core/src/pty.ts
  5. 4 4
      packages/core/src/schema.ts
  6. 2 2
      packages/core/src/session/context-epoch.ts
  7. 2 2
      packages/core/src/session/info.ts
  8. 0 2
      packages/core/src/session/message-id.ts
  9. 1 2
      packages/core/src/session/projector.ts
  10. 2 3
      packages/core/src/session/todo.ts
  11. 27 1
      packages/core/test/shared-schema.test.ts
  12. 1 2
      packages/opencode/src/permission/index.ts
  13. 6 6
      packages/opencode/src/provider/auth.ts
  14. 7 7
      packages/opencode/src/provider/provider.ts
  15. 3 3
      packages/opencode/src/session/schema.ts
  16. 23 23
      packages/opencode/src/session/session.ts
  17. 2 2
      packages/opencode/src/sync/schema.ts
  18. 2 2
      packages/opencode/src/tool/schema.ts
  19. 2 2
      packages/protocol/src/groups/session.ts
  20. 2 2
      packages/schema/src/agent.ts
  21. 2 2
      packages/schema/src/credential.ts
  22. 2 2
      packages/schema/src/event.ts
  23. 2 2
      packages/schema/src/integration.ts
  24. 2 2
      packages/schema/src/location.ts
  25. 2 2
      packages/schema/src/model.ts
  26. 2 2
      packages/schema/src/permission-saved.ts
  27. 2 3
      packages/schema/src/permission-v1.ts
  28. 2 2
      packages/schema/src/permission.ts
  29. 0 2
      packages/schema/src/plugin.ts
  30. 0 1
      packages/schema/src/project-directories.ts
  31. 2 2
      packages/schema/src/project-id.ts
  32. 10 10
      packages/schema/src/project.ts
  33. 3 3
      packages/schema/src/prompt.ts
  34. 3 3
      packages/schema/src/provider.ts
  35. 2 5
      packages/schema/src/pty.ts
  36. 2 2
      packages/schema/src/question-v1.ts
  37. 2 2
      packages/schema/src/question.ts
  38. 2 2
      packages/schema/src/revert.ts
  39. 2 2
      packages/schema/src/schema.ts
  40. 20 21
      packages/schema/src/session-event.ts
  41. 2 2
      packages/schema/src/session-id.ts
  42. 2 2
      packages/schema/src/session-input.ts
  43. 0 11
      packages/schema/src/session-message-id.ts
  44. 7 4
      packages/schema/src/session-message.ts
  45. 0 1
      packages/schema/src/session-todo.ts
  46. 22 22
      packages/schema/src/session-v1.ts
  47. 2 2
      packages/schema/src/session.ts
  48. 2 2
      packages/schema/src/workspace-id.ts
  49. 1 1
      specs/v2/instructions.md
  50. 1 1
      specs/v2/provider-model.md

+ 7 - 8
packages/core/src/plugin.ts

@@ -1,8 +1,8 @@
 export * as PluginV2 from "./plugin"
 
 import { Context, Deferred, Effect, Exit, Layer, Scope } from "effect"
-import type { Plugin } from "@opencode-ai/plugin/v2/effect"
-import { PluginEvent, PluginID } from "@opencode-ai/schema/plugin"
+import type { Plugin as PluginRuntime } from "@opencode-ai/plugin/v2/effect"
+import { Plugin } from "@opencode-ai/schema/plugin"
 import { AgentV2 } from "./agent"
 import { AISDK } from "./aisdk"
 import { Catalog } from "./catalog"
@@ -15,13 +15,12 @@ import { Reference } from "./reference"
 import { SkillV2 } from "./skill"
 import { State } from "./state"
 
-export const ID = PluginID
+export const ID = Plugin.ID
 export type ID = typeof ID.Type
-
-export const Event = PluginEvent
+export const Event = Plugin.Event
 
 export interface Interface {
-  readonly add: (id: ID, effect: Plugin["effect"]) => Effect.Effect<void>
+  readonly add: (id: ID, effect: PluginRuntime["effect"]) => Effect.Effect<void>
   readonly remove: (id: ID) => Effect.Effect<void>
   readonly wait: (id: ID) => Effect.Effect<void>
 }
@@ -38,9 +37,9 @@ export const layer = Layer.effect(
     const loading = new Set<ID>()
     const waiters = new Map<ID, Set<Deferred.Deferred<void>>>()
     const failures = new Map<ID, Exit.Exit<void, never>>()
-    let host: Parameters<Plugin["effect"]>[0]
+    let host: Parameters<PluginRuntime["effect"]>[0]
 
-    const add = Effect.fn("Plugin.add")(function* (id: ID, effect: Plugin["effect"]) {
+    const add = Effect.fn("Plugin.add")(function* (id: ID, effect: PluginRuntime["effect"]) {
       if (loading.has(id)) return yield* Effect.die(`Plugin load cycle detected for ${id}`)
 
       yield* locks.withLock(id)(

+ 2 - 2
packages/core/src/project/copy.ts

@@ -13,7 +13,7 @@ import { Slug } from "../util/slug"
 import { EventV2 } from "../event"
 import { Database } from "../database/database"
 import { Location } from "../location"
-import { ProjectDirectoriesEvent } from "@opencode-ai/schema/project-directories"
+import { Event } from "@opencode-ai/schema/project-directories"
 import { ProjectCopy } from "@opencode-ai/schema/project-copy"
 
 export const StrategyID = ProjectCopy.StrategyID
@@ -96,7 +96,7 @@ export interface Strategy {
   readonly list: (directory: AbsolutePath) => Effect.Effect<ListEntry[], Git.WorktreeError | DirectoryUnavailableError>
 }
 
-export const Event = ProjectDirectoriesEvent
+export { Event }
 
 export interface Interface {
   readonly register: (strategy: Strategy) => Effect.Effect<void, DuplicateStrategyError>

+ 2 - 2
packages/core/src/project/directories.ts

@@ -4,7 +4,7 @@ import { and, asc, desc, eq, isNotNull, isNull, ne, or } from "drizzle-orm"
 import { Context, Effect, Layer, Schema } from "effect"
 import { Database } from "../database/database"
 import { LayerNode } from "../effect/layer-node"
-import { AbsolutePath, optionalOmitUndefined } from "../schema"
+import { AbsolutePath, optional } from "../schema"
 import { ProjectSchema } from "./schema"
 import { ProjectDirectoryTable } from "./sql"
 import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
@@ -39,7 +39,7 @@ export type ListInput = typeof ListInput.Type
 export const ListOutput = Schema.Array(
   Schema.Struct({
     directory: AbsolutePath,
-    strategy: optionalOmitUndefined(Schema.String),
+    strategy: optional(Schema.String),
   }),
 ).annotate({ identifier: "Project.Directories" })
 export type ListOutput = typeof ListOutput.Type

+ 4 - 5
packages/core/src/pty.ts

@@ -2,7 +2,7 @@ export * as Pty from "./pty"
 
 import type { Disp, Proc } from "#pty"
 import { Context, Effect, Layer, Schema, Types } from "effect"
-import { PtyEvent, PtyInfo, Pty } from "@opencode-ai/schema/pty"
+import { Pty } from "@opencode-ai/schema/pty"
 import { Config } from "./config"
 import { EventV2 } from "./event"
 import { Location } from "./location"
@@ -35,8 +35,7 @@ type Active = {
   listeners: Disp[]
 }
 
-export const Info = PtyInfo
-
+export const Info = Pty.Info
 export type Info = Types.DeepMutable<typeof Info.Type>
 
 export const CreateInput = Pty.CreateInput
@@ -47,6 +46,8 @@ export const UpdateInput = Pty.UpdateInput
 
 export type UpdateInput = Types.DeepMutable<typeof UpdateInput.Type>
 
+export const Event = Pty.Event
+
 export type AttachInput = {
   // Absolute output cursor to replay from. -1 tails from the current end; omitted replays the full retained buffer.
   readonly cursor?: number
@@ -75,8 +76,6 @@ export class ExitedError extends Schema.TaggedErrorClass<ExitedError>()("Pty.Exi
   ptyID: PtyID,
 }) {}
 
-export const Event = PtyEvent
-
 export interface Interface {
   readonly list: () => Effect.Effect<Info[]>
   readonly get: (id: PtyID) => Effect.Effect<Info, NotFoundError>

+ 4 - 4
packages/core/src/schema.ts

@@ -3,20 +3,20 @@ import {
   AbsolutePath,
   DateTimeUtcFromMillis,
   NonNegativeInt,
-  optionalOmitUndefined,
+  optional,
   PositiveInt,
   RelativePath,
-  withStatics,
+  statics,
 } from "@opencode-ai/schema/schema"
 
 export {
   AbsolutePath,
   DateTimeUtcFromMillis,
   NonNegativeInt,
-  optionalOmitUndefined,
+  optional,
   PositiveInt,
   RelativePath,
-  withStatics,
+  statics,
 }
 
 /**

+ 2 - 2
packages/core/src/session/context-epoch.ts

@@ -9,7 +9,7 @@ import { ContextSnapshotDecodeError } from "./error"
 import { SessionEvent } from "./event"
 import { SessionHistory } from "./history"
 import { SessionInput } from "./input"
-import { SessionMessageID } from "./message-id"
+import { SessionMessage } from "./message"
 import { SessionSchema } from "./schema"
 import { SessionContextEpochTable } from "./sql"
 
@@ -71,7 +71,7 @@ const prepareOnce = Effect.fnUntraced(function* (
 
   yield* events.publish(
     SessionEvent.ContextUpdated,
-    { sessionID, messageID: SessionMessageID.ID.create(), timestamp: yield* DateTime.now, text: result.text },
+    { sessionID, messageID: SessionMessage.ID.create(), timestamp: yield* DateTime.now, text: result.text },
     { commit: () => advance(db, sessionID, result.snapshot).pipe(Effect.orDie) },
   )
   return { baseline: stored.baseline, baselineSeq: stored.baseline_seq }

+ 2 - 2
packages/core/src/session/info.ts

@@ -8,7 +8,7 @@ import { AbsolutePath, RelativePath } from "../schema"
 import { WorkspaceV2 } from "../workspace"
 import { SessionSchema } from "./schema"
 import { SessionTable } from "./sql"
-import { SessionMessageID } from "./message-id"
+import { SessionMessage } from "./message"
 import { Snapshot } from "../snapshot"
 
 export function fromRow(row: typeof SessionTable.$inferSelect): SessionSchema.Info {
@@ -40,7 +40,7 @@ export function fromRow(row: typeof SessionTable.$inferSelect): SessionSchema.In
       workspaceID: row.workspace_id ? WorkspaceV2.ID.make(row.workspace_id) : undefined,
     }),
     subpath: row.path ? RelativePath.make(row.path) : undefined,
-    revert: row.revert ? { ...row.revert, messageID: SessionMessageID.ID.make(row.revert.messageID) } : undefined,
+    revert: row.revert ? { ...row.revert, messageID: SessionMessage.ID.make(row.revert.messageID) } : undefined,
     time: {
       created: DateTime.makeUnsafe(row.time_created),
       updated: DateTime.makeUnsafe(row.time_updated),

+ 0 - 2
packages/core/src/session/message-id.ts

@@ -1,2 +0,0 @@
-export * as SessionMessageID from "./message-id"
-export { ID } from "@opencode-ai/schema/session-message-id"

+ 1 - 2
packages/core/src/session/projector.ts

@@ -15,7 +15,6 @@ import { WorkspaceV2 } from "../workspace"
 import { SessionContextEpoch } from "./context-epoch"
 import { MessageTable, PartTable, SessionInputTable, SessionMessageTable, SessionTable } from "./sql"
 import type { DeepMutable } from "../schema"
-import { SessionMessageID } from "./message-id"
 
 type DatabaseService = Database.Interface["db"]
 
@@ -67,7 +66,7 @@ function sessionRow(info: SessionV1.SessionInfo): typeof SessionTable.$inferInse
     tokens_reasoning: (info.tokens ?? { reasoning: 0 }).reasoning,
     tokens_cache_read: (info.tokens ?? { cache: { read: 0 } }).cache.read,
     tokens_cache_write: (info.tokens ?? { cache: { write: 0 } }).cache.write,
-    revert: info.revert ? { ...info.revert, messageID: SessionMessageID.ID.make(info.revert.messageID) } : null,
+    revert: info.revert ? { ...info.revert, messageID: SessionMessage.ID.make(info.revert.messageID) } : null,
     permission: info.permission ? [...info.permission] : undefined,
     time_created: info.time.created,
     time_updated: info.time.updated,

+ 2 - 3
packages/core/src/session/todo.ts

@@ -2,15 +2,14 @@ export * as SessionTodo from "./todo"
 
 import { asc, eq } from "drizzle-orm"
 import { Context, Effect, Layer } from "effect"
-import { SessionTodo, SessionTodoInfo } from "@opencode-ai/schema/session-todo"
+import { SessionTodo } from "@opencode-ai/schema/session-todo"
 import { Database } from "../database/database"
 import { EventV2 } from "../event"
 import { SessionSchema } from "./schema"
 import { TodoTable } from "./sql"
 
-export const Info = SessionTodoInfo
+export const Info = SessionTodo.Info
 export type Info = typeof Info.Type
-
 export const Event = SessionTodo.Event
 
 export interface Interface {

+ 27 - 1
packages/core/test/shared-schema.test.ts

@@ -9,6 +9,8 @@ import { Model } from "@opencode-ai/schema/model"
 import { AgentAttachment, FileAttachment, Prompt, Source } from "@opencode-ai/schema/prompt"
 import { Provider } from "@opencode-ai/schema/provider"
 import { Project } from "@opencode-ai/schema/project"
+import { ProjectDirectories } from "@opencode-ai/schema/project-directories"
+import { PermissionV1 } from "@opencode-ai/schema/permission-v1"
 import { Session } from "@opencode-ai/schema/session"
 import { SessionInput } from "@opencode-ai/schema/session-input"
 import { SessionMessage } from "@opencode-ai/schema/session-message"
@@ -20,10 +22,14 @@ import { FileSystem } from "@opencode-ai/schema/filesystem"
 import { Integration } from "@opencode-ai/schema/integration"
 import { LLM } from "@opencode-ai/schema/llm"
 import { Permission } from "@opencode-ai/schema/permission"
+import { Plugin } from "@opencode-ai/schema/plugin"
+import { Pty } from "@opencode-ai/schema/pty"
 import { Reference } from "@opencode-ai/schema/reference"
+import { SessionTodo } from "@opencode-ai/schema/session-todo"
 import { Skill } from "@opencode-ai/schema/skill"
-import { AbsolutePath, DateTimeUtcFromMillis } from "@opencode-ai/schema/schema"
+import { AbsolutePath, DateTimeUtcFromMillis, optional, statics } from "@opencode-ai/schema/schema"
 import { ProviderV2 } from "@opencode-ai/core/provider"
+import { PluginV2 } from "@opencode-ai/core/plugin"
 
 test("Core reuses the canonical shared schemas", async () => {
   const [
@@ -35,13 +41,18 @@ test("Core reuses the canonical shared schemas", async () => {
     coreLocation,
     coreLLM,
     corePermission,
+    corePermissionV1,
+    coreProjectCopy,
+    corePty,
     coreProject,
     coreReference,
     coreSessionInput,
     coreSessionMessage,
+    coreSessionTodo,
     corePrompt,
     coreSkill,
     coreV2Schema,
+    coreSchema,
     coreWorkspace,
   ] = await Promise.all([
     import("@opencode-ai/core/command"),
@@ -52,13 +63,18 @@ test("Core reuses the canonical shared schemas", async () => {
     import("@opencode-ai/core/location"),
     import("@opencode-ai/llm"),
     import("@opencode-ai/core/permission"),
+    import("@opencode-ai/core/v1/permission"),
+    import("@opencode-ai/core/project/copy"),
+    import("@opencode-ai/core/pty"),
     import("@opencode-ai/core/project/schema"),
     import("@opencode-ai/core/reference"),
     import("@opencode-ai/core/session/input"),
     import("@opencode-ai/core/session/message"),
+    import("@opencode-ai/core/session/todo"),
     import("@opencode-ai/core/session/prompt"),
     import("@opencode-ai/core/skill"),
     import("@opencode-ai/core/v2-schema"),
+    import("@opencode-ai/core/schema"),
     import("@opencode-ai/core/workspace"),
   ])
 
@@ -111,6 +127,12 @@ test("Core reuses the canonical shared schemas", async () => {
     [corePermission.Effect, Permission.Effect],
     [corePermission.Rule, Permission.Rule],
     [corePermission.Ruleset, Permission.Ruleset],
+    [corePermissionV1.Event, PermissionV1.Event],
+    [coreProjectCopy.Event, ProjectDirectories.Event],
+    [PluginV2.ID, Plugin.ID],
+    [PluginV2.Event, Plugin.Event],
+    [corePty.Info, Pty.Info],
+    [corePty.Event, Pty.Event],
     [coreProject.ID, Project.ID],
     [coreReference.LocalSource, Reference.LocalSource],
     [coreReference.GitSource, Reference.GitSource],
@@ -140,6 +162,8 @@ test("Core reuses the canonical shared schemas", async () => {
     [coreSessionMessage.Assistant, SessionMessage.Assistant],
     [coreSessionMessage.Compaction, SessionMessage.Compaction],
     [coreSessionMessage.Message, SessionMessage.Message],
+    [coreSessionTodo.Info, SessionTodo.Info],
+    [coreSessionTodo.Event, SessionTodo.Event],
     [corePrompt.Source, Source],
     [corePrompt.FileAttachment, FileAttachment],
     [corePrompt.AgentAttachment, AgentAttachment],
@@ -150,6 +174,8 @@ test("Core reuses the canonical shared schemas", async () => {
     [coreSkill.Source, Skill.Source],
     [coreSkill.Info, Skill.Info],
     [coreV2Schema.DateTimeUtcFromMillis, DateTimeUtcFromMillis],
+    [coreSchema.optional, optional],
+    [coreSchema.statics, statics],
     [coreWorkspace.ID, Workspace.ID],
   ]
   for (const [core, shared] of schemas) expect(core).toBe(shared)

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

@@ -6,9 +6,8 @@ import { Deferred, Effect, Layer, Context } from "effect"
 import os from "os"
 import { PermissionV1 } from "@opencode-ai/core/v1/permission"
 import { EventV2Bridge } from "@/event-v2-bridge"
-import { PermissionV1Event } from "@opencode-ai/schema/permission-v1"
 
-export const Event = PermissionV1Event
+export const Event = PermissionV1.Event
 
 export interface Interface {
   readonly ask: (input: PermissionV1.AskInput) => Effect.Effect<void, PermissionV1.Error>

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

@@ -3,7 +3,7 @@ import type { AuthOAuthResult, Hooks } from "@opencode-ai/plugin"
 import { serviceUse } from "@opencode-ai/core/effect/service-use"
 import { Auth } from "@/auth"
 import { InstanceState } from "@/effect/instance-state"
-import { optionalOmitUndefined } from "@opencode-ai/core/schema"
+import { optional } from "@opencode-ai/core/schema"
 import { Plugin } from "../plugin"
 import { ProviderV2 } from "@opencode-ai/core/provider"
 import { Array as Arr, Effect, Layer, Record, Result, Context, Schema } from "effect"
@@ -18,14 +18,14 @@ const TextPrompt = Schema.Struct({
   type: Schema.Literal("text"),
   key: Schema.String,
   message: Schema.String,
-  placeholder: optionalOmitUndefined(Schema.String),
-  when: optionalOmitUndefined(When),
+  placeholder: optional(Schema.String),
+  when: optional(When),
 })
 
 const SelectOption = Schema.Struct({
   label: Schema.String,
   value: Schema.String,
-  hint: optionalOmitUndefined(Schema.String),
+  hint: optional(Schema.String),
 })
 
 const SelectPrompt = Schema.Struct({
@@ -33,7 +33,7 @@ const SelectPrompt = Schema.Struct({
   key: Schema.String,
   message: Schema.String,
   options: Schema.Array(SelectOption),
-  when: optionalOmitUndefined(When),
+  when: optional(When),
 })
 
 const Prompt = Schema.Union([TextPrompt, SelectPrompt])
@@ -41,7 +41,7 @@ const Prompt = Schema.Union([TextPrompt, SelectPrompt])
 export class Method extends Schema.Class<Method>("ProviderAuthMethod")({
   type: Schema.Literals(["oauth", "api"]),
   label: Schema.String,
-  prompts: optionalOmitUndefined(Schema.Array(Prompt)),
+  prompts: optional(Schema.Array(Prompt)),
 }) {}
 
 export const Methods = Schema.Record(Schema.String, Schema.Array(Method))

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

@@ -24,7 +24,7 @@ import { InstanceState } from "@/effect/instance-state"
 import { EffectPromise } from "@/effect/promise"
 import { FSUtil } from "@opencode-ai/core/fs-util"
 import { isRecord } from "@/util/record"
-import { optionalOmitUndefined } from "@opencode-ai/core/schema"
+import { optional } from "@opencode-ai/core/schema"
 import { ProviderTransform } from "./transform"
 import { ProviderV2 } from "@opencode-ai/core/provider"
 import { ModelV2 } from "@opencode-ai/core/model"
@@ -999,8 +999,8 @@ const ProviderCost = Schema.Struct({
   input: Schema.Finite,
   output: Schema.Finite,
   cache: ProviderCacheCost,
-  tiers: optionalOmitUndefined(Schema.Array(ProviderCostTier)),
-  experimentalOver200K: optionalOmitUndefined(
+  tiers: optional(Schema.Array(ProviderCostTier)),
+  experimentalOver200K: optional(
     Schema.Struct({
       input: Schema.Finite,
       output: Schema.Finite,
@@ -1011,7 +1011,7 @@ const ProviderCost = Schema.Struct({
 
 const ProviderLimit = Schema.Struct({
   context: Schema.Finite,
-  input: optionalOmitUndefined(Schema.Finite),
+  input: optional(Schema.Finite),
   output: Schema.Finite,
 })
 
@@ -1020,7 +1020,7 @@ export const Model = Schema.Struct({
   providerID: ProviderV2.ID,
   api: ProviderApiInfo,
   name: Schema.String,
-  family: optionalOmitUndefined(Schema.String),
+  family: optional(Schema.String),
   capabilities: ProviderCapabilities,
   cost: ProviderCost,
   limit: ProviderLimit,
@@ -1028,7 +1028,7 @@ export const Model = Schema.Struct({
   options: Schema.Record(Schema.String, Schema.Any),
   headers: Schema.Record(Schema.String, Schema.String),
   release_date: Schema.String,
-  variants: optionalOmitUndefined(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))),
+  variants: optional(Schema.Record(Schema.String, Schema.Record(Schema.String, Schema.Any))),
 }).annotate({ identifier: "Model" })
 export type Model = Types.DeepMutable<Schema.Schema.Type<typeof Model>>
 
@@ -1037,7 +1037,7 @@ export const Info = Schema.Struct({
   name: Schema.String,
   source: Schema.Literals(["env", "config", "custom", "api"]),
   env: Schema.Array(Schema.String),
-  key: optionalOmitUndefined(Schema.String),
+  key: optional(Schema.String),
   options: Schema.Record(Schema.String, Schema.Any),
   models: Schema.Record(Schema.String, Model),
 }).annotate({ identifier: "Provider" })

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

@@ -2,14 +2,14 @@ import { Schema } from "effect"
 
 import { Identifier } from "@/id/id"
 import { SessionV2 } from "@opencode-ai/core/session"
-import { withStatics } from "@opencode-ai/core/schema"
+import { statics } from "@opencode-ai/core/schema"
 
 export const SessionID = SessionV2.ID
 export type SessionID = Schema.Schema.Type<typeof SessionID>
 
 export const MessageID = Schema.String.check(Schema.isStartsWith("msg")).pipe(
   Schema.brand("MessageID"),
-  withStatics((s) => ({
+  statics((s) => ({
     ascending: (id?: string) => s.make(Identifier.ascending("message", id)),
   })),
 )
@@ -18,7 +18,7 @@ export type MessageID = Schema.Schema.Type<typeof MessageID>
 
 export const PartID = Schema.String.check(Schema.isStartsWith("prt")).pipe(
   Schema.brand("PartID"),
-  withStatics((s) => ({
+  statics((s) => ({
     ascending: (id?: string) => s.make(Identifier.ascending("part", id)),
   })),
 )

+ 23 - 23
packages/opencode/src/session/session.ts

@@ -39,11 +39,11 @@ import { SessionID, MessageID, PartID } from "./schema"
 import type { Provider } from "@/provider/provider"
 import { Global } from "@opencode-ai/core/global"
 import { Effect, Layer, Option, Context, Schema, Types } from "effect"
-import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
+import { NonNegativeInt, optional } from "@opencode-ai/core/schema"
 import { RuntimeFlags } from "@/effect/runtime-flags"
 import { ProviderV2 } from "@opencode-ai/core/provider"
 import { ModelV2 } from "@opencode-ai/core/model"
-import { SessionMessageID } from "@opencode-ai/schema/session-message-id"
+import { SessionMessage } from "@opencode-ai/schema/session-message"
 
 const runtime = makeRuntime(Database.Service, Database.defaultLayer)
 
@@ -146,7 +146,7 @@ export function toRow(info: Info) {
     tokens_cache_write: (info.tokens ?? EmptyTokens).cache.write,
     revert: info.revert
       ? {
-          messageID: SessionMessageID.ID.make(info.revert.messageID),
+          messageID: SessionMessage.ID.make(info.revert.messageID),
           partID: info.revert.partID,
           snapshot: info.revert.snapshot,
           diff: info.revert.diff,
@@ -178,7 +178,7 @@ const Summary = Schema.Struct({
   additions: Schema.Finite,
   deletions: Schema.Finite,
   files: Schema.Finite,
-  diffs: optionalOmitUndefined(Schema.Array(Snapshot.FileDiff)),
+  diffs: optional(Schema.Array(Snapshot.FileDiff)),
 })
 
 const Tokens = Schema.Struct({
@@ -204,21 +204,21 @@ export const ArchivedTimestamp = Schema.Finite
 const Time = Schema.Struct({
   created: NonNegativeInt,
   updated: NonNegativeInt,
-  compacting: optionalOmitUndefined(NonNegativeInt),
-  archived: optionalOmitUndefined(ArchivedTimestamp),
+  compacting: optional(NonNegativeInt),
+  archived: optional(ArchivedTimestamp),
 })
 
 const Revert = Schema.Struct({
   messageID: MessageID,
-  partID: optionalOmitUndefined(PartID),
-  snapshot: optionalOmitUndefined(Schema.String),
-  diff: optionalOmitUndefined(Schema.String),
+  partID: optional(PartID),
+  snapshot: optional(Schema.String),
+  diff: optional(Schema.String),
 })
 
 const Model = Schema.Struct({
   id: ModelV2.ID,
   providerID: ProviderV2.ID,
-  variant: optionalOmitUndefined(Schema.String),
+  variant: optional(Schema.String),
 })
 
 export const Metadata = Schema.Record(Schema.String, Schema.Any)
@@ -227,28 +227,28 @@ export const Info = Schema.Struct({
   id: SessionID,
   slug: Schema.String,
   projectID: ProjectV2.ID,
-  workspaceID: optionalOmitUndefined(WorkspaceV2.ID),
+  workspaceID: optional(WorkspaceV2.ID),
   directory: Schema.String,
-  path: optionalOmitUndefined(Schema.String),
-  parentID: optionalOmitUndefined(SessionID),
-  summary: optionalOmitUndefined(Summary),
-  cost: optionalOmitUndefined(Schema.Finite),
-  tokens: optionalOmitUndefined(Tokens),
-  share: optionalOmitUndefined(Share),
+  path: optional(Schema.String),
+  parentID: optional(SessionID),
+  summary: optional(Summary),
+  cost: optional(Schema.Finite),
+  tokens: optional(Tokens),
+  share: optional(Share),
   title: Schema.String,
-  agent: optionalOmitUndefined(Schema.String),
-  model: optionalOmitUndefined(Model),
+  agent: optional(Schema.String),
+  model: optional(Model),
   version: Schema.String,
-  metadata: optionalOmitUndefined(Metadata),
+  metadata: optional(Metadata),
   time: Time,
-  permission: optionalOmitUndefined(PermissionV1.Ruleset),
-  revert: optionalOmitUndefined(Revert),
+  permission: optional(PermissionV1.Ruleset),
+  revert: optional(Revert),
 }).annotate({ identifier: "Session" })
 export type Info = Types.DeepMutable<Schema.Schema.Type<typeof Info>>
 
 export const ProjectInfo = Schema.Struct({
   id: ProjectV2.ID,
-  name: optionalOmitUndefined(Schema.String),
+  name: optional(Schema.String),
   worktree: Schema.String,
 }).annotate({ identifier: "ProjectSummary" })
 export type ProjectInfo = Types.DeepMutable<Schema.Schema.Type<typeof ProjectInfo>>

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

@@ -1,11 +1,11 @@
 import { Schema } from "effect"
 
 import { Identifier } from "@/id/id"
-import { withStatics } from "@opencode-ai/core/schema"
+import { statics } from "@opencode-ai/core/schema"
 
 export const EventID = Schema.String.check(Schema.isStartsWith("evt")).pipe(
   Schema.brand("EventID"),
-  withStatics((s) => ({
+  statics((s) => ({
     ascending: (id?: string) => s.make(Identifier.ascending("event", id)),
   })),
 )

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

@@ -1,14 +1,14 @@
 import { Schema } from "effect"
 
 import { Identifier } from "@/id/id"
-import { withStatics } from "@opencode-ai/core/schema"
+import { statics } from "@opencode-ai/core/schema"
 
 const toolIdSchema = Schema.String.check(Schema.isStartsWith("tool")).pipe(Schema.brand("ToolID"))
 
 export type ToolID = typeof toolIdSchema.Type
 
 export const ToolID = toolIdSchema.pipe(
-  withStatics((schema: typeof toolIdSchema) => ({
+  statics((schema: typeof toolIdSchema) => ({
     ascending: (id?: string) => schema.make(Identifier.ascending("tool", id)),
   })),
 )

+ 2 - 2
packages/protocol/src/groups/session.ts

@@ -3,7 +3,7 @@ import { SessionInput } from "@opencode-ai/schema/session-input"
 import { Prompt } from "@opencode-ai/schema/prompt"
 import { Session } from "@opencode-ai/schema/session"
 import { Project } from "@opencode-ai/schema/project"
-import { AbsolutePath, PositiveInt, RelativePath, withStatics } from "@opencode-ai/schema/schema"
+import { AbsolutePath, PositiveInt, RelativePath, statics } from "@opencode-ai/schema/schema"
 import { Workspace } from "@opencode-ai/schema/workspace"
 import { Context, Encoding, Result, Schema, Struct } from "effect"
 import { HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
@@ -62,7 +62,7 @@ const decodeSessionsCursor = Schema.decodeUnknownEffect(SessionsCursorJson)
 
 export const SessionsCursor = Schema.String.pipe(
   Schema.brand("SessionsCursor"),
-  withStatics((schema) => {
+  statics((schema) => {
     const make = schema.make.bind(schema)
     return {
       make: (input: typeof SessionsCursorInput.Type) => make(Encoding.encodeBase64Url(encodeSessionsCursor(input))),

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

@@ -4,7 +4,7 @@ import { Schema } from "effect"
 import { Model } from "./model"
 import { Permission } from "./permission"
 import { Provider } from "./provider"
-import { PositiveInt, withStatics } from "./schema"
+import { PositiveInt, statics } from "./schema"
 
 export const ID = Schema.String.pipe(Schema.brand("AgentV2.ID"))
 export type ID = typeof ID.Type
@@ -30,7 +30,7 @@ export const Info = Schema.Struct({
 })
   .annotate({ identifier: "AgentV2.Info" })
   .pipe(
-    withStatics((schema) => ({
+    statics((schema) => ({
       empty: (id: ID) =>
         schema.make({ id, request: { headers: {}, body: {} }, mode: "all", hidden: false, permissions: [] }),
     })),

+ 2 - 2
packages/schema/src/credential.ts

@@ -3,11 +3,11 @@ export * as Credential from "./credential"
 import { Schema } from "effect"
 import { IntegrationMethodID } from "./integration-id"
 import { ascending } from "./identifier"
-import { NonNegativeInt, withStatics } from "./schema"
+import { NonNegativeInt, statics } from "./schema"
 
 export const ID = Schema.String.pipe(
   Schema.brand("Credential.ID"),
-  withStatics((schema) => ({ create: () => schema.make("cred_" + ascending()) })),
+  statics((schema) => ({ create: () => schema.make("cred_" + ascending()) })),
 )
 export type ID = typeof ID.Type
 

+ 2 - 2
packages/schema/src/event.ts

@@ -3,11 +3,11 @@ export * as Event from "./event"
 import { Schema } from "effect"
 import { ascending } from "./identifier"
 import { Location } from "./location"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ID = Schema.String.check(Schema.isStartsWith("evt_")).pipe(
   Schema.brand("Event.ID"),
-  withStatics((schema) => ({ create: () => schema.make("evt_" + ascending()) })),
+  statics((schema) => ({ create: () => schema.make("evt_" + ascending()) })),
 )
 export type ID = typeof ID.Type
 

+ 2 - 2
packages/schema/src/integration.ts

@@ -4,7 +4,7 @@ import { Schema } from "effect"
 import { define, inventory } from "./event"
 import { Connection } from "./connection"
 import { ascending } from "./identifier"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 import { IntegrationID, IntegrationMethodID } from "./integration-id"
 
 export const ID = IntegrationID
@@ -102,7 +102,7 @@ export class Info extends Schema.Class<Info>("Integration.Info")({
 
 export const AttemptID = Schema.String.pipe(
   Schema.brand("Integration.AttemptID"),
-  withStatics((schema) => ({ create: () => schema.make("con_" + ascending()) })),
+  statics((schema) => ({ create: () => schema.make("con_" + ascending()) })),
 )
 export type AttemptID = typeof AttemptID.Type
 

+ 2 - 2
packages/schema/src/location.ts

@@ -1,7 +1,7 @@
 export * as Location from "./location"
 
 import { Effect, Schema } from "effect"
-import { AbsolutePath, optionalOmitUndefined } from "./schema"
+import { AbsolutePath, optional } from "./schema"
 import { ProjectID } from "./project-id"
 import { WorkspaceID } from "./workspace-id"
 
@@ -16,7 +16,7 @@ export const Ref = Schema.Struct({
 
 export class Info extends Schema.Class<Info>("Location.Info")({
   directory: AbsolutePath,
-  workspaceID: optionalOmitUndefined(WorkspaceID),
+  workspaceID: optional(WorkspaceID),
   project: Schema.Struct({
     id: ProjectID,
     directory: AbsolutePath,

+ 2 - 2
packages/schema/src/model.ts

@@ -2,7 +2,7 @@ export * as Model from "./model"
 
 import { Schema } from "effect"
 import { Provider } from "./provider"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ID = Schema.String.pipe(Schema.brand("ModelV2.ID"))
 export type ID = typeof ID.Type
@@ -83,7 +83,7 @@ export const Info = Schema.Struct({
 })
   .annotate({ identifier: "ModelV2.Info" })
   .pipe(
-    withStatics((schema) => ({
+    statics((schema) => ({
       empty: (providerID: Provider.ID, modelID: ID) =>
         schema.make({
           id: modelID,

+ 2 - 2
packages/schema/src/permission-saved.ts

@@ -3,11 +3,11 @@ export * as PermissionSaved from "./permission-saved"
 import { Schema } from "effect"
 import { ascending } from "./identifier"
 import { ProjectID } from "./project-id"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ID = Schema.String.pipe(
   Schema.brand("PermissionSaved.ID"),
-  withStatics((schema) => ({ create: () => schema.make("psv_" + ascending()) })),
+  statics((schema) => ({ create: () => schema.make("psv_" + ascending()) })),
 )
 export type ID = typeof ID.Type
 

+ 2 - 3
packages/schema/src/permission-v1.ts

@@ -4,12 +4,12 @@ import { Schema } from "effect"
 import { define, inventory } from "./event"
 import { ascending } from "./identifier"
 import { Project } from "./project"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 import { SessionID } from "./session-id"
 
 export const ID = Schema.String.check(Schema.isStartsWith("per")).pipe(
   Schema.brand("PermissionID"),
-  withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
+  statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
 )
 export type ID = typeof ID.Type
 
@@ -64,4 +64,3 @@ const Replied = define({
   schema: { sessionID: SessionID, requestID: ID, reply: Reply },
 })
 export const Event = { Asked, Replied, Definitions: inventory(Asked, Replied) }
-export const PermissionV1Event = Event

+ 2 - 2
packages/schema/src/permission.ts

@@ -4,11 +4,11 @@ import { Schema } from "effect"
 import { define, inventory } from "./event"
 import { ascending } from "./identifier"
 import { SessionID } from "./session-id"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ID = Schema.String.check(Schema.isStartsWith("per")).pipe(
   Schema.brand("PermissionV2.ID"),
-  withStatics((schema) => ({ create: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
+  statics((schema) => ({ create: (id?: string) => schema.make(id ?? "per_" + ascending()) })),
 )
 export type ID = typeof ID.Type
 

+ 0 - 2
packages/schema/src/plugin.ts

@@ -5,11 +5,9 @@ import { define, inventory } from "./event"
 
 export const ID = Schema.String.pipe(Schema.brand("Plugin.ID"))
 export type ID = typeof ID.Type
-export const PluginID = ID
 
 const Added = define({
   type: "plugin.added",
   schema: { id: ID },
 })
 export const Event = { Added, Definitions: inventory(Added) }
-export const PluginEvent = Event

+ 0 - 1
packages/schema/src/project-directories.ts

@@ -8,4 +8,3 @@ const Updated = define({
   schema: { projectID: Project.ID },
 })
 export const Event = { Updated, Definitions: inventory(Updated) }
-export const ProjectDirectoriesEvent = Event

+ 2 - 2
packages/schema/src/project-id.ts

@@ -1,8 +1,8 @@
 import { Schema } from "effect"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ProjectID = Schema.String.pipe(
   Schema.brand("Project.ID"),
-  withStatics((schema) => ({ global: schema.make("global") })),
+  statics((schema) => ({ global: schema.make("global") })),
 )
 export type ProjectID = typeof ProjectID.Type

+ 10 - 10
packages/schema/src/project.ts

@@ -2,7 +2,7 @@ export * as Project from "./project"
 
 import { Schema } from "effect"
 import { define, inventory } from "./event"
-import { NonNegativeInt, optionalOmitUndefined } from "./schema"
+import { NonNegativeInt, optional } from "./schema"
 import { ProjectID } from "./project-id"
 
 export const ID = ProjectID
@@ -10,28 +10,28 @@ export type ID = typeof ID.Type
 
 export const Vcs = Schema.Literal("git")
 export const Icon = Schema.Struct({
-  url: optionalOmitUndefined(Schema.String),
-  override: optionalOmitUndefined(Schema.String),
-  color: optionalOmitUndefined(Schema.String),
+  url: optional(Schema.String),
+  override: optional(Schema.String),
+  color: optional(Schema.String),
 })
 export const Commands = Schema.Struct({
-  start: optionalOmitUndefined(
+  start: optional(
     Schema.String.annotate({ description: "Startup script to run when creating a new workspace (worktree)" }),
   ),
 })
 export const Time = Schema.Struct({
   created: NonNegativeInt,
   updated: NonNegativeInt,
-  initialized: optionalOmitUndefined(NonNegativeInt),
+  initialized: optional(NonNegativeInt),
 })
 
 export const Info = Schema.Struct({
   id: ID,
   worktree: Schema.String,
-  vcs: optionalOmitUndefined(Vcs),
-  name: optionalOmitUndefined(Schema.String),
-  icon: optionalOmitUndefined(Icon),
-  commands: optionalOmitUndefined(Commands),
+  vcs: optional(Vcs),
+  name: optional(Schema.String),
+  icon: optional(Icon),
+  commands: optional(Commands),
   time: Time,
   sandboxes: Schema.Array(Schema.String),
 }).annotate({ identifier: "Project" })

+ 3 - 3
packages/schema/src/prompt.ts

@@ -1,5 +1,5 @@
 import { Schema } from "effect"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export interface Source extends Schema.Schema.Type<typeof Source> {}
 export const Source = Schema.Struct({
@@ -18,7 +18,7 @@ export const FileAttachment = Schema.Struct({
 })
   .annotate({ identifier: "Prompt.FileAttachment" })
   .pipe(
-    withStatics((schema) => ({
+    statics((schema) => ({
       create: (input: FileAttachment) =>
         schema.make({
           uri: input.uri,
@@ -44,7 +44,7 @@ export const Prompt = Schema.Struct({
 })
   .annotate({ identifier: "Prompt" })
   .pipe(
-    withStatics((schema) => ({
+    statics((schema) => ({
       equivalence: Schema.toEquivalence(schema),
       fromUserMessage: (input: Pick<Prompt, "text" | "files" | "agents">) =>
         schema.make({

+ 3 - 3
packages/schema/src/provider.ts

@@ -2,11 +2,11 @@ export * as Provider from "./provider"
 
 import { Schema } from "effect"
 import { Integration } from "./integration"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ID = Schema.String.pipe(
   Schema.brand("ProviderV2.ID"),
-  withStatics((schema) => ({
+  statics((schema) => ({
     opencode: schema.make("opencode"),
     anthropic: schema.make("anthropic"),
     openai: schema.make("openai"),
@@ -57,7 +57,7 @@ export const Info = Schema.Struct({
 })
   .annotate({ identifier: "ProviderV2.Info" })
   .pipe(
-    withStatics((schema) => ({
+    statics((schema) => ({
       empty: (id: ID) =>
         schema.make({
           id,

+ 2 - 5
packages/schema/src/pty.ts

@@ -3,13 +3,12 @@ export * as Pty from "./pty"
 import { Schema } from "effect"
 import { define, inventory } from "./event"
 import { ascending } from "./identifier"
-import { NonNegativeInt, PositiveInt } from "./schema"
-import { withStatics } from "./schema"
+import { NonNegativeInt, PositiveInt, statics } from "./schema"
 
 const IDSchema = Schema.String.check(Schema.isStartsWith("pty")).pipe(Schema.brand("PtyID"))
 
 export const ID = IDSchema.pipe(
-  withStatics((schema: typeof IDSchema) => ({
+  statics((schema: typeof IDSchema) => ({
     ascending: (id?: string) => schema.make(id ?? "pty_" + ascending()),
   })),
 )
@@ -25,14 +24,12 @@ export const Info = Schema.Struct({
   pid: NonNegativeInt,
   exitCode: Schema.optional(NonNegativeInt),
 }).annotate({ identifier: "Pty" })
-export const PtyInfo = Info
 
 const Created = define({ type: "pty.created", schema: { info: Info } })
 const Updated = define({ type: "pty.updated", schema: { info: Info } })
 const Exited = define({ type: "pty.exited", schema: { id: ID, exitCode: NonNegativeInt } })
 const Deleted = define({ type: "pty.deleted", schema: { id: ID } })
 export const Event = { Created, Updated, Exited, Deleted, Definitions: inventory(Created, Updated, Exited, Deleted) }
-export const PtyEvent = Event
 
 export const CreateInput = Schema.Struct({
   command: Schema.optional(Schema.String),

+ 2 - 2
packages/schema/src/question-v1.ts

@@ -3,13 +3,13 @@ export * as QuestionV1 from "./question-v1"
 import { Schema } from "effect"
 import { define, inventory } from "./event"
 import { ascending } from "./identifier"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 import { SessionID } from "./session-id"
 import { SessionV1 } from "./session-v1"
 
 export const ID = Schema.String.check(Schema.isStartsWith("que")).pipe(
   Schema.brand("QuestionID"),
-  withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
+  statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
 )
 
 export const Option = Schema.Struct({

+ 2 - 2
packages/schema/src/question.ts

@@ -4,11 +4,11 @@ import { Schema } from "effect"
 import { define, inventory } from "./event"
 import { ascending } from "./identifier"
 import { SessionID } from "./session-id"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const ID = Schema.String.check(Schema.isStartsWith("que")).pipe(
   Schema.brand("QuestionV2.ID"),
-  withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
+  statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "que_" + ascending()) })),
 )
 export type ID = typeof ID.Type
 

+ 2 - 2
packages/schema/src/revert.ts

@@ -2,7 +2,7 @@ export * as Revert from "./revert"
 
 import { Schema } from "effect"
 import { NonNegativeInt, RelativePath } from "./schema"
-import { SessionMessageID } from "./session-message-id"
+import { SessionMessage } from "./session-message"
 
 export const FileDiff = Schema.Struct({
   path: RelativePath,
@@ -14,7 +14,7 @@ export const FileDiff = Schema.Struct({
 export type FileDiff = typeof FileDiff.Type
 
 export const State = Schema.Struct({
-  messageID: SessionMessageID.ID,
+  messageID: SessionMessage.ID,
   partID: Schema.String.pipe(Schema.optional),
   snapshot: Schema.String.pipe(Schema.optional),
   diff: Schema.String.pipe(Schema.optional),

+ 2 - 2
packages/schema/src/schema.ts

@@ -9,7 +9,7 @@ export type RelativePath = typeof RelativePath.Type
 export const AbsolutePath = Schema.String.pipe(Schema.brand("AbsolutePath"))
 export type AbsolutePath = typeof AbsolutePath.Type
 
-export const optionalOmitUndefined = <S extends Schema.Top>(schema: S) =>
+export const optional = <S extends Schema.Top>(schema: S) =>
   Schema.optionalKey(schema).pipe(
     Schema.decodeTo(Schema.optional(schema), {
       decode: SchemaGetter.passthrough({ strict: false }),
@@ -17,7 +17,7 @@ export const optionalOmitUndefined = <S extends Schema.Top>(schema: S) =>
     }),
   )
 
-export const withStatics =
+export const statics =
   <S extends object, M extends Record<string, unknown>>(methods: (schema: S) => M) =>
   (schema: S): S & M =>
     Object.assign(schema, methods(schema))

+ 20 - 21
packages/schema/src/session-event.ts

@@ -9,7 +9,6 @@ import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema"
 import { FileAttachment, Prompt } from "./prompt"
 import { SessionID } from "./session-id"
 import { Location } from "./location"
-import { SessionMessageID } from "./session-message-id"
 import { SessionMessage } from "./session-message"
 import { Revert } from "./revert"
 
@@ -30,7 +29,7 @@ const Base = {
 }
 const PromptFields = {
   ...Base,
-  messageID: SessionMessageID.ID,
+  messageID: SessionMessage.ID,
   prompt: Prompt,
   delivery: Delivery,
 }
@@ -56,7 +55,7 @@ export const AgentSwitched = Event.define({
   ...options,
   schema: {
     ...Base,
-    messageID: SessionMessageID.ID,
+    messageID: SessionMessage.ID,
     agent: Schema.String,
   },
 })
@@ -67,7 +66,7 @@ export const ModelSwitched = Event.define({
   ...options,
   schema: {
     ...Base,
-    messageID: SessionMessageID.ID,
+    messageID: SessionMessage.ID,
     model: Model.Ref,
   },
 })
@@ -103,7 +102,7 @@ export const ContextUpdated = Event.define({
   ...options,
   schema: {
     ...Base,
-    messageID: SessionMessageID.ID,
+    messageID: SessionMessage.ID,
     text: Schema.String,
   },
 })
@@ -114,7 +113,7 @@ export const Synthetic = Event.define({
   ...options,
   schema: {
     ...Base,
-    messageID: SessionMessageID.ID,
+    messageID: SessionMessage.ID,
     text: Schema.String,
   },
 })
@@ -126,7 +125,7 @@ export namespace Shell {
     ...options,
     schema: {
       ...Base,
-      messageID: SessionMessageID.ID,
+      messageID: SessionMessage.ID,
       callID: Schema.String,
       command: Schema.String,
     },
@@ -151,7 +150,7 @@ export namespace Step {
     ...options,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       agent: Schema.String,
       model: Model.Ref,
       snapshot: Schema.String.pipe(Schema.optional),
@@ -164,7 +163,7 @@ export namespace Step {
     ...stepSettlementOptions,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       finish: Schema.String,
       cost: Schema.Finite,
       tokens: Schema.Struct({
@@ -187,7 +186,7 @@ export namespace Step {
     ...stepSettlementOptions,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       error: UnknownError,
     },
   })
@@ -200,7 +199,7 @@ export namespace Text {
     ...options,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       textID: Schema.String,
     },
   })
@@ -211,7 +210,7 @@ export namespace Text {
     type: "session.next.text.delta",
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       textID: Schema.String,
       delta: Schema.String,
     },
@@ -223,7 +222,7 @@ export namespace Text {
     ...options,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       textID: Schema.String,
       text: Schema.String,
     },
@@ -237,7 +236,7 @@ export namespace Reasoning {
     ...options,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       reasoningID: Schema.String,
       providerMetadata: ProviderMetadata.pipe(Schema.optional),
     },
@@ -249,7 +248,7 @@ export namespace Reasoning {
     type: "session.next.reasoning.delta",
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       reasoningID: Schema.String,
       delta: Schema.String,
     },
@@ -261,7 +260,7 @@ export namespace Reasoning {
     ...options,
     schema: {
       ...Base,
-      assistantMessageID: SessionMessageID.ID,
+      assistantMessageID: SessionMessage.ID,
       reasoningID: Schema.String,
       text: Schema.String,
       providerMetadata: ProviderMetadata.pipe(Schema.optional),
@@ -273,7 +272,7 @@ export namespace Reasoning {
 export namespace Tool {
   const ToolBase = {
     ...Base,
-    assistantMessageID: SessionMessageID.ID,
+    assistantMessageID: SessionMessage.ID,
     callID: Schema.String,
   }
 
@@ -401,7 +400,7 @@ export namespace Compaction {
     ...options,
     schema: {
       ...Base,
-      messageID: SessionMessageID.ID,
+      messageID: SessionMessage.ID,
       reason: Schema.Union([Schema.Literal("auto"), Schema.Literal("manual")]),
     },
   })
@@ -411,7 +410,7 @@ export namespace Compaction {
     type: "session.next.compaction.delta",
     schema: {
       ...Base,
-      messageID: SessionMessageID.ID,
+      messageID: SessionMessage.ID,
       text: Schema.String,
     },
   })
@@ -422,7 +421,7 @@ export namespace Compaction {
     ...options,
     schema: {
       ...Base,
-      messageID: SessionMessageID.ID,
+      messageID: SessionMessage.ID,
       reason: Started.data.fields.reason,
       text: Schema.String,
       recent: Schema.String,
@@ -441,7 +440,7 @@ export namespace RevertEvent {
   export const Committed = Event.define({
     type: "session.next.revert.committed",
     ...options,
-    schema: { ...Base, messageID: SessionMessageID.ID },
+    schema: { ...Base, messageID: SessionMessage.ID },
   })
 }
 

+ 2 - 2
packages/schema/src/session-id.ts

@@ -1,10 +1,10 @@
 import { Schema } from "effect"
 import { descending } from "./identifier"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const SessionID = Schema.String.check(Schema.isStartsWith("ses")).pipe(
   Schema.brand("SessionID"),
-  withStatics((schema) => {
+  statics((schema) => {
     const create = () => schema.make("ses_" + descending())
     return {
       create,

+ 2 - 2
packages/schema/src/session-input.ts

@@ -5,7 +5,7 @@ import { Prompt } from "./prompt"
 import { DateTimeUtcFromMillis, NonNegativeInt } from "./schema"
 import { SessionDelivery } from "./session-delivery"
 import { SessionID } from "./session-id"
-import { SessionMessageID } from "./session-message-id"
+import { SessionMessage } from "./session-message"
 
 export const Delivery = SessionDelivery.Delivery
 export type Delivery = SessionDelivery.Delivery
@@ -13,7 +13,7 @@ export type Delivery = SessionDelivery.Delivery
 export interface Admitted extends Schema.Schema.Type<typeof Admitted> {}
 export const Admitted = Schema.Struct({
   admittedSeq: NonNegativeInt,
-  id: SessionMessageID.ID,
+  id: SessionMessage.ID,
   sessionID: SessionID,
   prompt: Prompt,
   delivery: Delivery,

+ 0 - 11
packages/schema/src/session-message-id.ts

@@ -1,11 +0,0 @@
-export * as SessionMessageID from "./session-message-id"
-
-import { Schema } from "effect"
-import { ascending } from "./identifier"
-import { withStatics } from "./schema"
-
-export const ID = Schema.String.check(Schema.isStartsWith("msg_")).pipe(
-  Schema.brand("Session.Message.ID"),
-  withStatics((schema) => ({ create: () => schema.make("msg_" + ascending()) })),
-)
-export type ID = typeof ID.Type

+ 7 - 4
packages/schema/src/session-message.ts

@@ -4,12 +4,15 @@ import { Schema } from "effect"
 import { ProviderMetadata, ToolContent } from "./llm"
 import { Model } from "./model"
 import { FileAttachment, Prompt } from "./prompt"
-import { DateTimeUtcFromMillis, RelativePath } from "./schema"
+import { DateTimeUtcFromMillis, RelativePath, statics } from "./schema"
 import { SessionID } from "./session-id"
-import { SessionMessageID } from "./session-message-id"
+import { ascending } from "./identifier"
 
-export const ID = SessionMessageID.ID
-export type ID = SessionMessageID.ID
+export const ID = Schema.String.check(Schema.isStartsWith("msg_")).pipe(
+  Schema.brand("Session.Message.ID"),
+  statics((schema) => ({ create: () => schema.make("msg_" + ascending()) })),
+)
+export type ID = typeof ID.Type
 
 export interface UnknownError extends Schema.Schema.Type<typeof UnknownError> {}
 export const UnknownError = Schema.Struct({

+ 0 - 1
packages/schema/src/session-todo.ts

@@ -12,7 +12,6 @@ export const Info = Schema.Struct({
   priority: Schema.String.annotate({ description: "Priority level of the task: high, medium, low" }),
 }).annotate({ identifier: "Todo" })
 export type Info = typeof Info.Type
-export const SessionTodoInfo = Info
 
 const Updated = define({
   type: "todo.updated",

+ 22 - 22
packages/schema/src/session-v1.ts

@@ -7,7 +7,7 @@ import { PermissionV1 } from "./permission-v1"
 import { Project } from "./project"
 import { Provider } from "./provider"
 import { Model } from "./model"
-import { NonNegativeInt, optionalOmitUndefined, withStatics } from "./schema"
+import { NonNegativeInt, optional, statics } from "./schema"
 import { ascending } from "./identifier"
 import { SessionID } from "./session-id"
 import { WorkspaceID } from "./workspace-id"
@@ -16,13 +16,13 @@ const Timestamp = Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0))
 
 export const MessageID = Schema.String.check(Schema.isStartsWith("msg")).pipe(
   Schema.brand("MessageID"),
-  withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "msg_" + ascending()) })),
+  statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "msg_" + ascending()) })),
 )
 export type MessageID = typeof MessageID.Type
 
 export const PartID = Schema.String.check(Schema.isStartsWith("prt")).pipe(
   Schema.brand("PartID"),
-  withStatics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "prt_" + ascending()) })),
+  statics((schema) => ({ ascending: (id?: string) => schema.make(id ?? "prt_" + ascending()) })),
 )
 export type PartID = typeof PartID.Type
 
@@ -510,7 +510,7 @@ const SessionSummary = Schema.Struct({
   additions: Schema.Finite,
   deletions: Schema.Finite,
   files: Schema.Finite,
-  diffs: optionalOmitUndefined(Schema.Array(FileDiff.Info)),
+  diffs: optional(Schema.Array(FileDiff.Info)),
 })
 
 const SessionTokens = Schema.Struct({
@@ -529,42 +529,42 @@ const SessionShare = Schema.Struct({
 
 const SessionRevert = Schema.Struct({
   messageID: MessageID,
-  partID: optionalOmitUndefined(PartID),
-  snapshot: optionalOmitUndefined(Schema.String),
-  diff: optionalOmitUndefined(Schema.String),
+  partID: optional(PartID),
+  snapshot: optional(Schema.String),
+  diff: optional(Schema.String),
 })
 
 const SessionModel = Schema.Struct({
   id: Model.ID,
   providerID: Provider.ID,
-  variant: optionalOmitUndefined(Schema.String),
+  variant: optional(Schema.String),
 })
 
 export const SessionInfo = Schema.Struct({
   id: SessionID,
   slug: Schema.String,
   projectID: Project.ID,
-  workspaceID: optionalOmitUndefined(WorkspaceID),
+  workspaceID: optional(WorkspaceID),
   directory: Schema.String,
-  path: optionalOmitUndefined(Schema.String),
-  parentID: optionalOmitUndefined(SessionID),
-  summary: optionalOmitUndefined(SessionSummary),
-  cost: optionalOmitUndefined(Schema.Finite),
-  tokens: optionalOmitUndefined(SessionTokens),
-  share: optionalOmitUndefined(SessionShare),
+  path: optional(Schema.String),
+  parentID: optional(SessionID),
+  summary: optional(SessionSummary),
+  cost: optional(Schema.Finite),
+  tokens: optional(SessionTokens),
+  share: optional(SessionShare),
   title: Schema.String,
-  agent: optionalOmitUndefined(Schema.String),
-  model: optionalOmitUndefined(SessionModel),
+  agent: optional(Schema.String),
+  model: optional(SessionModel),
   version: Schema.String,
-  metadata: optionalOmitUndefined(Schema.Record(Schema.String, Schema.Any)),
+  metadata: optional(Schema.Record(Schema.String, Schema.Any)),
   time: Schema.Struct({
     created: NonNegativeInt,
     updated: NonNegativeInt,
-    compacting: optionalOmitUndefined(NonNegativeInt),
-    archived: optionalOmitUndefined(Schema.Finite),
+    compacting: optional(NonNegativeInt),
+    archived: optional(Schema.Finite),
   }),
-  permission: optionalOmitUndefined(PermissionV1.Ruleset),
-  revert: optionalOmitUndefined(SessionRevert),
+  permission: optional(PermissionV1.Ruleset),
+  revert: optional(SessionRevert),
 }).annotate({ identifier: "Session" })
 export type SessionInfo = typeof SessionInfo.Type
 

+ 2 - 2
packages/schema/src/session.ts

@@ -5,7 +5,7 @@ import { Agent } from "./agent"
 import { Location } from "./location"
 import { Model } from "./model"
 import { Project } from "./project"
-import { DateTimeUtcFromMillis, optionalOmitUndefined, RelativePath } from "./schema"
+import { DateTimeUtcFromMillis, optional, RelativePath } from "./schema"
 import { SessionEvent } from "./session-event"
 import { SessionID } from "./session-id"
 import { Revert } from "./revert"
@@ -18,7 +18,7 @@ export const Event = SessionEvent
 export interface Info extends Schema.Schema.Type<typeof Info> {}
 export const Info = Schema.Struct({
   id: ID,
-  parentID: ID.pipe(optionalOmitUndefined),
+  parentID: ID.pipe(optional),
   projectID: Project.ID,
   agent: Agent.ID.pipe(Schema.optional),
   model: Model.Ref.pipe(Schema.optional),

+ 2 - 2
packages/schema/src/workspace-id.ts

@@ -1,10 +1,10 @@
 import { Schema } from "effect"
 import { ascending } from "./identifier"
-import { withStatics } from "./schema"
+import { statics } from "./schema"
 
 export const WorkspaceID = Schema.String.check(Schema.isStartsWith("wrk")).pipe(
   Schema.brand("WorkspaceV2.ID"),
-  withStatics((schema) => {
+  statics((schema) => {
     const create = () => schema.make("wrk_" + ascending())
     return {
       ascending: (id?: string) => {

+ 1 - 1
specs/v2/instructions.md

@@ -92,7 +92,7 @@ Use Effect schemas as the public contract:
 - branded schemas for ids
 - `Schema.Class` or `Schema.Struct` for domain data
 - `Schema.TaggedErrorClass` for expected errors
-- existing core helpers like `DeepMutable`, `withStatics`, and integer schemas where appropriate
+- existing core helpers like `DeepMutable`, `statics`, and integer schemas where appropriate
 
 Prefer `Info` objects as the stored domain records. Add static `empty(...)` constructors when update APIs need to create records on first mutation.
 

+ 1 - 1
specs/v2/provider-model.md

@@ -5,7 +5,7 @@
 ```ts
 export const ID = Schema.String.pipe(
   Schema.brand("ProviderV2.ID"),
-  withStatics((schema) => ({
+  statics((schema) => ({
     opencode: schema.make("opencode"),
     anthropic: schema.make("anthropic"),
     openai: schema.make("openai"),