From 3a1fb5ae65b01c25bafc663ba12070d1d04df3e2 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Fri, 21 Aug 2026 14:40:53 -0400 Subject: [PATCH] refactor(session): simplify projection state (#43957) --- packages/core/src/session/compaction.ts | 7 ++++--- packages/core/src/session/instruction-state.ts | 16 ++++++++++------ packages/core/src/session/projector.ts | 17 +---------------- packages/core/src/session/system-prompt.ts | 3 --- packages/core/src/session/transfer.ts | 4 +--- 5 files changed, 16 insertions(+), 31 deletions(-) diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts index 08bb4c5d60..69e4be9472 100644 --- a/packages/core/src/session/compaction.ts +++ b/packages/core/src/session/compaction.ts @@ -217,13 +217,14 @@ const planContent = (messages: readonly SessionMessage.Info[], tokens: number) = const selected = select(messages, tokens) if (!selected) return const previousSummary = messages.findLast( - (message) => message.type === "compaction" && message.status === "completed", + (message): message is SessionMessage.CompactionCompleted => + message.type === "compaction" && message.status === "completed", ) - const previousRecent = previousSummary?.type === "compaction" ? previousSummary.recent : "" + const previousRecent = previousSummary?.recent ?? "" const summarizeRecent = !previousRecent && !selected.head return { prompt: buildPrompt({ - previousSummary: previousSummary?.type === "compaction" ? previousSummary.summary : undefined, + previousSummary: previousSummary?.summary, context: summarizeRecent ? [selected.recent] : [previousRecent, selected.head].filter(Boolean), }), recent: summarizeRecent ? "" : selected.recent, diff --git a/packages/core/src/session/instruction-state.ts b/packages/core/src/session/instruction-state.ts index e1c122a1e3..81747587d6 100644 --- a/packages/core/src/session/instruction-state.ts +++ b/packages/core/src/session/instruction-state.ts @@ -248,19 +248,23 @@ const loadBlobs = Effect.fnUntraced(function* (db: DatabaseService, values: Read return blobs }) -function dereference(values: Instructions.Values, blobs: ReadonlyMap) { - return Object.fromEntries(Object.entries(values).map(([key, hash]) => [key, requireBlob(blobs, hash)])) as Readonly< - Record - > +function dereference( + values: Instructions.Values, + blobs: ReadonlyMap, +): Readonly> { + return Object.fromEntries(Object.entries(values).map(([key, hash]) => [key, requireBlob(blobs, hash)])) } -function dereferenceDelta(delta: Instructions.Delta, blobs: ReadonlyMap) { +function dereferenceDelta( + delta: Instructions.Delta, + blobs: ReadonlyMap, +): Readonly>> { return Object.fromEntries( Object.entries(delta).map(([key, hash]) => [ key, hash === "removed" ? Option.none() : Option.some(requireBlob(blobs, hash)), ]), - ) as Readonly>> + ) } function requireBlob(blobs: ReadonlyMap, hash: Instructions.Hash) { diff --git a/packages/core/src/session/projector.ts b/packages/core/src/session/projector.ts index e2cf0173ff..a53635d519 100644 --- a/packages/core/src/session/projector.ts +++ b/packages/core/src/session/projector.ts @@ -218,8 +218,6 @@ function run(db: DatabaseService, event: MessageEvent) { const decodeRow = (row: typeof SessionMessageTable.$inferSelect) => decodeMessage({ ...row.data, id: row.id, type: row.type }) const updateMessage = (message: SessionMessage.Info) => { - if (event.durable === undefined) - return Effect.die(new Error("Durable Session event is missing aggregate sequence")) const encoded = encodeMessage(message) const { id, type, ...data } = encoded return db @@ -374,7 +372,6 @@ function run(db: DatabaseService, event: MessageEvent) { } function insertMessage(db: DatabaseService, event: SessionEvent.DurableEvent, message: SessionMessage.Info) { - if (event.durable === undefined) return Effect.die(new Error("Durable Session event is missing aggregate sequence")) const encoded = encodeMessage(message) const { id, type, ...data } = encoded return db @@ -516,8 +513,6 @@ const layer = Layer.effectDiscard( yield* bus.project(SessionEvent.Forked, (event) => projectFork(db, event)) yield* bus.project(SessionEvent.InboxDelivered, (event) => Effect.gen(function* () { - if (event.durable === undefined) - return yield* Effect.die(new Error("Durable Session event is missing aggregate sequence")) const input = yield* SessionInbox.projectDelivered(db, { id: event.data.inboxID, sessionID: event.data.sessionID, @@ -550,8 +545,6 @@ const layer = Layer.effectDiscard( ) yield* bus.project(SessionEvent.InboxEnqueued, (event) => Effect.gen(function* () { - if (event.durable === undefined) - return yield* Effect.die(new Error("Durable Session event is missing aggregate sequence")) yield* SessionInbox.projectAdmitted(db, { enqueuedSeq: event.durable.seq, id: event.data.inboxID, @@ -622,17 +615,9 @@ const layer = Layer.effectDiscard( Effect.gen(function* () { yield* run(db, event) yield* InstructionState.advanceEpoch(db, event.data.sessionID, event.durable.seq) - if (event.durable === undefined) - return yield* Effect.die(new Error("Durable Session event is missing aggregate sequence")) - }), - ) - yield* bus.project(SessionEvent.Compaction.Failed, (event) => - Effect.gen(function* () { - yield* run(db, event) - if (event.durable === undefined) - return yield* Effect.die(new Error("Durable Session event is missing aggregate sequence")) }), ) + yield* bus.project(SessionEvent.Compaction.Failed, (event) => run(db, event)) yield* bus.project(SessionEvent.RevertEvent.Staged, (event) => Effect.gen(function* () { const revert = event.data.revert diff --git a/packages/core/src/session/system-prompt.ts b/packages/core/src/session/system-prompt.ts index a480ed2ca1..32bf47c2e4 100644 --- a/packages/core/src/session/system-prompt.ts +++ b/packages/core/src/session/system-prompt.ts @@ -14,9 +14,6 @@ export function make(tools: string[]) { "- Use the edit tool for targeted changes to existing text files. It replaces the exact text in `oldString` with `newString`, and the values must differ. By default, `oldString` must occur exactly once. If it occurs multiple times, include more surrounding context to make it unique or set `replaceAll` to true to replace every occurrence.", ) } - // if (tools.includes("patch")) { - // // instructions.push(...) - // } if (tools.includes("read")) { instructions.push("- Prefer using the read tool rather than shell commands like `cat`.") } diff --git a/packages/core/src/session/transfer.ts b/packages/core/src/session/transfer.ts index 843d923113..0e51c38126 100644 --- a/packages/core/src/session/transfer.ts +++ b/packages/core/src/session/transfer.ts @@ -49,8 +49,6 @@ const layer = Layer.effect( const sessions = yield* Session.Service const encodeMessage = Schema.encodeSync(SessionMessage.Info) - const persistProject = (project: Project.Resolved) => upsertProject(db, project).pipe(Effect.orDie) - return Service.of({ export: Effect.fn("SessionTransfer.export")(function* (input) { const data = { @@ -69,7 +67,7 @@ const layer = Layer.effect( .pipe(Effect.orDie) if (recorded) return yield* new ImportConflictError({ sessionID }) const project = yield* projects.resolve(input.location.directory) - yield* persistProject(project) + yield* upsertProject(db, project).pipe(Effect.orDie) const messages = input.data.messages.map((message, index) => { const encoded = encodeMessage(message) const { id: _, type, ...data } = encoded