fix(cli): ignore deleted cost sync

This commit is contained in:
Alex Alecu
2026-04-24 19:19:35 +03:00
committed by Imanol Maiztegui
parent af0227b1ab
commit 2a94a0cc11
2 changed files with 90 additions and 12 deletions
+19 -12
View File
@@ -19,6 +19,7 @@ import type { Provider } from "@/provider"
import { Question } from "@/question"
import { KiloSessionProcessor } from "@/kilocode/session/processor" // kilocode_change
import { Suggestion } from "@/kilocode/suggestion" // kilocode_change
import { NotFoundError } from "@/storage" // kilocode_change
import { errorMessage } from "@/util/error"
import { Log } from "@/util"
import { isRecord } from "@/util/record"
@@ -157,6 +158,22 @@ export const layer: Layer.Layer<
return { call, part }
})
// kilocode_change start - tolerate deleted sessions during subagent cost reconciliation (#6321)
const reconcile = Effect.fn("SessionProcessor.reconcileCost")(function* () {
const fresh = yield* Effect.sync(() => {
try {
return MessageV2.get({ sessionID: ctx.assistantMessage.sessionID, messageID: ctx.assistantMessage.id })
} catch (err) {
if (NotFoundError.isInstance(err)) return
throw err
}
})
if (fresh?.info.role !== "assistant") return
if (fresh.info.cost <= ctx.assistantMessage.cost) return
ctx.assistantMessage.cost = fresh.info.cost
})
// kilocode_change end
const updateToolCall = Effect.fn("SessionProcessor.updateToolCall")(function* (
toolCallID: string,
update: (part: MessageV2.ToolPart) => MessageV2.ToolPart,
@@ -411,12 +428,7 @@ export const layer: Layer.Layer<
// kilocode_change end
ctx.assistantMessage.finish = value.finishReason
// kilocode_change start - capture any subagent cost propagated by tool calls during this step (#6321)
const fresh = yield* Effect.sync(() =>
MessageV2.get({ sessionID: ctx.assistantMessage.sessionID, messageID: ctx.assistantMessage.id }),
)
if (fresh.info.role === "assistant" && fresh.info.cost > ctx.assistantMessage.cost) {
ctx.assistantMessage.cost = fresh.info.cost
}
yield* reconcile()
// kilocode_change end
ctx.assistantMessage.cost += usage.cost
ctx.assistantMessage.tokens = usage.tokens
@@ -576,12 +588,7 @@ export const layer: Layer.Layer<
KiloSessionProcessor.guardEmptyToolCalls(ctx.assistantMessage, MessageV2.parts(ctx.assistantMessage.id)) // kilocode_change
ctx.assistantMessage.time.completed = Date.now()
// kilocode_change start - reconcile cost with any subagent propagation written during tool calls (#6321)
const fresh = yield* Effect.sync(() =>
MessageV2.get({ sessionID: ctx.assistantMessage.sessionID, messageID: ctx.assistantMessage.id }),
)
if (fresh.info.role === "assistant" && fresh.info.cost > ctx.assistantMessage.cost) {
ctx.assistantMessage.cost = fresh.info.cost
}
yield* reconcile()
// kilocode_change end
yield* session.updateMessage(ctx.assistantMessage)
})
@@ -183,6 +183,77 @@ describe("session processor empty tool-calls", () => {
),
)
it.live("ignores deleted session during cost reconciliation", () =>
provideTmpdirInstance(
(dir) =>
Effect.gen(function* () {
const test = yield* TestLLM
const processors = yield* SessionProcessor.Service
const session = yield* Session.Service
yield* test.reply(
{ type: "start" },
{ type: "start-step" } as LLM.Event,
{
type: "finish-step",
finishReason: "stop",
usage: usage(),
providerMetadata: undefined,
} as LLM.Event,
{ type: "finish" } as LLM.Event,
)
const chat = yield* session.create({})
const parent = yield* session.updateMessage({
id: MessageID.ascending(),
role: "user",
sessionID: chat.id,
agent: "code",
model: ref,
time: { created: Date.now() },
})
const msg: MessageV2.Assistant = {
id: MessageID.ascending(),
role: "assistant",
sessionID: chat.id,
parentID: parent.id,
mode: "code",
agent: "code",
path: { cwd: path.resolve(dir), root: path.resolve(dir) },
cost: 0,
tokens: { input: 0, output: 0, reasoning: 0, cache: { read: 0, write: 0 } },
modelID: ref.modelID,
providerID: ref.providerID,
time: { created: Date.now() },
}
yield* session.updateMessage(msg)
const mdl = model()
const handle = yield* processors.create({
assistantMessage: msg,
sessionID: chat.id,
model: mdl,
})
yield* session.remove(chat.id)
const input: LLM.StreamInput = {
user: parent as MessageV2.User,
sessionID: chat.id,
model: mdl,
agent: { name: "code", mode: "primary", permission: [], options: {} } as any,
system: [],
messages: [],
tools: {},
}
const result = yield* handle.process(input)
expect(result).toBe("continue")
expect(handle.message.error).toBeUndefined()
}),
{ git: true },
),
)
it.live("preserves tool-calls finish when tool parts exist", () =>
provideTmpdirInstance(
(dir) =>