#18539 · @KnutZuidema · opened Mar 21, 2026 at 4:43 PM UTC · last updated Mar 21, 2026 at 8:50 PM UTC

fix: discourage _noop tool call during LiteLLM compaction

appfix
80
+1232 files

Score breakdown

Impact

9.0

Clarity

9.0

Urgency

9.0

Ease Of Review

8.0

Guidelines

9.0

Readiness

9.0

Size

10.0

Trust

5.0

Traction

6.0

Summary

This PR fixes a critical bug where the agent loses conversation context after compaction when using LiteLLM/Bedrock. It addresses an issue where the _noop tool, injected as a workaround, was causing compaction summaries to be empty or rejected due to an invalid schema or unintended invocation. The fix provides a valid schema, updates the tool's description, and adds a prompt instruction to discourage the model from calling it.

Open in GitHub

Description

Issue for this PR

Closes #14368 Closes #18053

Type of change

  • [x] Bug fix
  • [ ] New feature
  • [ ] Refactor / code improvement
  • [ ] Documentation

What does this PR do?

When compaction runs with no active tools, LiteLLM/Bedrock rejects the request if the message history contains tool calls but the tools param is absent. The existing fix injected a _noop stub, but with an empty inputSchema: {} which Bedrock also rejects. The model would also call the stub instead of generating a summary — producing the same post-compaction amnesia. This PR gives the stub a valid inputSchema so Bedrock accepts it, changes its description to explicitly say it must never be called, and adds a "Do not call any tools" instruction to the compaction prompt so the model is discouraged from invoking it via both the tool definition and the prompt.

How did you verify your code works?

Ran bun typecheck from packages/opencode/ — no errors. I also ran opencode locally with the fix applied and triggered compaction in a live session (the session in which this PR was created). The session successfully compacted and retained context across the compaction boundary, which was not possible before the fix.

Screenshots / recordings

N/A — no UI change.

Checklist

  • [x] I have tested my changes locally
  • [x] I have not included unrelated changes in this PR

Linked Issues

#18053 `_noop` tool injection causes empty compaction summaries on LiteLLM proxies

View issue

#14368 Compaction loses entire conversation

View issue

Comments

Review comments

rekram1-node

is a read tool better than just adding an input schema for noop tool?

I dont use litellm

KnutZuidema

to be honest, this worked for me once, but now upon retrying, it doesn't work anymore.

I must've gotten lucky and the compaction agent didn't make a tool call.

It appears, that when compacting, the compaction agent tends to first respond with a tool call and the first response causes the previous history to be deleted, causing the compaction to fail.

Ideally, the agent would just never make a tool call. I'm not sure yet how to fix this, will report back if I find out more.

This issue is specific to litellm, because the provider apparently requires a non-empty tool list.

Without it, I can't get any completion request to succeed.

KnutZuidema

I think the best bet to fix this is to simply state clearly that the noop tool shouldn't be used and discourage tool use in the compaction prompt in general.

Let me know if that works for you. For me it did every time I tried it so far, but of course there's technically no guarantee.

I've also thought about actually adjusting the compaction process to simply not finish until a text response has been received, but that seemed like an unnecessarily invasive change, when this problem only affects LiteLLM/Bedrock.

Changed Files

packages/opencode/src/session/compaction.ts

+10
@@ -174,6 +174,7 @@ export namespace SessionCompaction {
const defaultPrompt = `Provide a detailed prompt for continuing our conversation above.
Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next.
The summary that you construct will be used so that another agent can read it and continue the work.
Do not call any tools. Respond only with the summary text.
When constructing the summary, try to stick to this template:
---

packages/opencode/src/session/llm.ts

+113
@@ -176,11 +176,19 @@ export namespace LLM {
input.model.providerID.toLowerCase().includes("litellm") ||
input.model.api.id.toLowerCase().includes("litellm")
// LiteLLM/Bedrock rejects requests where the message history contains tool
// calls but no tools param is present. When there are no active tools (e.g.
// during compaction), inject a stub tool to satisfy the validation requirement.
// The stub description explicitly tells the model not to call it.
if (isLiteLLMProxy && Object.keys(tools).length === 0 && hasToolCalls(input.messages)) {
tools["_noop"] = tool({
description:
"Placeholder for LiteLLM/Anthropic proxy compatibility - required when message history contains tool calls but no active tools are needed",
inputSchema: jsonSchema({ type: "object", properties: {} }),
description: "Do not call this tool. It exists only for API compatibility and must never be invoked.",
inputSchema: jsonSchema({
type: "object",
properties: {
reason: { type: "string", description: "Unused" },
},
}),
execute: async () => ({ output: "", ti