fix(agent): counted Anthropic server-tool blocks in token estimate

estimateTokens now charges for serialized anthropicServerTool blocks so context maintenance sees the server-tool payload replayed on the wire; excluded from the compaction floor like other encrypted reasoning.
This commit is contained in:
roboomp
2026-07-24 10:43:58 +00:00
parent d868519149
commit b461b8e766
2 changed files with 36 additions and 0 deletions
@@ -427,6 +427,14 @@ function computeMessageTokens(message: AgentMessage, options?: { excludeEncrypte
// Encrypted reasoning blob the provider still bills for on replay;
// excluded from the compaction floor for the same reason as above.
if (!options?.excludeEncryptedReasoning) fragments.push(block.data);
} else if (block.type === "anthropicServerTool") {
// Native Anthropic server-tool call/result replayed verbatim on the
// wire (server_tool_use input, web_search_tool_result
// encrypted_content). Opaque provider-replay state the provider still
// bills for on same-provider replay; excluded from the compaction
// floor like other encrypted reasoning because its local byte size
// diverges from provider billing.
if (!options?.excludeEncryptedReasoning) fragments.push(stringifyJson(block.block) ?? "null");
}
}
break;
+28
View File
@@ -110,6 +110,34 @@ describe("estimate cache option split", () => {
expect(estimateTokens(msg as AgentMessage)).toBe(withBlob);
expect(estimateTokens(msg as AgentMessage, { excludeEncryptedReasoning: true })).toBe(floored);
});
test("counts native server-tool blocks by default and drops them from the compaction floor", () => {
const encrypted = "cipher".repeat(4000);
const msg: AssistantMessage = {
...settledAssistant("with search"),
content: [
{ type: "text", text: "answer" },
{
type: "anthropicServerTool",
block: {
type: "web_search_tool_result",
tool_use_id: "srvtoolu_1",
content: [{ type: "web_search_result", encrypted_content: encrypted }],
},
},
],
};
const textOnly = estimateTokens({
...settledAssistant("x"),
content: [{ type: "text", text: "answer" }],
} as AgentMessage);
const withServerTool = estimateTokens(msg as AgentMessage);
const floored = estimateTokens(msg as AgentMessage, { excludeEncryptedReasoning: true });
// Default estimate charges for the serialized server-tool payload…
expect(withServerTool).toBeGreaterThan(floored + 500);
// …while the compaction floor ignores the opaque encrypted blob entirely.
expect(floored).toBe(textOnly);
});
});
describe("estimate cache invalidation seams", () => {