# Examples

Twelve programs, in reading order. Each file is in `examples/` next to this page.
Run one with `yaag run <file>`, or with the `yaag_run` tool.

## 01 — one Agent, one Ask

The smallest program. It starts one Agent, sends one Ask, and returns the text.
The return value of `run` is the result of the Run.

<!-- embed: docs/examples/01-minimal.ts -->

```ts
export default defineRun({
  name: "minimal",
  description: "Asks one Agent for one short answer.",
  async run(ctx) {
    const agent = await ctx.spawn({ name: "writer" });
    return await agent.ask(prompt`Write one sentence about the sea. Report only that sentence.`);
  },
});
```

Full file: [`examples/01-minimal.ts`](examples/01-minimal.ts). Run it with
`yaag run examples/01-minimal.ts`.

## 02 — declared arguments

A program declares its arguments with a typebox schema. `yaag describe` shows
that schema, and the Orchestrator rejects invalid arguments before it starts an
Agent.

<!-- embed: docs/examples/02-args.ts -->

```ts
  args: Type.Object({
    topic: Type.String({ description: "What to write about" }),
    sentences: Type.Optional(Type.Integer({ description: "How many sentences" })),
  }),
```

Full file: [`examples/02-args.ts`](examples/02-args.ts). Run it with
`yaag run examples/02-args.ts --args '{"topic":"tides"}'`.

## 03 — parallel Agents with restrictions

An Agent Definition holds policy: which tools and which skills the Agent gets.
A spawn holds topology: the name and the working directory. Three Agents run at
the same time.

<!-- embed: docs/examples/03-fan-out.ts -->

```ts
const reviewer = defineAgent({
  name: "reviewer",
  tools: ["read"],
  disallowedTools: ["yaag_run"],
  skills: [],
});
```

Full file: [`examples/03-fan-out.ts`](examples/03-fan-out.ts). Run it with
`yaag run examples/03-fan-out.ts`.

## 04 — Ask limits and a structured result

Soft limits steer the Agent first, then fail the Ask with `ASK_LIMIT`. The
Agent stays alive, so the program can recover. An `outputSchema` makes the Ask
return data instead of text.

<!-- embed: docs/examples/04-controlled-ask.ts -->

```ts
      const report = await agent.ask(prompt`Read README.md and report your verdict.`, {
        maxTurns: 6,
        maxToolCalls: 12,
        maxDurationMs: 120_000,
        wrapUpPrompt: "Stop the work and report what you have now.",
        outputSchema: Report,
      });
```

Full file: [`examples/04-controlled-ask.ts`](examples/04-controlled-ask.ts). Run
it with `yaag run examples/04-controlled-ask.ts`.

## 05 — record and resume

A Cassette records every frame of a Run. `--record <file>` writes it. `--resume
<file>` runs the program again and replays the recorded Asks, so only the new
work costs money. A resume needs the program too, because a Cassette never
holds it.

<!-- embed: docs/examples/05-record-resume.ts -->

```ts
    const agent = await ctx.spawn({ name: "writer" });
    const draft = await agent.ask(prompt`Write two sentences about rain. Report only the text.`);
```

Full file: [`examples/05-record-resume.ts`](examples/05-record-resume.ts). Run
it with `yaag run examples/05-record-resume.ts --record run.json`, then resume
it with `yaag run examples/05-record-resume.ts --resume run.json`.

## 06 — lineage

`parent` places an Agent under another Agent in the Run tree. Here the
implementer reports the helper roles it wants, and the program decides which of
them to spawn. The helpers become a subtree of the implementer, and no Agent
ever spawns anything itself.

<!-- embed: docs/examples/06-lineage.ts -->

```ts
      const agent = await ctx.spawn({ name: helper.role, parent: implementer });
      reports.push(await agent.ask(prompt`Review this report: ${wish.report}`));
```

Full file: [`examples/06-lineage.ts`](examples/06-lineage.ts). Run it with
`yaag run examples/06-lineage.ts`.
## 07 — fork and compaction

One Agent reads the repository. Each worker forks that Agent, so it inherits
the whole conversation instead of reading the repository again. `compact: true`
summarizes the inherited context before the worker starts.

<!-- embed: docs/examples/07-fork.ts -->

```ts
        const worker = await builder.fork({ name: topic.split(" ")[0], compact: true });
        return worker.ask(prompt`From what you already read, report on ${topic}.`);
```

Full file: [`examples/07-fork.ts`](examples/07-fork.ts). Run it with
`yaag run examples/07-fork.ts`.

## 08 — the system prompt

An Agent reads the Agent System Prompt. It states that an Orchestration Program
drives the Agent, prints the Agent's runtime facts, lists the Agent's tools, and
states the Rules. `appendSystemPrompt` keeps that prompt and places its text
inside it. `systemPrompt` replaces the whole prompt: the Agent then has only the
text the program gives it, so the runtime facts, the tool list and the Rules are
gone. Use the replacement when you want that removal, and `appendSystemPrompt`
for every other rule you add.

`appendSystemPrompt` no longer hides the machine's own `APPEND_SYSTEM.md`: the
Agent System Prompt prints both texts, the program's text first.

An Agent Definition takes the same two fields, with the same meaning: an
Agent Policy is one shape, in a definition and in a spawn call.

