diff --git a/packages/workflow-role-coder/src/coder.ts b/packages/workflow-role-coder/src/coder.ts index f0753c9..811de3c 100644 --- a/packages/workflow-role-coder/src/coder.ts +++ b/packages/workflow-role-coder/src/coder.ts @@ -1,4 +1,4 @@ -import type { AgentFn, Role, ThreadContext } from "@uncaged/workflow"; +import type { AgentFn, Role } from "@uncaged/workflow"; import { createRole } from "@uncaged/workflow-agent-llm"; import type { LlmProvider } from "@uncaged/workflow-util-role"; import * as z from "zod/v4"; @@ -40,7 +40,7 @@ export function createCoderRole( return createRole({ name: "coder", schema: coderMetaSchema, - systemPrompt: async (_ctx: ThreadContext) => coderSystemPrompt(config), + systemPrompt: coderSystemPrompt(config), agent: adapter, extract: { provider: extract.provider, diff --git a/packages/workflow-role-committer/src/committer.ts b/packages/workflow-role-committer/src/committer.ts index 15f9486..349362c 100644 --- a/packages/workflow-role-committer/src/committer.ts +++ b/packages/workflow-role-committer/src/committer.ts @@ -1,4 +1,4 @@ -import type { AgentFn, Role, ThreadContext } from "@uncaged/workflow"; +import type { AgentFn, Role } from "@uncaged/workflow"; import { createRole, decorateRole, @@ -46,13 +46,9 @@ function resolveExtractDryRun(extractDryRun: boolean | null): boolean { return extractDryRun === true; } -function committerSystemPrompt(ctx: ThreadContext, config: CommitterConfig): string { +function committerSystemPrompt(config: CommitterConfig): string { return `You are the git committer for this workflow. The project is at \`${config.cwd}\`. -## Context - -Use \`uncaged-workflow thread ${ctx.threadId}\` to read the full workflow thread for context on what was done and why. - ## Task Create a branch, commit the changes, and push. Report whether the push succeeded or failed, the branch name, and the commit SHA. @@ -77,7 +73,7 @@ export function createCommitterRole( const inner: Role = createRole({ name: "committer", schema: committerMetaSchema, - systemPrompt: async (ctx) => committerSystemPrompt(ctx, config), + systemPrompt: committerSystemPrompt(config), agent: adapter, extract, }); diff --git a/packages/workflow-role-planner/src/planner.ts b/packages/workflow-role-planner/src/planner.ts index eb508e3..89f5276 100644 --- a/packages/workflow-role-planner/src/planner.ts +++ b/packages/workflow-role-planner/src/planner.ts @@ -1,4 +1,4 @@ -import type { AgentFn, Role, ThreadContext } from "@uncaged/workflow"; +import type { AgentFn, Role } from "@uncaged/workflow"; import { createRole } from "@uncaged/workflow-agent-llm"; import type { LlmProvider } from "@uncaged/workflow-util-role"; import * as z from "zod/v4"; @@ -26,22 +26,18 @@ Each phase must have: a short **name** (stable identifier), a **description** of Order phases so earlier steps unblock later ones. Cover root cause, edge cases, and verification across the phases. Do not emit separate file lists or a free-form "approach" field — put that detail inside phase descriptions.`; -function plannerSystemPrompt(_config: PlannerConfig): string { - return PLANNER_SYSTEM; -} - /** * Planner role: produces ordered implementation phases for the coder to execute sequentially. */ export function createPlannerRole( adapter: AgentFn, extract: { provider: LlmProvider; dryRun: boolean | null; dryRunMeta: PlannerMeta }, - config: PlannerConfig = DEFAULT_PLANNER_CONFIG, + _config: PlannerConfig = DEFAULT_PLANNER_CONFIG, ): Role { return createRole({ name: "planner", schema: plannerMetaSchema, - systemPrompt: async (_ctx: ThreadContext) => plannerSystemPrompt(config), + systemPrompt: PLANNER_SYSTEM, agent: adapter, extract: { provider: extract.provider, diff --git a/packages/workflow-role-reviewer/__tests__/reviewer.test.ts b/packages/workflow-role-reviewer/__tests__/reviewer.test.ts index ff7ae48..d4003e9 100644 --- a/packages/workflow-role-reviewer/__tests__/reviewer.test.ts +++ b/packages/workflow-role-reviewer/__tests__/reviewer.test.ts @@ -88,7 +88,7 @@ describe("createReviewerRole", () => { expect(out.meta).toEqual({ status: "rejected", issues: ["secrets in code"] }); }); - test("prompt includes threadId from context", async () => { + test("system prompt includes configured cwd (thread CLI hint comes from agent layer)", async () => { globalThis.fetch = (() => Promise.resolve( toolCallResponse(JSON.stringify({ status: "approved" })), @@ -106,6 +106,8 @@ describe("createReviewerRole", () => { { cwd: "/proj" }, ); await role(makeCtx()); - expect(seen).toContain("uncaged-workflow thread 01TEST000000000000000000TR"); + expect(seen).toContain("/proj"); + expect(seen).toContain("code reviewer"); + expect(seen).not.toContain("uncaged-workflow thread"); }); }); diff --git a/packages/workflow-role-reviewer/src/reviewer.ts b/packages/workflow-role-reviewer/src/reviewer.ts index 220d068..c3e9afe 100644 --- a/packages/workflow-role-reviewer/src/reviewer.ts +++ b/packages/workflow-role-reviewer/src/reviewer.ts @@ -1,4 +1,4 @@ -import type { AgentFn, Role, ThreadContext } from "@uncaged/workflow"; +import type { AgentFn, Role } from "@uncaged/workflow"; import { createRole } from "@uncaged/workflow-agent-llm"; import type { LlmProvider } from "@uncaged/workflow-util-role"; import * as z from "zod/v4"; @@ -22,15 +22,11 @@ export const DEFAULT_REVIEWER_CONFIG: ReviewerConfig = { cwd: ".", }; -function reviewerPrompt(config: ReviewerConfig, ctx: ThreadContext): string { +function reviewerPrompt(config: ReviewerConfig): string { const { cwd } = config; return `You are a code reviewer. The project is at \`${cwd}\`. -## Context - -Use \`uncaged-workflow thread ${ctx.threadId}\` to read the full workflow thread for context on what was done and why. - ## Task Review the current git diff in \`${cwd}\`. Give a clear **approve** or **reject** verdict. @@ -51,7 +47,7 @@ export function createReviewerRole( return createRole({ name: "reviewer", schema: reviewerMetaSchema, - systemPrompt: async (ctx) => reviewerPrompt(config, ctx), + systemPrompt: reviewerPrompt(config), agent: adapter, extract: { provider: extract.provider, diff --git a/packages/workflow-util-agent/__tests__/build-agent-prompt.test.ts b/packages/workflow-util-agent/__tests__/build-agent-prompt.test.ts index 1844eb1..e274b4a 100644 --- a/packages/workflow-util-agent/__tests__/build-agent-prompt.test.ts +++ b/packages/workflow-util-agent/__tests__/build-agent-prompt.test.ts @@ -46,7 +46,7 @@ describe("buildAgentPrompt", () => { expect(text).toContain("only step full body"); expect(text).toContain('Meta: {"files":["a.ts"]}'); expect(text).toContain("## Tools"); - expect(text).toContain("uncaged-workflow thread "); + expect(text).toContain("uncaged-workflow thread 01TEST000000000000000000TR"); }); test("two or more steps: previous steps are meta-only; latest step is full", () => { @@ -78,6 +78,7 @@ describe("buildAgentPrompt", () => { expect(text).toContain("last step full content"); expect(text).toContain('Meta: {"done":true}'); expect(text).toContain("## Tools"); + expect(text).toContain("uncaged-workflow thread 01TEST000000000000000000TR"); }); test("middle steps show meta summary only, not full content", () => { diff --git a/packages/workflow-util-agent/src/build-agent-prompt.ts b/packages/workflow-util-agent/src/build-agent-prompt.ts index 45676cc..35b70b7 100644 --- a/packages/workflow-util-agent/src/build-agent-prompt.ts +++ b/packages/workflow-util-agent/src/build-agent-prompt.ts @@ -41,7 +41,9 @@ export function buildAgentPrompt(systemPrompt: string, ctx: ThreadContext): stri lines.push(""); lines.push("## Tools"); - lines.push("Use `uncaged-workflow thread ` to read full details of any previous step."); + lines.push( + `Use \`uncaged-workflow thread ${ctx.threadId}\` to read full details of any previous step.`, + ); return lines.join("\n"); } diff --git a/packages/workflow-util-role/__tests__/create-role.test.ts b/packages/workflow-util-role/__tests__/create-role.test.ts index 5505e5c..ef6a7e2 100644 --- a/packages/workflow-util-role/__tests__/create-role.test.ts +++ b/packages/workflow-util-role/__tests__/create-role.test.ts @@ -96,26 +96,6 @@ describe("createRole", () => { expect(seen[0].steps).toEqual([]); }); - test("resolves dynamic systemPrompt functions before AgentFn", async () => { - globalThis.fetch = (() => - Promise.resolve(toolCallResponse(JSON.stringify({ n: 99 })))) as unknown as typeof fetch; - - const schema = z.object({ n: z.number() }); - const agent: AgentFn = async (_ctx, prompt) => prompt; - const role = createRole({ - name: "test", - schema, - systemPrompt: async (ctx) => `rounds=${ctx.steps.length}`, - agent, - extract: { provider, dryRun: null, dryRunMeta: { n: 0 } }, - }); - - const ctx = makeCtx(); - const out = await role(ctx); - expect(out.content).toBe("rounds=0"); - expect(out.meta).toEqual({ n: 99 }); - }); - test("extract dryRun null runs live extract path", async () => { const spy = spyOn(extractMetaModule, "extractMetaOrThrow").mockResolvedValue({ n: 0 }); diff --git a/packages/workflow-util-role/src/create-role.ts b/packages/workflow-util-role/src/create-role.ts index 3ec9fc1..6199be6 100644 --- a/packages/workflow-util-role/src/create-role.ts +++ b/packages/workflow-util-role/src/create-role.ts @@ -6,7 +6,7 @@ import type { LlmProvider } from "./types.js"; export type CreateRoleArgs> = { name: string; schema: z.ZodType; - systemPrompt: string | ((ctx: ThreadContext) => Promise); + systemPrompt: string; agent: AgentFn; extract: { provider: LlmProvider; @@ -23,9 +23,7 @@ function resolveExtractDryRun(extractDryRun: boolean | null): boolean { /** Builds a {@link Role} from an {@link AgentFn}, system prompt, Zod meta schema, and extract wiring. */ export function createRole>(args: CreateRoleArgs): Role { return async (ctx: ThreadContext) => { - const promptText = - typeof args.systemPrompt === "string" ? args.systemPrompt : await args.systemPrompt(ctx); - const raw = await args.agent(ctx, promptText); + const raw = await args.agent(ctx, args.systemPrompt); const meta = await extractMetaOrThrow(args.name, raw, args.schema, { provider: args.extract.provider, dryRun: resolveExtractDryRun(args.extract.dryRun),