feat: add buildRolePrompt in agent-kit, integrate with uwf-hermes

- New buildRolePrompt() in workflow-agent-kit: four-phase prompt assembly
  with fallback to systemPrompt
- Export from agent-kit index
- Update uwf-hermes to use buildRolePrompt instead of raw systemPrompt
- Add tests for all modes: four-phase, legacy, mixed

Refs #359, #362
This commit is contained in:
2026-05-21 02:31:56 +00:00
parent fc7d482b4f
commit 505f85e3c4
4 changed files with 113 additions and 3 deletions
@@ -0,0 +1,68 @@
import { describe, expect, test } from "vitest";
import type { RoleDefinition } from "@uncaged/workflow-protocol";
import { buildRolePrompt } from "../src/build-role-prompt.js";
describe("buildRolePrompt", () => {
test("four-phase: all fields present", () => {
const role: RoleDefinition = {
description: "A coder",
systemPrompt: "legacy prompt",
identity: "You are a senior developer.",
prepare: "Load cursor-agent skill.",
execute: "Implement the feature.",
report: "Summarize changes.",
outputSchema: "placeholder00000" as string,
};
const result = buildRolePrompt(role);
expect(result).toContain("## Identity");
expect(result).toContain("You are a senior developer.");
expect(result).toContain("## Prepare");
expect(result).toContain("Load cursor-agent skill.");
expect(result).toContain("## Execute");
expect(result).toContain("Implement the feature.");
expect(result).toContain("## Report");
expect(result).toContain("Summarize changes.");
expect(result).not.toContain("legacy prompt");
});
test("legacy: only systemPrompt", () => {
const role: RoleDefinition = {
description: "A planner",
systemPrompt: "You are a planning agent.",
identity: null,
prepare: null,
execute: null,
report: null,
outputSchema: "placeholder00000" as string,
};
const result = buildRolePrompt(role);
expect(result).toBe("You are a planning agent.");
});
test("legacy: no fields at all", () => {
const role = {
description: "Minimal",
outputSchema: "placeholder00000",
} as unknown as RoleDefinition;
const result = buildRolePrompt(role);
expect(result).toBe("");
});
test("mixed: identity present, partial other fields", () => {
const role: RoleDefinition = {
description: "A reviewer",
systemPrompt: "legacy",
identity: "You are a code reviewer.",
prepare: null,
execute: "Review the PR diff carefully.",
report: null,
outputSchema: "placeholder00000" as string,
};
const result = buildRolePrompt(role);
expect(result).toContain("## Identity");
expect(result).toContain("## Execute");
expect(result).not.toContain("## Prepare");
expect(result).not.toContain("## Report");
expect(result).not.toContain("legacy");
});
});
@@ -0,0 +1,41 @@
import type { RoleDefinition } from "@uncaged/workflow-protocol";
/**
* Build the role prompt from a RoleDefinition.
*
* Four-phase mode (identity/prepare/execute/report present):
* Assembles structured sections for each phase.
*
* Legacy mode (only systemPrompt):
* Returns systemPrompt as-is.
*
* When both are present, four-phase fields take priority.
*/
export function buildRolePrompt(role: RoleDefinition): string {
const hasFourPhase =
role.identity !== null &&
role.identity !== undefined &&
role.identity !== "";
if (!hasFourPhase) {
return role.systemPrompt ?? "";
}
const sections: string[] = [];
sections.push(`## Identity\n\n${role.identity}`);
if (role.prepare !== null && role.prepare !== undefined && role.prepare !== "") {
sections.push(`## Prepare\n\n${role.prepare}`);
}
if (role.execute !== null && role.execute !== undefined && role.execute !== "") {
sections.push(`## Execute\n\n${role.execute}`);
}
if (role.report !== null && role.report !== undefined && role.report !== "") {
sections.push(`## Report\n\n${role.report}`);
}
return sections.join("\n\n");
}
+1
View File
@@ -7,6 +7,7 @@ export {
resolveModel,
} from "./extract.js";
export { buildOutputFormatInstruction } from "./build-output-format-instruction.js";
export { buildRolePrompt } from "./build-role-prompt.js";
export type { FrontmatterFastPathResult } from "./frontmatter.js";
export { tryFrontmatterFastPath } from "./frontmatter.js";
export { createAgent } from "./run.js";