cd /news/ai-agents/compose-ai-agents-like-reusable-comp… · home topics ai-agents article
[ARTICLE · art-80917] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Compose AI agents like reusable components

A developer introduced Agent Markup Language (AML), a JSX-based framework for composing AI agents as reusable components, enabling explicit dataflow and parallel execution. The Codex SDK provides a low-level thread API, while AML abstracts orchestration into a declarative component model.

read3 min views3 publishedJul 30, 2026

Imagine composing AI assistants the way you create reusable React components.

One agent reviews correctness. Another plans the tests. Their outputs flow into a third agent that produces one release recommendation.

Correctness ──┐
              ├──> Synthesis ──> Release recommendation
Test plan ────┘

That is agent composition: small specialists, explicit dataflow, one final result.

The Codex SDK gives TypeScript applications a clean thread-and-turn API. Composing several threads is still your application's job:

import { Codex } from "@openai/codex-sdk"

const codex = new Codex()

async function run(prompt: string) {
  const thread = codex.startThread()
  return (await thread.run(prompt)).finalResponse
}

const correctness = await run("Review the current diff for correctness defects.")
const tests = await run("Write the smallest useful test plan for the current diff.")

const coordinator = codex.startThread()
const result = await coordinator.run(`
Write one release recommendation from these reports.
Do not invent findings.

CORRECTNESS
${correctness}

TEST PLAN
${tests}
`)

console.log(result.finalResponse)

This is perfectly reasonable TypeScript. It also makes the application responsible for threads, execution order, result extraction, and prompt assembly. As the workflow grows, the glue becomes the thing you spend time reading.

Agent Markup Language puts that composition into TypeScript and JSX:

function ReleaseReview() {
  return (
    <Agent provider={Codex} system="Synthesize evidence without inventing findings.">
      ## Correctness
      <Agent provider={Codex}>Review the current diff for correctness defects.</Agent>
      ## Test plan
      <Agent provider={Codex}>Write the smallest useful test plan.</Agent>
      Write one release recommendation.
    </Agent>
  )
}

AML runs the two child agents from left to right. Their outputs land exactly where they appear in the parent prompt. The synthesis agent starts after both are ready.

AML is not React, but the component model is deliberately familiar. Functions package behaviour. Props carry inputs. JSX shows which agent receives each result. Ordinary TypeScript handles conditions.

You can keep the workflow compact, or split a larger one into named specialists:

import { Agent, AmlRuntime, type AmlRenderable, codexAgent, FollowUp } from "@aml-jsx/sdk"

const Codex = codexAgent({})

function CorrectnessReview() {
  return <Agent provider={Codex}>Find concrete correctness defects in the current diff.</Agent>
}

function SecurityReview({ paths }: { paths: readonly string[] }) {
  return <Agent provider={Codex}>Find security risks in: {paths.join(", ")}.</Agent>
}

function TestPlan({ children, runner }: { children?: AmlRenderable; runner: string }) {
  return (
    <Agent provider={Codex}>
      Write the smallest useful test plan for {children}. Use {runner}.
    </Agent>
  )
}

function DocsImpact({ publicChange }: { publicChange: boolean }) {
  return publicChange ? (
    <Agent provider={Codex}>Check whether the current diff changes documented behaviour.</Agent>
  ) : (
    "No public documentation review requested."
  )
}

function ReleaseCouncil({
  changedFiles,
  publicChange,
  target,
}: {
  changedFiles: readonly string[]
  publicChange: boolean
  target: string
}) {
  return (
    <Agent provider={Codex} system="Use only the supplied evidence. Resolve conflicts explicitly.">
      ## Correctness
      <CorrectnessReview />
      ## Security
      <SecurityReview paths={changedFiles} />
      ## Tests
      <TestPlan runner="npm test">regressions around {target}</TestPlan>
      ## Documentation
      <DocsImpact publicChange={publicChange} />
      <FollowUp>Decide whether this is ready to release and explain why or why not.</FollowUp>
    </Agent>
  )
}

const runtime = new AmlRuntime()
await runtime.evaluate(
  <ReleaseCouncil
    changedFiles={["src/auth.ts", "src/session.ts"]}
    publicChange
    target="the current diff"
  />,
)

The components now use JSX in different ways: no props, an array prop, nested children, and a boolean that conditionally returns an Agent or plain text. They run in authored order. The parent opens after their outputs have been assembled into its prompt, then FollowUp

asks for the release decision in the same session.

Swap a specialist, reuse it elsewhere, or give it a different provider without redesigning the workflow.

The developer still authors the control flow. Agent output stays data; AML never executes model-generated JSX as a new workflow. Tools and MCP servers also stay scoped to the agent that receives them.

We are building AML so complex agent systems can be read as composed parts instead of a pile of orchestration glue.

AML is open source, MIT licensed, and under active development. Read the code, run the examples, and star the repository on GitHub.

── more in #ai-agents 4 stories · sorted by recency
── more on @codex sdk 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/compose-ai-agents-li…] indexed:0 read:3min 2026-07-30 ·