From 81d81e63341ac1831126ab2cab1c9073fca31cd6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 24 Apr 2026 18:24:42 -0400 Subject: [PATCH] fix sdk generation for date time schemas --- packages/opencode/src/config/permission.ts | 29 +- packages/opencode/src/util/effect-zod.ts | 2 + packages/sdk/js/src/v2/gen/types.gen.ts | 609 ++++++++++++++++++++- 3 files changed, 606 insertions(+), 34 deletions(-) diff --git a/packages/opencode/src/config/permission.ts b/packages/opencode/src/config/permission.ts index fdd5746837..b81a9f7577 100644 --- a/packages/opencode/src/config/permission.ts +++ b/packages/opencode/src/config/permission.ts @@ -1,6 +1,8 @@ export * as ConfigPermission from "./permission" import { Schema, SchemaGetter } from "effect" +import z from "zod" import { zod } from "@/util/effect-zod" +import { ZodOverride } from "@/util/effect-zod" import { withStatics } from "@/util/schema" export const Action = Schema.Literals(["ask", "allow", "deny"]) @@ -18,17 +20,9 @@ export const Rule = Schema.Union([Action, Object]) .pipe(withStatics((s) => ({ zod: zod(s) }))) export type Rule = Schema.Schema.Type -// Known permission keys get explicit types — most are full Rule (either a -// single Action or a per-pattern object), but a handful of tools take no -// sub-target patterns and are Action-only. Unknown keys fall through the -// Record rest signature as Rule. -// -// StructWithRest canonicalises key order on decode (known first, then rest), -// which used to require the `__originalKeys` preprocess hack because -// `Permission.fromConfig` depended on the user's insertion order. That -// dependency is gone — `fromConfig` now sorts top-level keys so wildcard -// permissions come before specifics, making the final precedence -// order-independent. +// Known permission keys get explicit types in the Effect schema for generated +// docs/types. Runtime config parsing uses `InfoZod` below so user key order is +// preserved for permission precedence. const InputObject = Schema.StructWithRest( Schema.Struct({ read: Schema.optional(Rule), @@ -60,6 +54,18 @@ const InputSchema = Schema.Union([Action, InputObject]) const normalizeInput = (input: Schema.Schema.Type): Schema.Schema.Type => typeof input === "string" ? { "*": input } : input +const ACTION_ONLY = new Set(["todowrite", "question", "webfetch", "websearch", "codesearch", "doom_loop"]) + +const InfoZod = z + .union([zod(Action), z.record(z.string(), z.union([zod(Action), z.record(z.string(), zod(Action))]))]) + .transform(normalizeInput) + .superRefine((input, ctx) => { + for (const [key, value] of globalThis.Object.entries(input)) { + if (!ACTION_ONLY.has(key) || typeof value === "string") continue + ctx.addIssue({ code: "custom", message: `${key} must be a permission action`, path: [key] }) + } + }) + export const Info = InputSchema.pipe( Schema.decodeTo(InputObject, { decode: SchemaGetter.transform(normalizeInput), @@ -70,6 +76,7 @@ export const Info = InputSchema.pipe( }), ) .annotate({ identifier: "PermissionConfig" }) + .annotate({ [ZodOverride]: InfoZod }) .pipe( // Walker already emits the decodeTo transform into the derived zod (see // `encoded()` in effect-zod.ts), so just expose that directly. diff --git a/packages/opencode/src/util/effect-zod.ts b/packages/opencode/src/util/effect-zod.ts index 332a5c76eb..1e27353fd3 100644 --- a/packages/opencode/src/util/effect-zod.ts +++ b/packages/opencode/src/util/effect-zod.ts @@ -360,6 +360,8 @@ function array(ast: SchemaAST.Arrays): z.ZodTypeAny { } function decl(ast: SchemaAST.Declaration): z.ZodTypeAny { + const typeConstructor = (ast.annotations as { typeConstructor?: { _tag?: string } }).typeConstructor + if (typeConstructor?._tag === "effect/DateTime.Utc") return z.string().datetime() if (ast.typeParameters.length !== 1) return fail(ast) return walk(ast.typeParameters[0]) } diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index d28ce25794..3bb867ff70 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -987,6 +987,371 @@ export type EventSessionDeleted = { } } +export type SessionEventSource = { + start: number + end: number + text: string +} + +export type SessionEventFileAttachment = { + uri: string + mime: string + name?: string + description?: string + source?: SessionEventSource +} + +export type SessionEventAgentAttachment = { + name: string + source?: SessionEventSource +} + +export type SessionEventPrompt = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "prompt" + text: string + files?: Array + agents?: Array +} + +export type EventPrompt = { + type: "prompt" + properties: SessionEventPrompt +} + +export type SessionEventSynthetic = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "synthetic" + text: string +} + +export type EventSynthetic = { + type: "synthetic" + properties: SessionEventSynthetic +} + +export type SessionEventStepStarted = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "step.started" + model: { + id: string + providerID: string + variant?: string + } +} + +export type EventStepStarted = { + type: "step.started" + properties: SessionEventStepStarted +} + +export type SessionEventStepEnded = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "step.ended" + reason: string + cost: number + tokens: { + input: number + output: number + reasoning: number + cache: { + read: number + write: number + } + } +} + +export type EventStepEnded = { + type: "step.ended" + properties: SessionEventStepEnded +} + +export type SessionEventTextStarted = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "text.started" +} + +export type EventTextStarted = { + type: "text.started" + properties: SessionEventTextStarted +} + +export type SessionEventTextDelta = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "text.delta" + delta: string +} + +export type EventTextDelta = { + type: "text.delta" + properties: SessionEventTextDelta +} + +export type SessionEventTextEnded = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "text.ended" + text: string +} + +export type EventTextEnded = { + type: "text.ended" + properties: SessionEventTextEnded +} + +export type SessionEventReasoningStarted = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "reasoning.started" +} + +export type EventReasoningStarted = { + type: "reasoning.started" + properties: SessionEventReasoningStarted +} + +export type SessionEventReasoningDelta = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "reasoning.delta" + delta: string +} + +export type EventReasoningDelta = { + type: "reasoning.delta" + properties: SessionEventReasoningDelta +} + +export type SessionEventReasoningEnded = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "reasoning.ended" + text: string +} + +export type EventReasoningEnded = { + type: "reasoning.ended" + properties: SessionEventReasoningEnded +} + +export type SessionEventToolInputStarted = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "tool.input.started" + callID: string + name: string +} + +export type EventToolInputStarted = { + type: "tool.input.started" + properties: SessionEventToolInputStarted +} + +export type SessionEventToolInputDelta = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "tool.input.delta" + callID: string + delta: string +} + +export type EventToolInputDelta = { + type: "tool.input.delta" + properties: SessionEventToolInputDelta +} + +export type SessionEventToolInputEnded = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "tool.input.ended" + callID: string + text: string +} + +export type EventToolInputEnded = { + type: "tool.input.ended" + properties: SessionEventToolInputEnded +} + +export type SessionEventToolCalled = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "tool.called" + callID: string + tool: string + input: { + [key: string]: unknown + } + provider: { + executed: boolean + metadata?: { + [key: string]: unknown + } + } +} + +export type EventToolCalled = { + type: "tool.called" + properties: SessionEventToolCalled +} + +export type SessionEventToolSuccess = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "tool.success" + callID: string + title: string + output?: string + attachments?: Array + provider: { + executed: boolean + metadata?: { + [key: string]: unknown + } + } +} + +export type EventToolSuccess = { + type: "tool.success" + properties: SessionEventToolSuccess +} + +export type SessionEventToolError = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "tool.error" + callID: string + error: string + provider: { + executed: boolean + metadata?: { + [key: string]: unknown + } + } +} + +export type EventToolError = { + type: "tool.error" + properties: SessionEventToolError +} + +export type SessionEventRetryError = { + message: string + statusCode?: number + isRetryable: boolean + responseHeaders?: { + [key: string]: string + } + responseBody?: string + metadata?: { + [key: string]: string + } +} + +export type SessionEventRetried = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "retried" + attempt: number + error: SessionEventRetryError +} + +export type EventRetried = { + type: "retried" + properties: SessionEventRetried +} + +export type SessionEventCompacted = { + id: string + sessionID: string + metadata?: { + [key: string]: unknown + } + timestamp: string + type: "compacted" + auto: boolean + overflow?: boolean +} + +export type EventCompacted = { + type: "compacted" + properties: SessionEventCompacted +} + export type SyncEventMessageUpdated = { type: "sync" name: "message.updated.1" @@ -1104,6 +1469,168 @@ export type SyncEventSessionDeleted = { } } +export type SyncEventPrompt = { + type: "sync" + name: "prompt.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventPrompt +} + +export type SyncEventSynthetic = { + type: "sync" + name: "synthetic.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventSynthetic +} + +export type SyncEventStepStarted = { + type: "sync" + name: "step.started.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventStepStarted +} + +export type SyncEventStepEnded = { + type: "sync" + name: "step.ended.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventStepEnded +} + +export type SyncEventTextStarted = { + type: "sync" + name: "text.started.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventTextStarted +} + +export type SyncEventTextDelta = { + type: "sync" + name: "text.delta.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventTextDelta +} + +export type SyncEventTextEnded = { + type: "sync" + name: "text.ended.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventTextEnded +} + +export type SyncEventReasoningStarted = { + type: "sync" + name: "reasoning.started.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventReasoningStarted +} + +export type SyncEventReasoningDelta = { + type: "sync" + name: "reasoning.delta.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventReasoningDelta +} + +export type SyncEventReasoningEnded = { + type: "sync" + name: "reasoning.ended.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventReasoningEnded +} + +export type SyncEventToolInputStarted = { + type: "sync" + name: "tool.input.started.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventToolInputStarted +} + +export type SyncEventToolInputDelta = { + type: "sync" + name: "tool.input.delta.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventToolInputDelta +} + +export type SyncEventToolInputEnded = { + type: "sync" + name: "tool.input.ended.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventToolInputEnded +} + +export type SyncEventToolCalled = { + type: "sync" + name: "tool.called.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventToolCalled +} + +export type SyncEventToolSuccess = { + type: "sync" + name: "tool.success.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventToolSuccess +} + +export type SyncEventToolError = { + type: "sync" + name: "tool.error.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventToolError +} + +export type SyncEventRetried = { + type: "sync" + name: "retried.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventRetried +} + +export type SyncEventCompacted = { + type: "sync" + name: "compacted.1" + id: string + seq: number + aggregateID: "sessionID" + data: SessionEventCompacted +} + export type GlobalEvent = { directory: string project?: string @@ -1156,6 +1683,24 @@ export type GlobalEvent = { | EventSessionCreated | EventSessionUpdated | EventSessionDeleted + | EventPrompt + | EventSynthetic + | EventStepStarted + | EventStepEnded + | EventTextStarted + | EventTextDelta + | EventTextEnded + | EventReasoningStarted + | EventReasoningDelta + | EventReasoningEnded + | EventToolInputStarted + | EventToolInputDelta + | EventToolInputEnded + | EventToolCalled + | EventToolSuccess + | EventToolError + | EventRetried + | EventCompacted | SyncEventMessageUpdated | SyncEventMessageRemoved | SyncEventMessagePartUpdated @@ -1163,6 +1708,24 @@ export type GlobalEvent = { | SyncEventSessionCreated | SyncEventSessionUpdated | SyncEventSessionDeleted + | SyncEventPrompt + | SyncEventSynthetic + | SyncEventStepStarted + | SyncEventStepEnded + | SyncEventTextStarted + | SyncEventTextDelta + | SyncEventTextEnded + | SyncEventReasoningStarted + | SyncEventReasoningDelta + | SyncEventReasoningEnded + | SyncEventToolInputStarted + | SyncEventToolInputDelta + | SyncEventToolInputEnded + | SyncEventToolCalled + | SyncEventToolSuccess + | SyncEventToolError + | SyncEventRetried + | SyncEventCompacted } /** @@ -1198,32 +1761,14 @@ export type ServerConfig = { export type PermissionActionConfig = "ask" | "allow" | "deny" -export type PermissionObjectConfig = { - [key: string]: PermissionActionConfig -} - -export type PermissionRuleConfig = PermissionActionConfig | PermissionObjectConfig - export type PermissionConfig = | PermissionActionConfig | { - read?: PermissionRuleConfig - edit?: PermissionRuleConfig - glob?: PermissionRuleConfig - grep?: PermissionRuleConfig - list?: PermissionRuleConfig - bash?: PermissionRuleConfig - task?: PermissionRuleConfig - external_directory?: PermissionRuleConfig - todowrite?: PermissionActionConfig - question?: PermissionActionConfig - webfetch?: PermissionActionConfig - websearch?: PermissionActionConfig - codesearch?: PermissionActionConfig - lsp?: PermissionRuleConfig - doom_loop?: PermissionActionConfig - skill?: PermissionRuleConfig - [key: string]: PermissionRuleConfig | PermissionActionConfig | undefined + [key: string]: + | PermissionActionConfig + | { + [key: string]: PermissionActionConfig + } } export type AgentConfig = { @@ -2082,6 +2627,24 @@ export type Event = | EventSessionCreated | EventSessionUpdated | EventSessionDeleted + | EventPrompt + | EventSynthetic + | EventStepStarted + | EventStepEnded + | EventTextStarted + | EventTextDelta + | EventTextEnded + | EventReasoningStarted + | EventReasoningDelta + | EventReasoningEnded + | EventToolInputStarted + | EventToolInputDelta + | EventToolInputEnded + | EventToolCalled + | EventToolSuccess + | EventToolError + | EventRetried + | EventCompacted export type McpStatusConnected = { status: "connected"