fix(session): hide pending admission sequence

This commit is contained in:
Dax Raad 2026-07-28 22:32:06 -04:00
commit 2a85c861e0
17 changed files with 32 additions and 48 deletions

View file

@ -26,7 +26,6 @@ function setup(
return new Response(undefined, { status: 204 })
if (request.method === "POST" && request.url.endsWith("/prompt")) {
return Response.json({
admittedSeq: 1,
id: "msg_1",
sessionID: "ses_1",
timeCreated: 1,

View file

@ -229,7 +229,6 @@ function createV1Api(input: CompatibleInput): CompatibleApi {
],
})
return {
admittedSeq: 0,
id: value.id ?? "",
sessionID: value.sessionID,
timeCreated: Date.now(),
@ -255,7 +254,6 @@ function createV1Api(input: CompatibleInput): CompatibleApi {
})),
})
return {
admittedSeq: 0,
id: value.id ?? "",
sessionID: value.sessionID,
timeCreated: Date.now(),
@ -280,7 +278,6 @@ function createV1Api(input: CompatibleInput): CompatibleApi {
modelID: value.model.modelID,
})
return {
admittedSeq: 0,
id: value.id ?? "",
sessionID: value.sessionID,
timeCreated: Date.now(),

View file

@ -267,7 +267,7 @@ async function run(input: {
values.push(...input.turn(messageID))
wake?.()
wake = undefined
return ok({ admittedSeq: 1, id: messageID, sessionID: "ses_1", timeCreated: 1 }) as never
return ok({ id: messageID, sessionID: "ses_1", timeCreated: 1 }) as never
})
await runNonInteractivePrompt({
client: sdk,

View file

@ -43,13 +43,7 @@ export type PromptMention = { start: number; end: number; text: string }
export type SessionPendingSyntheticData = { text: string; description?: string; metadata?: { [x: string]: JsonValue } }
export type SessionPendingCompaction = {
admittedSeq: number
id: string
sessionID: string
timeCreated: number
type: "compaction"
}
export type SessionPendingCompaction = { id: string; sessionID: string; timeCreated: number; type: "compaction" }
export type SessionMessageAgentSelected = {
id: string
@ -1210,7 +1204,6 @@ export type PromptFileAttachment = {
export type PromptAgentAttachment = { name: string; mention?: PromptMention }
export type SessionPendingSynthetic = {
admittedSeq: number
id: string
sessionID: string
timeCreated: number
@ -1966,7 +1959,6 @@ export type AgentInfo = {
export type SessionsResponse = { data: Array<SessionInfo>; cursor: { previous?: string | null; next?: string | null } }
export type SessionPendingUser = {
admittedSeq: number
id: string
sessionID: string
timeCreated: number

View file

@ -268,7 +268,6 @@ const session = {
const admission = {
data: {
admittedSeq: 0,
id: "msg_test",
sessionID: "ses_test",
type: "user",
@ -281,7 +280,6 @@ const admission = {
const compactionAdmission = {
data: {
type: "compaction",
admittedSeq: 1,
id: "msg_compaction",
sessionID: "ses_test",
timeCreated: 1_717_171_717_000,

View file

@ -304,7 +304,6 @@ test("session.pending.list uses the public HTTP contract", async () => {
const requests: Array<{ method: string; url: string }> = []
const pending = [
{
admittedSeq: 3,
id: "msg_pending",
sessionID: "ses_test",
timeCreated: 1_717_171_717_000,
@ -547,7 +546,6 @@ const session = {
const admission = {
data: {
admittedSeq: 0,
id: "msg_test",
sessionID: "ses_test",
type: "user",
@ -559,7 +557,6 @@ const admission = {
const syntheticAdmission = {
data: {
admittedSeq: 1,
id: "msg_synthetic",
sessionID: "ses_test",
type: "synthetic",
@ -572,7 +569,6 @@ const syntheticAdmission = {
const compactionAdmission = {
data: {
type: "compaction",
admittedSeq: 1,
id: "msg_compaction",
sessionID: "ses_test",
timeCreated: 1_717_171_717_000,

View file

@ -53,7 +53,6 @@ export class LifecycleConflict extends Schema.TaggedErrorClass<LifecycleConflict
const fromRow = (row: typeof SessionPendingTable.$inferSelect): Info => {
const base = {
admittedSeq: row.admitted_seq,
id: SessionMessage.ID.make(row.id),
sessionID: SessionSchema.ID.make(row.session_id),
timeCreated: DateTime.makeUnsafe(row.time_created),
@ -134,7 +133,6 @@ const promotedFromHistory = Effect.fn("SessionPending.promotedFromHistory")(func
const decoded = decodeAdmittedEvent(row.data)
if (decoded._tag !== "Some" || decoded.value.inputID !== id) continue
const base = {
admittedSeq: row.seq,
id,
sessionID,
timeCreated: DateTime.makeUnsafe(row.created),
@ -172,10 +170,7 @@ export const admit = Effect.fn("SessionPending.admit")(function* (
})
.pipe(
Effect.flatMap((event) => {
if (event.durable === undefined)
return Effect.die(new Error("Session input admission event is missing aggregate sequence"))
const base = {
admittedSeq: event.durable.seq,
id: request.id,
sessionID: request.sessionID,
timeCreated: event.created,

View file

@ -62,7 +62,6 @@ describe("fromPromise", () => {
seen = value
return Effect.succeed(
SessionPending.Synthetic.make({
admittedSeq: 1,
id: SessionMessage.ID.make(input.id),
sessionID: Session.ID.make(input.sessionID),
timeCreated: DateTime.makeUnsafe(0),

View file

@ -484,7 +484,6 @@ describe("Session.create", () => {
type: "user",
data: { text: "Replay lifecycle" },
delivery: "steer",
admittedSeq: 1,
})
expect(yield* store.context(created.id)).toEqual([])

View file

@ -3,7 +3,7 @@ export * as SessionPending from "./session-pending.js"
import { Schema } from "effect"
import { optional } from "./schema.js"
import { Prompt } from "./prompt.js"
import { DateTimeUtcFromMillis, NonNegativeInt } from "./schema.js"
import { DateTimeUtcFromMillis } from "./schema.js"
import { SessionDelivery } from "./session-delivery.js"
import { SessionID } from "./session-id.js"
import { SessionMessage } from "./session-message.js"
@ -45,7 +45,6 @@ export const Message = Schema.Union([UserMessage, SyntheticMessage]).pipe(
export type Message = typeof Message.Type
const Admitted = {
admittedSeq: NonNegativeInt,
id: SessionMessage.ID,
sessionID: SessionID,
timeCreated: DateTimeUtcFromMillis,

View file

@ -54,6 +54,29 @@ describe("contract hygiene", () => {
).toEqual({ text: "completed" })
})
test("pending session items omit the internal admission sequence", () => {
expect(
Schema.encodeSync(SessionPending.Info)(
Schema.decodeUnknownSync(SessionPending.Info)({
admittedSeq: 3,
id: "msg_pending",
sessionID: "ses_pending",
timeCreated: 1,
type: "user",
data: { text: "hello" },
delivery: "steer",
}),
),
).toEqual({
id: "msg_pending",
sessionID: "ses_pending",
timeCreated: 1,
type: "user",
data: { text: "hello" },
delivery: "steer",
})
})
test("forms require at least one field", () => {
expect(() =>
Schema.decodeUnknownSync(Form.Info)({

View file

@ -407,7 +407,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
addPending({
id: event.data.inputID,
sessionID: event.data.sessionID,
admittedSeq: event.durable.seq,
timeCreated: event.created,
...event.data.input,
})
@ -702,7 +701,6 @@ export const { use: useData, provider: DataProvider } = createSimpleContext({
addPending({
id: event.data.inputID,
sessionID: event.data.sessionID,
admittedSeq: event.durable.seq,
timeCreated: event.created,
type: "compaction",
})

View file

@ -186,7 +186,6 @@ function pendingPrompt(item: SessionPendingInfo): FooterQueuedPrompt | undefined
messageID: item.id,
prompt: { messageID: item.id, text: item.data.text, parts: [] },
delivery: item.delivery,
admittedSeq: item.admittedSeq,
}
}
@ -517,7 +516,7 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
}
const syncPending = () => {
const prompts = [...state.pending.values()].toSorted((left, right) => left.admittedSeq - right.admittedSeq)
const prompts = [...state.pending.values()]
input.trace?.write("ui.patch", { pending: prompts.length })
input.footer.event({ type: "queued.prompts", prompts })
}
@ -905,7 +904,6 @@ export async function createSessionTransport(input: StreamInput): Promise<Sessio
if (event.type === "session.input.admitted") {
if (event.data.input.type !== "user") return
mergePending({
admittedSeq: event.durable.seq,
id: event.data.inputID,
sessionID: event.data.sessionID,
timeCreated: event.created,

View file

@ -88,7 +88,6 @@ export type FooterQueuedPrompt = {
messageID: string
prompt: RunPrompt
delivery: "steer" | "queue"
admittedSeq: number
}
export type RunAgent = {

View file

@ -1346,14 +1346,12 @@ test("restores queued compaction from durable pending input", async () => {
const sessionID = "session-compaction-queued"
let pending = [
{
admittedSeq: 3,
id: "message-compaction-queued",
sessionID,
timeCreated: 1,
type: "compaction" as const,
},
{
admittedSeq: 4,
id: "message-compaction-later",
sessionID,
timeCreated: 2,
@ -2474,7 +2472,6 @@ test("renders admitted prompts immediately and tracks them until promoted", asyn
{
id: messageID,
sessionID,
admittedSeq: 0,
timeCreated: 0,
type: "user",
data: { text: "hello" },

View file

@ -919,7 +919,6 @@ test("direct pending panel shows durable delivery without edit actions", async (
messageID: "m-1",
prompt: { text: "fix the auth test", parts: [] },
delivery: "queue" as const,
admittedSeq: 1,
},
])
@ -1284,7 +1283,6 @@ test("direct footer shows authoritative pending work while running", async () =>
messageID: "m-queued",
prompt: { text: "follow up", parts: [] },
delivery: "queue",
admittedSeq: 1,
},
]}
theme={() => RUN_THEME_FALLBACK}

View file

@ -77,7 +77,6 @@ function durable(sessionID: string, seq = 0, version: 1 | 2 = 1) {
function promptAdmission(input: Parameters<OpenCodeClient["session"]["prompt"]>[0], sessionID = "ses_1") {
return {
admittedSeq: 1,
id: input.id ?? "msg_prompt",
sessionID,
type: "user" as const,
@ -659,7 +658,6 @@ describe("V2 mini transport", () => {
pending: {
ses_1: [
{
admittedSeq: 1,
id: "msg_queued",
sessionID: "ses_1",
timeCreated: 1,
@ -680,9 +678,9 @@ describe("V2 mini transport", () => {
const pending = () =>
ui.events
.findLast((item) => item.type === "queued.prompts")
?.prompts.map((item) => [item.messageID, item.delivery, item.admittedSeq])
?.prompts.map((item) => [item.messageID, item.delivery])
expect(pending()).toEqual([["msg_queued", "queue", 1]])
expect(pending()).toEqual([["msg_queued", "queue"]])
events.push({
id: "evt_promoted",
created: 2,
@ -697,7 +695,7 @@ describe("V2 mini transport", () => {
)
expect(pending()).toEqual([])
const prompt = spyOn(client.session, "prompt").mockImplementation(
(request) => ok({ ...promptAdmission(request), admittedSeq: 2 }) as never,
(request) => ok(promptAdmission(request)) as never,
)
await transport.queuePromptTurn({
agent: "review",
@ -726,8 +724,8 @@ describe("V2 mini transport", () => {
await Bun.sleep(0)
}
expect(pending()).toEqual([
["msg_earlier", "steer", 1],
["msg_next", "queue", 2],
["msg_next", "queue"],
["msg_earlier", "steer"],
])
await transport.close()
})
@ -2694,7 +2692,6 @@ describe("V2 mini transport", () => {
})
})
return ok({
admittedSeq: 1,
id: input.id ?? "msg_cmd",
sessionID: "ses_1",
type: "user" as const,