Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .claude/skills/write-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,17 @@ Spelling to enforce on every page:

- **"realtime", one word.** Never "real time" or "real-time" when describing Ably delivery ("sees the same conversation in realtime", "realtime messaging"). This is Ably house spelling. Sweep with `grep -rn "real[- ]time" "$P"` and expect zero hits.

### Stay inside the product vocabulary

Use the terms the product actually defines. Do not introduce vocabulary that is not part of the AI Transport domain — not internal-codebase jargon, and not generic AI/ML terms the docs have chosen not to use. A reader who has read the concept pages should never hit a word the docs never taught them.

- **Prefer the named primitives.** The concept pages define the vocabulary: [Session](/docs/ai-transport/concepts/sessions), [Connection](/docs/ai-transport/concepts/connections), [Run](/docs/ai-transport/concepts/runs), [Step](/docs/ai-transport/concepts/steps), [Invocation](/docs/ai-transport/concepts/invocations), [Codec](/docs/ai-transport/concepts/codecs), [conversation tree](/docs/ai-transport/concepts/conversation-tree), and view. Reach for these before inventing a synonym.
- **Do not leak internal jargon.** Words that name SDK internals but never appear in the concept or API docs (for example `projection`, `RunNode`, or other type names not documented for readers) read as jargon. Use the documented term instead: "the conversation" or "the Run's view", not "the projection"; "the Run" or "the conversation tree", not "`RunNode`".
- **Do not smuggle in generic AI/ML vocab the docs avoid.** Describe a loop cycle as an "iteration", not a "pass". Describe a model invocation as a "model call" or "response", not an "inference pass". The verb "pass" (to pass an argument, hook, or signal) is fine; the noun "pass" (meaning a loop cycle) is not.
- **Respect the Run/turn split.** "Run" is the only primitive the docs explain. Use "turn" only in jobs-to-be-done framing (titles, intros, the "concurrent turns" feature name), never as a defined primitive alongside Run.

When a word is genuinely new and needed, define it on first use or link to the concept page that does. If you cannot point to where it is defined, it does not belong on the page.

## Per-page workflow

1. **Identify the page type.** Pick the matching template above.
Expand Down Expand Up @@ -401,6 +412,11 @@ grep -rn "—" "$P"
# "realtime" is one word (no "real time" / "real-time")
grep -rn "real[- ]time" "$P"

# No off-vocabulary terms. Noun "pass" (loop cycle), "inference pass", and internal
# jargon that never appears in the concept/API docs. The verb "pass" (an argument,
# hook, signal) is fine, so this targets the noun forms and known jargon only.
grep -rniE "inference pass|\b(this|that|next|current|each|one|its|a|the|per|another) (pass|passes)\b|\bprojection\b|\bRunNode\b" "$P"

# No bold-prefix bullets
grep -rnE "^\s*[\*\-]\s+\*\*[^*]+:\*\*" "$P"

Expand Down Expand Up @@ -481,7 +497,7 @@ When reviewing someone else's docs PR (or your own at PR time), walk this list.
**Writing standards**

9. Page complies with the rules in [`writing-style-guide.md`](../../../writing-style-guide.md). Use the verification greps above to spot the common violations (em dashes, bold-prefix bullets, Latin abbreviations, subjective phrases, vague modals, "we" as the subject, AI-fingerprint patterns).
10. Author ran `/deslop` (or equivalent AI-pattern check).

@VeskeR VeskeR Jul 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should keep this as item 11

@zknill zknill Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not a shared skill, so I think this is a meaningless line

10. Page stays inside the product vocabulary — no internal-codebase jargon (`projection`, `RunNode`) and no generic AI/ML terms the docs avoid (noun "pass", "inference pass"). Every non-obvious term is defined on first use or links to the concept page that defines it. Use the off-vocabulary grep above.

**Code accuracy**

