<!-- Generated by scripts/gen-docs.ts from the TSDoc in src/. Do not edit by hand. -->

# `workflows/plan`

Source: [`src/workflows/plan.ts`](https://github.com/AI-for-dev/combo/blob/main/src/workflows/plan.ts)

Reading a plan an agent wrote: the prompt, the parser, the validation.

Split out from `orchestrate` so the prompt, the parser and the validation
can be read, and tested, apart from the fan-out that follows them.

The open question this file answers is **how to read what an agent decided**.
Three candidates were weighed: a tool call, structured output, or a parsed
convention. This is a parsed convention, and the reasoning is worth keeping:

- **A tool call** is possible (`createAgentSession` takes `customTools`), and
  it would give validated arguments for free. It also means teaching
  `SessionPort` about tool definitions, and betting the whole thing on a model
  that reliably emits tool calls. The weak models this library is routinely
  run against do not.
- **Structured output** is not uniformly available across providers, and pi's
  `prompt()` returns text either way.
- **A parsed convention** costs one function, works on every provider, and the
  plan is validated against the known agents regardless - which is the check
  that actually matters. A schema would not have caught a hallucinated agent
  name; the name lookup does.

{@link parsePlan} is therefore lenient, and *only* it: its input is a language
model, not a caller. Everywhere else in this library a malformed input is an
error.

## `parsePlan`

*function*

```typescript
export function parsePlan(output: string, workers: readonly Agent[]): PlannedTask[] { /* … */ }
```

Reads a plan out of whatever the planner actually wrote.

Lenient by design - the input is a model. Two shapes are accepted, because
both are what models really produce when asked for the first:

- **any JSON objects carrying `agent` and `task`**, in the order they appear:
  inside an array, alone, one per line, wrapped in a code fence or in prose.
  Asked for an array, a real planner answered with bare objects and no
  brackets - so the parser collects objects rather than requiring the shape;
- one subtask per line, `agent: task`.

A step naming an unknown agent is **dropped**, never remapped: guessing which
agent a hallucinated name meant is how a workflow silently does the wrong
work. An empty result means "nothing usable was said", which the caller
reports rather than papers over.

## `PlannedTask`

*type*

```typescript
export type PlannedTask = {
	/** Already resolved: an unknown name is dropped by the parser, never carried here. */
	agent: Agent;
	/** What that agent is asked to do, in the planner's own words. */
	task: string;
};
```

One step of a plan: a *resolved* agent - an unknown name never gets this far - and its task.

## `planningPrompt`

*function*

```typescript
export function planningPrompt(input: string, workers: readonly Agent[], maxTasks: number): string { /* … */ }
```

The question put to the planner: who is available, and how to answer.

## `PlanOptions`

*type*

```typescript
export type PlanOptions = WorkflowOptions & {
	/** The agent that decides the split. */
	planner: Agent;
	/** Who it may delegate to. Their `description` is what the planner reads. */
	workers: Agent[];
	/** What is to be split. The planner sees this and the roster, nothing else. */
	input: string;
	/**
	 * How many subtasks a plan may contain. Defaults to 8.
	 *
	 * Same reasoning as `loop`'s `maxIterations`: every subtask is a session and
	 * a bill, so "a plan of two hundred steps" must not be reachable by a
	 * hallucination. Exceeding it fails **before** anything is spawned - losing
	 * a run costs less than paying for a runaway one.
	 */
	maxTasks?: number;
	/** Overrides how the plan is read. See {@link parsePlan}. */
	parse?: (output: string, workers: readonly Agent[]) => PlannedTask[];
	/** Overrides the question put to the planner. See {@link planningPrompt}. */
	format?: (input: string, workers: readonly Agent[], maxTasks: number) => string;
};
```

Who plans, who may be delegated to, and how large a plan may get.

## `PlanOutcome`

*type*

```typescript
export type PlanOutcome = {
	/** What the planner asked for, after validation. Empty when nothing is runnable. */
	plan: PlannedTask[];
	/** The planner's own turn. Kept whatever happened next. */
	planning: Result;
	/** False when the planner failed, or produced nothing runnable. */
	ok: boolean;
	/** Set if and only if `ok` is false. It carries what the planner actually wrote. */
	error?: string;
};
```

A validated plan, or the reason there is none - decided before anything is spawned.