<!-- embed: docs/examples/08-system-prompt.ts -->

```ts
    const appended = await ctx.spawn({ name: "house", appendSystemPrompt: RULES });
    // Replaces the Agent System Prompt: this text is the whole prompt.
    const replaced = await ctx.spawn({ name: "bare", systemPrompt: RULES });
```

A Prompt Definition is the third way: it builds the prompt at spawn time, so the
program keeps the default text and adds its own rules to it. The builder runs
once for each spawn and learns the Agent name.

<!-- embed: docs/examples/08-system-prompt.ts -->

```ts
      systemPrompt: defineSystemPrompt(
        ({ agent }) => prompt`
          ${yaagSystemPrompt({ audience: "program" })}

          House rules for ${agent.name}: ${RULES}
        `,
      ),
```

Full file: [`examples/08-system-prompt.ts`](examples/08-system-prompt.ts). Run
it with `yaag run examples/08-system-prompt.ts`.

## 09 — two vendors, one flag

The program declares two Profiles and one Role. The Agent takes its model from
the active Profile, so one flag moves the whole Run to the other vendor and no
Agent Definition changes.

<!-- embed: docs/examples/09-profiles.ts -->

```ts
profiles
  .register({ id: "claude" })
  .provider("anthropic")
  .bind(writer)
  .model({ name: "claude-opus-4-8", thinking: "medium" })
  .fallback({ name: "claude-sonnet-4-8" });

profiles
  .register({ id: "glm" })
  .provider("ollama")
  .bind(writer)
  .model({ name: "glm5.3", thinking: "medium" })
  .fallback("zai-org/glm5.3:low");

const writerAgent = useProfile<typeof profiles>(({ profile }) => ({
  name: "writer",
  model: profile.role.writer.model,
  appendSystemPrompt: "Answer in one sentence.",
}));
```

Run the default Profile with `yaag run examples/09-profiles.ts`, and the other
Profile with `yaag run examples/09-profiles.ts --profile glm`. The tool does the
same: `yaag_run({ file: "examples/09-profiles.ts" })` and
`yaag_run({ file: "examples/09-profiles.ts", profile: "glm" })`.

`yaag describe examples/09-profiles.ts` prints the `profiles` block: the default
id, the Roles, and the model list of each Role of each Profile. For the rules,
read [Profiles](authoring.md#profiles).

Full file: [`examples/09-profiles.ts`](examples/09-profiles.ts). The model ids
are examples: edit them to models you have.

## 10 — import a registered Definition

The program declares no Agent Definition. It takes one from a Registry File by
name, and then spawns it.

<!-- embed: docs/examples/10-catalog-resolve.ts -->

```ts
// Top-level `await` is permitted only for a Catalog Resolve (ADR-0053).
const greeter = await $yaag.global.importAgent("greeter");
```

The Catalog is in `examples/catalog/`: a `registry.json` and the Definition
module `greeter.ts`. Point the Global Config directory at it to run the
program: `YAAG_CONFIG_DIR=examples/catalog yaag run examples/10-catalog-resolve.ts`.

Full file: [`examples/10-catalog-resolve.ts`](examples/10-catalog-resolve.ts).
For the scopes and the failure codes, read
[Import a registered Definition](authoring.md#import-a-registered-definition).

## 11 — a Host Fork of your pi session

The program continues your own pi conversation. `ctx.host` is the Host Source:
the conversation that started the Run. `ctx.spawn({ fork: ctx.host })` starts a
Host Fork, an Agent that begins from a copy of it, so the program restates no
context and sends one short Ask.

Start the Run from pi with `yaag_run({ file: "examples/11-continue-here.ts",
forkHost: true })`, or from a shell with `yaag run
examples/11-continue-here.ts --host-session <file> --host-tool-call <id>`.
`ctx.host` is `undefined` for every other Run, which is why the program guards
it first.

<!-- embed: docs/examples/11-continue-here.ts -->

```ts
    if (ctx.host === undefined) throw new Error("run this from a pi session with forkHost");
    const worker = await ctx.spawn({ name: "worker", model: "opus", fork: ctx.host });
    return await worker.ask(prompt`Continue the work we discussed. Report what you did.`);
```

Full file: [`examples/11-continue-here.ts`](examples/11-continue-here.ts). For
the options and the three failure codes, read
[A Host Fork of your pi session](authoring.md#a-host-fork-of-your-pi-session).

## 12 — Groups

`ctx.group` names a position in the Run tree. It starts no process, so a Group
costs nothing. The program opens one ticket Group, and it opens a planning
Group and an implementing Group under it. Each spawn takes a Group as its
`parent`.

<!-- embed: docs/examples/12-groups.ts -->

```ts
    const ticket = ctx.group({ name: "ticket 01 (write the release note)" });
    const planning = ctx.group({ name: "planning", parent: ticket });
    const implementing = ctx.group({ name: "implementing", parent: ticket });

    const planner = await ctx.spawn({ name: "planner", parent: planning });
```

Full file: [`examples/12-groups.ts`](examples/12-groups.ts). Run it with
`yaag run examples/12-groups.ts`. For the rules and the two failure codes, read
[Groups](authoring.md#groups).
