Prepare Effect HttpApi backend parity (#24853)

This commit is contained in:
Kit Langton 2026-04-29 09:34:50 -04:00 committed by GitHub
commit 6015084fa2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
98 changed files with 4294 additions and 2770 deletions

View file

@ -42,7 +42,7 @@ export const OutputLengthError = namedSchemaError("MessageOutputLengthError", {}
export const AbortedError = namedSchemaError("MessageAbortedError", { message: Schema.String })
export const StructuredOutputError = namedSchemaError("StructuredOutputError", {
message: Schema.String,
retries: Schema.Number,
retries: NonNegativeInt,
})
export const AuthError = namedSchemaError("ProviderAuthError", {
providerID: Schema.String,
@ -50,7 +50,7 @@ export const AuthError = namedSchemaError("ProviderAuthError", {
})
export const APIError = namedSchemaError("APIError", {
message: Schema.String,
statusCode: Schema.optional(Schema.Number),
statusCode: Schema.optional(NonNegativeInt),
isRetryable: Schema.Boolean,
responseHeaders: Schema.optional(Schema.Record(Schema.String, Schema.String)),
responseBody: Schema.optional(Schema.String),
@ -116,8 +116,8 @@ export const TextPart = Schema.Struct({
ignored: Schema.optional(Schema.Boolean),
time: Schema.optional(
Schema.Struct({
start: Schema.Number,
end: Schema.optional(Schema.Number),
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
@ -132,8 +132,8 @@ export const ReasoningPart = Schema.Struct({
text: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: Schema.Number,
end: Schema.optional(Schema.Number),
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
})
.annotate({ identifier: "ReasoningPart" })
@ -143,8 +143,8 @@ export type ReasoningPart = Types.DeepMutable<Schema.Schema.Type<typeof Reasonin
const filePartSourceBase = {
text: Schema.Struct({
value: Schema.String,
start: Schema.Int,
end: Schema.Int,
start: NonNegativeInt,
end: NonNegativeInt,
}).annotate({ identifier: "FilePartSourceText" }),
}
@ -162,7 +162,7 @@ export const SymbolSource = Schema.Struct({
path: Schema.String,
range: LSP.Range,
name: Schema.String,
kind: Schema.Int,
kind: NonNegativeInt,
})
.annotate({ identifier: "SymbolSource" })
.pipe(withStatics((s) => ({ zod: zod(s) })))
@ -201,8 +201,8 @@ export const AgentPart = Schema.Struct({
source: Schema.optional(
Schema.Struct({
value: Schema.String,
start: Schema.Int,
end: Schema.Int,
start: NonNegativeInt,
end: NonNegativeInt,
}),
),
})
@ -242,11 +242,10 @@ export type SubtaskPart = Types.DeepMutable<Schema.Schema.Type<typeof SubtaskPar
export const RetryPart = Schema.Struct({
...partBase,
type: Schema.Literal("retry"),
attempt: Schema.Number,
// APIError is still NamedError-based Zod; bridge via ZodOverride until errors migrate.
error: Schema.Any.annotate({ [ZodOverride]: APIError.Schema }),
attempt: NonNegativeInt,
error: APIError.EffectSchema,
time: Schema.Struct({
created: Schema.Number,
created: NonNegativeInt,
}),
})
.annotate({ identifier: "RetryPart" })
@ -269,15 +268,15 @@ export const StepFinishPart = Schema.Struct({
type: Schema.Literal("step-finish"),
reason: Schema.String,
snapshot: Schema.optional(Schema.String),
cost: Schema.Number,
cost: Schema.Finite,
tokens: Schema.Struct({
total: Schema.optional(Schema.Number),
input: Schema.Number,
output: Schema.Number,
reasoning: Schema.Number,
total: Schema.optional(NonNegativeInt),
input: NonNegativeInt,
output: NonNegativeInt,
reasoning: NonNegativeInt,
cache: Schema.Struct({
read: Schema.Number,
write: Schema.Number,
read: NonNegativeInt,
write: NonNegativeInt,
}),
}),
})
@ -300,7 +299,7 @@ export const ToolStateRunning = Schema.Struct({
title: Schema.optional(Schema.String),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: Schema.Number,
start: NonNegativeInt,
}),
})
.annotate({ identifier: "ToolStateRunning" })
@ -314,9 +313,9 @@ export const ToolStateCompleted = Schema.Struct({
title: Schema.String,
metadata: Schema.Record(Schema.String, Schema.Any),
time: Schema.Struct({
start: Schema.Number,
end: Schema.Number,
compacted: Schema.optional(Schema.Number),
start: NonNegativeInt,
end: NonNegativeInt,
compacted: Schema.optional(NonNegativeInt),
}),
attachments: Schema.optional(Schema.Array(FilePart)),
})
@ -336,8 +335,8 @@ export const ToolStateError = Schema.Struct({
error: Schema.String,
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
time: Schema.Struct({
start: Schema.Number,
end: Schema.Number,
start: NonNegativeInt,
end: NonNegativeInt,
}),
})
.annotate({ identifier: "ToolStateError" })
@ -380,7 +379,7 @@ export const User = Schema.Struct({
...messageBase,
role: Schema.Literal("user"),
time: Schema.Struct({
created: Schema.Number,
created: NonNegativeInt,
}),
format: Schema.optional(_Format),
summary: Schema.optional(
@ -447,9 +446,7 @@ export type Part =
| RetryPart
| CompactionPart
// Errors are still NamedError-based Zod; bridge via ZodOverride so the derived
// Zod + JSON Schema emit the original discriminatedUnion shape. Migrating the
// error classes to Schema.TaggedErrorClass is a separate slice.
// Zod discriminated union kept for the legacy Hono OpenAPI path.
const AssistantErrorZod = z.discriminatedUnion("name", [
AuthError.Schema,
NamedError.Unknown.Schema,
@ -461,6 +458,17 @@ const AssistantErrorZod = z.discriminatedUnion("name", [
])
type AssistantError = z.infer<typeof AssistantErrorZod>
// Effect Schema for the same union — used by HttpApi OpenAPI generation.
const AssistantErrorSchema = Schema.Union([
AuthError.EffectSchema,
Schema.Struct({ name: Schema.Literal("UnknownError"), data: Schema.Struct({ message: Schema.String }) }).annotate({ identifier: "UnknownError" }),
OutputLengthError.EffectSchema,
AbortedError.EffectSchema,
StructuredOutputError.EffectSchema,
ContextOverflowError.EffectSchema,
APIError.EffectSchema,
]).annotate({ discriminator: "name" })
// ── Prompt input schemas ─────────────────────────────────────────────────────
//
// Consumers of `SessionPrompt.PromptInput.parts` send part drafts without the
@ -477,8 +485,8 @@ export const TextPartInput = Schema.Struct({
ignored: Schema.optional(Schema.Boolean),
time: Schema.optional(
Schema.Struct({
start: Schema.Number,
end: Schema.optional(Schema.Number),
start: NonNegativeInt,
end: Schema.optional(NonNegativeInt),
}),
),
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Any)),
@ -506,8 +514,8 @@ export const AgentPartInput = Schema.Struct({
source: Schema.optional(
Schema.Struct({
value: Schema.String,
start: Schema.Int,
end: Schema.Int,
start: NonNegativeInt,
end: NonNegativeInt,
}),
),
})
@ -537,10 +545,10 @@ export const Assistant = Schema.Struct({
...messageBase,
role: Schema.Literal("assistant"),
time: Schema.Struct({
created: Schema.Number,
completed: Schema.optional(Schema.Number),
created: NonNegativeInt,
completed: Schema.optional(NonNegativeInt),
}),
error: Schema.optional(Schema.Any.annotate({ [ZodOverride]: AssistantErrorZod })),
error: Schema.optional(AssistantErrorSchema),
parentID: MessageID,
modelID: ModelID,
providerID: ProviderID,
@ -554,15 +562,15 @@ export const Assistant = Schema.Struct({
root: Schema.String,
}),
summary: Schema.optional(Schema.Boolean),
cost: Schema.Number,
cost: Schema.Finite,
tokens: Schema.Struct({
total: Schema.optional(Schema.Number),
input: Schema.Number,
output: Schema.Number,
reasoning: Schema.Number,
total: Schema.optional(NonNegativeInt),
input: NonNegativeInt,
output: NonNegativeInt,
reasoning: NonNegativeInt,
cache: Schema.Struct({
read: Schema.Number,
write: Schema.Number,
read: NonNegativeInt,
write: NonNegativeInt,
}),
}),
structured: Schema.optional(Schema.Any),
@ -594,7 +602,7 @@ const RemovedEventSchema = Schema.Struct({
const PartUpdatedEventSchema = Schema.Struct({
sessionID: SessionID,
part: _Part,
time: Schema.Number,
time: NonNegativeInt,
})
const PartRemovedEventSchema = Schema.Struct({
@ -651,7 +659,7 @@ export type WithParts = {
const Cursor = Schema.Struct({
id: MessageID,
time: Schema.Number,
time: Schema.Finite.check(Schema.isGreaterThanOrEqualTo(0)),
})
type Cursor = typeof Cursor.Type