Gemini Live API Dev
google-gemini/gemini-skills
A skill your agent uses when building real-time, bidirectional streaming applications with the Gemini Live API, or migrating legacy Live models (2.0/2.5/3.1) to Gemini 3.8 Live.
Type-safe Server-Sent Events (SSE) and WebSocket communication using river.ts library.
$ npx skills add Bewinxed/river.ts --skill river-ts-streaming -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install Bewinxed/river.ts river-ts-streaming --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
Claude Code skills documentation · loads skills from .claude/skills/
Install the "river-ts-streaming" agent skill from https://github.com/Bewinxed/river.ts/tree/main into .claude/skills/river-ts-streaming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "river-ts-streaming", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Bewinxed/river.ts --skill river-ts-streaming -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install Bewinxed/river.ts river-ts-streaming --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "river-ts-streaming" agent skill from https://github.com/Bewinxed/river.ts/tree/main into .agents/skills/river-ts-streaming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "river-ts-streaming", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Bewinxed/river.ts --skill river-ts-streaming -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install Bewinxed/river.ts river-ts-streaming --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "river-ts-streaming" agent skill from https://github.com/Bewinxed/river.ts/tree/main into .cursor/skills/river-ts-streaming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "river-ts-streaming", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Bewinxed/river.ts --skill river-ts-streaming -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install Bewinxed/river.ts river-ts-streaming --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "river-ts-streaming" agent skill from https://github.com/Bewinxed/river.ts/tree/main into .gemini/skills/river-ts-streaming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "river-ts-streaming", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install Bewinxed/river.ts river-ts-streamingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add Bewinxed/river.ts --skill river-ts-streaming -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "river-ts-streaming" agent skill from https://github.com/Bewinxed/river.ts/tree/main into .github/skills/river-ts-streaming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "river-ts-streaming", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add Bewinxed/river.ts --skill river-ts-streaming -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install Bewinxed/river.ts river-ts-streaming --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "river-ts-streaming" agent skill from https://github.com/Bewinxed/river.ts/tree/main into .opencode/skills/river-ts-streaming/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "river-ts-streaming", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
river-ts-streamingType-safe Server-Sent Events (SSE) and WebSocket communication using river.ts library.
River TS Streaming is an agent skill from Bewinxed/river.ts. Type-safe Server-Sent Events (SSE) and WebSocket communication using river.ts library. Use when working with this codebase to: (1) Define typed event schemas with RiverEvents builder, (2) Implement SSE streaming on server with RiverEmitter, (3) Consume SSE streams on client with RiverClient, (4) Handle WebSocket communication with RiverSocketAdapter, (5) Implement request/response RPC patterns over WebSocket, (6) Work with chunked/streamed data events.
Its SKILL.md is about 2.5k tokens, which your agent loads only when the skill is triggered. The skill folder holds 56 other files (for example `.github/workflows/publish.yml`, `README.md` and `build.config.ts`).
It sits in Backend & APIs, covering Realtime and WebSockets. It works with TypeScript. The repository describes itself as: Easy, Composable, and type-safe Server-Sent Events (SSE) & Websocket Messages. The licence is MIT.
Read from SKILL.md and the folder at commit 845ed95. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships script files (TypeScript and JavaScript, from the files we listed), which the agent can run.
Shell commands in SKILL.md call:
npmbunxbunFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
standardschema.devFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
River TS Streaming loads about 2.5k tokens when it runs. Until then it costs about 119 tokens; SKILL.md has 435 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from Bewinxed/river.ts at commit 845ed95, republished under its MIT licence (© Bewinxed). 435 words, ~2,487 tokens.
.claude/skills/river-ts-streaming/SKILL.md (or your agent's skills folder). This skill also uses 52 other files; get the full folder from GitHub.river.ts provides three main components:
RiverEvents - Type-safe event schema builderRiverEmitter - Server-side SSE streamingRiverClient - Client-side SSE consumptionRiverSocketAdapter - WebSocket message handling with request/response supportDefine events using the builder pattern:
import { RiverEvents } from 'river.ts';
const events = new RiverEvents()
.defineEvent('message', { message: 'Hello' })
.defineEvent('data', { data: {} as { id: number; name: string } })
.defineEvent('stream', { data: [] as string[], stream: true, chunkSize: 100 })
// Request/response pattern with explicit response type
.defineEvent('rpc.call', {
data: {} as { method: string; params: unknown },
response: {} as { result: unknown; error?: string }
})
// Runtime validation: `data` is inferred from the schema's output
.defineEvent('job.run', {
schema: z.object({ id: z.string(), priority: z.number() }),
responseSchema: z.object({ ok: z.boolean() }) // optional, for request()
})
.build();Reserved event types: close, error - do not define these.
schema and responseSchema accept any Standard Schema (zod, valibot, arktype; https://standardschema.dev). Events without one are type-checked only.
import { RiverEmitter } from 'river.ts/server';
const emitter = RiverEmitter.init(events);
// Create SSE stream for HTTP response
const stream = emitter.stream({
callback: async (emit, clientId) => {
await emit('message', { message: 'Connected' });
await emit('data', { data: { id: 1, name: 'test' } });
},
clientId: 'optional-custom-id',
ondisconnect: (clientId) => console.log(`${clientId} disconnected`)
});
return new Response(stream, { headers: emitter.headers() });
// Broadcast to all clients
await emitter.broadcast('message', { message: 'Update' });
// Send to specific client
await emitter.sendToClient('client-id', 'data', { data: { id: 2, name: 'specific' } });Resumable streams. All options are optional:
const stream = emitter.stream({
retry: 3000, // ms; written once as `retry:` when the stream opens
keepAlive: 15_000, // ms; writes a `: keep-alive` comment so proxies keep the connection
lastEventId: request.headers.get('Last-Event-ID'), // read it from the request yourself
signal: request.signal,
callback: async (emit, clientId, lastEventId) => {
// lastEventId is undefined on a first connection; replay everything after it
for (const entry of log.after(lastEventId)) {
await emit('message', { message: entry.text }, entry.id); // 3rd arg = event id
}
}
});
await emitter.broadcast('message', { message: 'Update' }, 42); // optional id
await emitter.sendToClient('client-id', 'message', { message: 'x' }, 43); // optional idEvent ids are string | number and may be non-ASCII: stream() decodes the Last-Event-ID header as UTF-8, so the callback gets the id as it was emitted. For stream: true events the id is written after the last chunk.
import { RiverClient } from 'river.ts/client';
const client = RiverClient.init(events, { reconnect: true });
client
.prepare('http://localhost:3000/events', { method: 'GET' })
.on('message', (data) => console.log(data.message))
.on('data', (data) => console.log(data.id, data.name))
.stream();
// Close connection
client.close();
// stream() can be called again after close()Client options (all optional; reconnect is off by default):
const client = RiverClient.init(events, {
reconnect: true, // or { initialDelay: 1000, maxDelay: 30_000 }
lastEventId: savedId, // sent as Last-Event-ID on the first connection
onInvalid: (type, issues, raw) => {}, // events that fail their schema; never dispatched
fetchFn: fetch,
headers: { Authorization: 'Bearer ...' }
});
client.lastEventId; // read-only: id of the last event received; persist it to resume later
client.addEventListener('open', () => {}); // each successful connection
client.addEventListener('reconnect', (e) => {}); // (e as CustomEvent).detail = { attempt, delay, error }
client.addEventListener('close', () => {}); // stopped for goodReconnect rules:
close event.client.close(), the server's close event.Retry-After (429/503), else the server's retry: value, else exponential backoff with jitter, capped at maxDelay.Last-Event-ID (fetch path). A GET with no headers at all (none in init(), none in prepare()) uses the browser EventSource, which reconnects and resumes by itself; any header, another method or an initial lastEventId selects fetch.The parser follows the WHATWG event-stream rules: CRLF/LF/CR line endings, : comments, event/data/id/retry fields, multiple data: lines joined with \n, default type message. Event data must be JSON.
import { RiverSocketAdapter } from 'river.ts/websocket';
const adapter = new RiverSocketAdapter(events, { debug: false });
// Register event handlers
adapter.on('message', (data) => console.log(data));
adapter.off('message', handler); // Unregister
// Handle incoming messages (call from ws.onmessage)
adapter.handleMessage(messageData);
// Send messages
adapter.send('data', { data: { id: 1, name: 'test' } }, (msg) => ws.send(msg));Runtime validation (events with a schema):
import { RiverSocketAdapter, InvalidMessageError } from 'river.ts/websocket';
const adapter = new RiverSocketAdapter(events, {
onInvalid: (type, issues, raw) => console.warn(type, issues, raw)
});handleMessage() validates data against the event's schema; an invalid message goes to onInvalid and is not dispatched. Without onInvalid it is logged with console.warn.request() validates the response against responseSchema (or schema when the event has neither responseSchema nor a response type) and rejects with InvalidMessageError (.type, .issues) when invalid.RiverClient does the same for the data field of incoming SSE events.For RPC-style communication with automatic type inference:
import {
RiverSocketAdapter,
RequestTimeoutError,
WebSocketClosedError
} from 'river.ts/websocket';
// Events with explicit response types
const events = new RiverEvents()
.defineEvent('instance.spawn', {
data: {} as { cwd: string },
response: {} as { instanceId: string; status: 'created' | 'error' }
})
.build();
const adapter = new RiverSocketAdapter(events);
// Route messages through adapter
ws.onmessage = (e) => adapter.handleMessage(e.data);
ws.onclose = () => adapter.clearPendingRequests();
// Make request - response type is inferred from event definition
const response = await adapter.request(
'instance.spawn',
{ cwd: '/app' },
(msg) => ws.send(msg),
10000 // timeout in ms (default: 30000)
);
// response is typed as { instanceId: string; status: 'created' | 'error' }Wire format for request/response:
// Request (outgoing)
{ "type": "instance.spawn", "data": { "cwd": "/app" }, "id": "uuid" }
// Response (incoming) - server echoes back the id
{ "type": "instance.spawn", "data": { "instanceId": "123", "status": "created" }, "id": "uuid" }import { EventData, ResponseData, EmitPayload } from 'river.ts';
// EventData<T, K> - Extract data type for receiving/handling
// ResponseData<T, K> - Extract response type for request() return value
// EmitPayload<T, K> - Extract payload type for emitting (excludes type/stream/chunkSize/schema/responseSchema)
// InvalidHandler - (type, issues, raw) => void, the `onInvalid` signature
// InvalidMessageError - thrown by request() for a response that fails its schemasrc/
├── index.ts # Main exports (RiverEvents, types)
├── builder.ts # RiverEvents builder class
├── validate.ts # Standard Schema validation shared by client and websocket
├── client/ # RiverClient for SSE consumption
├── server/ # RiverEmitter for SSE streaming
├── websocket/ # RiverSocketAdapter for WebSocket
└── types/
├── core.ts # BaseEvent, EventMap, EventData, ResponseData
└── http.ts # HTTPMethods typeThere are no unit tests. Check a change with bunx tsc --noEmit, bun run build, and a live run against a real server.
Build with: npm run build (uses unbuild)
Output goes to dist/ with separate entry points for /client, /server, /websocket.
Release by bumping the version in package.json and pushing to main: .github/workflows/publish.yml typechecks, builds and publishes to npm when the registry does not have that version yet. A prerelease version (one containing -, such as 1.3.0-test.1 from npm run bump:test) is published under the test dist-tag; any other version becomes latest. The workflow is the only publisher.
© Bewinxed, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 52 other files in the repository root of Bewinxed/river.ts.
Open the folder on GitHubat commit 845ed95
River TS Streaming next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| River TS Streaming this skillBewinxed/river.ts | 109 | — | ~2.5k | Automated safety check: Pass | MIT | |
| Gemini Live API Devgoogle-gemini/gemini-skills | 4.3k | — | ~4.6k | Automated safety check: Pass | Apache-2.0 | |
| Web3 PolymarketPolymarket/agent-skills | 191 | 2 repos | ~2k | Automated safety check: Pass | None | |
| Trigger.dev Realtimepapermark/papermark | 9.2k | — | ~1.7k | Automated safety check: Pass | Custom licence | |
| Kkrpckunkunsh/kkrpc | 174 | — | ~3.1k | Automated safety check: Pass | MIT | |
| Kkrpc Interopkunkunsh/kkrpc | 174 | — | ~1.8k | Automated safety check: Pass | MIT |
google-gemini/gemini-skills
A skill your agent uses when building real-time, bidirectional streaming applications with the Gemini Live API, or migrating legacy Live models (2.0/2.5/3.1) to Gemini 3.8 Live.
Polymarket/agent-skills
Polymarket integration for prediction market trading on Polygon.
papermark/papermark
Shows how to subscribe to Trigger.dev task runs from the backend and from React for progress indicators, live dashboards, AI response streams and approval waits.
kunkunsh/kkrpc
A skill your agent uses when building TypeScript RPC with kkrpc stable APIs, choosing native Transport<RPCMessage adapters, or integrating validation, middleware, transferables, streaming, remote…
kunkunsh/kkrpc
A skill your agent uses when implementing kkrpc clients or servers in non-TypeScript languages, speaking the stable compact protocol, transports, and reference implementations in Go, Python, Rust…
internet-court/internet-court-skill
Covers integrating Switchboard oracles on Solana: pull feeds, oracle quotes, VRF randomness and Surge WebSocket streaming, from both the TypeScript SDK and Rust.
Works with
Categories
Type-safe Server-Sent Events (SSE) and WebSocket communication using river.ts library. ts.ts library.
River TS Streaming fits situations like: working with this codebase to:; define typed event schemas with RiverEvents builder; implement SSE streaming on server with RiverEmitter; consume SSE streams on client with RiverClient.
Run `npx skills add Bewinxed/river.ts --skill river-ts-streaming -a claude-code`. Or copy the skill folder (the Bewinxed/river.ts repository) into .claude/skills/river-ts-streaming in your project. Claude Code loads it when a task matches its description.
Run `npx skills add Bewinxed/river.ts --skill river-ts-streaming -a codex`. Or copy the skill folder (the Bewinxed/river.ts repository) into .agents/skills/river-ts-streaming in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add Bewinxed/river.ts --skill river-ts-streaming -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/river-ts-streaming, .gemini/skills/river-ts-streaming, .github/skills/river-ts-streaming and .opencode/skills/river-ts-streaming in your project.
Going by SKILL.md and its folder, River TS Streaming needs TypeScript and JavaScript for the scripts in its folder and the command-line tools its instructions call (npm, bunx and bun). Our summary lists: Node.js.
SKILL.md names 1 domain. As links in the text: standardschema.dev. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
River TS Streaming is published under the MIT licence (from the LICENSE file in the skill folder). It allows redistribution, so the full SKILL.md is shown on this page.
About 2.5k tokens (SKILL.md is roughly 9.9k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with River TS Streaming: Gemini Live API Dev (google-gemini/gemini-skills, 4.3k stars), Web3 Polymarket (Polymarket/agent-skills, 191 stars), Trigger.dev Realtime (papermark/papermark, 9.2k stars) and Kkrpc (kunkunsh/kkrpc, 174 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
Bewinxed (a GitHub user) maintains it in Bewinxed/river.ts, which has 109 GitHub stars. The repository was last updated on October 4, 2026.
Source: Bewinxed/river.ts on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.