Drop prompt input Zod statics (#26923)
This commit is contained in:
parent
9067218b74
commit
cc95197d72
4 changed files with 16 additions and 52 deletions
|
|
@ -46,8 +46,6 @@ import { Truncate } from "@/tool/truncate"
|
||||||
import { decodeDataUrl } from "@/util/data-url"
|
import { decodeDataUrl } from "@/util/data-url"
|
||||||
import { Process } from "@/util/process"
|
import { Process } from "@/util/process"
|
||||||
import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema, Types } from "effect"
|
import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema, Types } from "effect"
|
||||||
import { zod } from "@opencode-ai/core/effect-zod"
|
|
||||||
import { withStatics } from "@opencode-ai/core/schema"
|
|
||||||
import * as EffectLogger from "@opencode-ai/core/effect/logger"
|
import * as EffectLogger from "@opencode-ai/core/effect/logger"
|
||||||
import { InstanceState } from "@/effect/instance-state"
|
import { InstanceState } from "@/effect/instance-state"
|
||||||
import { TaskTool, type TaskPromptOps } from "@/tool/task"
|
import { TaskTool, type TaskPromptOps } from "@/tool/task"
|
||||||
|
|
@ -2054,14 +2052,12 @@ export const PromptInput = Schema.Struct({
|
||||||
MessageV2.SubtaskPartInput,
|
MessageV2.SubtaskPartInput,
|
||||||
]).annotate({ discriminator: "type" }),
|
]).annotate({ discriminator: "type" }),
|
||||||
),
|
),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export type PromptInput = Schema.Schema.Type<typeof PromptInput>
|
export type PromptInput = Schema.Schema.Type<typeof PromptInput>
|
||||||
|
|
||||||
export class LoopInput extends Schema.Class<LoopInput>("SessionPrompt.LoopInput")({
|
export class LoopInput extends Schema.Class<LoopInput>("SessionPrompt.LoopInput")({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
}) {
|
}) {}
|
||||||
static readonly zod = zod(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ShellInput = Schema.Struct({
|
export const ShellInput = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
|
|
@ -2069,7 +2065,7 @@ export const ShellInput = Schema.Struct({
|
||||||
agent: Schema.String,
|
agent: Schema.String,
|
||||||
model: Schema.optional(ModelRef),
|
model: Schema.optional(ModelRef),
|
||||||
command: Schema.String,
|
command: Schema.String,
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export type ShellInput = Schema.Schema.Type<typeof ShellInput>
|
export type ShellInput = Schema.Schema.Type<typeof ShellInput>
|
||||||
|
|
||||||
export const CommandInput = Schema.Struct({
|
export const CommandInput = Schema.Struct({
|
||||||
|
|
@ -2097,7 +2093,7 @@ export const CommandInput = Schema.Struct({
|
||||||
]).annotate({ discriminator: "type" }),
|
]).annotate({ discriminator: "type" }),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export type CommandInput = Schema.Schema.Type<typeof CommandInput>
|
export type CommandInput = Schema.Schema.Type<typeof CommandInput>
|
||||||
|
|
||||||
/** @internal Exported for testing */
|
/** @internal Exported for testing */
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,7 @@ import type { Provider } from "@/provider/provider"
|
||||||
import { Permission } from "@/permission"
|
import { Permission } from "@/permission"
|
||||||
import { Global } from "@opencode-ai/core/global"
|
import { Global } from "@opencode-ai/core/global"
|
||||||
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
|
import { Effect, Layer, Option, Context, Schema, Types } from "effect"
|
||||||
import { zod } from "@opencode-ai/core/effect-zod"
|
import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||||
import { NonNegativeInt, optionalOmitUndefined, withStatics } from "@opencode-ai/core/schema"
|
|
||||||
|
|
||||||
const log = Log.create({ service: "session" })
|
const log = Log.create({ service: "session" })
|
||||||
|
|
||||||
|
|
@ -193,26 +192,20 @@ export const Info = Schema.Struct({
|
||||||
time: Time,
|
time: Time,
|
||||||
permission: optionalOmitUndefined(Permission.Ruleset),
|
permission: optionalOmitUndefined(Permission.Ruleset),
|
||||||
revert: optionalOmitUndefined(Revert),
|
revert: optionalOmitUndefined(Revert),
|
||||||
})
|
}).annotate({ identifier: "Session" })
|
||||||
.annotate({ identifier: "Session" })
|
|
||||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
|
||||||
export type Info = Types.DeepMutable<Schema.Schema.Type<typeof Info>>
|
export type Info = Types.DeepMutable<Schema.Schema.Type<typeof Info>>
|
||||||
|
|
||||||
export const ProjectInfo = Schema.Struct({
|
export const ProjectInfo = Schema.Struct({
|
||||||
id: ProjectID,
|
id: ProjectID,
|
||||||
name: optionalOmitUndefined(Schema.String),
|
name: optionalOmitUndefined(Schema.String),
|
||||||
worktree: Schema.String,
|
worktree: Schema.String,
|
||||||
})
|
}).annotate({ identifier: "ProjectSummary" })
|
||||||
.annotate({ identifier: "ProjectSummary" })
|
|
||||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
|
||||||
export type ProjectInfo = Types.DeepMutable<Schema.Schema.Type<typeof ProjectInfo>>
|
export type ProjectInfo = Types.DeepMutable<Schema.Schema.Type<typeof ProjectInfo>>
|
||||||
|
|
||||||
export const GlobalInfo = Schema.Struct({
|
export const GlobalInfo = Schema.Struct({
|
||||||
...Info.fields,
|
...Info.fields,
|
||||||
project: Schema.NullOr(ProjectInfo),
|
project: Schema.NullOr(ProjectInfo),
|
||||||
})
|
}).annotate({ identifier: "GlobalSession" })
|
||||||
.annotate({ identifier: "GlobalSession" })
|
|
||||||
.pipe(withStatics((s) => ({ zod: zod(s) })))
|
|
||||||
export type GlobalInfo = Types.DeepMutable<Schema.Schema.Type<typeof GlobalInfo>>
|
export type GlobalInfo = Types.DeepMutable<Schema.Schema.Type<typeof GlobalInfo>>
|
||||||
|
|
||||||
export const CreateInput = Schema.optional(
|
export const CreateInput = Schema.optional(
|
||||||
|
|
@ -224,36 +217,34 @@ export const CreateInput = Schema.optional(
|
||||||
permission: Schema.optional(Permission.Ruleset),
|
permission: Schema.optional(Permission.Ruleset),
|
||||||
workspaceID: Schema.optional(WorkspaceID),
|
workspaceID: Schema.optional(WorkspaceID),
|
||||||
}),
|
}),
|
||||||
).pipe(withStatics((s) => ({ zod: zod(s) })))
|
)
|
||||||
export type CreateInput = Types.DeepMutable<Schema.Schema.Type<typeof CreateInput>>
|
export type CreateInput = Types.DeepMutable<Schema.Schema.Type<typeof CreateInput>>
|
||||||
|
|
||||||
export const ForkInput = Schema.Struct({
|
export const ForkInput = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
messageID: Schema.optional(MessageID),
|
messageID: Schema.optional(MessageID),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export const GetInput = SessionID
|
export const GetInput = SessionID
|
||||||
export const ChildrenInput = SessionID
|
export const ChildrenInput = SessionID
|
||||||
export const RemoveInput = SessionID
|
export const RemoveInput = SessionID
|
||||||
export const SetTitleInput = Schema.Struct({ sessionID: SessionID, title: Schema.String }).pipe(
|
export const SetTitleInput = Schema.Struct({ sessionID: SessionID, title: Schema.String })
|
||||||
withStatics((s) => ({ zod: zod(s) })),
|
|
||||||
)
|
|
||||||
export const SetArchivedInput = Schema.Struct({
|
export const SetArchivedInput = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
time: Schema.optional(ArchivedTimestamp),
|
time: Schema.optional(ArchivedTimestamp),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export const SetPermissionInput = Schema.Struct({
|
export const SetPermissionInput = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
permission: Permission.Ruleset,
|
permission: Permission.Ruleset,
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export const SetRevertInput = Schema.Struct({
|
export const SetRevertInput = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
revert: Schema.optional(Revert),
|
revert: Schema.optional(Revert),
|
||||||
summary: Schema.optional(Summary),
|
summary: Schema.optional(Summary),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export const MessagesInput = Schema.Struct({
|
export const MessagesInput = Schema.Struct({
|
||||||
sessionID: SessionID,
|
sessionID: SessionID,
|
||||||
limit: Schema.optional(NonNegativeInt),
|
limit: Schema.optional(NonNegativeInt),
|
||||||
}).pipe(withStatics((s) => ({ zod: zod(s) })))
|
})
|
||||||
export type ListInput = {
|
export type ListInput = {
|
||||||
directory?: string
|
directory?: string
|
||||||
scope?: "project"
|
scope?: "project"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import z from "zod"
|
|
||||||
import { Instance } from "../../src/project/instance"
|
|
||||||
import { WithInstance } from "../../src/project/with-instance"
|
import { WithInstance } from "../../src/project/with-instance"
|
||||||
import { Project } from "@/project/project"
|
import { Project } from "@/project/project"
|
||||||
import { Session as SessionNs } from "@/session/session"
|
import { Session as SessionNs } from "@/session/session"
|
||||||
|
|
@ -19,7 +17,7 @@ const svc = {
|
||||||
create(input?: SessionNs.CreateInput) {
|
create(input?: SessionNs.CreateInput) {
|
||||||
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
return run(SessionNs.Service.use((svc) => svc.create(input)))
|
||||||
},
|
},
|
||||||
setArchived(input: z.output<typeof SessionNs.SetArchivedInput.zod>) {
|
setArchived(input: typeof SessionNs.SetArchivedInput.Type) {
|
||||||
return run(SessionNs.Service.use((svc) => svc.setArchived(input)))
|
return run(SessionNs.Service.use((svc) => svc.setArchived(input)))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ describe("Session.Info", () => {
|
||||||
time: { created: 1, updated: 2 },
|
time: { created: 1, updated: 2 },
|
||||||
}
|
}
|
||||||
expect(decode(input)).toEqual(input)
|
expect(decode(input)).toEqual(input)
|
||||||
expect(Session.Info.zod.parse(input)).toEqual(input)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("round-trips every optional field", () => {
|
test("round-trips every optional field", () => {
|
||||||
|
|
@ -80,7 +79,6 @@ describe("Session.Info", () => {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expect(decode(input)).toEqual(input)
|
expect(decode(input)).toEqual(input)
|
||||||
expect(Session.Info.zod.parse(input)).toEqual(input)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("accepts migrated summary diffs without file details", () => {
|
test("accepts migrated summary diffs without file details", () => {
|
||||||
|
|
@ -100,19 +98,16 @@ describe("Session.Info", () => {
|
||||||
time: { created: 1, updated: 2 },
|
time: { created: 1, updated: 2 },
|
||||||
}
|
}
|
||||||
expect(decode(input)).toEqual(input)
|
expect(decode(input)).toEqual(input)
|
||||||
expect(Session.Info.zod.parse(input)).toEqual(input)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("rejects unbranded session id", () => {
|
test("rejects unbranded session id", () => {
|
||||||
const bad = { id: "not-a-session-id" } as unknown
|
const bad = { id: "not-a-session-id" } as unknown
|
||||||
expect(() => decode(bad)).toThrow()
|
expect(() => decode(bad)).toThrow()
|
||||||
expect(() => Session.Info.zod.parse(bad)).toThrow()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("rejects missing required fields", () => {
|
test("rejects missing required fields", () => {
|
||||||
const bad = { id: sessionID } as unknown
|
const bad = { id: sessionID } as unknown
|
||||||
expect(() => decode(bad)).toThrow()
|
expect(() => decode(bad)).toThrow()
|
||||||
expect(() => Session.Info.zod.parse(bad)).toThrow()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -124,8 +119,6 @@ describe("Session.ProjectInfo", () => {
|
||||||
const withName = { ...noName, name: "alpha" }
|
const withName = { ...noName, name: "alpha" }
|
||||||
expect(decode(noName)).toEqual(noName)
|
expect(decode(noName)).toEqual(noName)
|
||||||
expect(decode(withName)).toEqual(withName)
|
expect(decode(withName)).toEqual(withName)
|
||||||
expect(Session.ProjectInfo.zod.parse(noName)).toEqual(noName)
|
|
||||||
expect(Session.ProjectInfo.zod.parse(withName)).toEqual(withName)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -144,7 +137,6 @@ describe("Session.GlobalInfo", () => {
|
||||||
project: null,
|
project: null,
|
||||||
}
|
}
|
||||||
expect(decode(input)).toEqual(input)
|
expect(decode(input)).toEqual(input)
|
||||||
expect(Session.GlobalInfo.zod.parse(input)).toEqual(input)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("accepts populated project", () => {
|
test("accepts populated project", () => {
|
||||||
|
|
@ -159,7 +151,6 @@ describe("Session.GlobalInfo", () => {
|
||||||
project: { id: projectID, worktree: "/tmp/wt", name: "alpha" },
|
project: { id: projectID, worktree: "/tmp/wt", name: "alpha" },
|
||||||
}
|
}
|
||||||
expect(decode(input)).toEqual(input)
|
expect(decode(input)).toEqual(input)
|
||||||
expect(Session.GlobalInfo.zod.parse(input)).toEqual(input)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -167,7 +158,6 @@ describe("Session input schemas", () => {
|
||||||
test("CreateInput accepts undefined and populated forms", () => {
|
test("CreateInput accepts undefined and populated forms", () => {
|
||||||
const decode = decodeUnknown(Session.CreateInput)
|
const decode = decodeUnknown(Session.CreateInput)
|
||||||
expect(decode(undefined)).toBeUndefined()
|
expect(decode(undefined)).toBeUndefined()
|
||||||
expect(Session.CreateInput.zod.parse(undefined)).toBeUndefined()
|
|
||||||
|
|
||||||
const populated = {
|
const populated = {
|
||||||
parentID: sessionID,
|
parentID: sessionID,
|
||||||
|
|
@ -176,23 +166,19 @@ describe("Session input schemas", () => {
|
||||||
workspaceID,
|
workspaceID,
|
||||||
}
|
}
|
||||||
expect(decode(populated)).toEqual(populated)
|
expect(decode(populated)).toEqual(populated)
|
||||||
expect(Session.CreateInput.zod.parse(populated)).toEqual(populated)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("ForkInput round-trips", () => {
|
test("ForkInput round-trips", () => {
|
||||||
const decode = decodeUnknown(Session.ForkInput)
|
const decode = decodeUnknown(Session.ForkInput)
|
||||||
const input = { sessionID, messageID }
|
const input = { sessionID, messageID }
|
||||||
expect(decode(input)).toEqual(input)
|
expect(decode(input)).toEqual(input)
|
||||||
expect(Session.ForkInput.zod.parse(input)).toEqual(input)
|
|
||||||
// messageID is optional
|
// messageID is optional
|
||||||
const bare = { sessionID }
|
const bare = { sessionID }
|
||||||
expect(decode(bare)).toEqual(bare)
|
expect(decode(bare)).toEqual(bare)
|
||||||
expect(Session.ForkInput.zod.parse(bare)).toEqual(bare)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("SetTitleInput rejects missing title", () => {
|
test("SetTitleInput rejects missing title", () => {
|
||||||
expect(() => decodeUnknown(Session.SetTitleInput)({ sessionID })).toThrow()
|
expect(() => decodeUnknown(Session.SetTitleInput)({ sessionID })).toThrow()
|
||||||
expect(() => Session.SetTitleInput.zod.parse({ sessionID })).toThrow()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("SetArchivedInput accepts both with and without time", () => {
|
test("SetArchivedInput accepts both with and without time", () => {
|
||||||
|
|
@ -282,7 +268,6 @@ describe("SessionPrompt input schemas", () => {
|
||||||
test("LoopInput is just sessionID", () => {
|
test("LoopInput is just sessionID", () => {
|
||||||
const decode = decodeUnknown(SessionPrompt.LoopInput)
|
const decode = decodeUnknown(SessionPrompt.LoopInput)
|
||||||
expect(decode({ sessionID })).toEqual({ sessionID })
|
expect(decode({ sessionID })).toEqual({ sessionID })
|
||||||
expect(SessionPrompt.LoopInput.zod.parse({ sessionID } as unknown)).toEqual({ sessionID })
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("ShellInput requires agent + command", () => {
|
test("ShellInput requires agent + command", () => {
|
||||||
|
|
@ -290,7 +275,6 @@ describe("SessionPrompt input schemas", () => {
|
||||||
const expected = { sessionID, agent: "build", command: "echo hi" }
|
const expected = { sessionID, agent: "build", command: "echo hi" }
|
||||||
const input: unknown = expected
|
const input: unknown = expected
|
||||||
expect(decode(input)).toEqual(expected)
|
expect(decode(input)).toEqual(expected)
|
||||||
expect(SessionPrompt.ShellInput.zod.parse(input as unknown)).toEqual(expected)
|
|
||||||
expect(() => decode({ sessionID })).toThrow()
|
expect(() => decode({ sessionID })).toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -308,9 +292,6 @@ describe("SessionPrompt input schemas", () => {
|
||||||
expect(decoded.parts).toHaveLength(2)
|
expect(decoded.parts).toHaveLength(2)
|
||||||
expect(decoded.parts[0]).toMatchObject({ type: "text", text: "hello" })
|
expect(decoded.parts[0]).toMatchObject({ type: "text", text: "hello" })
|
||||||
expect(decoded.parts[1]).toMatchObject({ type: "file", mime: "image/png" })
|
expect(decoded.parts[1]).toMatchObject({ type: "file", mime: "image/png" })
|
||||||
|
|
||||||
const viaZod = SessionPrompt.PromptInput.zod.parse(input)
|
|
||||||
expect(viaZod.parts).toHaveLength(2)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("PromptInput rejects unknown part type", () => {
|
test("PromptInput rejects unknown part type", () => {
|
||||||
|
|
@ -320,7 +301,6 @@ describe("SessionPrompt input schemas", () => {
|
||||||
parts: [{ type: "nonsense", payload: 42 }],
|
parts: [{ type: "nonsense", payload: 42 }],
|
||||||
}
|
}
|
||||||
expect(() => decode(bad)).toThrow()
|
expect(() => decode(bad)).toThrow()
|
||||||
expect(() => SessionPrompt.PromptInput.zod.parse(bad)).toThrow()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test("CommandInput round-trips core fields", () => {
|
test("CommandInput round-trips core fields", () => {
|
||||||
|
|
@ -332,6 +312,5 @@ describe("SessionPrompt input schemas", () => {
|
||||||
}
|
}
|
||||||
const input: unknown = expected
|
const input: unknown = expected
|
||||||
expect(decode(input)).toEqual(expected)
|
expect(decode(input)).toEqual(expected)
|
||||||
expect(SessionPrompt.CommandInput.zod.parse(input)).toEqual(expected)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue