summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-30 19:28:46 -0400
committerGitHub <[email protected]>2026-04-30 19:28:46 -0400
commite3134a2a995f3e30b9a21a0546ace1e8c4d3cc5d (patch)
tree47bc867e7bc62f2c4a1f2833c5daf99085a58aed
parent8805104b8d3912d93081faf021e70dd15a73613f (diff)
downloadopencode-e3134a2a995f3e30b9a21a0546ace1e8c4d3cc5d.tar.gz
opencode-e3134a2a995f3e30b9a21a0546ace1e8c4d3cc5d.zip
refactor(session): align prompt input types with their schemas (#25178)
-rw-r--r--packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts8
-rw-r--r--packages/opencode/src/session/prompt.ts31
2 files changed, 14 insertions, 25 deletions
diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
index c4def3e74..91afd0045 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts
@@ -269,7 +269,7 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
promptSvc.prompt({
...ctx.payload,
sessionID: ctx.params.sessionID,
- } as unknown as SessionPrompt.PromptInput),
+ }),
),
),
).pipe(
@@ -288,7 +288,7 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
yield* Effect.sync(() => {
bridge.fork(
promptSvc
- .prompt({ ...ctx.payload, sessionID: ctx.params.sessionID } as unknown as SessionPrompt.PromptInput)
+ .prompt({ ...ctx.payload, sessionID: ctx.params.sessionID })
.pipe(
Effect.catchCause((error) =>
Effect.sync(() => {
@@ -309,14 +309,14 @@ export const sessionHandlers = HttpApiBuilder.group(InstanceHttpApi, "session",
params: { sessionID: SessionID }
payload: typeof CommandPayload.Type
}) {
- return yield* promptSvc.command({ ...ctx.payload, sessionID: ctx.params.sessionID } as SessionPrompt.CommandInput)
+ return yield* promptSvc.command({ ...ctx.payload, sessionID: ctx.params.sessionID })
})
const shell = Effect.fn("SessionHttpApi.shell")(function* (ctx: {
params: { sessionID: SessionID }
payload: typeof ShellPayload.Type
}) {
- return yield* promptSvc.shell({ ...ctx.payload, sessionID: ctx.params.sessionID } as SessionPrompt.ShellInput)
+ return yield* promptSvc.shell({ ...ctx.payload, sessionID: ctx.params.sessionID })
})
const revert = Effect.fn("SessionHttpApi.revert")(function* (ctx: {
diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts
index c4d867322..155b86b58 100644
--- a/packages/opencode/src/session/prompt.ts
+++ b/packages/opencode/src/session/prompt.ts
@@ -45,7 +45,7 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem"
import { Truncate } from "@/tool/truncate"
import { decodeDataUrl } from "@/util/data-url"
import { Process } from "@/util/process"
-import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema } from "effect"
+import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema, Types } from "effect"
import { zod } from "@/util/effect-zod"
import { withStatics } from "@/util/schema"
import * as EffectLogger from "@opencode-ai/core/effect/logger"
@@ -127,7 +127,7 @@ export const layer = Layer.effect(
const resolvePromptParts = Effect.fn("SessionPrompt.resolvePromptParts")(function* (template: string) {
const ctx = yield* InstanceState.context
- const parts: PromptInput["parts"] = [{ type: "text", text: template }]
+ const parts: Types.DeepMutable<PromptInput["parts"]> = [{ type: "text", text: template }]
const files = ConfigMarkdown.files(template)
const seen = new Set<string>()
yield* Effect.forEach(
@@ -1012,7 +1012,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
case "file:": {
log.info("file", { mime: part.mime })
const filepath = fileURLToPath(part.url)
- if (yield* fsys.isDir(filepath)) part.mime = "application/x-directory"
+ const mime = (yield* fsys.isDir(filepath)) ? "application/x-directory" : part.mime
const { read } = yield* registry.named()
const execRead = (args: Parameters<typeof read.execute>[0], extra?: Tool.Context["extra"]) => {
@@ -1031,7 +1031,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
.pipe(Effect.onInterrupt(() => Effect.sync(() => controller.abort())))
}
- if (part.mime === "text/plain") {
+ if (mime === "text/plain") {
let offset: number | undefined
let limit: number | undefined
const range = { start: url.searchParams.get("start"), end: url.searchParams.get("end") }
@@ -1089,7 +1089,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
})),
)
} else {
- pieces.push({ ...part, messageID: info.id, sessionID: input.sessionID })
+ pieces.push({ ...part, mime, messageID: info.id, sessionID: input.sessionID })
}
} else {
const error = Cause.squash(exit.cause)
@@ -1110,7 +1110,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
return pieces
}
- if (part.mime === "application/x-directory") {
+ if (mime === "application/x-directory") {
const args = { filePath: filepath }
const exit = yield* execRead(args).pipe(Effect.exit)
if (Exit.isFailure(exit)) {
@@ -1146,7 +1146,7 @@ NOTE: At any point in time through this workflow you should feel free to ask the
synthetic: true,
text: exit.value.output,
},
- { ...part, messageID: info.id, sessionID: input.sessionID },
+ { ...part, mime, messageID: info.id, sessionID: input.sessionID },
]
}
@@ -1164,9 +1164,9 @@ NOTE: At any point in time through this workflow you should feel free to ask the
sessionID: input.sessionID,
type: "file",
url:
- `data:${part.mime};base64,` +
+ `data:${mime};base64,` +
Buffer.from(yield* fsys.readFile(filepath).pipe(Effect.catch(Effect.die))).toString("base64"),
- mime: part.mime,
+ mime,
filename: part.filename!,
source: part.source,
},
@@ -1700,18 +1700,7 @@ export const PromptInput = Schema.Struct({
]).annotate({ discriminator: "type" }),
),
}).pipe(withStatics((s) => ({ zod: zod(s) })))
-// `z.discriminatedUnion` erases the discriminated members' shapes back to
-// `{}` when walked from the generic `z.ZodType` input. Restore the precise
-// `parts` type from the exported Schema input types so callers see a proper
-// tagged union.
-type PartInputUnion =
- | MessageV2.TextPartInput
- | MessageV2.FilePartInput
- | MessageV2.AgentPartInput
- | MessageV2.SubtaskPartInput
-export type PromptInput = Omit<Schema.Schema.Type<typeof PromptInput>, "parts"> & {
- parts: PartInputUnion[]
-}
+export type PromptInput = Schema.Schema.Type<typeof PromptInput>
export class LoopInput extends Schema.Class<LoopInput>("SessionPrompt.LoopInput")({
sessionID: SessionID,