tests / agenticProtocol.test.ts
tests / agenticProtocol.test.ts
import { describe, expect, test } from "vitest";
import {
AGENTIC_WORKSPACE_PROTOCOL,
chunkHasAgenticResults,
compactAgenticToolResult,
isAgenticWorkspaceResult,
renderAgenticToolResultForSummary,
} from "../src/agenticProtocol";
import { CanonMessage } from "../src/view";
function envelope() {
return {
protocol: AGENTIC_WORKSPACE_PROTOCOL,
ok: true,
operation: "edit.apply",
summary: "Applied 2 file changes (+20/-4).",
retention: {
importance: "critical",
facts: [
"transaction tx_abc123 applied",
"todo_board item_7 completed",
"source source_primary: https://example.com/spec",
],
omit_when_summarizing: ["data.diffPreview", "data.stdout"],
},
artifacts: [
{
kind: "diff",
path: ".agentic/transactions/tx_abc123/changes.diff",
sha256: "f".repeat(64),
bytes: 9000,
},
],
data: {
transactionId: "tx_abc123",
status: "applied",
reviewPath: ".agentic/transactions/tx_abc123/review.md",
diffPreview: "x".repeat(50_000),
stdout: "y".repeat(30_000),
files: [
{ path: "src/a.ts", added: 10, removed: 2 },
{ path: "src/b.ts", added: 10, removed: 2 },
],
},
};
}
describe("agentic workspace protocol compaction", () => {
test("detects structured object and JSON-string envelopes", () => {
expect(isAgenticWorkspaceResult(envelope())).toBe(true);
expect(isAgenticWorkspaceResult(JSON.stringify(envelope()))).toBe(true);
expect(isAgenticWorkspaceResult({ protocol: "other" })).toBe(false);
});
test("drops declared bulky payloads while retaining recovery state", () => {
const compact = compactAgenticToolResult(envelope(), 4000) as Record<string, unknown>;
const text = JSON.stringify(compact);
expect(text).toContain("tx_abc123");
expect(text).toContain("transaction tx_abc123 applied");
expect(text).toContain("review.md");
expect(text).toContain("changes.diff");
expect(text).toContain("src/a.ts");
expect(text).not.toContain("x".repeat(200));
expect(text).not.toContain("y".repeat(200));
expect(text).toContain("data.diffPreview");
expect((compact as Record<string, unknown>).error).toBeUndefined();
});
test("preserves task next-actions and command verification fields", () => {
const value: any = envelope();
value.data = {
...value.data,
nextActionable: [
{
id: "item_next",
text: "Run the integration test",
status: "pending",
priority: "critical",
dependsOn: ["item_previous"],
},
],
command: {
id: "job_verify",
executable: "npm",
args: ["test"],
cwd: ".",
status: "completed",
exitCode: 0,
durationMs: 1234,
},
};
const compact = compactAgenticToolResult(value, 6000);
const text = JSON.stringify(compact);
for (const required of [
"item_next",
"Run the integration test",
"critical",
"item_previous",
"job_verify",
"npm",
"exitCode",
"1234",
]) {
expect(text).toContain(required);
}
});
test("preserves input representation for JSON strings", () => {
const compact = compactAgenticToolResult(JSON.stringify(envelope()), 4000);
expect(typeof compact).toBe("string");
const parsed = JSON.parse(compact as string);
expect(parsed.data.transactionId).toBe("tx_abc123");
});
test("summary rendering is bounded and keeps critical IDs", () => {
const rendered = renderAgenticToolResultForSummary(envelope(), 1200);
expect(rendered.length).toBeLessThan(1600);
expect(rendered).toContain("tx_abc123");
expect(rendered).toContain("changes.diff");
});
test("unrelated tool results pass through unchanged", () => {
const value = { ok: true, data: "x".repeat(100) };
expect(compactAgenticToolResult(value, 10)).toBe(value);
});
});
describe("chunkHasAgenticResults", () => {
test("true when a tool result carries an object-form envelope", () => {
const msgs: CanonMessage[] = [
{
role: "tool",
text: "",
// Real CanonMessage.toolResults[].content is always a string, but the
// detection machinery (parseEnvelope) accepts either form — exercise
// both here since chunkHasAgenticResults must reuse it as-is.
toolResults: [{ content: envelope() as unknown as string }],
},
];
expect(chunkHasAgenticResults(msgs)).toBe(true);
});
test("true when a tool result carries a JSON-string envelope", () => {
const msgs: CanonMessage[] = [
{ role: "tool", text: "", toolResults: [{ content: JSON.stringify(envelope()) }] },
];
expect(chunkHasAgenticResults(msgs)).toBe(true);
});
test("false for a plain JSON tool result", () => {
const msgs: CanonMessage[] = [
{
role: "tool",
text: "",
toolResults: [{ content: JSON.stringify({ ok: true, data: "x" }) }],
},
];
expect(chunkHasAgenticResults(msgs)).toBe(false);
});
test("false for prose tool results and regular chat messages", () => {
const msgs: CanonMessage[] = [
{ role: "user", text: "please fix the bug in app.py" },
{ role: "assistant", text: "reading the file first" },
{ role: "tool", text: "", toolResults: [{ content: "def main(): ..." }] },
];
expect(chunkHasAgenticResults(msgs)).toBe(false);
});
test("false when there are no tool results at all", () => {
const msgs: CanonMessage[] = [
{ role: "user", text: "hello" },
{ role: "assistant", text: "hi there" },
];
expect(chunkHasAgenticResults(msgs)).toBe(false);
});
});
import { describe, expect, test } from "vitest";
import {
AGENTIC_WORKSPACE_PROTOCOL,
chunkHasAgenticResults,
compactAgenticToolResult,
isAgenticWorkspaceResult,
renderAgenticToolResultForSummary,
} from "../src/agenticProtocol";
import { CanonMessage } from "../src/view";
function envelope() {
return {
protocol: AGENTIC_WORKSPACE_PROTOCOL,
ok: true,
operation: "edit.apply",
summary: "Applied 2 file changes (+20/-4).",
retention: {
importance: "critical",
facts: [
"transaction tx_abc123 applied",
"todo_board item_7 completed",
"source source_primary: https://example.com/spec",
],
omit_when_summarizing: ["data.diffPreview", "data.stdout"],
},
artifacts: [
{
kind: "diff",
path: ".agentic/transactions/tx_abc123/changes.diff",
sha256: "f".repeat(64),
bytes: 9000,
},
],
data: {
transactionId: "tx_abc123",
status: "applied",
reviewPath: ".agentic/transactions/tx_abc123/review.md",
diffPreview: "x".repeat(50_000),
stdout: "y".repeat(30_000),
files: [
{ path: "src/a.ts", added: 10, removed: 2 },
{ path: "src/b.ts", added: 10, removed: 2 },
],
},
};
}
describe("agentic workspace protocol compaction", () => {
test("detects structured object and JSON-string envelopes", () => {
expect(isAgenticWorkspaceResult(envelope())).toBe(true);
expect(isAgenticWorkspaceResult(JSON.stringify(envelope()))).toBe(true);
expect(isAgenticWorkspaceResult({ protocol: "other" })).toBe(false);
});
test("drops declared bulky payloads while retaining recovery state", () => {
const compact = compactAgenticToolResult(envelope(), 4000) as Record<string, unknown>;
const text = JSON.stringify(compact);
expect(text).toContain("tx_abc123");
expect(text).toContain("transaction tx_abc123 applied");
expect(text).toContain("review.md");
expect(text).toContain("changes.diff");
expect(text).toContain("src/a.ts");
expect(text).not.toContain("x".repeat(200));
expect(text).not.toContain("y".repeat(200));
expect(text).toContain("data.diffPreview");
expect((compact as Record<string, unknown>).error).toBeUndefined();
});
test("preserves task next-actions and command verification fields", () => {
const value: any = envelope();
value.data = {
...value.data,
nextActionable: [
{
id: "item_next",
text: "Run the integration test",
status: "pending",
priority: "critical",
dependsOn: ["item_previous"],
},
],
command: {
id: "job_verify",
executable: "npm",
args: ["test"],
cwd: ".",
status: "completed",
exitCode: 0,
durationMs: 1234,
},
};
const compact = compactAgenticToolResult(value, 6000);
const text = JSON.stringify(compact);
for (const required of [
"item_next",
"Run the integration test",
"critical",
"item_previous",
"job_verify",
"npm",
"exitCode",
"1234",
]) {
expect(text).toContain(required);
}
});
test("preserves input representation for JSON strings", () => {
const compact = compactAgenticToolResult(JSON.stringify(envelope()), 4000);
expect(typeof compact).toBe("string");
const parsed = JSON.parse(compact as string);
expect(parsed.data.transactionId).toBe("tx_abc123");
});
test("summary rendering is bounded and keeps critical IDs", () => {
const rendered = renderAgenticToolResultForSummary(envelope(), 1200);
expect(rendered.length).toBeLessThan(1600);
expect(rendered).toContain("tx_abc123");
expect(rendered).toContain("changes.diff");
});
test("unrelated tool results pass through unchanged", () => {
const value = { ok: true, data: "x".repeat(100) };
expect(compactAgenticToolResult(value, 10)).toBe(value);
});
});
describe("chunkHasAgenticResults", () => {
test("true when a tool result carries an object-form envelope", () => {
const msgs: CanonMessage[] = [
{
role: "tool",
text: "",
// Real CanonMessage.toolResults[].content is always a string, but the
// detection machinery (parseEnvelope) accepts either form — exercise
// both here since chunkHasAgenticResults must reuse it as-is.
toolResults: [{ content: envelope() as unknown as string }],
},
];
expect(chunkHasAgenticResults(msgs)).toBe(true);
});
test("true when a tool result carries a JSON-string envelope", () => {
const msgs: CanonMessage[] = [
{ role: "tool", text: "", toolResults: [{ content: JSON.stringify(envelope()) }] },
];
expect(chunkHasAgenticResults(msgs)).toBe(true);
});
test("false for a plain JSON tool result", () => {
const msgs: CanonMessage[] = [
{
role: "tool",
text: "",
toolResults: [{ content: JSON.stringify({ ok: true, data: "x" }) }],
},
];
expect(chunkHasAgenticResults(msgs)).toBe(false);
});
test("false for prose tool results and regular chat messages", () => {
const msgs: CanonMessage[] = [
{ role: "user", text: "please fix the bug in app.py" },
{ role: "assistant", text: "reading the file first" },
{ role: "tool", text: "", toolResults: [{ content: "def main(): ..." }] },
];
expect(chunkHasAgenticResults(msgs)).toBe(false);
});
test("false when there are no tool results at all", () => {
const msgs: CanonMessage[] = [
{ role: "user", text: "hello" },
{ role: "assistant", text: "hi there" },
];
expect(chunkHasAgenticResults(msgs)).toBe(false);
});
});