workflows/interview

Source: src/workflows/interview.ts

interview: a conversation with the user, ending in a brief.

Every other combinator here composes agents. This one puts a human in the loop: an agent asks one question at a time through the {@link AskUser} port, and turns the answers into the specification the rest of the pipeline works from.

Why one question at a time rather than a form: a good second question depends on the first answer. A form has to guess all of them up front, and guesses wrong the moment the first answer surprises it.

interview

function

export async function interview(options: InterviewOptions): Promise<InterviewResult> { /* … */ }

Interviews the user, then asks the agent to write the brief.

The lifetime defaults to "workflow", and that is not an arbitrary choice: an interview is a conversation. A "task" interviewer would forget the answer it just received and ask around it forever.

The interview ends when the agent says {@link READY}, when the user submits (ask returns undefined), or at maxQuestions - and in all three cases the agent still writes a brief from what it has. Stopping early is a legitimate outcome, not a failure: a brief written from two answers is worth more than an interrogation nobody finished.

InterviewOptions

type

export type InterviewOptions = WorkflowOptions & {
	/** The agent conducting the interview. */
	agent: Agent;
	/** What the user asked for, in their own words. */
	input: string;
	/** How questions reach the user. See {@link AskUser}. */
	ask: AskUser;
	/**
	 * Hard cap on questions. Defaults to 6.
	 *
	 * The same reasoning as `loop`'s `maxIterations`, with a human on the other
	 * end: an agent that keeps finding one more thing to clarify is the normal
	 * failure mode, and being interrogated forever is worse than a slightly
	 * under-specified brief.
	 */
	maxQuestions?: number;
	/** Overrides how a question is read. See {@link parseQuestion}. */
	parse?: (output: string) => Question | undefined;
};

Who asks, what about, how it reaches the user, and when it must stop.

InterviewResult

type

export type InterviewResult = WorkflowResult & {
	/** The consolidated specification - `output`, under the name the rest of the pipeline reads. */
	brief: string;
	/** Everything the user answered, in order. */
	answers: Answer[];
	/** True when the user submitted before the agent said it was done. */
	submitted: boolean;
};

The brief, and everything that led to it.

As a Result: the interviewer’s last turn, whose output is the brief. usage covers every turn, the brief included; ok says every turn ran, and a short brief can still be true.

parseQuestion

function

export function parseQuestion(output: string): Question | undefined { /* … */ }

Reads one question out of whatever the agent actually wrote.

Same decision as parsePlan, for the same reason: the input is a language model, so this is lenient about shape - fenced, wrapped in prose, an array with one object in it - and strict about content. No question string, or no usable option, means no question: the caller moves on to the brief rather than putting a malformed card in front of the user.

READY

const

export const READY = "READY";

The agent says this - alone - when it has enough to write the brief.