Skip to content

feat(ai): subagent tree limits; harness ctx.agents and harness children - #1520

Open
AlemTuzlak wants to merge 5 commits into
feat/harness-p3-pluginsfrom
feat/harness-p4-subagents
Open

AlemTuzlak wants to merge 5 commits into
feat/harness-p3-pluginsfrom
feat/harness-p4-subagents

Conversation

@AlemTuzlak

Copy link
Copy Markdown
Contributor

Subagent trees now have limits, and a harness can run agents from code and call other harnesses. chat({ subagents: { limits } }) caps depth, total calls, children at once, and time per child across the whole tree. Harness plugins get ctx.agents.run / start / group, and harnessAgent(harness) makes a harness a child agent. This is Phase 4 of the harness stack.

Stacked on #1519 (Phase 3) → #1518 → #1515 → #1513 → #1509. Merge those first.

🎯 Changes

  • subagents.limits in @tanstack/ai (maxDepth, maxConcurrent, maxCalls, timeoutMs). One SubagentBudget per root run, shared by every child and passed on through ctx.chat({ subagents }). A refused start is a tool error like subagent limit reached (maxCalls 12). No defaults in core, so current apps behave as before.
  • ctx.agents in harness plugins: run, start (wake), and group (onFailure: 'cancel-siblings' | 'collect', with group.run and group.runSettled). Every child settles before group returns.
  • harnessAgent(harness) for subagents.agents. defineHarness takes description.
  • Harness defaults: DEFAULT_SUBAGENT_LIMITS (depth 2, 3 at once, 12 per tree). Code-started agents count against the limits.
  • Docs: new "Limit the tree" section in docs/chat/subagents.md, new docs/harness/subagents.md. Changeset: ai, harness (minor).

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.
  • Docs: I updated docs/ for this change, or this change is not user-facing.
  • Changeset: I added a changeset (pnpm changeset), or this PR does not change a published package.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Testing

Commands run (not the full pnpm test:pr, see below):

  • @tanstack/ai: vitest run (1923 passed, all files), tsc --noEmit, test:oxlint, build, test:build.
  • @tanstack/ai-harness: vitest run (68 passed), tsc --noEmit, test:oxlint, build, test:build. @tanstack/ai-harness-cli (8 passed) and @tanstack/ai-acp (61 passed): tests, types, lint, build.
  • testing/e2e: tsc --noEmit, playwright test tests/harness.spec.ts tests/harness-protocol.spec.ts tests/subagents.spec.ts (14 passed).
  • pnpm test:docs, kiira check on the changed pages, pnpm test:sherif, pnpm test:knip.
  • Skipped the full pnpm test:pr run because it is slow on this machine. CI runs it.

Manual test

  1. Run pnpm --filter @tanstack/ai exec vitest run tests/subagent-limits.test.ts.
  2. Run pnpm --filter @tanstack/ai-e2e test:e2e -- tests/harness.spec.ts -g limits. The second child start is refused and the model answers with that.

How this PR makes testing easy

  • packages/ai/tests/subagent-limits.test.ts: the budget math, maxCalls, maxDepth through a nested ctx.chat, timeoutMs, maxConcurrent.
  • packages/ai-harness/tests/subagents.test.ts: a harness as a child, ctx.agents.run and group (cancel and collect), default limits.
  • E2E: the limits scenario in testing/e2e/tests/harness.spec.ts.

Risk / rollback

Low. Core limits only apply when subagents.limits is set (all 1923 core tests pass unchanged). Harness sessions now apply default limits to their children. Revert the PR to undo.

Public API change

Before

chat({ adapter, messages, subagents: { agents: [researcher] } })

After

chat({
  adapter,
  messages,
  subagents: { agents: [researcher], limits: { maxDepth: 2, maxCalls: 12, maxConcurrent: 3, timeoutMs: 120_000 } },
})
defineHarness({ name: 'acme/lead', adapter, subagents: { agents: [harnessAgent(reviewer)] } })
await ctx.agents.group({}, (group) => Promise.all([group.run(explorer, { task: 'a' }), group.run(explorer, { task: 'b' })]))

🤖 Generated with Claude Code

https://claude.ai/code/session_01APYv1qshKyjPPpkFyRZhfZ

