Skip to content

Commit 27b6027

Browse files
authored
Merge branch 'main' into docs-sidebar-recency-pills
2 parents 4f1f104 + 4363ad0 commit 27b6027

26 files changed

Lines changed: 3196 additions & 539 deletions

‎docs/partner-placement.md‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Partner Placement
2+
3+
Partner placement is split into three separate decisions:
4+
5+
- **Eligibility:** whether a partner can appear for a surface, category, library, feature, or user context.
6+
- **Tier:** the partner's commercial/display tier, which controls visual treatment and relative access to surfaces.
7+
- **Order:** the sequence used inside a surface once eligible partners are known.
8+
9+
The order policy should be explicit per surface. Avoid treating the partner array order or legacy score as the policy itself.
10+
11+
## Order Strategies
12+
13+
### `static-curated`
14+
15+
Use for surfaces where the order is editorially or commercially curated and should remain stable. These surfaces can preserve the existing order, use partner seniority, or use private commercial priority when seniority is unavailable.
16+
17+
Do not expose private commercial criteria in client-facing field names, comments, or API responses. If a private priority is required, keep the public/client value generic, such as `placementPriority` or an already-resolved ordered list.
18+
19+
### `tier-rotated`
20+
21+
Use for visible partner surfaces where partners in the same tier should cycle over time. Tier order remains fixed, but partners inside the same tier are selected with weighted-random ordering for each page view and surface.
22+
23+
The root loader creates the initial page-view seed, so SSR and hydration receive the same value through loader data. A React provider owns the seed after hydration and refreshes it on client-side page views. Each surface combines its stable surface id with the page-view seed; avoid runtime-generated component ids in the seed because they can diverge across server and client rendering. Partners default to equal probability within their tier; a future `placementWeight` can bias same-tier odds without changing the ordering API.
24+
25+
### `contextual-recommendation`
26+
27+
Use when a surface behaves like a product recommendation or builder suggestion. Product fit should be established before commercial weighting affects order. This strategy currently preserves the legacy tier/priority behavior while giving recommendation surfaces a separate policy label.
28+
29+
### `machine-readable`
30+
31+
Use for AI- or crawler-facing outputs such as `llms.txt` and JSON feeds. These should be deterministic and relevance-first. Do not rotate them merely for logo fairness unless the downstream behavior has been designed and disclosed as such.
32+
33+
## Reserved Rules
34+
35+
Reserved rules should be narrow. For example, Cloudflare is reserved as the first deployment/hosting partner in any list that contains Cloudflare and other deployment partners. That should not imply Cloudflare is globally first across every partner surface; non-deployment partners can still appear before Cloudflare when the surface is mixed-category.
36+
37+
Deployment action buttons are a deployment-only surface. Cloudflare remains first when present. Tier order is still preserved, and providers within the same tier can rotate by page view.
38+
39+
## Current Surfaces
40+
41+
- Partner directory: `tier-rotated` for active partners, `static-curated` for previous partners.
42+
- Partner grids and embeds: `tier-rotated`.
43+
- Docs and blog rails: `tier-rotated`.
44+
- Mobile docs strip: `tier-rotated`.
45+
- Builder feature picker and starter partner suggestions: `contextual-recommendation`.
46+
- Deploy action buttons: `tier-rotated` with deployment provider tiers preserved.
47+
- `llms.txt` and `/api/data/partners`: `machine-readable`.
48+
49+
## Analytics
50+
51+
Partner impression and click events should include:
52+
53+
- `partner_id`
54+
- `placement`
55+
- `slot_index`
56+
- `partner_tier`
57+
- `order_strategy`
58+
- `rotation_seed` when the strategy rotates
59+
60+
This lets reporting distinguish partner performance from placement policy and makes under/over-exposure easier to debug.
61+
62+
## Contract Language
63+
64+
Suggested external framing:
65+
66+
> Partner tiers determine eligibility, visual treatment, reporting, and relative access to surfaces. Placement within the same tier may rotate or be curated depending on the surface. Some placements may include explicitly reserved rules for product, infrastructure, legal, or strategic reasons.
67+
68+
For AI-assisted selection:
69+
70+
> AI-assisted partner selection prioritizes user need and capability fit first. Partner tier may influence selection only among qualified options.
550 KB
Loading
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
---
2+
published: 2026-06-02
3+
authors:
4+
- Manuel Schiller
5+
title: 'TanStack Start Adds First-Class Rsbuild Support'
6+
excerpt: 'TanStack Start now supports Rsbuild 2 alongside Vite, so teams can choose the build tool that best fits their stack, preferences, and infrastructure.'
7+
library: start
8+
---
9+
10+
![React Server Components](/blog-assets/start-adds-rsbuild-support/header.jpg)
11+
12+
TanStack Start now supports Rsbuild 2 alongside Vite, giving teams two supported build-tool paths for Start.
13+
14+
Some teams prefer Vite. Some prefer Rsbuild because it is built on Rspack, feels familiar to teams with Webpack experience, or matches how the rest of their frontend infrastructure is organized. The new adapter makes that choice explicit: Start fits into either build tool through its plugin system.
15+
16+
We built the adapter together with the Rsbuild and Rspack team, and some of that work fed back into Rsbuild and Rspack along the way.
17+
18+
For a React app, the config looks like this:
19+
20+
```ts title='rsbuild.config.ts'
21+
import { defineConfig } from '@rsbuild/core'
22+
import { pluginReact } from '@rsbuild/plugin-react'
23+
import { tanstackStart } from '@tanstack/react-start/plugin/rsbuild'
24+
25+
export default defineConfig({
26+
plugins: [pluginReact(), tanstackStart()],
27+
})
28+
```
29+
30+
React and Solid are supported today, and because the Rsbuild work lives at Start's shared build layer, future framework adapters have the same path available to them.
31+
32+
Rsbuild owns the build, while the framework plugin handles React or Solid and `tanstackStart()` wires Start into both the client and server builds. If you have used Start with Vite, the pattern is intentionally familiar because the Start build-tool plugins are designed to fit naturally into the toolchain you choose.
33+
34+
## A Start path for teams using Rsbuild
35+
36+
Some teams want to use Start with Rsbuild because they want Rspack under the hood, a Webpack-familiar model, or a build tool that matches the rest of their stack.
37+
38+
That also matters for existing apps. A team moving to Start can keep more of its build setup in place instead of changing framework and build tool at once. One large company is already using the adapter while moving several apps from a Webpack setup to Start on Rsbuild.
39+
40+
That path includes the full Start feature set, including React and Solid apps, Server Functions, SSR and streaming SSR, HMR, import protection, and React Server Components for React apps.
41+
42+
## The two-adapter rule
43+
44+
There is a pattern we keep running into at TanStack: the first adapter proves something can work, and the second adapter shows which parts actually belong in the core.
45+
46+
TanStack Router and TanStack Start already went through this once at the UI framework layer, where adding Solid made the React assumptions easier to see and improved the shared core. Adding Rsbuild brought the same clarity to the build layer.
47+
48+
Because the first Start build adapter was Vite, some build logic naturally grew around Vite's plugin lifecycle, and adding Rsbuild made the boundary sharper: shared Start build behavior in one place, build-tool-specific behavior in the Vite and Rsbuild adapters.
49+
50+
## Your build tool, your choice
51+
52+
Choosing Start should let a team keep its preferred build tool. With Vite and Rsbuild both supported, teams can use the toolchain that fits their stack while building the same kind of Start app.
53+
54+
Teams that prefer Rsbuild, or already work in the Webpack and Rspack world, now get a native Start integration through Rsbuild's plugin system.
55+
56+
The Rsbuild adapter is available now. Try it in a new Rsbuild project or in an existing codebase moving to Start.

