feat(FN-4400): complete Step 4 — add heartbeat prompt trim helpers
Fusion-Task-Id: FN-4400 Fusion-Task-Lineage: 0d4f9c48-5b8b-49eb-9cf3-d1d585ffe7fc
This commit is contained in:
95
packages/engine/src/__tests__/heartbeat-prompt-trim.test.ts
Normal file
95
packages/engine/src/__tests__/heartbeat-prompt-trim.test.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { trimPromptMd, trimTaskDescription, trimTriggeringComments } from "../heartbeat-prompt-trim.js";
|
||||
|
||||
const TASK_MARKER = "… (truncated, use fn_task_show for full)";
|
||||
const COMMENT_MARKER = "… (older comments hidden, fetch via fn_task_show)";
|
||||
|
||||
describe("trimTaskDescription", () => {
|
||||
it("passes through under-cap text", () => {
|
||||
expect(trimTaskDescription("abc", "default")).toBe("abc");
|
||||
});
|
||||
|
||||
it("preserves exact-cap default", () => {
|
||||
const value = "a".repeat(800);
|
||||
expect(trimTaskDescription(value, "default")).toBe(value);
|
||||
});
|
||||
|
||||
it("truncates over-cap default", () => {
|
||||
const value = "a".repeat(900);
|
||||
const trimmed = trimTaskDescription(value, "default");
|
||||
expect(trimmed.length).toBe(800);
|
||||
expect(trimmed.endsWith(TASK_MARKER)).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves exact-cap compact", () => {
|
||||
const value = "b".repeat(400);
|
||||
expect(trimTaskDescription(value, "compact")).toBe(value);
|
||||
});
|
||||
|
||||
it("truncates over-cap compact", () => {
|
||||
const value = "b".repeat(500);
|
||||
const trimmed = trimTaskDescription(value, "compact");
|
||||
expect(trimmed.length).toBe(400);
|
||||
expect(trimmed.endsWith(TASK_MARKER)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("trimPromptMd", () => {
|
||||
it("returns undefined unchanged", () => {
|
||||
expect(trimPromptMd(undefined, "default")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("passes through under-cap text", () => {
|
||||
expect(trimPromptMd("abc", "compact")).toBe("abc");
|
||||
});
|
||||
|
||||
it("preserves exact-cap default", () => {
|
||||
const value = "x".repeat(4000);
|
||||
expect(trimPromptMd(value, "default")).toBe(value);
|
||||
});
|
||||
|
||||
it("truncates over-cap default", () => {
|
||||
const value = "x".repeat(4200);
|
||||
const trimmed = trimPromptMd(value, "default");
|
||||
expect(trimmed).toBeDefined();
|
||||
expect(trimmed!.length).toBe(4000);
|
||||
expect(trimmed!.endsWith(TASK_MARKER)).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves exact-cap compact", () => {
|
||||
const value = "y".repeat(1500);
|
||||
expect(trimPromptMd(value, "compact")).toBe(value);
|
||||
});
|
||||
|
||||
it("truncates over-cap compact", () => {
|
||||
const value = "y".repeat(1700);
|
||||
const trimmed = trimPromptMd(value, "compact");
|
||||
expect(trimmed).toBeDefined();
|
||||
expect(trimmed!.length).toBe(1500);
|
||||
expect(trimmed!.endsWith(TASK_MARKER)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("trimTriggeringComments", () => {
|
||||
it("passes through empty input", () => {
|
||||
expect(trimTriggeringComments([], "default")).toEqual([]);
|
||||
});
|
||||
|
||||
it("passes through <=3 lines", () => {
|
||||
const lines = ["one", "two", "three"];
|
||||
expect(trimTriggeringComments(lines, "compact")).toEqual(lines);
|
||||
});
|
||||
|
||||
it("takes the last 3 entries without re-sorting", () => {
|
||||
const lines = ["t1", "t2", "t3", "t4", "t5"];
|
||||
expect(trimTriggeringComments(lines, "default")).toEqual(["t3", "t4", "t5"]);
|
||||
});
|
||||
|
||||
it("caps joined body at 500 chars and appends marker", () => {
|
||||
const lines = ["h1", "h2", `A${"z".repeat(600)}`, `B${"z".repeat(600)}`, `C${"z".repeat(600)}`];
|
||||
const trimmed = trimTriggeringComments(lines, "default");
|
||||
const joined = trimmed.join("\n");
|
||||
expect(joined.length).toBe(500);
|
||||
expect(joined.endsWith(COMMENT_MARKER)).toBe(true);
|
||||
});
|
||||
});
|
||||
45
packages/engine/src/heartbeat-prompt-trim.ts
Normal file
45
packages/engine/src/heartbeat-prompt-trim.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import type { HeartbeatPromptTemplate } from "@fusion/core";
|
||||
|
||||
const TASK_DESC_CAP: Record<HeartbeatPromptTemplate, number> = {
|
||||
default: 800,
|
||||
compact: 400,
|
||||
};
|
||||
const PROMPT_MD_CAP: Record<HeartbeatPromptTemplate, number> = {
|
||||
default: 4000,
|
||||
compact: 1500,
|
||||
};
|
||||
const TASK_TRUNCATION_MARKER = "… (truncated, use fn_task_show for full)";
|
||||
const COMMENTS_TRUNCATION_MARKER = "… (older comments hidden, fetch via fn_task_show)";
|
||||
|
||||
function truncate(value: string, cap: number, marker: string): string {
|
||||
if (value.length <= cap) {
|
||||
return value;
|
||||
}
|
||||
const sliceLength = Math.max(0, cap - marker.length);
|
||||
return `${value.slice(0, sliceLength)}${marker}`;
|
||||
}
|
||||
|
||||
export function trimTaskDescription(description: string, template: HeartbeatPromptTemplate): string {
|
||||
return truncate(description, TASK_DESC_CAP[template], TASK_TRUNCATION_MARKER);
|
||||
}
|
||||
|
||||
export function trimPromptMd(prompt: string | undefined, template: HeartbeatPromptTemplate): string | undefined {
|
||||
if (prompt === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return truncate(prompt, PROMPT_MD_CAP[template], TASK_TRUNCATION_MARKER);
|
||||
}
|
||||
|
||||
export function trimTriggeringComments(lines: string[], _template: HeartbeatPromptTemplate): string[] {
|
||||
if (lines.length <= 3) {
|
||||
return lines;
|
||||
}
|
||||
|
||||
const selected = lines.slice(-3);
|
||||
const joined = selected.join("\n");
|
||||
if (joined.length <= 500) {
|
||||
return selected;
|
||||
}
|
||||
const truncated = truncate(joined, 500, COMMENTS_TRUNCATION_MARKER);
|
||||
return truncated.split("\n");
|
||||
}
|
||||
Reference in New Issue
Block a user