From 505f85e3c49fc0ccd9de611a29e2b53151cb6b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Thu, 21 May 2026 02:31:56 +0000 Subject: [PATCH] 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 --- packages/workflow-agent-hermes/src/hermes.ts | 6 +- .../__tests__/build-role-prompt.test.ts | 68 +++++++++++++++++++ .../src/build-role-prompt.ts | 41 +++++++++++ packages/workflow-agent-kit/src/index.ts | 1 + 4 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts create mode 100644 packages/workflow-agent-kit/src/build-role-prompt.ts diff --git a/packages/workflow-agent-hermes/src/hermes.ts b/packages/workflow-agent-hermes/src/hermes.ts index 549e4b5..5d853b2 100644 --- a/packages/workflow-agent-hermes/src/hermes.ts +++ b/packages/workflow-agent-hermes/src/hermes.ts @@ -1,6 +1,6 @@ import { spawn } from "node:child_process"; -import { type AgentContext, type AgentRunResult, createAgent } from "@uncaged/workflow-agent-kit"; +import { type AgentContext, type AgentRunResult, buildRolePrompt, createAgent } from "@uncaged/workflow-agent-kit"; import { loadHermesSession, @@ -34,12 +34,12 @@ function buildHistorySummary(steps: AgentContext["steps"]): string { /** Assemble system prompt, task, and prior step outputs for Hermes. */ export function buildHermesPrompt(ctx: AgentContext): string { const roleDef = ctx.workflow.roles[ctx.role]; - const systemPrompt = roleDef?.systemPrompt ?? ""; + const rolePrompt = roleDef !== undefined ? buildRolePrompt(roleDef) : ""; const parts: string[] = []; if (ctx.outputFormatInstruction !== undefined && ctx.outputFormatInstruction !== "") { parts.push(ctx.outputFormatInstruction, ""); } - parts.push(systemPrompt, "", "## Task", ctx.start.prompt); + parts.push(rolePrompt, "", "## Task", ctx.start.prompt); const historyBlock = buildHistorySummary(ctx.steps); if (historyBlock !== "") { parts.push("", historyBlock); diff --git a/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts b/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts new file mode 100644 index 0000000..e0a1c60 --- /dev/null +++ b/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts @@ -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"); + }); +}); diff --git a/packages/workflow-agent-kit/src/build-role-prompt.ts b/packages/workflow-agent-kit/src/build-role-prompt.ts new file mode 100644 index 0000000..e3ce3fe --- /dev/null +++ b/packages/workflow-agent-kit/src/build-role-prompt.ts @@ -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"); +} diff --git a/packages/workflow-agent-kit/src/index.ts b/packages/workflow-agent-kit/src/index.ts index 2d5f6b0..ce965c5 100644 --- a/packages/workflow-agent-kit/src/index.ts +++ b/packages/workflow-agent-kit/src/index.ts @@ -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";