Custom workflows
Define durable multi-step Pi harness workflows.
Pi harness workflows are normal @fragno-dev/workflows workflows. Use them when a Pi session needs
more than the stock interactive chat loop: multiple agent steps, tool-result branching, parallel
reviewers, races against user input, or explicit approvals.
Register workflow entries directly with createPiWorkflows(...) and with the Workflows fragment.
There is intentionally no createPi() or definePiWorkflow(...) builder in
@fragno-dev/pi-harness.
import { createPiHarness, createPiWorkflows } from "@fragno-dev/pi-harness/factory";
import { createAgentLoop } from "@fragno-dev/pi-harness/harness/commands";
import { createWorkflowsFragment } from "@fragno-dev/workflows";
import { defineWorkflow } from "@fragno-dev/workflows/workflow";
import { z } from "zod";
const supportWorkflow = defineWorkflow(
{ name: "support", schema: z.object({ topic: z.string() }) },
async (event, step) => {
const loop = createAgentLoop(step, {
env,
workflowName: "support",
sessionId: event.instanceId,
agentName: "support",
model,
systemPrompt: "You are a helpful support agent.",
tools: [searchTool],
});
await loop.runStep("initial", { kind: "prompt", args: [`Help with ${event.payload.topic}`] });
while (true) {
await loop.waitForCommandAndRunStep();
}
},
);
const piConfig = { workflows: [supportWorkflow] };
const workflows = createPiWorkflows(piConfig);Create sessions by selecting the workflow and passing schema-validated input.
{
"name": "Customer issue",
"input": { "topic": "durable LLM workflows" }
}Lower-level harness steps
Use runPiHarnessStep(...) directly when the workflow wants full control over operation names,
committed entries, active tools, or non-prompt operations such as compaction and tree navigation.
import {
runPiHarnessStep,
createPiHarnessSessionState,
} from "@fragno-dev/pi-harness/harness/run-pi-harness-step";
const state = createPiHarnessSessionState();
const result = await runPiHarnessStep(step, "classify", {
env,
workflowName: "triage",
sessionId: event.instanceId,
agentName: "triage",
model,
tools: [classifyRequest],
activeToolNames: ["classify_request"],
committedEntries: state.entries,
operation: {
kind: "prompt",
args: [event.payload.request],
stopOnTools: ["classify_request"],
},
});Replay rules
Keep workflow structure deterministic
Workflow replay depends on stable step structure. Do not build step names from random IDs, current time, or partial streamed LLM output.
- Step names should be string literals or derived from already completed durable data.
- Keep runtime-only capabilities (
env,streamFn, toolexecutefunctions) in workflow closures. - Persist only serializable params and step results.
- Use
activeToolNamesas a per-step policy when a turn should expose only a subset of registered tools.