‎src/blog/tanstack-ai-orchestration.md‎

Lines changed: 79 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,6 @@ This is where you come in. We need your help. We need people to test out our wor
2929

3030
The goal is simple: compose multiple typed LLM and agent steps as normal TypeScript async generators, stream each step to the UI, pause for human approval, and resume through the same SSE flow.
3131

32-
## Try the PR build
33-
34-
Install the PR packages directly from pkg.pr.new:
35-
36-
```bash
37-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-orchestration@542
38-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@542
39-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@542
40-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@542
41-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@542
42-
```
43-
44-
Use this only for evaluation, demos, and feedback. The public API can still change before stabilization.
45-
46-
Full documentation for this PR lives on the GitHub branch, not the released TanStack docs site:
47-
48-
- [Overview](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/overview.md)
49-
- [Workflows guide](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/workflows.md)
50-
- [Orchestrators](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/orchestrators.md)
51-
- [Approvals](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/approvals.md)
52-
- [Run persistence](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/run-persistence.md)
53-
- [API reference](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/api/ai-orchestration.md)
54-
5532
## Define your agents
5633

5734
Agents are typed wrappers around a `chat()` call or any async function. `defineAgent` gives each step an input schema, output schema, and implementation.
@@ -85,25 +62,23 @@ export const writer = defineAgent({
8562
}),
8663
})
8764

