mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
383 lines
13 KiB
TypeScript
383 lines
13 KiB
TypeScript
import { describe, it, expect } from "bun:test"
|
|
import { slimInfo, slimPart } from "../../src/kilo-provider/slim-metadata"
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
function part(tool: string, state: Record<string, unknown>) {
|
|
return { type: "tool", id: "p1", tool, state }
|
|
}
|
|
|
|
function bytes(obj: unknown): number {
|
|
return JSON.stringify(obj).length
|
|
}
|
|
|
|
function bigPatch() {
|
|
return `Index: a.ts\n===================================================================\n--- a.ts\t\n+++ a.ts\t\n@@ -1,1 +1,1 @@\n-${"x".repeat(70_000)}\n+${"y".repeat(70_000)}\n`
|
|
}
|
|
|
|
/**
|
|
* Hard ceiling per slimmed tool state (JSON bytes). Real slimmed parts
|
|
* should be well under this. If a slimmer leaks even one file-content
|
|
* field (~50-500 KB each) the test blows past this immediately.
|
|
*/
|
|
const MAX_SLIM_BYTES = 10_000
|
|
const PATCH =
|
|
"Index: a.ts\n===================================================================\n--- a.ts\t\n+++ a.ts\t\n@@ -1,2 +1,2 @@\n one\n-two\n+three\n"
|
|
|
|
const BIG = "x".repeat(200_000) // 200 KB — typical file content size
|
|
const DIAG = [
|
|
{ range: { start: { line: 1, character: 0 }, end: { line: 1, character: 5 } }, message: "err", severity: 1 },
|
|
]
|
|
// Regression for #13001: slimmers used to rebuild `metadata` from an explicit allowlist that
|
|
// didn't include `approval`, silently dropping the auto-approval reason (and the
|
|
// outside-workspace note) before it ever reached the webview.
|
|
const APPROVAL = { source: "agent", agent: "code", outsideWorkspace: true, outsideWorkspacePath: "/tmp/a.ts" }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Tests
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe("slimInfo", () => {
|
|
it("drops summary patches while keeping visible diff fields", () => {
|
|
const info = {
|
|
role: "user",
|
|
summary: {
|
|
title: "Updated files",
|
|
diffs: [{ file: "a.ts", patch: BIG, additions: 3, deletions: 1, status: "modified" }],
|
|
},
|
|
}
|
|
|
|
const slim = slimInfo(info)
|
|
expect(slim.summary.title).toBe("Updated files")
|
|
expect(slim.summary.diffs).toEqual([{ file: "a.ts", additions: 3, deletions: 1, status: "modified" }])
|
|
expect(info.summary.diffs[0]?.patch).toBe(BIG)
|
|
})
|
|
|
|
it("passes through summaries without patches unchanged", () => {
|
|
const info = { role: "user", summary: { diffs: [{ file: "a.ts", additions: 1, deletions: 0 }] } }
|
|
expect(slimInfo(info)).toBe(info)
|
|
})
|
|
})
|
|
|
|
describe("slimPart", () => {
|
|
it("passes through non-tool parts unchanged", () => {
|
|
const text = { type: "text", id: "t1", content: "hello" }
|
|
expect(slimPart(text)).toBe(text)
|
|
})
|
|
|
|
it("passes through unknown tool types unchanged", () => {
|
|
const p = part("some_new_tool", { status: "completed", metadata: { big: BIG } })
|
|
expect(slimPart(p)).toBe(p)
|
|
})
|
|
|
|
it("drops encrypted OpenAI reasoning metadata while keeping other provider fields", () => {
|
|
const reasoning = {
|
|
type: "reasoning",
|
|
id: "r1",
|
|
text: "Considering options",
|
|
metadata: {
|
|
openai: { reasoningEncryptedContent: BIG, itemId: "item-1" },
|
|
anthropic: { signature: "sig-1" },
|
|
},
|
|
}
|
|
|
|
const slim = slimPart(reasoning)
|
|
expect(slim.metadata).toEqual({ openai: { itemId: "item-1" }, anthropic: { signature: "sig-1" } })
|
|
expect(reasoning.metadata.openai.reasoningEncryptedContent).toBe(BIG)
|
|
})
|
|
|
|
// -----------------------------------------------------------------------
|
|
// edit
|
|
// -----------------------------------------------------------------------
|
|
describe("edit", () => {
|
|
const heavy = part("edit", {
|
|
status: "completed",
|
|
input: { filePath: "/a.ts", oldString: "old", newString: "new" },
|
|
output: "Edit applied successfully.",
|
|
metadata: {
|
|
diff: BIG,
|
|
filediff: { file: "/a.ts", patch: PATCH, before: BIG, after: BIG, additions: 3, deletions: 1 },
|
|
diagnostics: { "/a.ts": DIAG },
|
|
approval: APPROVAL,
|
|
},
|
|
})
|
|
|
|
it("stays under size cap", () => {
|
|
expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("keeps filediff counts, diagnostics, and approval", () => {
|
|
const slim = slimPart(heavy) as Record<string, any>
|
|
const meta = slim.state.metadata
|
|
expect(meta.filediff.file).toBe("/a.ts")
|
|
expect(meta.filediff.patch).toBe(PATCH)
|
|
expect(meta.filediff.additions).toBe(3)
|
|
expect(meta.filediff.deletions).toBe(1)
|
|
expect(meta.diagnostics).toEqual({ "/a.ts": DIAG })
|
|
expect(meta.approval).toEqual(APPROVAL)
|
|
})
|
|
|
|
it("keeps output and input intact", () => {
|
|
const slim = slimPart(heavy) as Record<string, any>
|
|
expect(slim.state.output).toBe("Edit applied successfully.")
|
|
expect(slim.state.input.filePath).toBe("/a.ts")
|
|
})
|
|
|
|
it("drops unknown heavy metadata fields", () => {
|
|
const withUnknown = part("edit", {
|
|
...heavy.state,
|
|
metadata: { ...(heavy.state.metadata as object), newUpstreamBlob: BIG, otherData: BIG },
|
|
})
|
|
expect(bytes(slimPart(withUnknown))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("drops oversized filediff patches", () => {
|
|
const wide = part("edit", {
|
|
...heavy.state,
|
|
metadata: {
|
|
...(heavy.state.metadata as object),
|
|
filediff: { file: "/a.ts", patch: bigPatch(), before: BIG, after: BIG, additions: 1, deletions: 1 },
|
|
},
|
|
})
|
|
const slim = slimPart(wide) as Record<string, any>
|
|
expect(slim.state.metadata.filediff.patch).toBeUndefined()
|
|
expect(bytes(slim)).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
})
|
|
|
|
// -----------------------------------------------------------------------
|
|
// apply_patch
|
|
// -----------------------------------------------------------------------
|
|
describe("apply_patch", () => {
|
|
const heavy = part("apply_patch", {
|
|
status: "completed",
|
|
input: { patchText: BIG },
|
|
output: "Success. Updated the following files:\n a.ts",
|
|
metadata: {
|
|
diff: BIG,
|
|
files: [
|
|
{
|
|
filePath: "/a.ts",
|
|
relativePath: "a.ts",
|
|
type: "update",
|
|
before: BIG,
|
|
after: BIG,
|
|
patch: PATCH,
|
|
diff: BIG,
|
|
additions: 5,
|
|
deletions: 2,
|
|
},
|
|
{
|
|
filePath: "/b.ts",
|
|
relativePath: "b.ts",
|
|
type: "add",
|
|
before: undefined,
|
|
after: BIG,
|
|
diff: BIG,
|
|
additions: 10,
|
|
deletions: 0,
|
|
movePath: undefined,
|
|
},
|
|
],
|
|
diagnostics: { "/a.ts": DIAG },
|
|
approval: APPROVAL,
|
|
},
|
|
})
|
|
|
|
it("stays under size cap", () => {
|
|
expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("keeps file summary fields, diagnostics, and approval", () => {
|
|
const slim = slimPart(heavy) as Record<string, any>
|
|
const meta = slim.state.metadata
|
|
expect(meta.files[0].filePath).toBe("/a.ts")
|
|
expect(meta.files[0].relativePath).toBe("a.ts")
|
|
expect(meta.files[0].type).toBe("update")
|
|
expect(meta.files[0].patch).toBe(PATCH)
|
|
expect(meta.files[0].additions).toBe(5)
|
|
expect(meta.files[1].type).toBe("add")
|
|
expect(meta.diagnostics).toEqual({ "/a.ts": DIAG })
|
|
expect(meta.approval).toEqual(APPROVAL)
|
|
})
|
|
|
|
it("drops unknown heavy metadata fields", () => {
|
|
const withUnknown = part("apply_patch", {
|
|
...heavy.state,
|
|
metadata: { ...(heavy.state.metadata as object), rawPatch: BIG, snapshot: BIG },
|
|
})
|
|
expect(bytes(slimPart(withUnknown))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("drops unknown heavy fields inside files[]", () => {
|
|
const withUnknown = part("apply_patch", {
|
|
...heavy.state,
|
|
metadata: {
|
|
...(heavy.state.metadata as Record<string, unknown>),
|
|
files: [
|
|
{
|
|
filePath: "/a.ts",
|
|
relativePath: "a.ts",
|
|
type: "update",
|
|
additions: 1,
|
|
deletions: 0,
|
|
newHeavyField: BIG,
|
|
anotherBlob: BIG,
|
|
},
|
|
],
|
|
},
|
|
})
|
|
expect(bytes(slimPart(withUnknown))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("drops oversized per-file patches", () => {
|
|
const wide = part("apply_patch", {
|
|
...heavy.state,
|
|
metadata: {
|
|
...(heavy.state.metadata as Record<string, unknown>),
|
|
files: [
|
|
{
|
|
filePath: "/a.ts",
|
|
relativePath: "a.ts",
|
|
type: "update",
|
|
patch: bigPatch(),
|
|
additions: 1,
|
|
deletions: 1,
|
|
},
|
|
],
|
|
},
|
|
})
|
|
const slim = slimPart(wide) as Record<string, any>
|
|
expect(slim.state.metadata.files[0].patch).toBeUndefined()
|
|
expect(bytes(slim)).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
})
|
|
|
|
// -----------------------------------------------------------------------
|
|
// multiedit
|
|
// -----------------------------------------------------------------------
|
|
describe("multiedit", () => {
|
|
const heavy = part("multiedit", {
|
|
status: "completed",
|
|
input: { edits: [] },
|
|
output: "Applied 2 edits.",
|
|
metadata: {
|
|
diff: BIG,
|
|
diagnostics: { "/a.ts": DIAG },
|
|
results: [
|
|
{
|
|
filediff: { file: "/a.ts", patch: PATCH, before: BIG, after: BIG, additions: 1, deletions: 1 },
|
|
diagnostics: { "/a.ts": DIAG },
|
|
diff: BIG,
|
|
},
|
|
{ filediff: { file: "/b.ts", before: BIG, after: BIG, additions: 2, deletions: 0 }, diagnostics: {} },
|
|
],
|
|
approval: APPROVAL,
|
|
},
|
|
})
|
|
|
|
it("stays under size cap", () => {
|
|
expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("keeps filediff counts, per-result diagnostics, and approval", () => {
|
|
const slim = slimPart(heavy) as Record<string, any>
|
|
const meta = slim.state.metadata
|
|
expect(meta.results[0].filediff.file).toBe("/a.ts")
|
|
expect(meta.results[0].filediff.patch).toBe(PATCH)
|
|
expect(meta.results[0].filediff.additions).toBe(1)
|
|
expect(meta.results[0].diagnostics).toEqual({ "/a.ts": DIAG })
|
|
expect(meta.results[1].filediff.file).toBe("/b.ts")
|
|
expect(meta.diagnostics).toEqual({ "/a.ts": DIAG })
|
|
expect(meta.approval).toEqual(APPROVAL)
|
|
})
|
|
|
|
it("drops unknown heavy metadata fields", () => {
|
|
const withUnknown = part("multiedit", {
|
|
...heavy.state,
|
|
metadata: { ...(heavy.state.metadata as object), rawCombinedDiff: BIG },
|
|
})
|
|
expect(bytes(slimPart(withUnknown))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("drops unknown heavy fields inside results[]", () => {
|
|
const withUnknown = part("multiedit", {
|
|
...heavy.state,
|
|
metadata: {
|
|
...(heavy.state.metadata as Record<string, unknown>),
|
|
results: [{ filediff: { file: "/a.ts", additions: 1, deletions: 0 }, diagnostics: {}, newBlob: BIG }],
|
|
},
|
|
})
|
|
expect(bytes(slimPart(withUnknown))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
})
|
|
|
|
// -----------------------------------------------------------------------
|
|
// write
|
|
// -----------------------------------------------------------------------
|
|
describe("write", () => {
|
|
const heavy = part("write", {
|
|
status: "completed",
|
|
input: { filePath: "/a.ts", content: BIG },
|
|
output: "File written.",
|
|
metadata: {
|
|
filepath: "/a.ts",
|
|
exists: true,
|
|
diff: BIG,
|
|
filediff: { file: "/a.ts", patch: PATCH, before: BIG, after: BIG, additions: 100, deletions: 0 },
|
|
diagnostics: { "/a.ts": DIAG },
|
|
approval: APPROVAL,
|
|
},
|
|
})
|
|
|
|
it("stays under size cap", () => {
|
|
expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
|
|
it("keeps filepath, exists, filediff counts, diagnostics, and approval", () => {
|
|
const slim = slimPart(heavy) as Record<string, any>
|
|
const meta = slim.state.metadata
|
|
expect(meta.filepath).toBe("/a.ts")
|
|
expect(meta.exists).toBe(true)
|
|
expect(meta.filediff.file).toBe("/a.ts")
|
|
expect(meta.filediff.patch).toBe(PATCH)
|
|
expect(meta.filediff.additions).toBe(100)
|
|
expect(meta.filediff.deletions).toBe(0)
|
|
expect(meta.diagnostics).toEqual({ "/a.ts": DIAG })
|
|
expect(meta.approval).toEqual(APPROVAL)
|
|
})
|
|
|
|
it("drops unknown heavy metadata fields", () => {
|
|
const withUnknown = part("write", {
|
|
...heavy.state,
|
|
metadata: { ...(heavy.state.metadata as object), compiled: BIG },
|
|
})
|
|
expect(bytes(slimPart(withUnknown))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
})
|
|
|
|
// -----------------------------------------------------------------------
|
|
// bash
|
|
// -----------------------------------------------------------------------
|
|
describe("bash", () => {
|
|
const heavy = part("bash", {
|
|
status: "completed",
|
|
input: { command: "ls" },
|
|
output: BIG,
|
|
metadata: { output: BIG },
|
|
})
|
|
|
|
it("truncates metadata.output and state.output", () => {
|
|
const slim = slimPart(heavy) as Record<string, any>
|
|
expect(slim.state.metadata.output.length).toBeLessThan(BIG.length)
|
|
expect((slim.state.output as string).length).toBeLessThan(BIG.length)
|
|
})
|
|
|
|
it("stays under size cap", () => {
|
|
expect(bytes(slimPart(heavy))).toBeLessThan(MAX_SLIM_BYTES)
|
|
})
|
|
})
|
|
})
|