Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/opencode/src/session/message-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { and } from "drizzle-orm"
import { desc } from "drizzle-orm"
import { eq } from "drizzle-orm"
import { inArray } from "drizzle-orm"
import { gt } from "drizzle-orm"
import { lt } from "drizzle-orm"
import { or } from "drizzle-orm"
import { MessageTable, PartTable, SessionTable } from "@opencode-ai/core/session/sql"
Expand Down Expand Up @@ -489,6 +490,19 @@ export function stream(sessionID: SessionID) {
})
}

/** Messages created after the given id; ids are monotonic per session (MessageID.ascending). */
export const after = Effect.fn("MessageV2.after")(function* (input: { sessionID: SessionID; id: MessageID }) {
const { db } = yield* Database.Service
const rows = yield* db
.select()
.from(MessageTable)
.where(and(eq(MessageTable.session_id, input.sessionID), gt(MessageTable.id, input.id)))
.orderBy(MessageTable.id)
.all()
.pipe(Effect.orDie)
return yield* hydrate(db, rows)
})

export function parts(messageID: MessageID) {
return Effect.gen(function* () {
const { db } = yield* Database.Service
Expand Down
25 changes: 22 additions & 3 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,7 @@ const layer = Layer.effect(
let firstRequestStartAt: number | undefined
let ttftStep: number | undefined
let historyMs = 0
let cachedMsgs: SessionV1.WithParts[] | undefined
let toolsMs = 0
let contextMs = 0
let snapshotMs = 0
Expand All @@ -1103,9 +1104,26 @@ const layer = Layer.effect(
yield* Effect.logInfo("loop", { "session.id": sessionID, step })

const historyStart = Date.now()
let msgs = yield* MessageV2.filterCompactedEffect(sessionID).pipe(
Effect.provideService(Database.Service, database),
)
let msgs: SessionV1.WithParts[]
if (cachedMsgs === undefined) {
msgs = yield* MessageV2.filterCompactedEffect(sessionID).pipe(
Effect.provideService(Database.Service, database),
)
} else {
const maxId = cachedMsgs.reduce((max, m) => (m.info.id > max ? m.info.id : max), cachedMsgs[0]!.info.id)
const fresh = yield* MessageV2.after({ sessionID, id: maxId }).pipe(
Effect.provideService(Database.Service, database),
)
// Compaction/summary rows change the retained window; recompute it from scratch.
const rewritesHistory = fresh.some(
(m) => m.parts.some((p) => p.type === "compaction") || (m.info.role === "assistant" && m.info.summary),
)
msgs = rewritesHistory
? yield* MessageV2.filterCompactedEffect(sessionID).pipe(
Effect.provideService(Database.Service, database),
)
: [...cachedMsgs, ...fresh]
}
historyMs += Date.now() - historyStart

const { user: lastUser, assistant: lastAssistant, finished: lastFinished, tasks } = MessageV2.latest(msgs)
Expand Down Expand Up @@ -1197,6 +1215,7 @@ const layer = Layer.effect(
Effect.provideService(FSUtil.Service, fsys),
Effect.provideService(Session.Service, sessions),
)
cachedMsgs = msgs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: On multi-step plan/build turns, cachedMsgs retains each iteration's in-memory reminder and plugin mutations, so synthetic prompts or transformed parts accumulate across model requests. Keep the cached history immutable, or run per-step transforms on a clone and merge only persisted changes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/session/prompt.ts, line 1218:

<comment>On multi-step plan/build turns, `cachedMsgs` retains each iteration's in-memory reminder and plugin mutations, so synthetic prompts or transformed parts accumulate across model requests. Keep the cached history immutable, or run per-step transforms on a clone and merge only persisted changes.</comment>

<file context>
@@ -1197,6 +1215,7 @@ const layer = Layer.effect(
             Effect.provideService(FSUtil.Service, fsys),
             Effect.provideService(Session.Service, sessions),
           )
+          cachedMsgs = msgs
 
           const msg: SessionV1.Assistant = {
</file context>


const msg: SessionV1.Assistant = {
id: MessageID.ascending(),
Expand Down
Loading