diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/.env.example b/apps/agentstack-sdk-ts/examples/chat-ui/.env.example new file mode 100644 index 000000000..55b1a7aed --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/.env.example @@ -0,0 +1,4 @@ +VITE_AGENTSTACK_BASE_URL="http://localhost:8333" + +VITE_AGENTSTACK_PROVIDER_ID="983ab5fd-d7c9-0be1-77d9-442a68c5949e" + diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/.gitignore b/apps/agentstack-sdk-ts/examples/chat-ui/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/eslint.config.js b/apps/agentstack-sdk-ts/examples/chat-ui/eslint.config.js new file mode 100644 index 000000000..325178462 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/eslint.config.js @@ -0,0 +1,10 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import baseConfig from '@i-am-bee/lint-config/eslint'; +import { defineConfig } from 'eslint/config'; +import reactHooks from 'eslint-plugin-react-hooks'; + +export default defineConfig([...baseConfig, reactHooks.configs.flat['recommended-latest']]); diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/index.html b/apps/agentstack-sdk-ts/examples/chat-ui/index.html new file mode 100644 index 000000000..01fc80e3e --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/index.html @@ -0,0 +1,20 @@ + + + + + + + + + Agent Stack Chat Example + + + +
+ + + + diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/package.json b/apps/agentstack-sdk-ts/examples/chat-ui/package.json new file mode 100644 index 000000000..20b388b0a --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/package.json @@ -0,0 +1,32 @@ +{ + "name": "@i-am-bee/chat-ui-example", + "author": "IBM Corp.", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@a2a-js/sdk": "^0.3.10", + "agentstack-sdk": "^0.6.1", + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@i-am-bee/lint-config": "workspace:*", + "@types/node": "^22.19.10", + "@types/react": "^19.2.13", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.4", + "babel-plugin-react-compiler": "^1.0.0", + "eslint": "^9.39.2", + "eslint-plugin-react-hooks": "^7.0.1", + "prettier": "^3.8.1", + "stylelint": "^16.26.1", + "typescript": "5.9.3", + "vite": "^7.3.1" + }, + "packageManager": "pnpm@9.9.0" +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/prettier.config.js b/apps/agentstack-sdk-ts/examples/chat-ui/prettier.config.js new file mode 100644 index 000000000..d6a490d19 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/prettier.config.js @@ -0,0 +1,6 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export { default } from '@i-am-bee/lint-config/prettier'; diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/App.tsx b/apps/agentstack-sdk-ts/examples/chat-ui/src/App.tsx new file mode 100644 index 000000000..ea6c00d5f --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/App.tsx @@ -0,0 +1,117 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { type SubmitEvent, useState } from 'react'; + +import { useAgent } from './client'; +import { BASE_URL, PROVIDER_ID } from './constants'; +import type { ChatMessage } from './types'; +import { createMessage } from './utils'; + +export function App() { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [isSending, setIsSending] = useState(false); + + const { session, isInitializing, error, sendMessage } = useAgent(); + + const isError = Boolean(error); + const isSubmitDisabled = isInitializing || isSending || isError || !input.trim(); + + const handleSubmit = async (event: SubmitEvent) => { + event.preventDefault(); + + if (isSubmitDisabled) { + return; + } + + const text = input.trim(); + + setMessages((prevMessages) => [...prevMessages, createMessage({ role: 'user', text })]); + setInput(''); + setIsSending(true); + + try { + const response = await sendMessage({ text }); + + setMessages((prevMessages) => [ + ...prevMessages, + createMessage({ role: 'agent', text: response.text || 'No response from agent.' }), + ]); + } catch (error) { + const message = error instanceof Error ? error.message : 'Failed to send message'; + + setMessages((prevMessages) => [...prevMessages, createMessage({ role: 'agent', text: `Error: ${message}` })]); + } finally { + setIsSending(false); + } + }; + + return ( +
+

Agent Stack Chat Example

+ +
+

+ Base URL: {BASE_URL} +

+ +

+ Provider ID: {PROVIDER_ID} +

+ +

+ Context ID: {session?.contextId} +

+
+ +
+ +
+ + {messages.length > 0 && ( +
+ {messages.map((message) => ( +
+
{message.role}
+ +

{message.text}

+
+ ))} +
+ )} + +
+

+ Status:{' '} + {isError ? 'Error' : isInitializing ? 'Connecting to agent…' : isSending ? 'Agent is thinking…' : 'Ready'} +

+ + {isError && ( +

+ Error: {error} +

+ )} +
+ +
+ setInput(event.target.value)} + placeholder="Type a message…" + autoFocus + /> + + +
+
+ ); +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/api.ts b/apps/agentstack-sdk-ts/examples/chat-ui/src/api.ts new file mode 100644 index 000000000..876783e8c --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/api.ts @@ -0,0 +1,31 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { buildApiClient, unwrapResult } from 'agentstack-sdk'; + +import { BASE_URL, PROVIDER_ID } from './constants'; + +export const api = buildApiClient({ baseUrl: BASE_URL }); + +export async function createContext() { + const result = await api.createContext({ provider_id: PROVIDER_ID }); + + return unwrapResult(result); +} + +export async function createContextToken(contextId: string) { + const result = await api.createContextToken({ + context_id: contextId, + grant_global_permissions: { + a2a_proxy: [PROVIDER_ID], + llm: ['*'], + }, + grant_context_permissions: { + context_data: ['*'], + }, + }); + + return unwrapResult(result); +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/client.ts b/apps/agentstack-sdk-ts/examples/chat-ui/src/client.ts new file mode 100644 index 000000000..21a404c97 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/client.ts @@ -0,0 +1,142 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + ClientFactory, + ClientFactoryOptions, + DefaultAgentCardResolver, + JsonRpcTransportFactory, +} from '@a2a-js/sdk/client'; +import { type ContextToken, createAuthenticatedFetch, getAgentCardPath } from 'agentstack-sdk'; +import { useEffect, useState } from 'react'; + +import { createContext, createContextToken } from './api'; +import { BASE_URL, PROVIDER_ID } from './constants'; +import type { Session } from './types'; +import { extractTextFromMessage, resolveAgentMetadata } from './utils'; + +async function ensureSession() { + if (!BASE_URL || !PROVIDER_ID) { + throw new Error(`Missing required environment variables.`); + } + + const context = await createContext(); + const contextToken = await createContextToken(context.id); + const client = await createClient(contextToken); + const metadata = await resolveAgentMetadata({ client, contextToken }); + + return { + client, + contextId: context.id, + metadata, + }; +} + +async function createClient(contextToken: ContextToken) { + const fetchImpl = createAuthenticatedFetch(contextToken.token); + + const factory = new ClientFactory( + ClientFactoryOptions.createFrom(ClientFactoryOptions.default, { + transports: [new JsonRpcTransportFactory({ fetchImpl })], + cardResolver: new DefaultAgentCardResolver({ fetchImpl }), + }), + ); + + const agentCardPath = getAgentCardPath(PROVIDER_ID); + const client = await factory.createFromUrl(BASE_URL, agentCardPath); + + return client; +} + +export function useAgent() { + const [session, setSession] = useState(); + const [isInitializing, setIsInitializing] = useState(false); + const [error, setError] = useState(''); + + useEffect(() => { + if (session) { + return; + } + + let cancelled = false; + + (async () => { + try { + setIsInitializing(true); + + const session = await ensureSession(); + + if (cancelled) { + return; + } + + setSession(session); + } catch (error) { + if (cancelled) { + return; + } + + const message = error instanceof Error ? error.message : 'Failed to connect to agent.'; + + setError(message); + } finally { + if (!cancelled) { + setIsInitializing(false); + } + } + })(); + + return () => { + cancelled = true; + }; + }, [session]); + + const sendMessage = async ({ text }: { text: string }) => { + if (!session) { + throw new Error('Agent is not ready yet.'); + } + + const { client, contextId, metadata } = session; + + const runStream = async () => { + const stream = client.sendMessageStream({ + message: { + kind: 'message', + role: 'user', + messageId: crypto.randomUUID(), + contextId, + parts: [{ kind: 'text', text }], + metadata, + }, + }); + + let agentText = ''; + + for await (const event of stream) { + if (event.kind === 'status-update' || event.kind === 'message') { + const message = event.kind === 'message' ? event : event.status.message; + const text = extractTextFromMessage(message); + + if (text) { + agentText += text; + } + } + } + + return { + text: agentText, + }; + }; + + return await runStream(); + }; + + return { + session, + isInitializing, + error, + sendMessage, + }; +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/constants.ts b/apps/agentstack-sdk-ts/examples/chat-ui/src/constants.ts new file mode 100644 index 000000000..f0774ce33 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/constants.ts @@ -0,0 +1,8 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +export const BASE_URL = import.meta.env.VITE_AGENTSTACK_BASE_URL; + +export const PROVIDER_ID = import.meta.env.VITE_AGENTSTACK_PROVIDER_ID; diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/main.tsx b/apps/agentstack-sdk-ts/examples/chat-ui/src/main.tsx new file mode 100644 index 000000000..43d4ce48e --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/main.tsx @@ -0,0 +1,19 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import './styles.css'; + +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + +import { App } from './App'; + +const rootElement = document.getElementById('root')!; + +createRoot(rootElement).render( + + + , +); diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/styles.css b/apps/agentstack-sdk-ts/examples/chat-ui/src/styles.css new file mode 100644 index 000000000..955f394b9 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/styles.css @@ -0,0 +1,114 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +:root { + font-family: sans-serif; + color: #161616; + background: #f3f4f6; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; +} + +.app { + max-inline-size: 48rem; + margin: 0 auto; + padding: 1rem; + display: grid; + gap: 1rem; +} + +.heading { + margin: 0; + font-size: 1.5rem; +} + +.meta { + font-size: 0.875rem; + display: grid; + gap: 0.5rem; +} + +.meta p { + margin: 0; +} + +.error { + color: #da1e28; +} + +.controls { + display: flex; + gap: 0.5rem; +} + +.button { + padding: 0.5rem 0.75rem; + background: #161616; + color: #ffffff; + border: 1px solid #161616; + border-radius: 0.5rem; + font-size: 1rem; + line-height: 1.375; + cursor: pointer; +} + +.button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.messages { + display: grid; + gap: 0.5rem; +} + +.message { + border-radius: 0.5rem; + padding: 0.5rem; + border: 1px solid; +} + +.message.user { + border-color: #78a9ff; + background-color: #edf5ff; +} + +.message.agent { + border-color: #42be65; + background-color: #defbe6; +} + +.message header { + margin-block-end: 0.5rem; + font-size: 0.75rem; + text-transform: uppercase; + color: #8d8d8d; +} + +.message p { + margin: 0; + white-space: pre-wrap; +} + +.form { + display: grid; + grid-template-columns: 1fr auto; + gap: 0.5rem; +} + +.input { + inline-size: 100%; + padding: 0.5rem; + border: 1px solid #d1d1d1; + border-radius: 0.5rem; + font-size: 1rem; + line-height: 1.375; +} \ No newline at end of file diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/types.ts b/apps/agentstack-sdk-ts/examples/chat-ui/src/types.ts new file mode 100644 index 000000000..0f70c6817 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/types.ts @@ -0,0 +1,18 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type { Client } from '@a2a-js/sdk/client'; + +export interface Session { + client: Client; + contextId: string; + metadata: Record; +} + +export interface ChatMessage { + id: string; + role: 'user' | 'agent'; + text: string; +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/utils.ts b/apps/agentstack-sdk-ts/examples/chat-ui/src/utils.ts new file mode 100644 index 000000000..bb2efe9fb --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/utils.ts @@ -0,0 +1,36 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import type { Client } from '@a2a-js/sdk/client'; +import { buildLLMExtensionFulfillmentResolver, type ContextToken, handleAgentCard, type Message } from 'agentstack-sdk'; + +import { api } from './api'; +import type { ChatMessage } from './types'; + +export function createMessage({ role, text }: Pick): ChatMessage { + return { + id: crypto.randomUUID(), + role, + text, + }; +} + +export async function resolveAgentMetadata({ client, contextToken }: { client: Client; contextToken: ContextToken }) { + const agentCard = await client.getAgentCard(); + const { resolveMetadata } = handleAgentCard(agentCard); + const llmResolver = buildLLMExtensionFulfillmentResolver(api, contextToken); + const agentMetadata = await resolveMetadata({ llm: llmResolver }); + + return agentMetadata; +} + +export function extractTextFromMessage(message: Message | undefined) { + const text = message?.parts + .filter((part) => part.kind === 'text') + .map((part) => part.text) + .join('\n'); + + return text; +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/src/vite-env.d.ts b/apps/agentstack-sdk-ts/examples/chat-ui/src/vite-env.d.ts new file mode 100644 index 000000000..77e8b8bda --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/src/vite-env.d.ts @@ -0,0 +1,13 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +interface ImportMetaEnv { + readonly VITE_AGENTSTACK_BASE_URL: string; + readonly VITE_AGENTSTACK_PROVIDER_ID: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/stylelint.config.js b/apps/agentstack-sdk-ts/examples/chat-ui/stylelint.config.js new file mode 100644 index 000000000..137bc437d --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/stylelint.config.js @@ -0,0 +1,11 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @type {import('stylelint').Config} */ +const config = { + extends: ['@i-am-bee/lint-config/stylelint'], +}; + +export default config; diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.app.json b/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.app.json new file mode 100644 index 000000000..a9b5a59ca --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.json b/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.json new file mode 100644 index 000000000..1ffef600d --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.node.json b/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.node.json new file mode 100644 index 000000000..8a67f62f4 --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/apps/agentstack-sdk-ts/examples/chat-ui/vite.config.ts b/apps/agentstack-sdk-ts/examples/chat-ui/vite.config.ts new file mode 100644 index 000000000..b7fa1f18a --- /dev/null +++ b/apps/agentstack-sdk-ts/examples/chat-ui/vite.config.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2025 © BeeAI a Series of LF Projects, LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + react({ + babel: { + plugins: ['babel-plugin-react-compiler'], + }, + }), + ], +}); diff --git a/docs/development/custom-ui/architecture-guide.mdx b/docs/development/custom-ui/architecture-guide.mdx new file mode 100644 index 000000000..ef75b2479 --- /dev/null +++ b/docs/development/custom-ui/architecture-guide.mdx @@ -0,0 +1,224 @@ +--- +title: Custom UI Architecture Guide +description: Practical approach to building a custom Agent Stack UI using the chat-ui reference example +--- + +If you are building your own user interface, start with the reference implementation: + +- [chat-ui example source](https://github.com/i-am-bee/agentstack/tree/main/apps/agentstack-sdk-ts/examples/chat-ui) +- Local path in this monorepo: `apps/agentstack-sdk-ts/examples/chat-ui` + +This page explains the approach used in that example and how to extend it into a full custom UI. + +The example is intentionally minimal: it runs against one provider/agent at a time, with `VITE_AGENTSTACK_PROVIDER_ID` set manually in `.env`. +This keeps the core SDK flow easy to follow before you add provider discovery, routing, and richer UI state. + +It uses React, TypeScript, and Vite for a clean demonstration and fast local iteration. +The architecture itself is not tied to this stack. You can apply the same API, context token, and A2A streaming flow in other frontend frameworks. + +This example assumes the following runtime setup: + +- Your Agent Stack server is running and reachable. +- The server has at least one provider/agent available. +- `VITE_AGENTSTACK_PROVIDER_ID` matches an existing provider in that server. +- When running from the Vite dev server (`http://localhost:5173`), CORS on the Agent Stack server allows that origin. + +If your server cannot allow the frontend origin directly, you can route requests through a frontend proxy, but proxy configuration is intentionally out of scope for this basic example. + +If you still need the SDK setup basics, start with **[Getting Started](./getting-started)** and then return here. + +## What a custom UI needs + +Most implementations follow the same core flow: + +1. Create a platform API client +2. Create a context and context token +3. Create an authenticated A2A client +4. Resolve agent card demands into metadata +5. Send messages and stream task events +6. Add UI flows for forms, approvals, OAuth, errors, and other extension-driven interactions + +## 1. Configure environment and target provider + +The example keeps runtime configuration in environment variables and injects the target provider ID directly. + +`.env` values: + +```bash +VITE_AGENTSTACK_BASE_URL="http://localhost:8333" +VITE_AGENTSTACK_PROVIDER_ID="your-provider-id" +``` + +- `VITE_AGENTSTACK_BASE_URL`: URL of your actual Agent Stack server instance +- `VITE_AGENTSTACK_PROVIDER_ID`: provider/agent ID the example will call + +In the example code, these values are read as: + +```typescript +const BASE_URL = import.meta.env.VITE_AGENTSTACK_BASE_URL; +const PROVIDER_ID = import.meta.env.VITE_AGENTSTACK_PROVIDER_ID; +``` + +## 2. Create a context and context token + +Before sending messages, create a conversation context and a token scoped for agent access: + +```typescript +import { buildApiClient, unwrapResult } from "agentstack-sdk"; + +const api = buildApiClient({ baseUrl: BASE_URL }); + +const context = unwrapResult(await api.createContext({ provider_id: PROVIDER_ID })); + +const contextToken = unwrapResult( + await api.createContextToken({ + context_id: context.id, + grant_global_permissions: { + a2a_proxy: [PROVIDER_ID], + llm: ["*"], + }, + grant_context_permissions: { + context_data: ["*"], + }, + }), +); +``` + +If your Agent Stack server requires user authentication, initialize `buildApiClient` with an authenticated fetch (for example, `createAuthenticatedFetch(accessToken)`), as shown in **[Getting Started](./getting-started)**. + +Keep permissions minimal for your use case. See **[Permissions and Tokens](./permissions-and-tokens)** for scope details. + +## 3. Create an authenticated A2A client + +Use the context token with `createAuthenticatedFetch`, then pass it to both the transport and card resolver: + +```typescript +import { + ClientFactory, + ClientFactoryOptions, + DefaultAgentCardResolver, + JsonRpcTransportFactory, +} from "@a2a-js/sdk/client"; +import { createAuthenticatedFetch, getAgentCardPath } from "agentstack-sdk"; + +const fetchImpl = createAuthenticatedFetch(contextToken.token); + +const factory = new ClientFactory( + ClientFactoryOptions.createFrom(ClientFactoryOptions.default, { + transports: [new JsonRpcTransportFactory({ fetchImpl })], + cardResolver: new DefaultAgentCardResolver({ fetchImpl }), + }), +); + +const agentCardPath = getAgentCardPath(PROVIDER_ID); +const client = await factory.createFromUrl(BASE_URL, agentCardPath); +``` + +## 4. Resolve agent requirements once per session + +Read the agent card and resolve demand fulfillments before the first message: + +```typescript +import { buildLLMExtensionFulfillmentResolver, handleAgentCard } from "agentstack-sdk"; + +const agentCard = await client.getAgentCard(); +const { resolveMetadata } = handleAgentCard(agentCard); + +const llmResolver = buildLLMExtensionFulfillmentResolver(api, contextToken); +const metadata = await resolveMetadata({ llm: llmResolver }); +``` + +This keeps extension fulfillment logic centralized and reusable. See **[Agent Requirements](./agent-requirements)**. + +## 5. Send messages and process stream events + +The example sends a user message and reads streamed output from both `status-update` and `message` events: + +```typescript +const stream = client.sendMessageStream({ + message: { + kind: "message", + role: "user", + messageId: crypto.randomUUID(), + contextId, + parts: [{ kind: "text", text }], + metadata, + }, +}); + +let agentText = ""; + +for await (const event of stream) { + if (event.kind === "status-update" || event.kind === "message") { + const message = event.kind === "message" ? event : event.status.message; + const text = extractTextFromMessage(message); + + if (text) { + agentText += text; + } + } +} +``` + +This aggregation is intentionally minimal and text-only for readability. + +For full handling of `task`, `artifact-update`, cancellation, and failure states, use the patterns in **[A2A Client Integration](./a2a-client)** and **[Error Handling](./error-handling)**. + +## 6. Extend the basic chat loop for production + +The example intentionally keeps UI logic minimal. Production apps usually add: + +- `handleTaskStatusUpdate` to drive form, approval, OAuth, and secret prompts +- `resolveUserMetadata` to submit structured user responses +- Citation and trajectory rendering from message metadata +- Artifact rendering for files and non-text outputs +- Retry and cancellation controls for long-running tasks + +Related guides: + +- **[User Messages](./user-messages)** +- **[Agent Responses](./agent-responses)** +- **[Agent Requirements](./agent-requirements)** + +## Implementation checklist + +1. Configure `VITE_AGENTSTACK_BASE_URL` and `VITE_AGENTSTACK_PROVIDER_ID` +2. Create `context` and `contextToken` +3. Build authenticated A2A client +4. Resolve agent card demands to metadata +5. Send message stream and render updates +6. Handle structured UI interactions and errors + +## Run the reference example + +```bash +cd apps/agentstack-sdk-ts/examples/chat-ui +cp .env.example .env +pnpm install +pnpm dev +``` + +Update `.env` with a valid `VITE_AGENTSTACK_BASE_URL` and `VITE_AGENTSTACK_PROVIDER_ID` before starting. + +## Troubleshooting + +- **`Missing required environment variables.` on startup** + `VITE_AGENTSTACK_BASE_URL` or `VITE_AGENTSTACK_PROVIDER_ID` is missing. Check your `.env` file and restart `pnpm dev`. + +- **Network errors when creating context/token** + `VITE_AGENTSTACK_BASE_URL` is wrong, unreachable, or points to a different environment. Verify the server URL and that the Agent Stack API is running. + +- **CORS errors in the browser console** + The Agent Stack server must allow the frontend origin (for Vite dev, `http://localhost:5173`). Update server CORS settings or use a proxy. + +- **401/403 responses from platform API endpoints** + Your server likely requires user auth. Use `buildApiClient` with authenticated fetch (for example, `createAuthenticatedFetch(accessToken)`), as shown in **[Getting Started](./getting-started)**. + +- **Context token created, but agent run fails with permission-related errors** + The token grants may be too narrow for the provider/agent. Recheck `grant_global_permissions` and `grant_context_permissions` in Step 2. + +- **Provider not found / invalid provider ID errors** + `VITE_AGENTSTACK_PROVIDER_ID` must match an existing provider on the target server. Confirm the ID in your Agent Stack instance. + +- **UI shows little or no useful output even though requests succeed** + This example intentionally aggregates only text parts. Agents that return files, data parts, citations, forms, or artifacts need additional rendering logic (see **[Agent Responses](./agent-responses)** and **[A2A Client Integration](./a2a-client)**). diff --git a/docs/development/custom-ui/getting-started.mdx b/docs/development/custom-ui/getting-started.mdx index f0eb8aa19..86242b300 100644 --- a/docs/development/custom-ui/getting-started.mdx +++ b/docs/development/custom-ui/getting-started.mdx @@ -178,6 +178,7 @@ These exports are useful when building strongly typed UI layers or validating in ## Next Steps +- **[Custom UI Architecture Guide](./architecture-guide)** for a practical implementation approach using the `chat-ui` reference example - **[A2A Client Integration](./a2a-client)** for the full streaming, UI events, and response flow - **[Agent Requirements](./agent-requirements)** for service and UI extension handling - **[Agent Responses](./agent-responses)** for rendering message parts and citations diff --git a/docs/docs.json b/docs/docs.json index ebe79a12d..ec71654df 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -70,6 +70,7 @@ "group": "Custom UI Integration", "pages": [ "stable/custom-ui/getting-started", + "stable/custom-ui/architecture-guide", "stable/custom-ui/a2a-client", "stable/custom-ui/agent-requirements", "stable/custom-ui/platform-api-client", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a68d04a82..57b85d0a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,6 +112,58 @@ importers: specifier: ^3.1.3 version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.2)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + apps/agentstack-sdk-ts/examples/chat-ui: + dependencies: + '@a2a-js/sdk': + specifier: ^0.3.10 + version: 0.3.10(@bufbuild/protobuf@2.11.0)(express@4.21.2) + agentstack-sdk: + specifier: ^0.6.1 + version: 0.6.1(@bufbuild/protobuf@2.11.0)(express@4.21.2) + react: + specifier: ^19.2.4 + version: 19.2.4 + react-dom: + specifier: ^19.2.4 + version: 19.2.4(react@19.2.4) + devDependencies: + '@i-am-bee/lint-config': + specifier: workspace:* + version: link:../../../lint-config + '@types/node': + specifier: ^22.19.10 + version: 22.19.10 + '@types/react': + specifier: ^19.2.13 + version: 19.2.13 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.13) + '@vitejs/plugin-react': + specifier: ^5.1.4 + version: 5.1.4(vite@7.3.1(@types/node@22.19.10)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + babel-plugin-react-compiler: + specifier: ^1.0.0 + version: 1.0.0 + eslint: + specifier: ^9.39.2 + version: 9.39.2(jiti@1.21.7) + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2(jiti@1.21.7)) + prettier: + specifier: ^3.8.1 + version: 3.8.1 + stylelint: + specifier: ^16.26.1 + version: 16.26.1(typescript@5.9.3) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(@types/node@22.19.10)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + apps/agentstack-ui: dependencies: '@a2a-js/sdk': @@ -119,7 +171,7 @@ importers: version: 0.3.10(@bufbuild/protobuf@2.11.0)(express@4.21.2) '@bprogress/next': specifier: ^3.2.12 - version: 3.2.12(next@15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 3.2.12(next@15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@carbon/icons-react': specifier: 'catalog:' version: 11.74.0(react@19.2.4) @@ -194,10 +246,10 @@ importers: version: 11.12.2 next: specifier: 'catalog:' - version: 15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) + version: 15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) next-auth: specifier: 5.0.0-beta.30 - version: 5.0.0-beta.30(next@15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react@19.2.4) + version: 5.0.0-beta.30(next@15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react@19.2.4) openid-client: specifier: ^6.8.2 version: 6.8.2 @@ -369,7 +421,7 @@ importers: version: 2.4.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) next: specifier: 'catalog:' - version: 15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) + version: 15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) react: specifier: 'catalog:' version: 19.2.4 @@ -3467,6 +3519,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rolldown/pluginutils@1.0.0-rc.3': + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -4343,6 +4398,12 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitejs/plugin-react@5.1.4': + resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -4411,6 +4472,9 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agentstack-sdk@0.6.1: + resolution: {integrity: sha512-f+TawO+0vZuuLSyhvhl5O3uSPOmGQ8oB+2EGRCcU9fvmiXAkKjr11TV7ouR7SXDvEsnSItZPeL4ETKaDm9IsAQ==} + aggregate-error@4.0.1: resolution: {integrity: sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==} engines: {node: '>=12'} @@ -4639,6 +4703,9 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -5691,6 +5758,12 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + eslint-plugin-react@7.37.5: resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} @@ -6240,6 +6313,12 @@ packages: hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + hex-rgb@5.0.0: resolution: {integrity: sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w==} engines: {node: '>=12'} @@ -7813,6 +7892,10 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -9372,6 +9455,12 @@ packages: peerDependencies: zod: ^3.20.0 + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} @@ -10207,11 +10296,11 @@ snapshots: '@bprogress/core@1.3.4': {} - '@bprogress/next@3.2.12(next@15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@bprogress/next@3.2.12(next@15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@bprogress/core': 1.3.4 '@bprogress/react': 1.2.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - next: 15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) + next: 15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) @@ -13048,6 +13137,8 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.0-rc.3': {} + '@rollup/pluginutils@5.3.0(rollup@4.57.1)': dependencies: '@types/estree': 1.0.8 @@ -13587,7 +13678,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/chai@5.2.3': dependencies: @@ -13596,13 +13687,13 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/cookie@0.4.1': {} '@types/cors@2.8.19': dependencies: - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/d3-array@3.2.2': {} @@ -13731,7 +13822,7 @@ snapshots: '@types/es-aggregate-error@1.0.6': dependencies: - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/estree-jsx@1.0.5': dependencies: @@ -13741,7 +13832,7 @@ snapshots: '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -13835,16 +13926,16 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/send@1.2.1': dependencies: - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.2.2 + '@types/node': 22.19.10 '@types/send': 0.17.6 '@types/trusted-types@2.0.7': @@ -13860,7 +13951,7 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 25.2.2 + '@types/node': 22.19.10 optional: true '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': @@ -14034,6 +14125,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@22.19.10)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-rc.3 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.3.1(@types/node@22.19.10)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -14107,6 +14210,15 @@ snapshots: agent-base@7.1.4: {} + agentstack-sdk@0.6.1(@bufbuild/protobuf@2.11.0)(express@4.21.2): + dependencies: + '@a2a-js/sdk': 0.3.10(@bufbuild/protobuf@2.11.0)(express@4.21.2) + zod: 4.3.6 + transitivePeerDependencies: + - '@bufbuild/protobuf' + - '@grpc/grpc-js' + - express + aggregate-error@4.0.1: dependencies: clean-stack: 4.2.0 @@ -14352,6 +14464,10 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.0 + bail@2.0.2: {} balanced-match@1.0.2: {} @@ -15232,7 +15348,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.19 - '@types/node': 25.2.2 + '@types/node': 22.19.10 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.4.2 @@ -15612,6 +15728,17 @@ snapshots: dependencies: eslint: 9.39.2(jiti@1.21.7) + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + eslint: 9.39.2(jiti@1.21.7) + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + eslint-plugin-react@7.37.5(eslint@9.39.2(jiti@1.21.7)): dependencies: array-includes: 3.1.9 @@ -16443,6 +16570,12 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + hex-rgb@5.0.0: {} highlight.js@10.7.3: {} @@ -17679,10 +17812,10 @@ snapshots: netmask@2.0.2: {} - next-auth@5.0.0-beta.30(next@15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react@19.2.4): + next-auth@5.0.0-beta.30(next@15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3))(react@19.2.4): dependencies: '@auth/core': 0.41.0 - next: 15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) + next: 15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3) react: 19.2.4 next-mdx-remote-client@1.1.4(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.3))(react@19.2.3)(unified@11.0.5): @@ -17701,7 +17834,7 @@ snapshots: - supports-color - unified - next@15.5.12(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3): + next@15.5.12(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.97.3): dependencies: '@next/env': 15.5.12 '@swc/helpers': 0.5.15 @@ -17719,6 +17852,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.5.12 '@next/swc-win32-arm64-msvc': 15.5.12 '@next/swc-win32-x64-msvc': 15.5.12 + babel-plugin-react-compiler: 1.0.0 sass: 1.97.3 sharp: 0.34.5 transitivePeerDependencies: @@ -18361,6 +18495,8 @@ snapshots: react-refresh@0.17.0: {} + react-refresh@0.18.0: {} + react-remove-scroll-bar@2.3.8(@types/react@19.2.13)(react@19.2.3): dependencies: react: 19.2.3 @@ -20155,6 +20291,24 @@ snapshots: sass-embedded: 1.97.3 terser: 5.46.0 + vite@7.3.1(@types/node@22.19.10)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.27.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.10 + fsevents: 2.3.3 + jiti: 1.21.7 + sass: 1.97.3 + sass-embedded: 1.97.3 + terser: 5.46.0 + tsx: 4.21.0 + yaml: 2.8.2 + vite@7.3.1(@types/node@25.2.2)(jiti@1.21.7)(sass-embedded@1.97.3)(sass@1.97.3)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: esbuild: 0.27.2 @@ -20424,6 +20578,10 @@ snapshots: dependencies: zod: 3.24.0 + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + zod@3.21.4: {} zod@3.23.8: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a68c17199..df9b7316f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,6 +3,7 @@ packages: - apps/beeai-web - apps/lint-config - apps/agentstack-sdk-ts + - apps/agentstack-sdk-ts/examples/chat-ui - apps/keycloak-theme - docs