Expand Down
68 changes: 34 additions & 34 deletions src/data/nav/aitransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,76 +132,76 @@ export default {
name: 'Features',
pages: [
{
name: 'Token streaming',
link: '/docs/ai-transport/features/token-streaming',
name: 'Agent presence',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bringing in @m-hulbert comment from #3468 regarding alphabetical ordering here:

I agree that the feature list is rapidly expanding, and it would help to make that easier to navigate; however I'm not convinced that alphabetising the list is the right way to do this. In terms of releative 'importance' - is 'Agent presence' the first thing we believe should be read to understand the value proposition and how AI Transport works?

IMO, the core value proposition should be immediately stated and clear in the "About AI Transport". "How AI Transport works" is covered by "Concepts".
And "Features" then shows you what you can build with it. If you're just skimming through the docs to see the overall feature set, I don't think there's a material difference whether "Features" is sorted alphabetically or based on our assumptions of what we deem important - people will still be able to find what they want. But when you're returning to the documentation, having the list sorted makes it way easier to navigate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, well @m-hulbert if you have a concrete proposal for an order then please share that here. In the mean time, this re-ordering makes the features findable when you want to return to a page you already know exists.

(I'm highly allergic to comments along the lines of "not this thing", without "this instead")

link: '/docs/ai-transport/features/agent-presence',
},
{
name: 'Cancellation',
link: '/docs/ai-transport/features/cancellation',
name: 'Branching, edit, and regenerate',
link: '/docs/ai-transport/features/branching',
},
{
name: 'Reconnection and recovery',
link: '/docs/ai-transport/features/reconnection-and-recovery',
name: 'Cancellation',
link: '/docs/ai-transport/features/cancellation',
},
{
name: 'Multi-device sessions',
link: '/docs/ai-transport/features/multi-device',
name: 'Chain of thought',
link: '/docs/ai-transport/features/chain-of-thought',
},
{
name: 'History and replay',
link: '/docs/ai-transport/features/history',
name: 'Concurrent turns',
link: '/docs/ai-transport/features/concurrent-turns',
},
{
name: 'Database hydration',
link: '/docs/ai-transport/features/database-hydration',
},
{
name: 'Branching, edit, and regenerate',
link: '/docs/ai-transport/features/branching',
},
{
name: 'Interruption',
link: '/docs/ai-transport/features/interruption',
},
{
name: 'Concurrent turns',
link: '/docs/ai-transport/features/concurrent-turns',
},
{
name: 'Tool calling',
link: '/docs/ai-transport/features/tool-calling',
name: 'Double texting',
link: '/docs/ai-transport/features/double-texting',
},
{
name: 'Durable execution',
link: '/docs/ai-transport/features/durable-execution',
},
{
name: 'Human-in-the-loop',
link: '/docs/ai-transport/features/human-in-the-loop',
name: 'History and replay',
link: '/docs/ai-transport/features/history',
},
{
name: 'Optimistic updates',
link: '/docs/ai-transport/features/optimistic-updates',
name: 'Human-in-the-loop',
link: '/docs/ai-transport/features/human-in-the-loop',
},
{
name: 'Agent presence',
link: '/docs/ai-transport/features/agent-presence',
name: 'Interruption and steering',
link: '/docs/ai-transport/features/interruption-and-steering',
},
{
name: 'LiveObjects State',
link: '/docs/ai-transport/features/liveobjects',
},
{
name: 'Multi-device sessions',
link: '/docs/ai-transport/features/multi-device',
},
{
name: 'Optimistic updates',
link: '/docs/ai-transport/features/optimistic-updates',
},
{
name: 'Push notifications',
link: '/docs/ai-transport/features/push-notifications',
},
{
name: 'Chain of thought',
link: '/docs/ai-transport/features/chain-of-thought',
name: 'Reconnection and recovery',
link: '/docs/ai-transport/features/reconnection-and-recovery',
},
{
name: 'Double texting',
link: '/docs/ai-transport/features/double-texting',
name: 'Token streaming',
link: '/docs/ai-transport/features/token-streaming',
},
{
name: 'Tool calling',
link: '/docs/ai-transport/features/tool-calling',
},
],
},
Expand Down
31 changes: 31 additions & 0 deletions src/pages/docs/ai-transport/concepts/runs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,37 @@ await clientRun.cancel();

On the agent side, the symmetric primitive is [`AgentSession.createRun(invocation, runtime?)`](/docs/ai-transport/api/javascript/core/agent-session#create-run). The agent mints `runId` (for a fresh run) or reads the existing `runId` off the triggering input event (for a continuation), and stamps it on every event it publishes; the client reads it from `clientRun.runId` once `clientRun.started` resolves.

## Steer a Run <a id="steer"/>

While a Run is active, a client can send a follow-up message into it. This is called *steering* the Run. The agent picks up the new message on the next loop iteration; no new Run starts.

The follow-up has the same shape as the input that started the Run. It joins the conversation tree under the same `RunNode` and every connected client sees it. Unlike a fresh `view.send`, it carries the active Run's `runId`, which tells the agent to add it to the existing Run.

Steering messages don't change how a Run ends. The terminal `RunEndReason` reports how the Run finished. Each steering message also gets its own outcome, so the client can tell which ones the agent saw before the Run ended and which it didn't.

<Code>
```javascript
const activeRun = await session.view.send(UIMessageCodec.createUserMessage({
id: crypto.randomUUID(),
role: 'user',
parts: [{ type: 'text', text: 'Plan a 3-day trip to Lisbon.' }],
}));

const { published, outcome } = activeRun.steer(UIMessageCodec.createUserMessage({
id: crypto.randomUUID(),
role: 'user',
parts: [{ type: 'text', text: 'Make it 5 days, and keep it under £800.' }],
}));

await published; // The steering message has been published to the channel.

const { consumed } = await outcome; // Resolves when the Run ends.
console.log('Reached the agent:', consumed);
```
</Code>

See [interruption and steering](/docs/ai-transport/features/interruption-and-steering#steer) for how to call `steer()`, what `outcome` contains, and how to write the agent's loop.

## Run concurrent Runs <a id="concurrent"/>

A session can hold multiple Runs in flight at the same time. They share the channel; they don't share state. See [concurrent turns](/docs/ai-transport/features/concurrent-turns) for the patterns that arise when more than one Run is `'active'`.
Expand Down
9 changes: 7 additions & 2 deletions src/pages/docs/ai-transport/features/cancellation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,16 @@ Every run exposes an `abortSignal` that fires when the run is cancelled. Pass it
<Code>
```javascript
const run = session.createRun(invocation, { signal: req.signal });

// Drain run.view for the full conversation to feed the model. run.messages
// is only this Run's own turn.
while (run.view.hasOlder()) await run.view.loadOlder();
await run.start();

const conversation = run.view.getMessages().map(({ message }) => message);
const result = streamText({
model: anthropic('claude-sonnet-4-20250514'),
messages: run.messages,
messages: await convertToModelMessages(conversation),
abortSignal: run.abortSignal,
});

Expand Down Expand Up @@ -155,6 +160,6 @@ The cancel signal is a published message on the channel. See [pricing](/docs/pla

## Related features <a id="related"/>

- [Interruption](/docs/ai-transport/features/interruption): cancel and immediately send a new message.
- [Interruption and steering](/docs/ai-transport/features/interruption-and-steering): steer the active Run, cancel and re-prompt, or send alongside.
- [Concurrent turns](/docs/ai-transport/features/concurrent-turns): multiple turns with independent cancel handles.
- [Token streaming](/docs/ai-transport/features/token-streaming): what gets cancelled when the abort fires.
4 changes: 2 additions & 2 deletions src/pages/docs/ai-transport/features/concurrent-turns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ const newRun = await view.send(UIMessageCodec.createUserMessage({
```
</Code>

See [Interruption](/docs/ai-transport/features/interruption) for the full pattern.
See [Interruption and steering](/docs/ai-transport/features/interruption-and-steering) for the full pattern.

### Multi-user sessions <a id="multi-user"/>

Expand Down Expand Up @@ -239,5 +239,5 @@ A Run is one unit of agent work that produces multiple messages. The `runId` gro
## Related features <a id="related"/>

- [Cancellation](/docs/ai-transport/features/cancellation): scoped cancel signals and server-side abort handling.
- [Interruption](/docs/ai-transport/features/interruption): cancel and immediately send a new message.
- [Interruption and steering](/docs/ai-transport/features/interruption-and-steering): steer the active Run, cancel and re-prompt, or send alongside.
- [Multi-device sessions](/docs/ai-transport/features/multi-device): concurrent turns across devices.
4 changes: 2 additions & 2 deletions src/pages/docs/ai-transport/features/double-texting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ This gives users feedback that their message was received and will be processed

## Cancel and re-prompt <a id="cancel-then-send"/>

To cancel the current Run before sending the new message, use the [interruption](/docs/ai-transport/features/interruption) pattern:
To cancel the current Run before sending the new message, use the [interruption and steering](/docs/ai-transport/features/interruption-and-steering) pattern:

<Code>
```javascript
Expand Down Expand Up @@ -141,5 +141,5 @@ No. Each user message is a sibling under the same parent. The tree records both
## Related features <a id="related"/>

- [Concurrent turns](/docs/ai-transport/features/concurrent-turns): the underlying mechanism for parallel turns.
- [Interruption](/docs/ai-transport/features/interruption): the cancel-then-send pattern in detail.
- [Interruption and steering](/docs/ai-transport/features/interruption-and-steering): steer, cancel-then-send, and send-alongside in detail.
- [Cancellation](/docs/ai-transport/features/cancellation): cancel signals and scoped cancellation.
Loading