spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: tests/anthropic/caching.test.ts4 * Description: Cache breakpoint planning tests — tier-end breakpoints, last-user-message breakpoints, 4-breakpoint budget.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { describe, expect, it } from "vitest";11import {12 MAX_CACHE_BREAKPOINTS,13 buildMessagesWithCacheControl,14 buildSystemBlocks,15 planCacheBreakpoints,16} from "../../src/anthropic/caching.js";17import type { AnthropicMessage, SystemTier } from "../../src/anthropic/types.js";1819const TIERS: SystemTier[] = [20 { name: "identity", text: "You are KHAELOR." },21 { name: "tool-guidance", text: "Use tools carefully." },22 { name: "project-instructions", text: "Follow KHAELOR.md." },23];2425function user(text: string): AnthropicMessage {26 return { role: "user", content: [{ type: "text", text }] };27}28function assistant(text: string): AnthropicMessage {29 return { role: "assistant", content: [{ type: "text", text }] };30}3132describe("planCacheBreakpoints", () => {33 it("marks the first and last system tier ends, plus the last two user messages", () => {34 const messages = [user("a"), assistant("b"), user("c"), assistant("d"), user("e")];35 const plan = planCacheBreakpoints({ system: TIERS, messages });36 expect(plan.systemTierIndices).toEqual([0, 2]);37 expect(plan.messageIndices).toEqual([2, 4]);38 expect(plan.systemTierIndices.length + plan.messageIndices.length).toBeLessThanOrEqual(39 MAX_CACHE_BREAKPOINTS,40 );41 });4243 it("uses a single breakpoint for a single system tier", () => {44 const plan = planCacheBreakpoints({ system: [TIERS[0] as SystemTier], messages: [user("a")] });45 expect(plan.systemTierIndices).toEqual([0]);46 expect(plan.messageIndices).toEqual([0]);47 });4849 it("handles empty system and empty messages", () => {50 const plan = planCacheBreakpoints({ system: [], messages: [] });51 expect(plan.systemTierIndices).toEqual([]);52 expect(plan.messageIndices).toEqual([]);53 });5455 it("is deterministic — same input shape produces the same plan", () => {56 const messages = [user("a"), user("b"), user("c")];57 const first = planCacheBreakpoints({ system: TIERS, messages });58 const second = planCacheBreakpoints({ system: TIERS, messages });59 expect(second).toEqual(first);60 });61});6263describe("buildSystemBlocks", () => {64 it("renders tiers verbatim with cache_control only at planned tier ends", () => {65 const plan = planCacheBreakpoints({ system: TIERS, messages: [] });66 const blocks = buildSystemBlocks(TIERS, plan);67 expect(blocks.map((b) => b.text)).toEqual(TIERS.map((t) => t.text)); // byte-stable68 expect(blocks[0]?.cache_control).toEqual({ type: "ephemeral" });69 expect(blocks[1]?.cache_control).toBeUndefined();70 expect(blocks[2]?.cache_control).toEqual({ type: "ephemeral" });71 });72});7374describe("buildMessagesWithCacheControl", () => {75 it("marks the last cacheable block of planned messages without mutating the input", () => {76 const messages = [user("a"), assistant("b"), user("c")];77 const plan = planCacheBreakpoints({ system: [], messages });78 const rendered = buildMessagesWithCacheControl(messages, plan);7980 const last = rendered[2]?.content[0];81 expect(last).toMatchObject({ type: "text", cache_control: { type: "ephemeral" } });82 // Input history untouched (byte-stability of the projection).83 expect(messages[2]?.content[0]).toEqual({ type: "text", text: "c" });84 // Unmarked messages pass through by reference.85 expect(rendered[1]).toBe(messages[1]);86 });8788 it("places the breakpoint on tool_result blocks (the common tool-loop tail)", () => {89 const messages: AnthropicMessage[] = [90 user("start"),91 {92 role: "user",93 content: [{ type: "tool_result", tool_use_id: "toolu_1", content: "ok" }],94 },95 ];96 const plan = planCacheBreakpoints({ system: [], messages });97 const rendered = buildMessagesWithCacheControl(messages, plan);98 expect(rendered[1]?.content[0]).toMatchObject({99 type: "tool_result",100 cache_control: { type: "ephemeral" },101 });102 });103104 it("skips non-cacheable blocks (thinking) when choosing the breakpoint block", () => {105 const messages: AnthropicMessage[] = [106 {107 role: "user",108 content: [109 { type: "text", text: "before" },110 { type: "thinking", thinking: "t", signature: "s" },111 ],112 },113 ];114 const plan = planCacheBreakpoints({ system: [], messages });115 const rendered = buildMessagesWithCacheControl(messages, plan);116 expect(rendered[0]?.content[0]).toMatchObject({117 type: "text",118 cache_control: { type: "ephemeral" },119 });120 expect(rendered[0]?.content[1]).not.toHaveProperty("cache_control");121 });122});123