Skip to content

Commit e3cbe7d

Browse files
committed
undo changes to local claude realtime skill, we'll do that in another PR
1 parent 6d1a29f commit e3cbe7d

File tree

1 file changed

+0
-175
lines changed

1 file changed

+0
-175
lines changed

.claude/skills/trigger-dev-tasks/realtime.md

Lines changed: 0 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Realtime allows you to:
99
- Subscribe to run status changes, metadata updates, and streams
1010
- Build real-time dashboards and UI updates
1111
- Monitor task progress from frontend and backend
12-
- Send data into running tasks with input streams
1312

1413
## Authentication
1514

@@ -104,178 +103,6 @@ for await (const chunk of stream) {
104103
}
105104
```
106105

107-
## Input Streams
108-
109-
Input streams let you send data **into** a running task from your backend or frontend. Output streams send data out of tasks; input streams complete the loop.
110-
111-
### Problems Input Streams Solve
112-
113-
**Cancelling AI streams mid-generation.** When you use AI SDK's `streamText` inside a task, the LLM keeps generating tokens until it's done — even if the user has navigated away or clicked "Stop generating." Without input streams, there's no way to tell the running task to abort. With input streams, your frontend sends a cancel signal and the task aborts the LLM call immediately.
114-
115-
**Human-in-the-loop workflows.** A task generates a draft, then pauses and waits for user approval before proceeding.
116-
117-
**Interactive agents.** An AI agent running as a task needs follow-up information from the user mid-execution.
118-
119-
### Defining Input Streams
120-
121-
```ts
122-
// trigger/streams.ts
123-
import { streams } from "@trigger.dev/sdk";
124-
125-
export const cancelSignal = streams.input<{ reason?: string }>({ id: "cancel" });
126-
export const approval = streams.input<{ approved: boolean; reviewer: string }>({ id: "approval" });
127-
```
128-
129-
### Receiving Data Inside a Task
130-
131-
#### `wait()` — Suspend until data arrives (recommended for long waits)
132-
133-
Suspends the task entirely, freeing compute. Returns `ManualWaitpointPromise<TData>` (same as `wait.forToken()`).
134-
135-
```ts
136-
import { task } from "@trigger.dev/sdk";
137-
import { approval } from "./streams";
138-
139-
export const publishPost = task({
140-
id: "publish-post",
141-
run: async (payload: { postId: string }) => {
142-
const draft = await prepareDraft(payload.postId);
143-
await notifyReviewer(draft);
144-
145-
// Suspend — no compute cost while waiting
146-
const result = await approval.wait({ timeout: "7d" });
147-
148-
if (result.ok) {
149-
return { published: result.output.approved };
150-
}
151-
return { published: false, timedOut: true };
152-
},
153-
});
154-
```
155-
156-
Options: `timeout` (period string), `idempotencyKey`, `idempotencyKeyTTL`, `tags`.
157-
158-
Use `.unwrap()` to throw on timeout: `const data = await approval.wait({ timeout: "24h" }).unwrap();`
159-
160-
**Use `.wait()` when:** nothing to do until data arrives, wait could be long, want zero compute cost.
161-
162-
#### `once()` — Wait for the next value (non-suspending)
163-
164-
Keeps the task process alive. Use for short waits or when doing concurrent work.
165-
166-
```ts
167-
import { task } from "@trigger.dev/sdk";
168-
import { approval } from "./streams";
169-
170-
export const draftEmailTask = task({
171-
id: "draft-email",
172-
run: async (payload: { to: string; subject: string }) => {
173-
const draft = await generateDraft(payload);
174-
const result = await approval.once(); // Blocks until data arrives
175-
176-
if (result.approved) {
177-
await sendEmail(draft);
178-
}
179-
return { sent: result.approved, reviewer: result.reviewer };
180-
},
181-
});
182-
```
183-
184-
Options: `once({ timeoutMs: 300_000 })` or `once({ signal: controller.signal })`.
185-
186-
**Use `.once()` when:** wait is short, doing concurrent work, need AbortSignal support.
187-
188-
#### `on()` — Listen for every value
189-
190-
```ts
191-
import { task } from "@trigger.dev/sdk";
192-
import { cancelSignal } from "./streams";
193-
194-
export const streamingTask = task({
195-
id: "streaming-task",
196-
run: async (payload: { prompt: string }) => {
197-
const controller = new AbortController();
198-
199-
const sub = cancelSignal.on(() => {
200-
controller.abort();
201-
});
202-
203-
try {
204-
const result = await streamText({
205-
model: openai("gpt-4o"),
206-
prompt: payload.prompt,
207-
abortSignal: controller.signal,
208-
});
209-
return result;
210-
} finally {
211-
sub.off();
212-
}
213-
},
214-
});
215-
```
216-
217-
#### `peek()` — Non-blocking check
218-
219-
```ts
220-
const latest = cancelSignal.peek(); // undefined if nothing received yet
221-
```
222-
223-
### Sending Data to a Running Task
224-
225-
```ts
226-
import { cancelSignal, approval } from "./trigger/streams";
227-
228-
await cancelSignal.send(runId, { reason: "User clicked stop" });
229-
await approval.send(runId, { approved: true, reviewer: "alice@example.com" });
230-
```
231-
232-
### Full Example: Cancellable AI Streaming
233-
234-
```ts
235-
// trigger/streams.ts
236-
import { streams } from "@trigger.dev/sdk";
237-
238-
export const aiOutput = streams.define<string>({ id: "ai" });
239-
export const cancelStream = streams.input<{ reason?: string }>({ id: "cancel" });
240-
```
241-
242-
```ts
243-
// trigger/ai-task.ts
244-
import { task } from "@trigger.dev/sdk";
245-
import { streamText } from "ai";
246-
import { openai } from "@ai-sdk/openai";
247-
import { aiOutput, cancelStream } from "./streams";
248-
249-
export const aiTask = task({
250-
id: "ai-chat",
251-
run: async (payload: { prompt: string }) => {
252-
const controller = new AbortController();
253-
const sub = cancelStream.on(() => controller.abort());
254-
255-
try {
256-
const result = streamText({
257-
model: openai("gpt-4o"),
258-
prompt: payload.prompt,
259-
abortSignal: controller.signal,
260-
});
261-
262-
const { waitUntilComplete } = aiOutput.pipe(result.textStream);
263-
await waitUntilComplete();
264-
return { text: await result.text };
265-
} finally {
266-
sub.off();
267-
}
268-
},
269-
});
270-
```
271-
272-
### Important Notes
273-
274-
- Input streams require v2 realtime streams (SDK 4.1.0+). Calling `.on()` or `.once()` without v2 throws an error.
275-
- Cannot send data to completed/failed/canceled runs.
276-
- Max 1MB per `.send()` call.
277-
- Data sent before a listener is registered gets buffered and delivered when a listener attaches.
278-
279106
## React Frontend Usage
280107

281108
### Installation
@@ -415,5 +242,3 @@ Key properties available in run subscriptions:
415242
- **Handle errors**: Always check for errors in hooks and subscriptions
416243
- **Type safety**: Use task types for proper payload/output typing
417244
- **Cleanup subscriptions**: Backend subscriptions auto-complete, frontend hooks auto-cleanup
418-
- **Clean up input stream listeners**: Always call `.off()` in a `finally` block to avoid leaks
419-
- **Use timeouts with `once()`**: Avoid hanging indefinitely if the signal never arrives

0 commit comments

Comments
 (0)