65+
export const EditorReviewSchema = z.object({
66+
approved: z.boolean(),
67+
article: ArticleSchema,
68+
notes: z.string(),
69+
})
70+
8871
export const editor = defineAgent({
8972
name: 'editor',
9073
input: z.object({
9174
article: ArticleSchema,
9275
feedback: z.string().optional(),
9376
}),
94-
output: z.object({
95-
approved: z.boolean(),
96-
article: ArticleSchema,
97-
notes: z.string(),
98-
}),
77+
output: EditorReviewSchema,
9978
run: ({ input }) =>
10079
chat({
10180
adapter: openaiText('gpt-4o'),
102-
outputSchema: z.object({
103-
approved: z.boolean(),
104-
article: ArticleSchema,
105-
notes: z.string(),
106-
}),
81+
outputSchema: EditorReviewSchema,
10782
stream: true,
10883
systemPrompts: [
10984
'Edit the article for accuracy, clarity, and practical developer tone. Apply any optional reviewer feedback.',
@@ -129,11 +104,11 @@ import {
129104
import { z } from 'zod'
130105
import { ArticleSchema, editor, writer } from './agents'
131106

132-
const ArticleInputSchema = z.object({
107+
export const ArticleInputSchema = z.object({
133108
topic: z.string(),
134109
})
135110

136-
const ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [
111+
export const ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [
137112
z.object({
138113
ok: z.literal(true),
139114
article: ArticleSchema,
@@ -174,13 +149,13 @@ export const articleWorkflow = defineWorkflow({
174149
})
175150
```
176151

177-
The interesting part is the lack of framework ceremony. TypeScript knows the input expected by `agents.writer`, and it knows the shape returned after the `yield*`. The runtime can emit lifecycle events around each yielded step, stream text while it runs, validate the result, snapshot state, and resume the generator with the typed output.
152+
The interesting part is the lack of framework ceremony. TypeScript knows the input expected by `agents.writer`, and it knows the shape returned after the `yield*`.
178153

179-
Why async generator workflows? There are a lot of ways to model agent workflows. You can build a graph DSL. You can define nodes in JSON. You can describe a DAG and ask the runtime to interpret it. Well, the reason we decided to go with generator workflows is because whenever you yield the agent's step, it's streamed straight down to the client. The user sees everything in real time, and then, by the end of it, you just get the final output back. The user saw everything that went on: tool calls, reasoning, whatever.
154+
Each `yield* agents.someAgent(...)` becomes a typed step. The runtime can emit lifecycle events around it, stream text while it runs, validate the result, snapshot state, and resume the generator with the typed output.
180155

181156
The workflow body is just TypeScript. Use `if`, `for`, `while`, `try`, `await`, helper functions, and whatever domain code you already have. The orchestration runtime only cares about the things you `yield*`.
182157

183-
Each `yield* agents.someAgent(...)` becomes a typed step. The runtime can emit lifecycle events around it, stream text while it runs, validate the result, snapshot state, and resume the generator with the typed output.
158+
Why async generator workflows? There are a lot of ways to model agent workflows. You can build a graph DSL. You can define nodes in JSON. You can describe a DAG and ask the runtime to interpret it. The reason we went with generator workflows is that whenever you yield the agent's step, it's streamed straight down to the client. The user sees everything in real time — tool calls, reasoning, whatever happens along the way — and by the end you just get the final output back.
184159

185160
## Expose it over SSE
186161

@@ -215,33 +190,57 @@ export async function POST(request: Request) {
215190

216191
The current built-in persistence is `inMemoryRunStore`. That is useful for local demos and single-process evaluation. Production durability is still future-facing and experimental run-store-interface territory, especially for long pauses, deploys, restarts, and multi-node environments. But the API is there to implement your own durable run store and swap it in when you're ready.
217192

193+
## Run it without a UI
194+
195+
A UI is optional. The SSE endpoint above exists so a browser can watch a run unfold, but the workflow itself is just a server-side async generator. If nothing is watching — a cron job, a queue worker, a batch script, or a plain JSON endpoint that only returns the final answer — you can run the workflow headless and never stream a single event to a client.
196+
197+
`runWorkflow` returns an async iterable, and iterating it is what drives the workflow forward. So for a headless run you drain the stream to completion, then read the final result back from the run store:
198+
199+
```typescript
200+
import { inMemoryRunStore, runWorkflow } from '@tanstack/ai-orchestration'
201+
import { articleWorkflow } from './article-workflow'
202+
203+
const runStore = inMemoryRunStore({ ttl: 60 * 60 * 1000 })
204+
205+
export async function generateArticle(topic: string) {
206+
const runId = crypto.randomUUID()
207+
208+
const stream = runWorkflow({
209+
workflow: articleWorkflow,
210+
runStore,
211+
runId,
212+
input: { topic },
213+
})
214+
215+
// No client is listening, so just drive the run to completion.
216+
for await (const _event of stream) {
217+
// Optionally log progress or persist events here.
218+
}
219+
220+
const run = await runStore.getRunState(runId)
221+
222+
if (run?.status === 'finished') {
223+
return run.output
224+
}
225+
226+
throw new Error(run?.error?.message ?? `Workflow ended as "${run?.status}"`)
227+
}
228+
```
229+
230+
The same typed agents, schemas, state snapshots, and validation apply. You just skip `toServerSentEventsResponse`, the React hooks, and the browser entirely.
231+
232+
One caveat: a headless run has nobody to answer a `yield* approve(...)`. If the workflow hits an approval step, it pauses and `getRunState` reports `status: 'paused'` instead of `'finished'`. Server-only workflows shine when the pipeline runs end to end on its own. If you need a human in the loop, either resume the paused run programmatically with an `approval` result or keep a UI connected.
233+
218234
## Consume it from React
219235

220236
On the client, `WorkflowClient`, `useWorkflow`, and `useOrchestration` consume the streamed events and keep local run state updated.
221237

222238
```tsx
223239
import { fetchWorkflowEvents, useWorkflow } from '@tanstack/ai-react'
224-
import { z } from 'zod'
225-
226-
const ArticleSchema = z.object({
227-
title: z.string(),
228-
body: z.string(),
229-
})
230-
231-
const ArticleInputSchema = z.object({
232-
topic: z.string(),
233-
})
234-
235-
const ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [
236-
z.object({
237-
ok: z.literal(true),
238-
article: ArticleSchema,
239-
}),
240-
z.object({
241-
ok: z.literal(false),
242-
reason: z.string(),
243-
}),
244-
])
240+
import {
241+
ArticleInputSchema,
242+
ArticleWorkflowOutputSchema,
243+
} from './article-workflow'
245244

246245
export function ArticleWorkflowDemo() {
247246
const workflow = useWorkflow({
@@ -384,6 +383,10 @@ import {
384383
} from '@tanstack/ai-orchestration'
385384
import { z } from 'zod'
386385
import { ArticleSchema, editor, writer } from './agents'
386+
import {
387+
ArticleInputSchema,
388+
ArticleWorkflowOutputSchema,
389+
} from './article-workflow'
387390

388391
const ArticleStateSchema = z.object({
389392
draft: ArticleSchema.optional(),
@@ -439,27 +442,27 @@ Routers may also yield if their decision needs async work, but their return valu
439442

440443
That gives you the same behavior with a different control-flow style. A workflow puts the sequence directly in the generator body. An orchestrator puts the next-step decision in a router.
441444

442-
## What is still experimental?
443-
444-
Almost everything important enough to name.
445-
446-
The package name is `@tanstack/ai-orchestration`, the public feature name is TanStack AI Workflows & Orchestrators, and the core shape is visible in PR 542. But this is still a PR build.
447-
448-
Expect scrutiny around:
445+
## Try it and send feedback
449446

450-
- API names and return shapes
451-
- Durable persistence beyond `inMemoryRunStore`
452-
- Resume behavior across deploys and multiple server nodes
453-
- Event naming and AG-UI compatibility details
454-
- How much of the run-store interface should be public now
455-
- How examples should guide production usage without overstating guarantees
447+
If you are building multi-step AI product flows, this is the moment to test the shape. This is a PR build meant for evaluation, demos, and feedback — the public API can still change before stabilization.
456448

457-
The current implementation is useful enough to try and early enough to change.
449+
Install the PR packages directly from pkg.pr.new:
458450

459-
## Try it and send feedback
451+
```bash
452+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-orchestration@542
453+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@542
454+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@542
455+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@542
456+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@542
457+
```
460458

461-
If you are building multi-step AI product flows, this is the moment to test the shape.
459+
Full documentation for this PR lives on the GitHub branch, not the released TanStack docs site:
462460

463-
Try the [PR build packages](https://github.com/TanStack/ai/pull/542#issuecomment-4416347869). Read the branch docs for [workflows](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/workflows.md), [orchestrators](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/orchestrators.md), [approvals](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/approvals.md), [run persistence](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/run-persistence.md), and the [API reference](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/api/ai-orchestration.md).
461+
- [Overview](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/overview.md)
462+
- [Workflows guide](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/workflows.md)
463+
- [Orchestrators](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/orchestrators.md)
464+
- [Approvals](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/approvals.md)
465+
- [Run persistence](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/run-persistence.md)
466+
- [API reference](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/api/ai-orchestration.md)
464467

465468
Then share demos, feedback, and rough edges on [PR 542](https://github.com/TanStack/ai/pull/542) before the API stabilizes.

0 commit comments

Comments
 (0)