@tanstack/ai: subagents.limits (maxDepth, maxConcurrent, maxCalls,
timeoutMs) for the tree of children the model starts through tools. One
SubagentBudget per root run is shared by every child and passed on through
ctx.chat({ subagents }), so a child cannot reset it. A refused start reaches
the model as a tool error.

@tanstack/ai-harness: plugins get ctx.agents.run, start, and group (with
cancel-siblings or collect). harnessAgent(harness) turns a harness into a
child agent for subagents.agents, and defineHarness takes a description.
A harness applies default limits (depth 2, 3 at once, 12 per tree), and
agents started from code count against them.
@coderabbitai

coderabbitai Bot commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: TanStack/ai/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 49e99f87-fb57-4de9-9ff5-9686f18d5fcf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit c2e6fb3

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 2s View ↗

☁️ Nx Cloud last updated this comment at 2026-09-26 14:08:52 UTC

@pkg-pr-new

pkg-pr-new Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@1520

@tanstack/ai-acp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-acp@1520

@tanstack/ai-angular

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-angular@1520

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@1520

@tanstack/ai-bedrock

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-bedrock@1520

@tanstack/ai-byteplus

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-byteplus@1520

@tanstack/ai-claude-code

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-claude-code@1520

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@1520

@tanstack/ai-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-cloudflare@1520

@tanstack/ai-code-mode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode@1520

@tanstack/ai-code-mode-snippets

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-code-mode-snippets@1520

@tanstack/ai-codex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-codex@1520

@tanstack/ai-cohere

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-cohere@1520

@tanstack/ai-compaction

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-compaction@1520

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@1520

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-durable-stream@1520

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-elevenlabs@1520

@tanstack/ai-event-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-event-client@1520

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@1520

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@1520

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@1520

@tanstack/ai-grok-build

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok-build@1520

@tanstack/ai-groq

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-groq@1520

@tanstack/ai-harness

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-harness@1520

@tanstack/ai-harness-cli

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-harness-cli@1520

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-cloudflare@1520

@tanstack/ai-isolate-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-daytona@1520

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-node@1520

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs@1520

@tanstack/ai-isolate-quickjs-bun

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-isolate-quickjs-bun@1520

@tanstack/ai-llmgateway

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-llmgateway@1520

@tanstack/ai-lovable

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-lovable@1520

@tanstack/ai-mcp

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mcp@1520

@tanstack/ai-memory

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-memory@1520

@tanstack/ai-mistral

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-mistral@1520

@tanstack/ai-octane

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-octane@1520

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@1520

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@1520

@tanstack/ai-opencode

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-opencode@1520

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@1520

@tanstack/ai-perplexity

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-perplexity@1520

@tanstack/ai-persistence

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-persistence@1520

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@1520

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@1520

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@1520

@tanstack/ai-reactor

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-reactor@1520

@tanstack/ai-remix

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-remix@1520

@tanstack/ai-sandbox

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox@1520

@tanstack/ai-sandbox-blaxel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-blaxel@1520

@tanstack/ai-sandbox-boxd

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-boxd@1520

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-cloudflare@1520

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-daytona@1520

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-docker@1520

@tanstack/ai-sandbox-e2b

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-e2b@1520

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-local-process@1520

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-sprites@1520

@tanstack/ai-sandbox-upstash-box

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-upstash-box@1520

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-sandbox-vercel@1520

@tanstack/ai-skills

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-skills@1520

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@1520

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@1520

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@1520

@tanstack/ai-typesafe

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-typesafe@1520

@tanstack/ai-utils

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-utils@1520

@tanstack/ai-vercel-gateway

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vercel-gateway@1520

@tanstack/ai-vertex

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vertex@1520

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@1520

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@1520

@tanstack/ai-worldlabs

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-worldlabs@1520

@tanstack/openai-base

npm i https://pkg.pr.new/TanStack/ai/@tanstack/openai-base@1520

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@1520

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@1520

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@1520

@tanstack/svelte-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/svelte-ai-devtools@1520

commit: c2e6fb3

@github-actions github-actions Bot added waiting-on: maintainer The ball is in the maintainers’ court and removed waiting-on: author Waiting for the author to respond or update labels Sep 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on: maintainer The ball is in the maintainers’ court

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants