
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
@aibind/solidstart
Advanced tools
AI SDK bindings for SolidStart — reactive hooks with defaults, server handlers, and agents
AI SDK bindings for SolidStart. Reactive SolidJS hooks, server handlers, and agents — all wired up with sensible defaults.
🤏 Tiny — Ships only what you use. Tree-shakes per entry point.
🐇 Simple — Three hooks: useStream, useStructuredStream, useAgent. Call and go.
🧙♀️ Elegant — Returns SolidJS signals. Reactive by nature.
🗃️ Highly customizable — Custom endpoints, custom fetch, per-request system overrides, named model registries.
⚛️ Reactive — Every piece of state is a SolidJS signal. Fine-grained reactivity out of the box.
🔌 Batteries included — Server handler and default endpoints out of the box.
npm install @aibind/solidstart ai solid-js
Peer dependencies: solid-js ^1.8, ai ^6.0.
useStructuredStream works with any Standard Schema-compatible library. Install one:
# Zod (v4 recommended — has built-in JSON Schema support)
npm install zod
# Valibot (requires JSON Schema converter)
npm install valibot @valibot/to-json-schema
# ArkType (built-in JSON Schema via .toJsonSchema())
npm install arktype
// src/routes/api/__aibind__/[...path].ts
import { createStreamHandler } from "@aibind/solidstart/server";
import { anthropic } from "@ai-sdk/anthropic";
const handler = createStreamHandler({
model: anthropic("claude-sonnet-4-20250514"),
});
export async function POST({ request }: { request: Request }) {
return handler(request);
}
import { useStream } from "@aibind/solidstart";
function Chat() {
const { text, loading, send } = useStream({
system: "You are a helpful assistant.",
});
let prompt = "";
return (
<form
onSubmit={(e) => {
e.preventDefault();
send(prompt);
}}
>
<input value={prompt} onInput={(e) => (prompt = e.target.value)} />
<button disabled={loading()}>Send</button>
<Show when={text()}>
<p>{text()}</p>
</Show>
</form>
);
}
@aibind/solidstart — Client Hooksimport {
useStream,
useStructuredStream,
defineModels,
} from "@aibind/solidstart";
defineModels(models)Define named AI models for type-safe model selection across client and server.
// src/models.ts
import { defineModels } from "@aibind/solidstart";
import { anthropic } from "@ai-sdk/anthropic";
export const models = defineModels({
default: anthropic("claude-sonnet-4-20250514"),
fast: anthropic("claude-haiku-20250514"),
});
export type Models = typeof models.$infer; // 'default' | 'fast'
useStream(options?)Reactive streaming text. Endpoint defaults to /api/__aibind__/stream.
const { text, loading, error, done, send, abort, retry } = useStream({
model: "fast", // optional model key
system: "You are a poet.",
endpoint: "/api/custom/stream", // override default
fetch: customFetch, // optional custom fetch
onFinish: (text) => console.log(text),
onError: (err) => console.error(err),
});
send("Write a haiku");
send("Now a limerick", { system: "Override system prompt" });
text(); // reactive accumulated text (signal accessor)
loading(); // true while streaming
error(); // Error | null
done(); // true when complete
abort(); // cancel in-flight request
retry(); // re-send last prompt
useStructuredStream(options)Streams JSON and parses partial objects as they arrive. Validates the final result with any Standard Schema-compatible library. Endpoint defaults to /api/__aibind__/structured.
import { useStructuredStream } from "@aibind/solidstart";
import { z } from "zod";
const { data, partial, raw, loading, error, send } = useStructuredStream({
schema: z.object({
sentiment: z.enum(["positive", "negative", "neutral"]),
score: z.number(),
topics: z.array(z.string()),
}),
system: "Analyze sentiment. Return JSON matching the schema.",
});
send("I love this product!");
partial(); // Partial<T> — updates as JSON streams in
data(); // T | null — fully validated after completion
raw(); // raw JSON string
@aibind/solidstart/server — Server Handlerimport { createStreamHandler, ServerAgent } from "@aibind/solidstart/server";
createStreamHandler(config)Generic Web Request/Response handler for streaming endpoints.
const handler = createStreamHandler({
model: anthropic("claude-sonnet-4-20250514"),
prefix: "/api/__aibind__", // default
});
// Use in a SolidStart catch-all route:
export async function POST({ request }: { request: Request }) {
return handler(request);
}
Handles two routes:
POST {prefix}/stream — text streamingPOST {prefix}/structured — JSON streamingServerAgentServer-side agent with tools, system prompt, and multi-step tool loops.
import { ServerAgent } from "@aibind/solidstart/server";
import { tool, stepCountIs } from "ai";
import { z } from "zod";
const agent = new ServerAgent({
model: anthropic("claude-sonnet-4-20250514"),
system: "You are a helpful assistant with access to tools.",
tools: {
get_weather: tool({
description: "Get weather for a city",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({
city,
temperature: "72°F",
condition: "sunny",
}),
}),
},
stopWhen: stepCountIs(5),
});
@aibind/solidstart/agent — Client Agentimport { useAgent } from "@aibind/solidstart/agent";
useAgent(options?)Reactive agent hook. Endpoint defaults to /api/__aibind__/agent.
import { useAgent } from "@aibind/solidstart/agent";
function AgentChat() {
const { messages, status, send, stop } = useAgent();
let prompt = "";
return (
<>
<form
onSubmit={(e) => {
e.preventDefault();
send(prompt);
prompt = "";
}}
>
<input value={prompt} onInput={(e) => (prompt = e.target.value)} />
<button disabled={status() === "running"}>Send</button>
</form>
<For each={messages()}>
{(message) => <div class={message.role}>{message.content}</div>}
</For>
<Show when={status() === "running"}>
<button onClick={() => stop()}>Stop</button>
</Show>
</>
);
}
Reactive signals:
messages() — array of { id, role, content, type } messagesstatus() — 'idle' | 'running' | 'awaiting-approval' | 'error'error() — Error | nullpendingApproval() — { id, toolName, args } | nullMethods:
send(prompt) — send a message, streams response incrementallystop() — abort the current requestapprove(id) / deny(id) — respond to tool approval requests@aibind/solidstart/markdown — Streaming Markdownimport { useStreamMarkdown } from "@aibind/solidstart/markdown";
Reactive hook that parses streaming markdown with recovery for unterminated syntax. Uses @aibind/markdown under the hood.
import { useStream } from "@aibind/solidstart";
import { useStreamMarkdown } from "@aibind/solidstart/markdown";
function Chat() {
const { text, loading } = useStream({
system: "You are a helpful assistant.",
});
const html = useStreamMarkdown(
() => text(),
() => loading(),
);
return <div innerHTML={html()} />;
}
Parameters:
getText — accessor returning the markdown stringgetStreaming — accessor returning whether the stream is active (enables recovery)@aibind/solidstart/history — Branching Conversation Historyimport {
ReactiveChatHistory,
ReactiveMessageTree,
ChatHistory,
MessageTree,
} from "@aibind/solidstart/history";
Tree-structured conversation history with branching support. Edit messages, regenerate responses, and navigate alternatives (ChatGPT-style).
import { ReactiveChatHistory } from "@aibind/solidstart/history";
function Chat() {
const chat = new ReactiveChatHistory<{ role: string; content: string }>();
chat.append({ role: "user", content: "Hello" });
chat.append({ role: "assistant", content: "Hi!" });
return (
<For each={chat.messages()}>
{(msg) => <div>{msg.role}: {msg.content}</div>}
</For>
);
}
See @aibind/core README for full API documentation.
MIT
FAQs
AI SDK bindings for SolidStart — reactive hooks with defaults, server handlers, and agents
The npm package @aibind/solidstart receives a total of 11 weekly downloads. As such, @aibind/solidstart popularity was classified as not popular.
We found that @aibind/solidstart demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.