fix(core): copy the context checkpoint when forking a session
Copied fork messages keep their parent seqs, but a fresh checkpoint's baseline_seq starts near zero, so nothing folded: forks received a new full baseline plus every stale context-update message from the parent, and copied compaction rows triggered a spurious rebaseline. Inheriting the parent row keeps the fold horizon consistent with the copied transcript. Also asserts the checkpoint reset on committed reverts and deletes the unused SessionStore.runnerContext / SessionHistory.loadForRunner surface.
This commit is contained in:
parent
cfdb9ab705
commit
aa2c1472fa
5 changed files with 66 additions and 17 deletions
|
|
@ -19,7 +19,12 @@ import { SessionMessageUpdater } from "@opencode-ai/core/session/message-updater
|
|||
import { SessionProjector } from "@opencode-ai/core/session/projector"
|
||||
import { SessionExecution } from "@opencode-ai/core/session/execution"
|
||||
import { SessionInput } from "@opencode-ai/core/session/input"
|
||||
import { SessionInputTable, SessionMessageTable, SessionTable } from "@opencode-ai/core/session/sql"
|
||||
import {
|
||||
SessionContextCheckpointTable,
|
||||
SessionInputTable,
|
||||
SessionMessageTable,
|
||||
SessionTable,
|
||||
} from "@opencode-ai/core/session/sql"
|
||||
import { testEffect } from "./lib/effect"
|
||||
import { Snapshot } from "@opencode-ai/core/snapshot"
|
||||
|
||||
|
|
@ -67,6 +72,10 @@ describe("SessionProjector", () => {
|
|||
.insert(SessionMessageTable)
|
||||
.values([assistantRow(boundary, 1), assistantRow(SessionMessage.ID.make("msg_later"), 2)])
|
||||
.run()
|
||||
yield* db
|
||||
.insert(SessionContextCheckpointTable)
|
||||
.values({ session_id: sessionID, baseline: "baseline", snapshot: {}, baseline_seq: 0 })
|
||||
.run()
|
||||
const events = yield* EventV2.Service
|
||||
yield* events.publish(SessionEvent.RevertEvent.Staged, {
|
||||
sessionID,
|
||||
|
|
@ -93,6 +102,8 @@ describe("SessionProjector", () => {
|
|||
expect(
|
||||
(yield* db.select({ id: SessionMessageTable.id }).from(SessionMessageTable).all()).map((row) => row.id),
|
||||
).toEqual([boundary])
|
||||
// A committed revert resets the context checkpoint so the next turn re-initializes.
|
||||
expect(yield* db.select().from(SessionContextCheckpointTable).get().pipe(Effect.orDie)).toBeUndefined()
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -751,6 +751,35 @@ describe("SessionRunnerLLM", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("copies the context checkpoint to a fork", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
const session = yield* SessionV2.Service
|
||||
const { db } = yield* Database.Service
|
||||
yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "First" }), resume: false })
|
||||
response = []
|
||||
yield* session.resume(sessionID)
|
||||
|
||||
const forked = yield* session.fork({ sessionID })
|
||||
|
||||
const parent = yield* db
|
||||
.select()
|
||||
.from(SessionContextCheckpointTable)
|
||||
.where(eq(SessionContextCheckpointTable.session_id, sessionID))
|
||||
.get()
|
||||
.pipe(Effect.orDie)
|
||||
expect(parent).toBeDefined()
|
||||
expect(
|
||||
yield* db
|
||||
.select()
|
||||
.from(SessionContextCheckpointTable)
|
||||
.where(eq(SessionContextCheckpointTable.session_id, forked.id))
|
||||
.get()
|
||||
.pipe(Effect.orDie),
|
||||
).toEqual({ ...parent!, session_id: forked.id })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("heals an undecodable stored applied record by re-announcing context", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* setup
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue