From 41367fa3bacaa67f86b5f6f6f444db60d9c7a23c Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 28 Apr 2026 20:20:36 -0400 Subject: [PATCH] refactor: use Schema.Finite instead of Schema.Number for OpenAPI-facing schemas Schema.Number emits anyOf:[number, string enum(Infinity/-Infinity/NaN)] in OpenAPI because JS numbers include non-finite values. Schema.Finite adds an isFinite check so the OpenAPI output is just {type:"number"}, eliminating the need for post-hoc normalization in public.ts. --- packages/opencode/src/agent/agent.ts | 6 +- packages/opencode/src/auth/index.ts | 2 +- packages/opencode/src/cli/cmd/tui/event.ts | 2 +- packages/opencode/src/config/agent.ts | 4 +- packages/opencode/src/config/mcp.ts | 4 +- packages/opencode/src/lsp/lsp.ts | 4 +- packages/opencode/src/project/project.ts | 6 +- packages/opencode/src/project/vcs.ts | 4 +- packages/opencode/src/provider/auth.ts | 4 +- packages/opencode/src/provider/models.ts | 22 +++---- packages/opencode/src/provider/provider.ts | 18 +++--- packages/opencode/src/pty/index.ts | 8 +-- packages/opencode/src/session/message-v2.ts | 70 ++++++++++----------- packages/opencode/src/session/session.ts | 26 ++++---- packages/opencode/src/session/status.ts | 4 +- packages/opencode/src/snapshot/index.ts | 4 +- packages/opencode/src/storage/storage.ts | 4 +- packages/opencode/src/tool/bash.ts | 2 +- packages/opencode/src/tool/read.ts | 4 +- 19 files changed, 99 insertions(+), 99 deletions(-) diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index 5e839ead5c..81dbded082 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -31,8 +31,8 @@ export const Info = Schema.Struct({ mode: Schema.Literals(["subagent", "primary", "all"]), native: Schema.optional(Schema.Boolean), hidden: Schema.optional(Schema.Boolean), - topP: Schema.optional(Schema.Number), - temperature: Schema.optional(Schema.Number), + topP: Schema.optional(Schema.Finite), + temperature: Schema.optional(Schema.Finite), color: Schema.optional(Schema.String), permission: Permission.Ruleset, model: Schema.optional( @@ -44,7 +44,7 @@ export const Info = Schema.Struct({ variant: Schema.optional(Schema.String), prompt: Schema.optional(Schema.String), options: Schema.Record(Schema.String, Schema.Unknown), - steps: Schema.optional(Schema.Number), + steps: Schema.optional(Schema.Finite), }) .annotate({ identifier: "Agent" }) .pipe(withStatics((s) => ({ zod: zod(s) }))) diff --git a/packages/opencode/src/auth/index.ts b/packages/opencode/src/auth/index.ts index 539c40c1ae..926ab7d36b 100644 --- a/packages/opencode/src/auth/index.ts +++ b/packages/opencode/src/auth/index.ts @@ -14,7 +14,7 @@ export class Oauth extends Schema.Class("OAuth")({ type: Schema.Literal("oauth"), refresh: Schema.String, access: Schema.String, - expires: Schema.Number, + expires: Schema.Finite, accountId: Schema.optional(Schema.String), enterpriseUrl: Schema.optional(Schema.String), }) {} diff --git a/packages/opencode/src/cli/cmd/tui/event.ts b/packages/opencode/src/cli/cmd/tui/event.ts index 1c764c12fe..01fb4535ad 100644 --- a/packages/opencode/src/cli/cmd/tui/event.ts +++ b/packages/opencode/src/cli/cmd/tui/event.ts @@ -38,7 +38,7 @@ export const TuiEvent = { title: Schema.optional(Schema.String), message: Schema.String, variant: Schema.Literals(["info", "success", "warning", "error"]), - duration: Schema.Number.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_TOAST_DURATION))).annotate({ + duration: Schema.Finite.pipe(Schema.withDecodingDefault(Effect.succeed(DEFAULT_TOAST_DURATION))).annotate({ description: "Duration in milliseconds", }), }), diff --git a/packages/opencode/src/config/agent.ts b/packages/opencode/src/config/agent.ts index e673edbad4..e72f658728 100644 --- a/packages/opencode/src/config/agent.ts +++ b/packages/opencode/src/config/agent.ts @@ -26,8 +26,8 @@ const AgentSchema = Schema.StructWithRest( variant: Schema.optional(Schema.String).annotate({ description: "Default model variant for this agent (applies only when using the agent's configured model).", }), - temperature: Schema.optional(Schema.Number), - top_p: Schema.optional(Schema.Number), + temperature: Schema.optional(Schema.Finite), + top_p: Schema.optional(Schema.Finite), prompt: Schema.optional(Schema.String), tools: Schema.optional(Schema.Record(Schema.String, Schema.Boolean)).annotate({ description: "@deprecated Use 'permission' field instead", diff --git a/packages/opencode/src/config/mcp.ts b/packages/opencode/src/config/mcp.ts index 0887fa984a..0fa810019c 100644 --- a/packages/opencode/src/config/mcp.ts +++ b/packages/opencode/src/config/mcp.ts @@ -13,7 +13,7 @@ export const Local = Schema.Struct({ enabled: Schema.optional(Schema.Boolean).annotate({ description: "Enable or disable the MCP server on startup", }), - timeout: Schema.optional(Schema.Number).annotate({ + timeout: Schema.optional(Schema.Finite).annotate({ description: "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.", }), }) @@ -49,7 +49,7 @@ export const Remote = Schema.Struct({ oauth: Schema.optional(Schema.Union([OAuth, Schema.Literal(false)])).annotate({ description: "OAuth authentication configuration for the MCP server. Set to false to disable OAuth auto-detection.", }), - timeout: Schema.optional(Schema.Number).annotate({ + timeout: Schema.optional(Schema.Finite).annotate({ description: "Timeout in ms for MCP server requests. Defaults to 5000 (5 seconds) if not specified.", }), }) diff --git a/packages/opencode/src/lsp/lsp.ts b/packages/opencode/src/lsp/lsp.ts index 45a8189976..cdb284783f 100644 --- a/packages/opencode/src/lsp/lsp.ts +++ b/packages/opencode/src/lsp/lsp.ts @@ -23,8 +23,8 @@ export const Event = { } const Position = Schema.Struct({ - line: Schema.Number, - character: Schema.Number, + line: Schema.Finite, + character: Schema.Finite, }) export const Range = Schema.Struct({ diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 648bfc8fed..a1c3f7eda6 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -35,9 +35,9 @@ const ProjectCommands = Schema.Struct({ }) const ProjectTime = Schema.Struct({ - created: Schema.Number, - updated: Schema.Number, - initialized: Schema.optional(Schema.Number), + created: Schema.Finite, + updated: Schema.Finite, + initialized: Schema.optional(Schema.Finite), }) export const Info = Schema.Struct({ diff --git a/packages/opencode/src/project/vcs.ts b/packages/opencode/src/project/vcs.ts index e12a031d63..b93d136c6d 100644 --- a/packages/opencode/src/project/vcs.ts +++ b/packages/opencode/src/project/vcs.ts @@ -125,8 +125,8 @@ export type Info = Schema.Schema.Type export const FileDiff = Schema.Struct({ file: Schema.String, patch: Schema.String, - additions: Schema.Number, - deletions: Schema.Number, + additions: Schema.Finite, + deletions: Schema.Finite, status: Schema.optional(Schema.Literals(["added", "deleted", "modified"])), }) .annotate({ identifier: "VcsFileDiff" }) diff --git a/packages/opencode/src/provider/auth.ts b/packages/opencode/src/provider/auth.ts index 4df83f0204..6cbfcf1be2 100644 --- a/packages/opencode/src/provider/auth.ts +++ b/packages/opencode/src/provider/auth.ts @@ -58,13 +58,13 @@ export class Authorization extends Schema.Class("ProviderAuthAuth } export const AuthorizeInput = Schema.Struct({ - method: Schema.Number.annotate({ description: "Auth method index" }), + method: Schema.Finite.annotate({ description: "Auth method index" }), inputs: Schema.optional(Schema.Record(Schema.String, Schema.String)).annotate({ description: "Prompt inputs" }), }).pipe(withStatics((s) => ({ zod: zod(s) }))) export type AuthorizeInput = Schema.Schema.Type export const CallbackInput = Schema.Struct({ - method: Schema.Number.annotate({ description: "Auth method index" }), + method: Schema.Finite.annotate({ description: "Auth method index" }), code: Schema.optional(Schema.String).annotate({ description: "OAuth authorization code" }), }).pipe(withStatics((s) => ({ zod: zod(s) }))) export type CallbackInput = Schema.Schema.Type diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index ed2d11eb72..170fe516c9 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -22,16 +22,16 @@ const filepath = path.join( const ttl = 5 * 60 * 1000 const Cost = Schema.Struct({ - input: Schema.Number, - output: Schema.Number, - cache_read: Schema.optional(Schema.Number), - cache_write: Schema.optional(Schema.Number), + input: Schema.Finite, + output: Schema.Finite, + cache_read: Schema.optional(Schema.Finite), + cache_write: Schema.optional(Schema.Finite), context_over_200k: Schema.optional( Schema.Struct({ - input: Schema.Number, - output: Schema.Number, - cache_read: Schema.optional(Schema.Number), - cache_write: Schema.optional(Schema.Number), + input: Schema.Finite, + output: Schema.Finite, + cache_read: Schema.optional(Schema.Finite), + cache_write: Schema.optional(Schema.Finite), }), ), }) @@ -55,9 +55,9 @@ export const Model = Schema.Struct({ ), cost: Schema.optional(Cost), limit: Schema.Struct({ - context: Schema.Number, - input: Schema.optional(Schema.Number), - output: Schema.Number, + context: Schema.Finite, + input: Schema.optional(Schema.Finite), + output: Schema.Finite, }), modalities: Schema.optional( Schema.Struct({ diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index c05d053193..48df5a4c9d 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -848,27 +848,27 @@ const ProviderCapabilities = Schema.Struct({ }) const ProviderCacheCost = Schema.Struct({ - read: Schema.Number, - write: Schema.Number, + read: Schema.Finite, + write: Schema.Finite, }) const ProviderCost = Schema.Struct({ - input: Schema.Number, - output: Schema.Number, + input: Schema.Finite, + output: Schema.Finite, cache: ProviderCacheCost, experimentalOver200K: Schema.optional( Schema.Struct({ - input: Schema.Number, - output: Schema.Number, + input: Schema.Finite, + output: Schema.Finite, cache: ProviderCacheCost, }), ), }) const ProviderLimit = Schema.Struct({ - context: Schema.Number, - input: Schema.optional(Schema.Number), - output: Schema.Number, + context: Schema.Finite, + input: Schema.optional(Schema.Finite), + output: Schema.Finite, }) export const Model = Schema.Struct({ diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts index beccade09b..80f0e077a0 100644 --- a/packages/opencode/src/pty/index.ts +++ b/packages/opencode/src/pty/index.ts @@ -62,7 +62,7 @@ export const Info = Schema.Struct({ args: Schema.Array(Schema.String), cwd: Schema.String, status: Schema.Literals(["running", "exited"]), - pid: Schema.Number, + pid: Schema.Finite, }) .annotate({ identifier: "Pty" }) .pipe(withStatics((s) => ({ zod: zod(s) }))) @@ -83,8 +83,8 @@ export const UpdateInput = Schema.Struct({ title: Schema.optional(Schema.String), size: Schema.optional( Schema.Struct({ - rows: Schema.Number, - cols: Schema.Number, + rows: Schema.Finite, + cols: Schema.Finite, }), ), }).pipe(withStatics((s) => ({ zod: zod(s) }))) @@ -94,7 +94,7 @@ export type UpdateInput = Types.DeepMutable ({ zod: zod(s) }))) export const SetPermissionInput = Schema.Struct({ sessionID: SessionID, @@ -228,7 +228,7 @@ export const SetRevertInput = Schema.Struct({ }).pipe(withStatics((s) => ({ zod: zod(s) }))) export const MessagesInput = Schema.Struct({ sessionID: SessionID, - limit: Schema.optional(Schema.Number), + limit: Schema.optional(Schema.Finite), }).pipe(withStatics((s) => ({ zod: zod(s) }))) const CreatedEventSchema = Schema.Struct({ @@ -241,10 +241,10 @@ const UpdatedShare = Schema.Struct({ }) const UpdatedTime = Schema.Struct({ - created: Schema.optional(Schema.NullOr(Schema.Number)), - updated: Schema.optional(Schema.NullOr(Schema.Number)), - compacting: Schema.optional(Schema.NullOr(Schema.Number)), - archived: Schema.optional(Schema.NullOr(Schema.Number)), + created: Schema.optional(Schema.NullOr(Schema.Finite)), + updated: Schema.optional(Schema.NullOr(Schema.Finite)), + compacting: Schema.optional(Schema.NullOr(Schema.Finite)), + archived: Schema.optional(Schema.NullOr(Schema.Finite)), }) const UpdatedInfo = Schema.Struct({ diff --git a/packages/opencode/src/session/status.ts b/packages/opencode/src/session/status.ts index fdd561b4ae..8ef5a3766e 100644 --- a/packages/opencode/src/session/status.ts +++ b/packages/opencode/src/session/status.ts @@ -13,9 +13,9 @@ export const Info = Schema.Union([ }), Schema.Struct({ type: Schema.Literal("retry"), - attempt: Schema.Number, + attempt: Schema.Finite, message: Schema.String, - next: Schema.Number, + next: Schema.Finite, }), Schema.Struct({ type: Schema.Literal("busy"), diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index cd28377aa7..766b01cd9a 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -22,8 +22,8 @@ export type Patch = typeof Patch.Type export const FileDiff = Schema.Struct({ file: Schema.String, patch: Schema.String, - additions: Schema.Number, - deletions: Schema.Number, + additions: Schema.Finite, + deletions: Schema.Finite, status: Schema.optional(Schema.Literals(["added", "deleted", "modified"])), }) .annotate({ identifier: "SnapshotFileDiff" }) diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts index af18d88b34..655f6c987f 100644 --- a/packages/opencode/src/storage/storage.ts +++ b/packages/opencode/src/storage/storage.ts @@ -41,8 +41,8 @@ const MessageFile = Schema.Struct({ }) const DiffFile = Schema.Struct({ - additions: Schema.Number, - deletions: Schema.Number, + additions: Schema.Finite, + deletions: Schema.Finite, }) const SummaryFile = Schema.Struct({ diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index 82f6e5aaeb..e6c8b39885 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -53,7 +53,7 @@ const SWITCHES = new Set(["-confirm", "-debug", "-force", "-nonewline", "-recurs export const Parameters = Schema.Struct({ command: Schema.String.annotate({ description: "The command to execute" }), - timeout: Schema.optional(Schema.Number).annotate({ description: "Optional timeout in milliseconds" }), + timeout: Schema.optional(Schema.Finite).annotate({ description: "Optional timeout in milliseconds" }), workdir: Schema.optional(Schema.String).annotate({ description: `The working directory to run the command in. Defaults to the current directory. Use this instead of 'cd' commands.`, }), diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 0f528b8f65..7a645fab1a 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -25,10 +25,10 @@ const SAMPLE_BYTES = 4096 // unchanged; purely CLI-facing uses must now send numbers rather than strings. export const Parameters = Schema.Struct({ filePath: Schema.String.annotate({ description: "The absolute path to the file or directory to read" }), - offset: Schema.optional(Schema.Number).annotate({ + offset: Schema.optional(Schema.Finite).annotate({ description: "The line number to start reading from (1-indexed)", }), - limit: Schema.optional(Schema.Number).annotate({ + limit: Schema.optional(Schema.Finite).annotate({ description: "The maximum number of lines to read (defaults to 2000)", }), })