{"_id":"@agilab/react","_rev":"3-5b09b15647d6cf83c55d3d8150191f82","name":"@agilab/react","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@agilab/react","version":"0.1.0","keywords":["ag-ui","react","agent","streaming","ai","llm"],"license":"MIT","_id":"@agilab/react@0.1.0","maintainers":[{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"}],"homepage":"https://github.com/rinkesh2010rpp/agui-react-client#readme","bugs":{"url":"https://github.com/rinkesh2010rpp/agui-react-client/issues"},"dist":{"shasum":"191c98b581cb5a270f05d0775ecd8c8ca617bf25","tarball":"https://registry.npmjs.org/@agilab/react/-/react-0.1.0.tgz","fileCount":12,"integrity":"sha512-A4CAAgBCLpsPRSAcSPYr9/HijFzZw7qKBrmZOzLa7wQhqfGnaBEyoy8rJWxzGIAkYeA7duo2c71/ATcaW+2PYA==","signatures":[{"sig":"MEYCIQCQVXtee1slxKDsGMTISwKF30MGqP8QaV42IPr+LbIffwIhAJRlBlWiAWtR4KGS0x8kkpqDZPPB/c3BzuxVHDxwW47d","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90592},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"a718d3389a0d291eb62c5e79eb19ecfd366f149e","private":false,"scripts":{"dev":"npm run dev --workspace=examples/with-chatscope","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts","typecheck":"tsc --noEmit && npm run typecheck --workspace=examples/with-chatscope","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"},"repository":{"url":"git+https://github.com/rinkesh2010rpp/agui-react-client.git","type":"git"},"workspaces":["examples/*"],"_npmVersion":"11.12.1","description":"Headless React library that converts AG-UI SSE events into streaming-aware React state","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@ag-ui/core":"latest","@ag-ui/client":"latest"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","vitest":"^4.1.6","typescript":"^5","@types/react":"^18"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/react_0.1.0_1779037509267_0.3281582184321077","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@agilab/react","version":"0.1.1","keywords":["ag-ui","react","agent","streaming","ai","llm"],"license":"MIT","_id":"@agilab/react@0.1.1","maintainers":[{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"}],"homepage":"https://github.com/rinkesh2010rpp/agui-react-client#readme","bugs":{"url":"https://github.com/rinkesh2010rpp/agui-react-client/issues"},"dist":{"shasum":"2ed82a5a5b97e4ff8801d01a6015dd5d35692cd6","tarball":"https://registry.npmjs.org/@agilab/react/-/react-0.1.1.tgz","fileCount":12,"integrity":"sha512-gsmiXRpoYh8kaaDyb3zMt8rgLQ0BOludk/lVmTWPJ7yWdwX1p6ofy+dkf3zu9H362lQDMOh1fKdhXZuu+M/PLw==","signatures":[{"sig":"MEYCIQC5vV06cJItjqvA10zzr1XiOAobRVJIXnH7+pE8pGnjJgIhAKKiI4gFf59tPKHH0sYBcAchAsAO1Zx15nNp5yFuDt1n","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":90494},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"0755cac72d51b98a3ae60d4b43376ac8e45f9cd7","private":false,"scripts":{"dev":"npm run dev --workspace=examples/with-chatscope","test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts","typecheck":"tsc --noEmit && npm run typecheck --workspace=examples/with-chatscope","test:watch":"vitest","prepublishOnly":"npm run build"},"_npmUser":{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"},"repository":{"url":"git+https://github.com/rinkesh2010rpp/agui-react-client.git","type":"git"},"workspaces":["examples/*"],"_npmVersion":"11.12.1","description":"Headless React library that converts AG-UI SSE events into streaming-aware React state","directories":{},"_nodeVersion":"24.15.0","dependencies":{"@ag-ui/core":"latest","@ag-ui/client":"latest"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8","vitest":"^4.1.6","typescript":"^5","@types/react":"^18"},"peerDependencies":{"react":">=18"},"_npmOperationalInternal":{"tmp":"tmp/react_0.1.1_1779038143102_0.9407825031952559","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@agilab/react","version":"0.1.2","private":false,"description":"Headless React library that converts AG-UI SSE events into streaming-aware React state","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.js","types":"./dist/index.d.ts"}},"workspaces":["examples/*"],"scripts":{"dev":"npm run dev --workspace=examples/with-chatscope","build":"tsup src/index.ts --format cjs,esm --dts","typecheck":"tsc --noEmit && npm run typecheck --workspace=examples/with-chatscope","test":"vitest run","test:watch":"vitest","prepublishOnly":"npm run build"},"peerDependencies":{"react":">=18"},"dependencies":{"@ag-ui/client":"latest","@ag-ui/core":"latest"},"devDependencies":{"@types/react":"^18","tsup":"^8","typescript":"^5","vitest":"^4.1.6"},"repository":{"type":"git","url":"git+https://github.com/rinkesh2010rpp/agui-react-client.git"},"homepage":"https://github.com/rinkesh2010rpp/agui-react-client#readme","bugs":{"url":"https://github.com/rinkesh2010rpp/agui-react-client/issues"},"keywords":["ag-ui","react","agent","streaming","ai","llm"],"license":"MIT","gitHead":"a386772f13a279eb789914e78f21c904a4a4a899","_id":"@agilab/react@0.1.2","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-uufyNR45KRVD01i+pE8kA7PHKH4i4AeMvuRwP8NG+z3xopmimGrBjdo8LVgOewEqcPdU+10A2lyohyqyofaKVA==","shasum":"22e9120caf2d3418c149c9dd3775680f24269098","tarball":"https://registry.npmjs.org/@agilab/react/-/react-0.1.2.tgz","fileCount":12,"unpackedSize":90664,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEIeABRLWDWuWMoL7BjHnYW+sgNmk7GQsPcHBM6bUfSdAiA/7kUOgvkKgJxbQ7q9nceJ8WYqE8ZGJap3496hnyt3Lw=="}]},"_npmUser":{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"},"directories":{},"maintainers":[{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react_0.1.2_1779038747962_0.20543049841370586"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-17T17:05:09.180Z","modified":"2026-05-17T17:25:48.244Z","0.1.0":"2026-05-17T17:05:09.409Z","0.1.1":"2026-05-17T17:15:43.247Z","0.1.2":"2026-05-17T17:25:48.092Z"},"bugs":{"url":"https://github.com/rinkesh2010rpp/agui-react-client/issues"},"license":"MIT","homepage":"https://github.com/rinkesh2010rpp/agui-react-client#readme","keywords":["ag-ui","react","agent","streaming","ai","llm"],"repository":{"type":"git","url":"git+https://github.com/rinkesh2010rpp/agui-react-client.git"},"description":"Headless React library that converts AG-UI SSE events into streaming-aware React state","maintainers":[{"name":"rinkesh2010rpp","email":"rinkesh2010rpp@gmail.com"}],"readme":"# @agilab/react\r\n\r\n[![npm](https://img.shields.io/npm/v/@agilab/react)](https://www.npmjs.com/package/@agilab/react)\r\n[![license](https://img.shields.io/npm/l/@agilab/react)](./LICENSE)\r\n\r\nA headless React library that converts raw [AG-UI](https://github.com/ag-ui-protocol/ag-ui) SSE events into streaming-aware React state. You bring your own UI — the library handles the protocol.\r\n\r\n## Repo structure\r\n\r\n| Path | Description |\r\n|---|---|\r\n| [`src/`](./src) | Library source — hooks, types, reducer |\r\n| [`examples/with-chatscope`](./examples/with-chatscope) | Demo chat UI using [@chatscope/chat-ui-kit-react](https://chatscope.io/) |\r\n\r\n## Run the demo locally\r\n\r\n```bash\r\nnpm install\r\nnpm run dev        # starts demo app on http://localhost:5173\r\nnpm run typecheck  # type-check library + example\r\n```\r\n\r\nOpen the app, enter your AG-UI agent's URL in the config panel, and start chatting.\r\n\r\n---\r\n\r\n## Using the library\r\n\r\n### Installation\r\n\r\n```bash\r\nnpm install @agilab/react\r\n```\r\n\r\n### Quick start\r\n\r\nThe two hooks give you everything you need. Here is the minimal pattern — replace the plain HTML with whatever component library you prefer:\r\n\r\n```tsx\r\nimport { useAgentRun, useUIState } from '@agilab/react';\r\nimport type { RunItem } from '@agilab/react';\r\n\r\nexport function AgentChat() {\r\n  const [uiState] = useUIState({});\r\n  const { agentState, sendMessage, abort } = useAgentRun({\r\n    config: { url: 'https://your-agent.example.com/run', headers: {} },\r\n    uiState,\r\n  });\r\n\r\n  const isStreaming =\r\n    agentState.status === 'streaming' || agentState.status === 'connecting';\r\n\r\n  const allRuns = [\r\n    ...agentState.runs,\r\n    ...(agentState.currentRun ? [agentState.currentRun] : []),\r\n  ];\r\n\r\n  return (\r\n    <div>\r\n      {allRuns.map(run => (\r\n        <div key={run.runId}>\r\n          {run.userInput && <p><strong>You:</strong> {run.userInput}</p>}\r\n\r\n          {run.items.map((item: RunItem) =>\r\n            item.kind === 'text'\r\n              ? <p key={item.messageId}>{item.content}{!item.isComplete && '▊'}</p>\r\n              : <pre key={item.toolCallId}>{item.toolCallName}({item.argsAccumulated})</pre>\r\n          )}\r\n        </div>\r\n      ))}\r\n\r\n      {agentState.status === 'error' && <p>{agentState.error}</p>}\r\n\r\n      <input onKeyDown={e => e.key === 'Enter' && sendMessage(e.currentTarget.value)} />\r\n      {isStreaming && <button onClick={abort}>Stop</button>}\r\n    </div>\r\n  );\r\n}\r\n```\r\n\r\n---\r\n\r\n### Hooks\r\n\r\n#### `useAgentRun(options)` → `{ agentState, sendMessage, abort }`\r\n\r\nThe main hook. Manages the SSE connection to your agent and reduces all incoming events into a single `agentState` object.\r\n\r\n```tsx\r\nimport { useAgentRun } from '@agilab/react';\r\n\r\nconst { agentState, sendMessage, abort } = useAgentRun({\r\n  config: {\r\n    url: 'https://your-agent.example.com/run',\r\n    headers: { Authorization: 'Bearer ...' },\r\n  },\r\n  uiState,    // sent to the agent as `state` on every run\r\n  handlers: {\r\n    // called when the agent emits a CUSTOM event named \"navigate\"\r\n    navigate: (data) => router.push((data as { path: string }).path),\r\n  },\r\n});\r\n```\r\n\r\n**Options**\r\n\r\n| Option | Type | Required | Description |\r\n|---|---|---|---|\r\n| `config.url` | `string` | Yes | Endpoint that accepts AG-UI runs |\r\n| `config.headers` | `Record<string, string>` | Yes | HTTP headers (e.g. auth) |\r\n| `uiState` | `TUIState` | Yes | Current UI state sent to the agent on every run |\r\n| `handlers` | `HandlerRegistry` | No | Map of custom event names to handler functions |\r\n\r\n**Returns**\r\n\r\n| Value | Type | Description |\r\n|---|---|---|\r\n| `agentState` | `AgentState` | Full reactive state — see [State reference](#state) below |\r\n| `sendMessage(text)` | `(text: string) => void` | Start a new run. Cancels any in-progress run first. |\r\n| `abort()` | `() => void` | Stop the current run immediately |\r\n\r\n#### `useUIState(initial)` → `[state, updaters]`\r\n\r\nManages the state your app sends to the agent so it can understand current UI context. Anything you want the agent to be aware of (current page, selected items, filters) goes here.\r\n\r\n```tsx\r\nimport { useUIState } from '@agilab/react';\r\n\r\nconst [uiState, ui] = useUIState({ page: 'home', selectedIds: [] });\r\n\r\nui.update({ page: 'settings' });           // partial update — merges into current state\r\nui.set({ page: 'home', selectedIds: [] }); // full replace\r\nui.reset();                                // back to initial value\r\n```\r\n\r\nPass `uiState` directly into `useAgentRun`. The agent receives it as `state` in every `RunAgentInput` payload.\r\n\r\n---\r\n\r\n### Custom event handlers\r\n\r\nWhen your agent emits a `CUSTOM` event, the library routes it to the matching handler in `handlers` rather than putting it in state. This is the escape hatch for agent-driven side effects (navigation, toasts, UI mutations) that don't fit the message model.\r\n\r\n```tsx\r\nconst { agentState, sendMessage } = useAgentRun({\r\n  config,\r\n  uiState,\r\n  handlers: {\r\n    navigate: (data) => {\r\n      // data is the raw `value` field from the CUSTOM event\r\n      router.push((data as { path: string }).path);\r\n    },\r\n    showToast: (data) => {\r\n      toast((data as { message: string }).message);\r\n    },\r\n  },\r\n});\r\n```\r\n\r\nThe handler type is `(data: unknown) => void`. Cast `data` to the shape your agent sends.\r\n\r\n---\r\n\r\n### State\r\n\r\n#### `agentState.runs` — completed turns\r\n\r\nAn array of `AgentRun` objects, one per completed agent turn. Each run holds everything that happened between `RUN_STARTED` and `RUN_FINISHED`:\r\n\r\n```typescript\r\nimport type { AgentRun, RunItem, ReasoningState } from '@agilab/react';\r\n\r\ninterface AgentRun {\r\n  runId: string;\r\n  source: 'user' | 'agent'; // 'agent' when the agent initiates a run autonomously\r\n  userInput?: string;        // the user's message (when source === 'user')\r\n  items: RunItem[];          // text messages and tool calls in arrival order\r\n  reasoning?: ReasoningState;\r\n  isStreaming: boolean;\r\n  status: 'streaming' | 'finished' | 'error';\r\n  error?: string;            // set when status === 'error'\r\n  timestamp: number;\r\n}\r\n\r\ninterface ReasoningState {\r\n  content: string;\r\n  isComplete: boolean;       // false while the thinking block is still streaming\r\n}\r\n\r\n// Each item is either a streamed text message or a tool call, in arrival order\r\ntype RunItem =\r\n  | {\r\n      kind: 'text';\r\n      messageId: string;\r\n      content: string;\r\n      isComplete: boolean;  // false while tokens are still arriving; true after TEXT_MESSAGE_END\r\n    }\r\n  | {\r\n      kind: 'tool';\r\n      toolCallId: string;\r\n      toolCallName: string;\r\n      argsAccumulated: string;  // raw JSON, grows with each TOOL_CALL_ARGS delta\r\n      argsComplete: boolean;    // true after TOOL_CALL_END\r\n      result?: string;          // set on TOOL_CALL_RESULT\r\n      status: 'streaming' | 'done' | 'has-result' | 'error';\r\n    }\r\n```\r\n\r\n#### `agentState.currentRun` — the live run\r\n\r\nSame shape as `AgentRun`, updated in real time while streaming. `undefined` when idle.\r\n\r\n```tsx\r\n// Combine completed + in-progress for rendering\r\nconst allRuns = [...agentState.runs, ...(agentState.currentRun ? [agentState.currentRun] : [])];\r\n\r\nallRuns.map(run => (\r\n  <div key={run.runId}>\r\n    {run.userInput && <Bubble direction=\"out\">{run.userInput}</Bubble>}\r\n    <Bubble direction=\"in\">\r\n      {/* reasoning block appears before items if the agent emitted extended thinking */}\r\n      {run.reasoning && <ReasoningBlock reasoning={run.reasoning} />}\r\n\r\n      {/* items render in the exact order they arrived from the stream */}\r\n      {run.items.map(item =>\r\n        item.kind === 'tool'\r\n          ? <ToolCallCard key={item.toolCallId} tc={item} />\r\n          : (\r\n            <span key={item.messageId}>\r\n              {item.content}\r\n              {/* streaming cursor — visible while this message is still being typed */}\r\n              {!item.isComplete && <span className=\"animate-pulse\">▊</span>}\r\n            </span>\r\n          )\r\n      )}\r\n    </Bubble>\r\n  </div>\r\n))\r\n```\r\n\r\n#### `agentState.status`\r\n\r\n```\r\n'idle'       — no active run\r\n'connecting' — request sent, waiting for first event\r\n'streaming'  — events arriving\r\n'finished'   — RUN_FINISHED received\r\n'error'      — stream error or RUN_ERROR event\r\n```\r\n\r\n#### `agentState.error`\r\n\r\nSet when `status === 'error'`. Contains the error message string from the agent or the network layer.\r\n\r\n#### `agentState.threadId`\r\n\r\nThe conversation session ID shared across all runs. Automatically generated on first run and kept stable throughout the component's lifetime. Pass it to your own storage layer if you want to persist and resume conversations.\r\n\r\n#### `agentState.currentStep`\r\n\r\nSet while a `STEP_STARTED` event is active (e.g. a named agent step like `\"search\"` or `\"plan\"`). Cleared on `STEP_FINISHED`. Useful for status bars.\r\n\r\n```tsx\r\n{agentState.currentStep && (\r\n  <span>Step: {agentState.currentStep.name}</span>\r\n)}\r\n```\r\n\r\n---\r\n\r\n### Tool call items\r\n\r\nTool call items in `run.items` stream in real time:\r\n\r\n```typescript\r\n// item.kind === 'tool'\r\n{\r\n  kind: 'tool';\r\n  toolCallId: string;\r\n  toolCallName: string;\r\n  argsAccumulated: string; // raw JSON, grows with each TOOL_CALL_ARGS delta\r\n  argsComplete: boolean;   // true after TOOL_CALL_END\r\n  result?: string;         // set on TOOL_CALL_RESULT\r\n  status: 'streaming' | 'done' | 'has-result' | 'error';\r\n}\r\n```\r\n\r\n```tsx\r\nfunction ToolCallCard({ tc }: { tc: RunItem & { kind: 'tool' } }) {\r\n  return (\r\n    <div>\r\n      <strong>{tc.toolCallName}</strong>\r\n      <pre>\r\n        {tc.argsComplete\r\n          ? JSON.stringify(JSON.parse(tc.argsAccumulated), null, 2)\r\n          : tc.argsAccumulated}\r\n      </pre>\r\n      {tc.result && <pre>Result: {tc.result}</pre>}\r\n    </div>\r\n  );\r\n}\r\n```\r\n\r\n---\r\n\r\n### Common patterns\r\n\r\n#### Detecting streaming / disabling input\r\n\r\n```tsx\r\nconst isStreaming =\r\n  agentState.status === 'streaming' || agentState.status === 'connecting';\r\n\r\n<button onClick={abort} disabled={!isStreaming}>Stop</button>\r\n<input disabled={isStreaming} onSubmit={sendMessage} />\r\n```\r\n\r\n#### Showing a typing indicator\r\n\r\n```tsx\r\n{isStreaming && <TypingIndicator />}\r\n```\r\n\r\n#### Handling errors\r\n\r\n```tsx\r\n{agentState.status === 'error' && (\r\n  <div className=\"error\">{agentState.error ?? 'Unknown error'}</div>\r\n)}\r\n```\r\n\r\n#### Re-sending cancels the active run\r\n\r\n`sendMessage` automatically aborts any in-progress run before starting a new one. You don't need to call `abort` first.\r\n\r\n#### Conversation history is managed automatically\r\n\r\nThe library maintains the full wire-format message history internally and sends it to the agent on every run. You don't need to build or track a messages array — just render from `agentState.runs`.\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT\r\n","readmeFilename":"README.md"}