mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-10 07:11:25 +03:00
fix(core): validate compaction keepID with SessionMessage.ID schema (#57945)
This commit is contained in:
committed by
GitHub
Unverified
parent
7012c9dbf3
commit
c87f6b710e
@@ -6,7 +6,7 @@ import { SessionLegacy } from "@opencode/schema/session-legacy"
|
||||
import { Database } from "../database/database.js"
|
||||
import { MessageDecodeError } from "./error.js"
|
||||
import { SessionMessage } from "./message.js"
|
||||
import { SessionSchema } from "./schema.js"
|
||||
import type { SessionSchema } from "./schema.js"
|
||||
import { Instructions } from "../instructions/index.js"
|
||||
import { InstructionState } from "./instruction-state.js"
|
||||
import { SessionRecentText } from "./recent-text.js"
|
||||
@@ -93,8 +93,8 @@ export const decodeMessageRow = (row: typeof SessionMessageTable.$inferSelect) =
|
||||
Effect.mapError(
|
||||
() =>
|
||||
new MessageDecodeError({
|
||||
sessionID: SessionSchema.ID.make(row.session_id),
|
||||
messageID: SessionMessage.ID.make(row.id),
|
||||
sessionID: row.session_id,
|
||||
messageID: row.id,
|
||||
}),
|
||||
),
|
||||
)
|
||||
@@ -154,13 +154,11 @@ const withRecentText = Effect.fnUntraced(function* (
|
||||
const compaction = first?.message
|
||||
if (compaction?.type !== "compaction" || compaction.status !== "completed") return entries
|
||||
const keepID = compaction.metadata?.keepID
|
||||
if (typeof keepID !== "string") return entries
|
||||
if (!Schema.is(SessionMessage.ID)(keepID)) return entries
|
||||
const start = yield* db
|
||||
.select({ seq: SessionMessageTable.seq })
|
||||
.from(SessionMessageTable)
|
||||
.where(
|
||||
and(eq(SessionMessageTable.session_id, sessionID), eq(SessionMessageTable.id, SessionMessage.ID.make(keepID))),
|
||||
)
|
||||
.where(and(eq(SessionMessageTable.session_id, sessionID), eq(SessionMessageTable.id, keepID)))
|
||||
.get()
|
||||
.pipe(Effect.orDie)
|
||||
if (!start) return entries
|
||||
|
||||
@@ -224,16 +224,11 @@ const projectFork = Effect.fn("SessionProjector.projectFork")(function* (
|
||||
}
|
||||
// A summary references the first message it kept, which the fork copied under a new ID.
|
||||
const keepID = row.type === "compaction" ? row.data.metadata?.keepID : undefined
|
||||
if (typeof keepID !== "string") return copy
|
||||
if (!Schema.is(SessionMessage.ID)(keepID)) return copy
|
||||
const kept = yield* db
|
||||
.select({ seq: SessionMessageTable.seq })
|
||||
.from(SessionMessageTable)
|
||||
.where(
|
||||
and(
|
||||
eq(SessionMessageTable.session_id, event.data.parentID),
|
||||
eq(SessionMessageTable.id, SessionMessage.ID.make(keepID)),
|
||||
),
|
||||
)
|
||||
.where(and(eq(SessionMessageTable.session_id, event.data.parentID), eq(SessionMessageTable.id, keepID)))
|
||||
.get()
|
||||
.pipe(Effect.orDie)
|
||||
if (!kept) return copy
|
||||
|
||||
@@ -199,6 +199,43 @@ it.effect("forks rebuild the recent text of a summary they copied", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("ignores non-message keepID strings when loading history or forking", () =>
|
||||
Effect.gen(function* () {
|
||||
const parentID = Session.ID.make("ses_invalid_keepid_parent")
|
||||
const childID = Session.ID.make("ses_invalid_keepid_child")
|
||||
yield* insertSession(parentID)
|
||||
const compaction = SessionMessage.CompactionCompleted.make({
|
||||
id: SessionMessage.ID.create(),
|
||||
type: "compaction",
|
||||
status: "completed",
|
||||
reason: "manual",
|
||||
summary: "## Objective\n- plugin summary",
|
||||
metadata: { keepID: "not-a-msg-id" },
|
||||
time: { created: DateTime.makeUnsafe(0) },
|
||||
})
|
||||
yield* storeMessages(parentID, [textUser("Earlier question"), compaction])
|
||||
|
||||
const store = yield* SessionStore.Service
|
||||
const [parent] = yield* store.context(parentID)
|
||||
expect(parent).toMatchObject({
|
||||
type: "compaction",
|
||||
metadata: { keepID: "not-a-msg-id" },
|
||||
})
|
||||
|
||||
const bus = yield* Bus.Service
|
||||
yield* bus.publish(SessionEvent.Forked, {
|
||||
sessionID: childID,
|
||||
parentID,
|
||||
boundary: { type: "through", messageID: compaction.id },
|
||||
})
|
||||
const [child] = yield* store.context(childID)
|
||||
expect(child).toMatchObject({
|
||||
type: "compaction",
|
||||
metadata: { keepID: "not-a-msg-id" },
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
const textUser = (text: string) =>
|
||||
SessionMessage.User.make({
|
||||
id: SessionMessage.ID.create(),
|
||||
|
||||
Reference in New Issue
Block a user