Const ExperimentalExperimentalDefine a subagent specification (compiles to agents/<name>.md).
// agents/reviewer.md.spec.ts
export default experimental_agent({
name: "reviewer",
description: "Review a diff for correctness. Dispatch PROACTIVELY after edits.",
model: "sonnet",
tools: ["Read", "Grep", "Bash"],
body: proseReview the diff. Run ${cmd("npm test")} first.,
rules: {
"no-floating": enforce("@typescript-eslint/no-floating-promises", "Await promises."),
},
});
Generic over a tool Vocabulary (default OpenToolVocabulary — no
constraint). A harness adapter re-exports a vocabulary-bound agent (e.g.
vigiles/claude-code) so purity: "pure" + a side-effecting tool is a tsc
error at edit time; the bare core experimental_agent() accepts any tools, as before.
Also generic over the result's Ok/Err shapes, inferred from output: result(...). The returned value is a TypedAgentSpec<Ok, Err> — an
AgentSpec that carries those shapes at the type level, so a typed pipe
can cross-reference the handoff. With no output the shapes default to the
erased Shape, and the value is still a plain AgentSpec — backwards-compatible.
The SHAPE is not settled — the author is unsure of the design,
which is exactly what this marker promises: the form may change. It is NOT a
claim that the surface is unproven. Measured 2026-06-20: real Claude Code
loaded a compiled agents/code-reviewer.md, dispatched to it and read it,
100% of trials — stronger end-to-end evidence than experimental_skill has.
Define a subagent — and the ROOT of the subagent vocabulary.
Everything that only makes sense for subagents hangs off this one symbol:
experimental_agent.result()(the outcome contract),.railway()/.delegate()(the flat orchestrator), and.pipe()/.start()/.andThen()/.pipeStep()/.needs()(the typed pipeline). Same chokepoint asexperimental_skill.input().WHY a root and not a prefix on each name:
railway()anddelegate()are meaningless without a subagent, yet they shipped under STABLE names while the builder they depend on is experimental — a stable name resting on an unstable one. Prefixing each would carry the warning but multiply the vocabulary; a namespace OBJECT was rejected for the reason thevigiles/experimentalsubpath was retired in #169 — it marks the import line, out of view by the time anyone reads the call. A member reached through the marked root cannot be destructured free of its warning without renaming it, so the warning rides every call site.The rule this establishes: ONE experimental root per feature, everything else a member of it, TYPES excluded (a type annotation is not a call site).