feat: add four-phase role description (identity/prepare/execute/report)

- Extend RoleDefinition with identity, prepare, execute, report fields
- Make systemPrompt optional (nullable) for four-phase workflows
- Update ROLE_DEFINITION JSON Schema (all new fields optional)
- Update validate.ts to accept new fields
- Update workflow.ts to strip null fields before CAS storage
- Update thread read to prefer identity over systemPrompt
- Add --version flag to uwf CLI
- Bump all packages to 0.5.0

Refs #359
This commit is contained in:
2026-05-21 01:41:20 +00:00
parent f9979c3c89
commit fc7d482b4f
12 changed files with 45 additions and 15 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@uncaged/cli-workflow", "name": "@uncaged/cli-workflow",
"version": "0.1.0", "version": "0.5.0",
"files": [ "files": [
"src", "src",
"dist", "dist",
+3 -1
View File
@@ -45,7 +45,9 @@ function runAction(action: () => Promise<void>): void {
const program = new Command(); const program = new Command();
program.name("uwf").description("Stateless workflow CLI"); // eslint-disable-next-line -- dynamic import for version
const pkg = await import("../package.json", { with: { type: "json" } });
program.name("uwf").description("Stateless workflow CLI").version(pkg.default.version, "-V, --version");
program.option("--format <fmt>", "Output format: json or yaml", "json"); program.option("--format <fmt>", "Output format: json or yaml", "json");
const workflow = program.command("workflow").description("Workflow registry and CAS"); const workflow = program.command("workflow").description("Workflow registry and CAS");
+2 -1
View File
@@ -500,7 +500,8 @@ function formatThreadReadMarkdown(options: {
]; ];
const roleDef = workflow.roles[item.payload.role]; const roleDef = workflow.roles[item.payload.role];
if (roleDef) { if (roleDef) {
stepLines.push("", "### Prompt", "", roleDef.systemPrompt); const prompt = roleDef.identity ?? roleDef.systemPrompt ?? "";
stepLines.push("", "### Prompt", "", prompt);
} }
if (item.payload.detail) { if (item.payload.detail) {
const content = extractLastAssistantContent(uwf, item.payload.detail); const content = extractLastAssistantContent(uwf, item.payload.detail);
+13 -2
View File
@@ -67,11 +67,22 @@ async function materializeWorkflowPayload(
`${raw.name}.${roleName}`, `${raw.name}.${roleName}`,
role.outputSchema, role.outputSchema,
); );
roles[roleName] = { const roleDef: RoleDefinition = {
description: role.description, description: role.description,
systemPrompt: role.systemPrompt, systemPrompt: role.systemPrompt ?? null,
identity: role.identity ?? null,
prepare: role.prepare ?? null,
execute: role.execute ?? null,
report: role.report ?? null,
outputSchema, outputSchema,
}; };
// Strip null fields so CAS payload stays lean and schema-valid
for (const key of ["systemPrompt", "identity", "prepare", "execute", "report"] as const) {
if (roleDef[key] === null) {
delete (roleDef as Record<string, unknown>)[key];
}
}
roles[roleName] = roleDef;
} }
return { return {
name: raw.name, name: raw.name,
+11 -3
View File
@@ -16,9 +16,17 @@ function isRoleDefinition(value: unknown): boolean {
} }
const outputSchema = value.outputSchema; const outputSchema = value.outputSchema;
const schemaOk = isRecord(outputSchema) && typeof outputSchema.type === "string"; const schemaOk = isRecord(outputSchema) && typeof outputSchema.type === "string";
return ( const hasSystemPrompt =
typeof value.description === "string" && typeof value.systemPrompt === "string" && schemaOk value.systemPrompt === undefined || value.systemPrompt === null || typeof value.systemPrompt === "string";
); const hasIdentity =
value.identity === undefined || value.identity === null || typeof value.identity === "string";
const hasPrepare =
value.prepare === undefined || value.prepare === null || typeof value.prepare === "string";
const hasExecute =
value.execute === undefined || value.execute === null || typeof value.execute === "string";
const hasReport =
value.report === undefined || value.report === null || typeof value.report === "string";
return typeof value.description === "string" && hasSystemPrompt && hasIdentity && hasPrepare && hasExecute && hasReport && schemaOk;
} }
function isConditionDefinition(value: unknown): boolean { function isConditionDefinition(value: unknown): boolean {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@uncaged/workflow-agent-hermes", "name": "@uncaged/workflow-agent-hermes",
"version": "0.1.0", "version": "0.5.0",
"files": [ "files": [
"src", "src",
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@uncaged/workflow-agent-kit", "name": "@uncaged/workflow-agent-kit",
"version": "0.1.0", "version": "0.5.0",
"files": [ "files": [
"src", "src",
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@uncaged/workflow-moderator", "name": "@uncaged/workflow-moderator",
"version": "0.1.0", "version": "0.5.0",
"files": [ "files": [
"src", "src",
"dist", "dist",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@uncaged/workflow-protocol", "name": "@uncaged/workflow-protocol",
"version": "0.1.0", "version": "0.5.0",
"files": [ "files": [
"src", "src",
"dist", "dist",
+5 -1
View File
@@ -2,10 +2,14 @@ import type { JSONSchema } from "@uncaged/json-cas";
const ROLE_DEFINITION: JSONSchema = { const ROLE_DEFINITION: JSONSchema = {
type: "object", type: "object",
required: ["description", "systemPrompt", "outputSchema"], required: ["description", "outputSchema"],
properties: { properties: {
description: { type: "string" }, description: { type: "string" },
systemPrompt: { type: "string" }, systemPrompt: { type: "string" },
identity: { type: "string" },
prepare: { type: "string" },
execute: { type: "string" },
report: { type: "string" },
outputSchema: { type: "string", format: "cas_ref" }, outputSchema: { type: "string", format: "cas_ref" },
}, },
additionalProperties: false, additionalProperties: false,
+5 -1
View File
@@ -18,7 +18,11 @@ export type StepRecord = {
export type RoleDefinition = { export type RoleDefinition = {
description: string; description: string;
systemPrompt: string; systemPrompt: string | null;
identity: string | null;
prepare: string | null;
execute: string | null;
report: string | null;
outputSchema: CasRef; outputSchema: CasRef;
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@uncaged/workflow-util", "name": "@uncaged/workflow-util",
"version": "0.5.0-alpha.4", "version": "0.5.0",
"files": [ "files": [
"src", "src",
"dist", "dist",