Merge pull request 'fix(thread-read): remove ### Output section and deduplicate ### Prompt globally' (#442) from fix/440-thread-read-prompt-dedup into main
This commit is contained in:
@@ -266,12 +266,7 @@ describe("cmdThreadRead ### Content section", () => {
|
||||
|
||||
expect(markdown).toContain("### Content");
|
||||
expect(markdown).toContain("The assistant response text");
|
||||
|
||||
const contentIdx = markdown.indexOf("### Content");
|
||||
const outputIdx = markdown.indexOf("### Output");
|
||||
expect(contentIdx).toBeGreaterThanOrEqual(0);
|
||||
expect(outputIdx).toBeGreaterThanOrEqual(0);
|
||||
expect(contentIdx).toBeLessThan(outputIdx);
|
||||
expect(markdown).not.toContain("### Output");
|
||||
});
|
||||
|
||||
test("omits ### Content when detail has no matching assistant turns", async () => {
|
||||
@@ -314,7 +309,7 @@ describe("cmdThreadRead ### Content section", () => {
|
||||
const markdown = await cmdThreadRead(tmpDir, threadId, THREAD_READ_DEFAULT_QUOTA, null, false);
|
||||
|
||||
expect(markdown).not.toContain("### Content");
|
||||
expect(markdown).toContain("### Output");
|
||||
expect(markdown).not.toContain("### Output");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -392,3 +387,87 @@ describe("cmdThreadStepDetails", () => {
|
||||
await expect(cmdThreadStepDetails(tmpDir, "nonexistenth0" as CasRef)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
// ── cmdThreadRead: ### Prompt deduplication ───────────────────────────────────
|
||||
|
||||
describe("cmdThreadRead ### Prompt deduplication", () => {
|
||||
async function makeThreadWithRoles(uwf: UwfStore, roles: string[]): Promise<string> {
|
||||
const roleMap: Record<string, unknown> = {};
|
||||
for (const r of [...new Set(roles)]) {
|
||||
roleMap[r] = {
|
||||
description: r,
|
||||
goal: `Goal for ${r}`,
|
||||
capabilities: [],
|
||||
procedure: "Do stuff.",
|
||||
output: "Output.",
|
||||
meta: "placeholder00" as CasRef,
|
||||
};
|
||||
}
|
||||
const workflowHash = await uwf.store.put(uwf.schemas.workflow, {
|
||||
name: "dedup-wf",
|
||||
description: "desc",
|
||||
roles: roleMap,
|
||||
conditions: {},
|
||||
graph: {},
|
||||
});
|
||||
const startHash = await uwf.store.put(uwf.schemas.startNode, {
|
||||
workflow: workflowHash,
|
||||
prompt: "Start",
|
||||
});
|
||||
const outputHash = await uwf.store.put(uwf.schemas.workflow, {
|
||||
name: "out",
|
||||
description: "",
|
||||
roles: {},
|
||||
conditions: {},
|
||||
graph: {},
|
||||
});
|
||||
|
||||
let prev: string | null = null;
|
||||
let stepHash = "";
|
||||
for (const role of roles) {
|
||||
stepHash = await uwf.store.put(uwf.schemas.stepNode, {
|
||||
start: startHash,
|
||||
prev: prev as CasRef | null,
|
||||
role,
|
||||
output: outputHash,
|
||||
detail: null,
|
||||
agent: "uwf-test",
|
||||
});
|
||||
prev = stepHash;
|
||||
}
|
||||
return stepHash;
|
||||
}
|
||||
|
||||
test("same consecutive role shows ### Prompt once", async () => {
|
||||
const uwf = await makeUwfStore(tmpDir);
|
||||
const headHash = await makeThreadWithRoles(uwf, ["writer", "writer"]);
|
||||
const threadId = "01JTEST0000000000000003" as ThreadId;
|
||||
await saveThreadsIndex(tmpDir, { [threadId]: headHash });
|
||||
|
||||
const markdown = await cmdThreadRead(tmpDir, threadId, THREAD_READ_DEFAULT_QUOTA, null, false);
|
||||
const count = (markdown.match(/### Prompt/g) ?? []).length;
|
||||
expect(count).toBe(1);
|
||||
});
|
||||
|
||||
test("different consecutive roles each show ### Prompt", async () => {
|
||||
const uwf = await makeUwfStore(tmpDir);
|
||||
const headHash = await makeThreadWithRoles(uwf, ["planner", "coder"]);
|
||||
const threadId = "01JTEST0000000000000004" as ThreadId;
|
||||
await saveThreadsIndex(tmpDir, { [threadId]: headHash });
|
||||
|
||||
const markdown = await cmdThreadRead(tmpDir, threadId, THREAD_READ_DEFAULT_QUOTA, null, false);
|
||||
const count = (markdown.match(/### Prompt/g) ?? []).length;
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
|
||||
test("non-consecutive same role shows ### Prompt twice", async () => {
|
||||
const uwf = await makeUwfStore(tmpDir);
|
||||
const headHash = await makeThreadWithRoles(uwf, ["roleA", "roleB", "roleA"]);
|
||||
const threadId = "01JTEST0000000000000005" as ThreadId;
|
||||
await saveThreadsIndex(tmpDir, { [threadId]: headHash });
|
||||
|
||||
const markdown = await cmdThreadRead(tmpDir, threadId, THREAD_READ_DEFAULT_QUOTA, null, false);
|
||||
const count = (markdown.match(/### Prompt/g) ?? []).length;
|
||||
expect(count).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -655,11 +655,11 @@ function formatThreadReadMarkdown(options: {
|
||||
|
||||
// Step blocks
|
||||
const startIndex = candidates.length - selected.length;
|
||||
const shownPromptRoles = new Set<string>();
|
||||
for (let i = 0; i < selected.length; i++) {
|
||||
const item = selected[i];
|
||||
if (item === undefined) continue;
|
||||
const stepNum = startIndex + i + 1;
|
||||
const outputYaml = formatYaml(expandOutput(uwf, item.payload.output));
|
||||
const ts = new Date(item.timestamp)
|
||||
.toISOString()
|
||||
.replace("T", " ")
|
||||
@@ -669,9 +669,10 @@ function formatThreadReadMarkdown(options: {
|
||||
`**Agent:** ${item.payload.agent} | **Time:** ${ts}`,
|
||||
];
|
||||
const roleDef = workflow.roles[item.payload.role];
|
||||
if (roleDef) {
|
||||
if (roleDef && !shownPromptRoles.has(item.payload.role)) {
|
||||
const prompt = roleDef.goal;
|
||||
stepLines.push("", "### Prompt", "", prompt);
|
||||
shownPromptRoles.add(item.payload.role);
|
||||
}
|
||||
if (item.payload.detail) {
|
||||
const content = extractLastAssistantContent(uwf, item.payload.detail);
|
||||
@@ -679,7 +680,6 @@ function formatThreadReadMarkdown(options: {
|
||||
stepLines.push("", "### Content", "", content);
|
||||
}
|
||||
}
|
||||
stepLines.push("", "### Output", "", "```yaml", outputYaml, "```");
|
||||
parts.push(stepLines.join("\n"));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user