Skip to content

Commit fdbb045

Browse files
authored
docs: polish pass + launch-readiness pages (#60)
Polish from a full review of the site: - Fix two real snippet bugs: Ollama baseUrl now includes /v1 (it speaks the OpenAI wire format), and the openai peer-dep list includes Azure. - Standardize on toolDefinition() (there is no tool() export); drop a dangling internal boost/skills reference. - Add uniform "See also" footers to the ai-sdk deep pages and the four family pages (which previously dead-ended on a License/MIT line); unify the MCP disambiguation heading and the structured-output link label. - Reword ai-autopilot from an internal-design register to user-facing voice. New pages for a launch-ready site: - guide/tutorial.md: an end-to-end "Build a Multi-Agent App" walkthrough that composes ai-sdk tools, an ai-skills SKILL.md, and an ai-autopilot Supervisor. - guide/when-to-use.md: positioning / when to reach for GemStack. - guide/contributing.md: the graduation model and how to contribute. All wired into the sidebar. docs:build is green, no dead links, no em-dashes.
1 parent 09388de commit fdbb045

17 files changed

Lines changed: 318 additions & 20 deletions

docs/.vitepress/config.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ export default defineConfig({
3333
text: 'Introduction',
3434
items: [
3535
{ text: 'What is GemStack?', link: '/guide/' },
36+
{ text: 'When to Use GemStack', link: '/guide/when-to-use' },
3637
{ text: 'Installation', link: '/guide/installation' },
3738
{ text: 'Your First Agent', link: '/guide/first-agent' },
39+
{ text: 'Build a Multi-Agent App', link: '/guide/tutorial' },
3840
],
3941
},
4042
{
@@ -48,6 +50,12 @@ export default defineConfig({
4850
{ text: 'mcp', link: '/packages/mcp' },
4951
],
5052
},
53+
{
54+
text: 'Project',
55+
items: [
56+
{ text: 'Contributing & Graduation', link: '/guide/contributing' },
57+
],
58+
},
5159
],
5260

5361
'/packages/': [

docs/guide/contributing.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Contributing & Graduation
2+
3+
GemStack is shared, community-governed infrastructure built in the open with the [Vike](https://vike.dev) team. This page explains how the project grows and how to get involved.
4+
5+
## The graduation model
6+
7+
GemStack does not grow by bulk-moving a framework's package set in. Packages join one at a time, by **graduating**: a package earns a place under the `@gemstack/` scope when it proves framework-agnostic value, not when it is merely useful to one framework.
8+
9+
In practice a package graduates when it is:
10+
11+
- **Framework-agnostic.** It runs in any `fetch`-capable Node runtime and does not depend on a specific web framework, ORM, or UI library. Anything framework-specific stays in that framework's own binding.
12+
- **Neutral about infrastructure.** Persistence, caching, and storage are expressed as contracts the caller implements, with in-memory defaults for getting started. The package does not bundle a database or a queue.
13+
- **Well-tested and documented.** It ships a real test suite and a guide here.
14+
- **Composable.** It works on its own and composes cleanly with the rest of the family through the shared primitives (one `toolDefinition()` shape, one `Agent` base, one provider config).
15+
16+
The AI engine is the worked example: it was spun out of Rudder's `@rudderjs/ai`, decoupled from every framework binding, and re-versioned as [`@gemstack/ai-sdk`](/packages/ai-sdk/). The framework-specific pieces (an ORM-backed store set, a service provider, CLI scaffolders) stayed behind in the Rudder binding, which now re-exports the engine.
17+
18+
## Bindings vs the engine
19+
20+
A recurring shape in GemStack is **engine + binding**. The engine is the framework-agnostic core that lives here. A binding is a thin, framework-specific package that re-exports the engine and wires it into one framework's conventions (its container, config, ORM, and CLI).
21+
22+
If you maintain a framework, the path is: depend on the GemStack engine, implement its neutral contracts against your framework's infrastructure, and ship that as your own binding. Your users keep importing from your package; the engine stays shared.
23+
24+
## Ways to contribute
25+
26+
- **File issues.** Bugs, missing capabilities, and rough edges in any package. Reproductions and a clear "expected vs actual" make these actionable fast.
27+
- **Improve the docs.** These guides live in the repo under `docs/`. Fixes and clarifications are welcome; run `pnpm --filter @gemstack/docs docs:build` before opening a PR to catch dead links.
28+
- **Propose a graduation.** If you have a framework-agnostic package that fits the bar above, open an issue describing what it does and why it belongs in GemStack rather than in a single framework.
29+
- **Build a binding.** Wire the engine into your framework and let us link it from here.
30+
31+
The repository, issues, and discussions live at [github.com/gemstack-land/gemstack](https://github.com/gemstack-land/gemstack).
32+
33+
## Next
34+
35+
- [What is GemStack?](/guide/) - the family and the design principles.
36+
- [When to Use GemStack](/guide/when-to-use) - where it fits.
37+
- [Packages overview](/packages/) - every package and how they compose.

docs/guide/first-agent.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,13 @@ The agent decides when to call the tool, validates the arguments against `inputS
7878

7979
| You want to… | Read |
8080
|---|---|
81+
| Compose tools, skills, and a multi-agent supervisor into one app | [Build a Multi-Agent App](/guide/tutorial) |
8182
| Understand the agent loop, sub-agents, multi-step runs | [Agents](/packages/ai-sdk/agents) |
8283
| Go deeper on tools, scoped tools, client tools, approval gates | [Tools](/packages/ai-sdk/tools) |
84+
| Add more model providers | [Providers](/packages/ai-sdk/providers) |
8385
| Stream tokens and tool progress to a UI | [Streaming](/packages/ai-sdk/streaming) |
8486
| Get typed objects back instead of text | [Structured Output](/packages/ai-sdk/structured-output) |
8587
| Persist conversations and give the agent memory | [Memory & Persistence](/packages/ai-sdk/memory) |
8688
| Retrieval-augmented generation over your documents | [Vector Stores & RAG](/packages/ai-sdk/rag) |
8789
| Test agents without hitting a real model | [Testing & Evals](/packages/ai-sdk/testing) |
90+
| See the whole family and how the pieces fit | [Packages overview](/packages/) |

docs/guide/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mcp standalone MCP server framework agent-agnostic, not
3333

3434
- **Framework-agnostic core.** Every package runs in any `fetch`-capable JS runtime - Node, the browser, Electron, React Native. The agent runtime has zero static `node:*` imports in its main entry, and its only required runtime dependency is `zod`.
3535
- **Neutral contracts, not bundled infrastructure.** Persistence (conversation history, user memory, budgets, suspended runs, generated-file storage) is defined as interfaces you implement against your own database, cache, or object store. In-memory defaults ship for getting started.
36-
- **One way to do a thing.** A single `tool()` shape, a single `Agent` base, a single provider config object - shared across the whole family.
36+
- **One way to do a thing.** A single `toolDefinition()` shape, a single `Agent` base, a single provider config object - shared across the whole family.
3737
- **Graduated, not dumped.** GemStack grows by promoting packages that earn framework-agnostic standing, with the API settling toward `1.0` in the open.
3838

3939
## Where these came from

docs/guide/installation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The agent runtime lives in [`@gemstack/ai-sdk`](/packages/ai-sdk/). Install it p
66
pnpm add @gemstack/ai-sdk
77

88
pnpm add @anthropic-ai/sdk # Anthropic (Claude)
9-
pnpm add openai # OpenAI (also OpenRouter / Mistral / DeepSeek / Groq / xAI / Ollama)
9+
pnpm add openai # OpenAI (also Azure / OpenRouter / Mistral / DeepSeek / Groq / xAI / Ollama)
1010
pnpm add @google/genai # Google (Gemini)
1111
pnpm add cohere-ai # Cohere (reranking + embeddings)
1212
pnpm add @aws-sdk/client-bedrock-runtime # AWS Bedrock
@@ -24,7 +24,7 @@ import { AiRegistry, AnthropicProvider, OpenAIProvider, OllamaProvider } from '@
2424

2525
AiRegistry.register(new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }))
2626
AiRegistry.register(new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }))
27-
AiRegistry.register(new OllamaProvider({ baseUrl: 'http://localhost:11434' }))
27+
AiRegistry.register(new OllamaProvider({ baseUrl: 'http://localhost:11434/v1' }))
2828

2929
AiRegistry.setDefault('anthropic/claude-sonnet-4-6')
3030
```

docs/guide/tutorial.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# Build a Multi-Agent App
2+
3+
[Your First Agent](/guide/first-agent) ended with a single agent answering one prompt. Real work rarely fits one prompt: a research question fans out into several lines of inquiry that each want their own tools, a shared house style, and someone to plan the work and stitch the findings back together.
4+
5+
This tutorial builds that app, a small research assistant, by composing three GemStack packages:
6+
7+
- [`@gemstack/ai-sdk`](/packages/ai-sdk/agents) for tools and the agent loop,
8+
- [`@gemstack/ai-skills`](/packages/ai-skills) to load a portable `SKILL.md` skill onto a worker,
9+
- [`@gemstack/ai-autopilot`](/packages/ai-autopilot) to plan a task into subtasks, dispatch them to workers, and synthesize the result.
10+
11+
By the end you will have a `Supervisor` that breaks a research question into subtasks, runs each on a skill-equipped worker agent, and combines the answers. We finish with a short note on exposing the whole thing over MCP.
12+
13+
If you have not registered a provider yet, do that first (see [Installation](/guide/installation)).
14+
15+
## Register a provider
16+
17+
Every example assumes a default provider registered once at startup:
18+
19+
```ts
20+
import { AiRegistry, AnthropicProvider } from '@gemstack/ai-sdk'
21+
22+
AiRegistry.register(new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }))
23+
AiRegistry.setDefault('anthropic/claude-sonnet-4-6')
24+
```
25+
26+
With a default model set, agents do not need to declare one.
27+
28+
## Step 1: two tools the worker can call
29+
30+
A research worker needs to reach the web. We give it two tools with `toolDefinition(...)`: one to search, one to fetch a page. Each declares its input with Zod and attaches a `.server()` handler that the agent calls (see [Tools](/packages/ai-sdk/tools)). Swap the stubbed bodies for a real search API and HTTP client.
31+
32+
```ts
33+
import { toolDefinition } from '@gemstack/ai-sdk'
34+
import { z } from 'zod'
35+
36+
export const searchWeb = toolDefinition({
37+
name: 'search_web',
38+
description: 'Search the web and return the top matching result snippets',
39+
inputSchema: z.object({
40+
query: z.string().describe('The search query'),
41+
limit: z.number().int().min(1).max(10).default(5),
42+
}),
43+
}).server(async ({ query, limit }) => {
44+
// Call your real search provider here.
45+
return await search(query, limit) // -> [{ title, url, snippet }, ...]
46+
})
47+
48+
export const fetchPage = toolDefinition({
49+
name: 'fetch_page',
50+
description: 'Fetch a URL and return its readable text content',
51+
inputSchema: z.object({ url: z.string().url() }),
52+
}).server(async ({ url }) => {
53+
const res = await fetch(url)
54+
return await res.text()
55+
})
56+
```
57+
58+
The agent decides when to call each tool, validates the arguments against `inputSchema` before your handler runs, and feeds the result back to the model on the next step.
59+
60+
## Step 2: a skill for house style
61+
62+
Every worker should cite its sources the same way, and that convention should travel with the agent rather than being copy-pasted into each system prompt. That is exactly what a skill is: a portable folder of instructions (and optionally tools and resources) you compose onto an agent on demand.
63+
64+
Create `skills/citations/SKILL.md`. The YAML frontmatter is the manifest; the markdown body becomes extra system-prompt text:
65+
66+
```markdown
67+
---
68+
name: citations
69+
description: Cite every claim with a source URL and never invent sources
70+
trigger: answering a research question that draws on web sources
71+
---
72+
73+
# Citations
74+
75+
When you state a fact drawn from a source, cite it inline with the page URL in
76+
parentheses, like (https://example.com/article). Only cite pages you actually
77+
fetched with `fetch_page`. If you could not verify a claim, say so plainly
78+
instead of guessing. End your answer with a "Sources" list of the URLs you used.
79+
```
80+
81+
This skill is instructions-only, so there is no build step to worry about. (A skill that ships tools co-locates them in a `tools.ts` that the loader imports from its compiled output; see the [compiled-output caveat](/packages/ai-skills) when you go that far.)
82+
83+
Load it once at module init, since loading is async and the agent hooks are synchronous:
84+
85+
```ts
86+
import { loadSkill } from '@gemstack/ai-skills'
87+
88+
const citations = await loadSkill('./skills/citations')
89+
```
90+
91+
## Step 3: the worker agent
92+
93+
The worker is a `SkillfulAgent`. You declare your own identity in `baseInstructions()` and your own tools in `baseTools()`; the skills listed in `skills()` are merged in, with your own declarations winning on any name collision. Because research is multi-step (search, fetch, read, repeat), we give it a stop condition with `stepCountIs(...)`.
94+
95+
```ts
96+
import { SkillfulAgent } from '@gemstack/ai-skills'
97+
import { stepCountIs } from '@gemstack/ai-sdk'
98+
99+
class ResearchWorker extends SkillfulAgent {
100+
baseInstructions() {
101+
return 'You research a focused question using the web tools, then answer concisely.'
102+
}
103+
skills() { return [citations] } // adds the citation house style
104+
baseTools() { return [searchWeb, fetchPage] }
105+
stopWhen() { return stepCountIs(6) } // up to 6 tool-calling rounds
106+
}
107+
```
108+
109+
Override the `base*` hooks, not `instructions()` / `tools()`: those are sealed on `SkillfulAgent` and do the merge for you. Overriding them directly would drop the skill composition.
110+
111+
You can run this worker on its own to sanity-check it before wiring up the supervisor:
112+
113+
```ts
114+
const probe = await new ResearchWorker().prompt(
115+
'What problem did the original Transformer paper set out to solve?',
116+
)
117+
console.log(probe.text) // answer, with a Sources list, thanks to the skill
118+
```
119+
120+
## Step 4: plan, dispatch, synthesize
121+
122+
Now the orchestration. A `Supervisor` takes three stages: a `plan` that decomposes the task into subtasks, the `workers` that run them, and a `synthesize` that combines the results. The planner and synthesizer are themselves ai-sdk agents, adapted with `agentPlanner(...)` and `agentSynthesizer(...)`.
123+
124+
```ts
125+
import { Supervisor, agentPlanner, agentSynthesizer } from '@gemstack/ai-autopilot'
126+
import { agent } from '@gemstack/ai-sdk'
127+
128+
const planner = agent(
129+
'You break a research question into a few independent sub-questions that can be researched in parallel.',
130+
)
131+
132+
const editor = agent(
133+
'You combine several researched answers into one coherent, well-cited brief. Preserve every source URL.',
134+
)
135+
136+
const supervisor = new Supervisor({
137+
plan: agentPlanner(planner), // LLM decomposition into subtasks
138+
workers: new ResearchWorker(), // every subtask runs on this worker
139+
synthesize: agentSynthesizer(editor), // LLM synthesis of the results
140+
concurrency: 3, // up to 3 workers in flight at once
141+
maxSubtasks: 5, // hard cap; a longer plan is trimmed
142+
budget: { maxTotalTokens: 200_000 }, // stop dispatching past this spend
143+
onEvent: (e) => console.log(e.type), // 'plan', 'dispatch-start', ...
144+
})
145+
```
146+
147+
`workers` here is a single agent, so each subtask runs on a fresh `ResearchWorker` prompt. When you want different subtasks handled by different specialists, pass a `Record<string, Agent>` instead and let the planner set each `subtask.worker` to route between them.
148+
149+
## Step 5: run it
150+
151+
```ts
152+
const run = await supervisor.run(
153+
'How did the Transformer architecture change machine translation, and what came after it?',
154+
)
155+
156+
console.log(run.text) // the synthesized, cited brief
157+
console.log(run.plan) // the subtasks that were executed
158+
console.log(run.results) // one result per subtask: { text, ok, error?, usage }
159+
console.log(run.usage) // aggregate token usage across dispatched subtasks
160+
console.log(run.stoppedEarly) // true if a guardrail trimmed or halted the work
161+
```
162+
163+
`run()` resolves to a `SupervisorRun`. A few properties worth leaning on:
164+
165+
- **`run.results`** is one entry per dispatched subtask, in plan order. A worker that throws becomes an `ok: false` result; its siblings still run, so one failed line of inquiry does not sink the whole report.
166+
- **`run.usage`** aggregates token usage across the dispatched workers. (Planning and synthesis spend are not counted: those contracts return data, not usage.)
167+
- **`run.stoppedEarly`** tells you a guardrail (the `maxSubtasks` cap or the token `budget`) cut the work short, so you can flag a partial answer.
168+
169+
That is the whole app: tools give a worker hands, a skill gives it a house style, and the supervisor plans the work, fans it out, and reassembles it.
170+
171+
## Optional: expose it over MCP
172+
173+
Once the supervisor works, you can publish it as a Model Context Protocol server so other agents and MCP-aware clients can call it as a tool. Wrap the run in a server tool and serve it with [`@gemstack/ai-mcp`](/packages/ai-mcp); the worker's own tools stay internal, and callers see one `research` capability. See [/packages/ai-mcp](/packages/ai-mcp) for the server surface and transport options.
174+
175+
## See also
176+
177+
- [Tools](/packages/ai-sdk/tools) - `toolDefinition(...).server(...)`, streaming, approval, and scoped tools.
178+
- [Running agents](/packages/ai-sdk/agents) - the agent loop, stop conditions, sub-agents, and suspend/resume.
179+
- [`@gemstack/ai-skills`](/packages/ai-skills) - authoring, loading, and composing `SKILL.md` skills.
180+
- [`@gemstack/ai-autopilot`](/packages/ai-autopilot) - the `Supervisor` topology and its guardrails.
181+
- [`@gemstack/ai-mcp`](/packages/ai-mcp) - expose agents and tools over the Model Context Protocol.

docs/guide/when-to-use.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# When to Use GemStack
2+
3+
GemStack is a set of standalone, framework-agnostic packages for building AI applications in Node. This page is about fit: what GemStack is good at, where it deliberately stops, and how it differs from the tools you might reach for instead.
4+
5+
## Reach for GemStack when
6+
7+
- **You want a provider-agnostic agent runtime, not a provider SDK.** Define an agent once and swap Anthropic, OpenAI, Google, Ollama, and others by changing one model string. The tool loop, streaming, structured output, middleware, and a test fake come with it.
8+
- **You are building on the server, in any stack.** GemStack is UI-agnostic and framework-agnostic. It runs in any `fetch`-capable runtime and ships no React/Vue/Svelte coupling, so it drops into an existing Express, Hono, Fastify, Nitro, or Rudder app without taking it over.
9+
- **You need production concerns as first-class APIs.** Conversation persistence, cross-conversation user memory, token/cost budgets, prompt caching, sub-agent streaming with mid-run suspend, and an eval harness are part of the runtime, behind neutral contracts you implement against your own infrastructure.
10+
- **You care about testing.** A full fake (`AiFake`) lets you assert on prompts, tool calls, and every modality without hitting a real model or spending a token.
11+
- **You are working with MCP from either side.** Bridge an agent to remote MCP servers, expose an agent as a server, or author a standalone MCP server, all with first-party packages.
12+
13+
## Look elsewhere when
14+
15+
- **You want a batteries-included frontend chat UI.** GemStack is a server runtime. It speaks the Vercel AI protocol (`toVercelResponse()`), so a frontend chat library can consume its stream, but it ships no `useChat`-style hooks of its own.
16+
- **You only ever call one provider and want its native SDK.** If you are committed to a single vendor and want every bleeding-edge feature the day it ships, that vendor's own SDK will always be a release ahead of any abstraction.
17+
- **You want a hosted platform.** GemStack is libraries you run, not a managed service with a dashboard and billing.
18+
19+
## How it differs from the usual suspects
20+
21+
| | GemStack | A provider SDK (e.g. one vendor's client) | A heavyweight agent framework |
22+
|---|---|---|---|
23+
| **Scope** | Agent runtime + skills + orchestration + MCP, as separate packages | One provider's API surface | Large, opinionated, many abstractions |
24+
| **Providers** | Many, swap by model string | One | Many, via adapters |
25+
| **Coupling** | Framework-agnostic, server-side, `zod` is the only hard dependency | None, but vendor-locked | Often heavy dependency graph |
26+
| **Persistence** | Neutral contracts you implement (BYO database / cache / store) | None | Often bundled and opinionated |
27+
| **Adopt incrementally** | Yes, take one package | N/A | Usually all-or-nothing |
28+
29+
The point is not that GemStack does the most. It is that each package does one thing, stays framework-agnostic, and composes with the others, so you can adopt a single piece without buying into a platform.
30+
31+
## Next
32+
33+
- [Installation](/guide/installation) - get the runtime and a provider running.
34+
- [Your First Agent](/guide/first-agent) - the smallest end-to-end example.
35+
- [Build a Multi-Agent App](/guide/tutorial) - compose tools, skills, and a supervisor.
36+
- [Packages overview](/packages/) - the whole family and how the pieces fit.

0 commit comments

Comments
 (0)