fix(core): harden compaction admission

This commit is contained in:
Kit Langton
2026-07-04 20:13:43 -04:00
parent 69d95f29d4
commit beecb243bc
6 changed files with 25 additions and 14 deletions
@@ -600,7 +600,6 @@ export type SessionPromptInput = {
export type SessionPromptOutput = {
readonly data: {
readonly type: "prompt"
readonly admittedSeq: number
readonly id: string
readonly sessionID: string
@@ -801,7 +800,6 @@ export type SessionCommandInput = {
export type SessionCommandOutput = {
readonly data: {
readonly type: "prompt"
readonly admittedSeq: number
readonly id: string
readonly sessionID: string
-1
View File
@@ -212,7 +212,6 @@ const session = {
const admission = {
data: {
type: "prompt",
admittedSeq: 0,
id: "msg_test",
sessionID: "ses_test",
-1
View File
@@ -309,7 +309,6 @@ const session = {
const admission = {
data: {
type: "prompt",
admittedSeq: 0,
id: "msg_test",
sessionID: "ses_test",
+18 -6
View File
@@ -2,7 +2,7 @@ export * as SessionInput from "./input"
import { and, asc, eq, isNull } from "drizzle-orm"
import { DateTime, Effect, Schema } from "effect"
import { Admitted, Compaction, Delivery, Entry } from "@opencode-ai/schema/session-input"
import { Admitted, Compaction, Delivery, Entry, PromptEntry } from "@opencode-ai/schema/session-input"
import type { Database } from "../database/database"
import type { EventV2 } from "../event"
import { KeyedMutex } from "../effect/keyed-mutex"
@@ -14,7 +14,7 @@ import { SessionInputTable, SessionMessageTable } from "./sql"
type DatabaseService = Database.Interface["db"]
export { Admitted, Compaction, Delivery, Entry }
export { Admitted, Compaction, Delivery, Entry, PromptEntry }
const decodePrompt = Schema.decodeUnknownSync(Prompt)
const encodePrompt = Schema.encodeSync(Prompt)
@@ -38,7 +38,7 @@ const fromRow = (row: typeof SessionInputTable.$inferSelect): Entry => {
...(row.promoted_seq === null ? {} : { handledSeq: row.promoted_seq }),
})
if (!row.prompt || !row.delivery) throw new LifecycleConflict({ id: base.id })
return Admitted.make({
return PromptEntry.make({
...base,
type: "prompt",
prompt: decodePrompt(row.prompt),
@@ -47,6 +47,17 @@ const fromRow = (row: typeof SessionInputTable.$inferSelect): Entry => {
})
}
const toAdmitted = (entry: PromptEntry): Admitted =>
Admitted.make({
admittedSeq: entry.admittedSeq,
id: entry.id,
sessionID: entry.sessionID,
prompt: entry.prompt,
delivery: entry.delivery,
timeCreated: entry.timeCreated,
...(entry.promotedSeq === undefined ? {} : { promotedSeq: entry.promotedSeq }),
})
export const find = Effect.fn("SessionInput.find")(function* (db: DatabaseService, id: SessionMessage.ID) {
const row = yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, id)).get().pipe(Effect.orDie)
return row === undefined ? undefined : fromRow(row)
@@ -88,7 +99,7 @@ export const admit = Effect.fn("SessionInput.admit")(function* (
const existing = yield* find(db, input.id)
if (existing !== undefined) {
if (existing.type !== "prompt") return yield* Effect.die(new LifecycleConflict({ id: input.id }))
return existing
return toAdmitted(existing)
}
return yield* events
.publish(SessionEvent.PromptAdmitted, {
@@ -103,7 +114,6 @@ export const admit = Effect.fn("SessionInput.admit")(function* (
? Effect.die(new Error("Prompt admission event is missing aggregate sequence"))
: Effect.succeed(
Admitted.make({
type: "prompt",
admittedSeq: event.durable.seq,
id: input.id,
sessionID: input.sessionID,
@@ -115,7 +125,9 @@ export const admit = Effect.fn("SessionInput.admit")(function* (
),
Effect.catchDefect((defect) =>
find(db, input.id).pipe(
Effect.flatMap((stored) => (stored?.type === "prompt" ? Effect.succeed(stored) : Effect.die(defect))),
Effect.flatMap((stored) =>
stored?.type === "prompt" ? Effect.succeed(toAdmitted(stored)) : Effect.die(defect),
),
),
),
)
+7 -2
View File
@@ -13,7 +13,6 @@ export type Delivery = SessionDelivery.Delivery
export interface Admitted extends Schema.Schema.Type<typeof Admitted> {}
export const Admitted = Schema.Struct({
type: Schema.Literal("prompt"),
admittedSeq: NonNegativeInt,
id: SessionMessage.ID,
sessionID: SessionID,
@@ -23,6 +22,12 @@ export const Admitted = Schema.Struct({
promotedSeq: NonNegativeInt.pipe(optional),
}).annotate({ identifier: "SessionInput.Admitted" })
export interface PromptEntry extends Schema.Schema.Type<typeof PromptEntry> {}
export const PromptEntry = Schema.Struct({
type: Schema.Literal("prompt"),
...Admitted.fields,
}).annotate({ identifier: "SessionInput.PromptEntry" })
export interface Compaction extends Schema.Schema.Type<typeof Compaction> {}
export const Compaction = Schema.Struct({
type: Schema.Literal("compaction"),
@@ -33,5 +38,5 @@ export const Compaction = Schema.Struct({
handledSeq: NonNegativeInt.pipe(optional),
}).annotate({ identifier: "SessionInput.Compaction" })
export const Entry = Schema.Union([Admitted, Compaction]).pipe(Schema.toTaggedUnion("type"))
export const Entry = Schema.Union([PromptEntry, Compaction]).pipe(Schema.toTaggedUnion("type"))
export type Entry = typeof Entry.Type
-2
View File
@@ -4399,7 +4399,6 @@ export type PromptInputFileAttachment = {
}
export type SessionInputAdmitted = {
type: "prompt"
admittedSeq: number
id: string
sessionID: string
@@ -8697,7 +8696,6 @@ export type SessionV2InfoV2 = {
}
export type SessionInputAdmittedV2 = {
type: "prompt"
admittedSeq: number
id: string
sessionID: string