refactor: all-agentic architecture — roles as pure data, agent binding at runtime

BREAKING: Major architecture change.

- RoleDefinition = pure data (systemPrompt + schema + dryRunMeta)
- AgentFn = (ctx: ThreadContext) => Promise<string>, reads ctx.currentRole
- WorkflowDefinition decoupled from agents, bound via AgentBinding at runtime
- createWorkflow(def, binding, extract) replaces createRoleModerator
- Meta extraction moved into engine loop
- Delete workflow-util-role package (createRole, decorators, extract all gone)
- Role packages become pure data exports
- Agent packages updated to single-arg AgentFn

小橘 <xiaoju@shazhou.work>
This commit is contained in:
2026-05-06 14:14:33 +00:00
parent fce2bf7441
commit fa9163e462
52 changed files with 434 additions and 1565 deletions
@@ -1,11 +1,4 @@
import type { AgentFn, Role } from "@uncaged/workflow";
import {
createRole,
decorateRole,
type LlmProvider,
onFail,
withDryRun,
} from "@uncaged/workflow-util-role";
import type { RoleDefinition } from "@uncaged/workflow";
import * as z from "zod/v4";
export const committerMetaSchema = z.discriminatedUnion("status", [
@@ -28,69 +21,17 @@ export const committerMetaSchema = z.discriminatedUnion("status", [
export type CommitterMeta = z.infer<typeof committerMetaSchema>;
export type CommitterConfig = {
cwd: string;
const COMMITTER_SYSTEM = `You are the git committer. Create a branch, commit the changes, and push.
Report the branch name and commit SHA. On failure, classify as recoverable or unrecoverable.
Do not attempt to fix failures yourself.`;
export const committerRole: RoleDefinition<CommitterMeta> = {
description: "Creates branch, commits, and pushes when review passes.",
systemPrompt: COMMITTER_SYSTEM,
schema: committerMetaSchema,
dryRunMeta: {
status: "committed",
branch: "dry-run/placeholder",
commitSha: "0000000",
},
};
export const DEFAULT_COMMITTER_CONFIG: CommitterConfig = {
cwd: ".",
};
const DRY_RUN_COMMITTED_META: CommitterMeta = {
status: "committed",
branch: "dry-run/placeholder",
commitSha: "0000000",
};
function resolveExtractDryRun(extractDryRun: boolean | null): boolean {
return extractDryRun === true;
}
function committerSystemPrompt(config: CommitterConfig): string {
return `You are the git committer for this workflow. The project is at \`${config.cwd}\`.
## Task
Create a branch, commit the changes, and push. Report whether the push succeeded or failed, the branch name, and the commit SHA.
## On failure
If any git operation fails, **do not attempt to fix it yourself**. Capture the key error output and classify it:
- **Recoverable**: failures that a coder can fix (lint/test hook rejection, merge conflict, commit validation errors)
- **Unrecoverable**: failures beyond code changes (no push permission, remote not found, authentication denied, disk full)`;
}
/**
* Git committer role: the agent runs git (branch, commit, push); structured extraction yields {@link CommitterMeta}.
* Dry-run skips the agent and returns a stable committed placeholder; unexpected throws yield `status: "failed"`.
*/
export function createCommitterRole(
adapter: AgentFn,
extract: { provider: LlmProvider; dryRun: boolean | null; dryRunMeta: CommitterMeta },
config: CommitterConfig = DEFAULT_COMMITTER_CONFIG,
): Role<CommitterMeta> {
const inner: Role<CommitterMeta> = createRole({
name: "committer",
schema: committerMetaSchema,
systemPrompt: committerSystemPrompt(config),
agent: adapter,
extract,
});
return decorateRole(inner, [
withDryRun<CommitterMeta>({
label: "committer",
meta: DRY_RUN_COMMITTED_META,
dryRun: resolveExtractDryRun(extract.dryRun),
}),
onFail<CommitterMeta>({
label: "committer",
meta: {
status: "unrecoverable",
error: "committer role threw before structured result",
logRef: null,
},
}),
]);
}