fix(opencode): use chronological message boundaries (#40991)
Co-authored-by: Dax <mail@thdxr.com>
This commit is contained in:
committed by
GitHub
parent
d468201952
commit
a54a693af2
@@ -71,7 +71,8 @@ const layer = Layer.effect(
|
|||||||
if (session.revert?.snapshot) yield* snap.restore(session.revert.snapshot)
|
if (session.revert?.snapshot) yield* snap.restore(session.revert.snapshot)
|
||||||
yield* snap.revert(patches)
|
yield* snap.revert(patches)
|
||||||
if (rev.snapshot) rev.diff = yield* snap.diff(rev.snapshot)
|
if (rev.snapshot) rev.diff = yield* snap.diff(rev.snapshot)
|
||||||
const range = all.filter((msg) => msg.info.id >= rev.messageID)
|
const index = all.findIndex((msg) => msg.info.id === rev.messageID)
|
||||||
|
const range = index < 0 ? [] : all.slice(index)
|
||||||
const diffs = yield* summary.computeDiff({ messages: range })
|
const diffs = yield* summary.computeDiff({ messages: range })
|
||||||
yield* storage.write(["session_diff", input.sessionID], diffs).pipe(Effect.ignore)
|
yield* storage.write(["session_diff", input.sessionID], diffs).pipe(Effect.ignore)
|
||||||
yield* events.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: diffs })
|
yield* events.publish(Session.Event.Diff, { sessionID: input.sessionID, diff: diffs })
|
||||||
@@ -102,20 +103,9 @@ const layer = Layer.effect(
|
|||||||
const sessionID = session.id
|
const sessionID = session.id
|
||||||
const msgs = yield* sessions.messages({ sessionID }).pipe(Effect.orDie)
|
const msgs = yield* sessions.messages({ sessionID }).pipe(Effect.orDie)
|
||||||
const messageID = session.revert.messageID
|
const messageID = session.revert.messageID
|
||||||
const remove = [] as SessionV1.WithParts[]
|
const index = msgs.findIndex((msg) => msg.info.id === messageID)
|
||||||
let target: SessionV1.WithParts | undefined
|
const target = index < 0 ? undefined : msgs[index]
|
||||||
for (const msg of msgs) {
|
const remove = index < 0 ? [] : msgs.slice(index + (session.revert.partID ? 1 : 0))
|
||||||
if (msg.info.id < messageID) continue
|
|
||||||
if (msg.info.id > messageID) {
|
|
||||||
remove.push(msg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (session.revert.partID) {
|
|
||||||
target = msg
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
remove.push(msg)
|
|
||||||
}
|
|
||||||
for (const msg of remove) {
|
for (const msg of remove) {
|
||||||
yield* sessions.removeMessage({ sessionID, messageID: msg.info.id })
|
yield* sessions.removeMessage({ sessionID, messageID: msg.info.id })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,9 +703,9 @@ const layer: Layer.Layer<
|
|||||||
})
|
})
|
||||||
const msgs = yield* messages({ sessionID: input.sessionID })
|
const msgs = yield* messages({ sessionID: input.sessionID })
|
||||||
const idMap = new Map<string, MessageID>()
|
const idMap = new Map<string, MessageID>()
|
||||||
|
const target = input.messageID ? msgs.findIndex((msg) => msg.info.id === input.messageID) : msgs.length
|
||||||
|
|
||||||
for (const msg of msgs) {
|
for (const msg of msgs.slice(0, target < 0 ? msgs.length : target)) {
|
||||||
if (input.messageID && msg.info.id >= input.messageID) break
|
|
||||||
const newID = MessageID.ascending()
|
const newID = MessageID.ascending()
|
||||||
idMap.set(msg.info.id, newID)
|
idMap.set(msg.info.id, newID)
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,18 @@ const user = Effect.fn("test.user")(function* (sessionID: SessionID, agent = "de
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const userAt = Effect.fn("test.userAt")(function* (sessionID: SessionID, id: string, created: number) {
|
||||||
|
const session = yield* Session.Service
|
||||||
|
return yield* session.updateMessage({
|
||||||
|
id: MessageID.make(id),
|
||||||
|
role: "user" as const,
|
||||||
|
sessionID,
|
||||||
|
agent: "default",
|
||||||
|
model: { providerID: ProviderV2.ID.make("openai"), modelID: ModelV2.ID.make("gpt-4") },
|
||||||
|
time: { created },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const assistant = Effect.fn("test.assistant")(function* (sessionID: SessionID, parentID: MessageID, dir: string) {
|
const assistant = Effect.fn("test.assistant")(function* (sessionID: SessionID, parentID: MessageID, dir: string) {
|
||||||
const session = yield* Session.Service
|
const session = yield* Session.Service
|
||||||
return yield* session.updateMessage({
|
return yield* session.updateMessage({
|
||||||
@@ -426,6 +438,39 @@ describe("revert + compact workflow", () => {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.live(
|
||||||
|
"reverts chronological suffixes on both sides of mixed message ID ordering",
|
||||||
|
provideTmpdirInstance(
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const session = yield* Session.Service
|
||||||
|
const revert = yield* SessionRevert.Service
|
||||||
|
const ids = ["msg_z9-before", "msg_z1-before-wrap", "msg_a0-after-wrap", "msg_a1-after"]
|
||||||
|
|
||||||
|
const run = Effect.fn("test.mixedIDRevert")(function* (target: number) {
|
||||||
|
const info = yield* session.create({})
|
||||||
|
for (const [index, id] of ids.entries()) {
|
||||||
|
const message = yield* userAt(info.id, id, index + 1)
|
||||||
|
yield* text(info.id, message.id, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
const reverted = yield* revert.revert({
|
||||||
|
sessionID: info.id,
|
||||||
|
messageID: MessageID.make(ids[target]!),
|
||||||
|
})
|
||||||
|
yield* revert.cleanup(reverted)
|
||||||
|
const remaining = yield* session.messages({ sessionID: info.id })
|
||||||
|
yield* session.remove(info.id)
|
||||||
|
return remaining.map((msg) => msg.info.time.created)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(yield* run(1)).toEqual([1])
|
||||||
|
expect(yield* run(2)).toEqual([1, 2])
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
it.live(
|
it.live(
|
||||||
"cleanup is a no-op when session has no revert state",
|
"cleanup is a no-op when session has no revert state",
|
||||||
provideTmpdirInstance(
|
provideTmpdirInstance(
|
||||||
|
|||||||
@@ -238,6 +238,38 @@ describe("Session", () => {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.instance("forks the chronological prefix across mixed message ID ordering", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const session = yield* SessionNs.Service
|
||||||
|
const created = yield* Effect.acquireRelease(session.create({}), (info) =>
|
||||||
|
session.remove(info.id).pipe(Effect.ignore),
|
||||||
|
)
|
||||||
|
const ids = ["msg_z9-before", "msg_z1-before-wrap", "msg_a0-after-wrap", "msg_a1-after"]
|
||||||
|
for (const [index, id] of ids.entries()) {
|
||||||
|
yield* session.updateMessage({
|
||||||
|
id: MessageID.make(id),
|
||||||
|
sessionID: created.id,
|
||||||
|
role: "user",
|
||||||
|
time: { created: index + 1 },
|
||||||
|
agent: "user",
|
||||||
|
model: { providerID: "test", modelID: "test" },
|
||||||
|
} as SessionV1.User)
|
||||||
|
}
|
||||||
|
|
||||||
|
const beforeWrap = yield* Effect.acquireRelease(
|
||||||
|
session.fork({ sessionID: created.id, messageID: MessageID.make(ids[1]!) }),
|
||||||
|
(info) => session.remove(info.id).pipe(Effect.ignore),
|
||||||
|
)
|
||||||
|
const afterWrap = yield* Effect.acquireRelease(
|
||||||
|
session.fork({ sessionID: created.id, messageID: MessageID.make(ids[2]!) }),
|
||||||
|
(info) => session.remove(info.id).pipe(Effect.ignore),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect((yield* session.messages({ sessionID: beforeWrap.id })).map((msg) => msg.info.time.created)).toEqual([1])
|
||||||
|
expect((yield* session.messages({ sessionID: afterWrap.id })).map((msg) => msg.info.time.created)).toEqual([1, 2])
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
it.instance("omits metadata when not provided", () =>
|
it.instance("omits metadata when not provided", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const session = yield* SessionNs.Service
|
const session = yield* SessionNs.Service
|
||||||
|
|||||||
Reference in New Issue
Block a user