|
|
@@ -11,8 +11,12 @@ import z from "zod/v4"
|
|
|
import type { ModelsDev } from "../provider/models"
|
|
|
import { SessionPrompt } from "./prompt"
|
|
|
import { Flag } from "../flag/flag"
|
|
|
+import { Token } from "../util/token"
|
|
|
+import { Log } from "../util/log"
|
|
|
|
|
|
export namespace SessionCompaction {
|
|
|
+ const log = Log.create({ service: "session.compaction" })
|
|
|
+
|
|
|
export const Event = {
|
|
|
Compacted: Bus.event(
|
|
|
"session.compacted",
|
|
|
@@ -32,6 +36,46 @@ export namespace SessionCompaction {
|
|
|
return count > usable
|
|
|
}
|
|
|
|
|
|
+ // goes backwards through parts until there are 40_000 tokens worth of tool
|
|
|
+ // calls. then erases output of previous tool calls. idea is to throw away old
|
|
|
+ // tool calls that are no longer relevant.
|
|
|
+ export async function prune(input: { sessionID: string }) {
|
|
|
+ if (Flag.OPENCODE_DISABLE_PRUNE) return
|
|
|
+ log.info("pruning")
|
|
|
+ const msgs = await Session.messages(input.sessionID)
|
|
|
+ let total = 0
|
|
|
+ let pruned = 0
|
|
|
+ const toPrune = []
|
|
|
+
|
|
|
+ loop: for (let msgIndex = msgs.length - 2; msgIndex >= 0; msgIndex--) {
|
|
|
+ const msg = msgs[msgIndex]
|
|
|
+ if (msg.info.role === "assistant" && msg.info.summary) return
|
|
|
+ for (let partIndex = msg.parts.length - 1; partIndex >= 0; partIndex--) {
|
|
|
+ const part = msg.parts[partIndex]
|
|
|
+ if (part.type === "tool")
|
|
|
+ if (part.state.status === "completed") {
|
|
|
+ if (part.state.time.compacted) break loop
|
|
|
+ const estimate = Token.estimate(part.state.output)
|
|
|
+ total += estimate
|
|
|
+ if (total > 40_000) {
|
|
|
+ pruned += estimate
|
|
|
+ toPrune.push(part)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("found", { pruned, total })
|
|
|
+ if (pruned > 20_000) {
|
|
|
+ for (const part of toPrune) {
|
|
|
+ if (part.state.status === "completed") {
|
|
|
+ part.state.time.compacted = Date.now()
|
|
|
+ await Session.updatePart(part)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.info("pruned", { count: toPrune.length })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
export async function run(input: { sessionID: string; providerID: string; modelID: string }) {
|
|
|
await Session.update(input.sessionID, (draft) => {
|
|
|
draft.time.compacting = Date.now()
|