diff --git a/.gitignore b/.gitignore
index ae16bb43e..2d5204ff6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,7 @@ node_modules
.vscode
**/mastra.db*
+
+.pnpm-store
+
+**/.poetry-cache
diff --git a/apps/dojo/package.json b/apps/dojo/package.json
index 74805947b..8a8cd3d68 100644
--- a/apps/dojo/package.json
+++ b/apps/dojo/package.json
@@ -12,6 +12,8 @@
"run-everything": "./scripts/prep-dojo-everything.js && ./scripts/run-dojo-everything.js"
},
"dependencies": {
+ "@a2a-js/sdk": "0.2.5",
+ "@ag-ui/a2a": "workspace:*",
"@ag-ui/a2a-middleware": "workspace:*",
"@ag-ui/adk": "workspace:*",
"@ag-ui/agno": "workspace:*",
@@ -20,10 +22,10 @@
"@ag-ui/llamaindex": "workspace:*",
"@ag-ui/mastra": "workspace:*",
"@ag-ui/middleware-starter": "workspace:*",
- "@ag-ui/spring-ai": "workspace:*",
"@ag-ui/pydantic-ai": "workspace:*",
"@ag-ui/server-starter": "workspace:*",
"@ag-ui/server-starter-all-features": "workspace:*",
+ "@ag-ui/spring-ai": "workspace:*",
"@ag-ui/vercel-ai-sdk": "workspace:*",
"@ai-sdk/openai": "^2.0.42",
"@copilotkit/react-core": "1.10.6",
@@ -31,6 +33,9 @@
"@copilotkit/runtime": "1.10.6",
"@copilotkit/runtime-client-gql": "1.10.6",
"@copilotkit/shared": "1.10.6",
+ "@copilotkitnext/react": "0.0.19-alpha.0",
+ "@copilotkitnext/runtime": "0.0.19-alpha.0",
+ "@copilotkitnext/agent": "0.0.19-alpha.0",
"@mastra/client-js": "^0.15.2",
"@mastra/core": "^0.20.2",
"@mastra/dynamodb": "^0.15.6",
@@ -58,6 +63,7 @@
"diff": "^7.0.0",
"embla-carousel-react": "^8.6.0",
"fast-json-patch": "^3.1.1",
+ "hono": "^4.10.3",
"lucide-react": "^0.477.0",
"markdown-it": "^14.1.0",
"markdown-it-ins": "^4.0.0",
diff --git a/apps/dojo/src/agents.ts b/apps/dojo/src/agents.ts
index 18d7e90e8..02db44e28 100644
--- a/apps/dojo/src/agents.ts
+++ b/apps/dojo/src/agents.ts
@@ -16,9 +16,11 @@ import getEnvVars from "./env";
import { mastra } from "./mastra";
import { PydanticAIAgent } from "@ag-ui/pydantic-ai";
import { ADKAgent } from "@ag-ui/adk";
-import { SpringAiAgent } from '@ag-ui/spring-ai';
+import { SpringAiAgent } from "@ag-ui/spring-ai";
import { HttpAgent } from "@ag-ui/client";
import { A2AMiddlewareAgent } from "@ag-ui/a2a-middleware";
+import { A2AAgent } from "@ag-ui/a2a";
+import { A2AClient } from "@a2a-js/sdk/client";
const envVars = getEnvVars();
export const agentsIntegrations: AgentIntegrationConfig[] = [
@@ -81,7 +83,9 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
backend_tool_rendering: new ADKAgent({
url: `${envVars.adkMiddlewareUrl}/backend_tool_rendering`,
}),
- shared_state: new ADKAgent({ url: `${envVars.adkMiddlewareUrl}/adk-shared-state-agent` }),
+ shared_state: new ADKAgent({
+ url: `${envVars.adkMiddlewareUrl}/adk-shared-state-agent`,
+ }),
// predictive_state_updates: new ADKAgent({ url: `${envVars.adkMiddlewareUrl}/adk-predictive-state-agent` }),
};
},
@@ -273,26 +277,26 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
},
},
{
- id: 'spring-ai',
+ id: "spring-ai",
agents: async () => {
return {
agentic_chat: new SpringAiAgent({
- url: `${envVars.springAiUrl}/agentic_chat/agui`
+ url: `${envVars.springAiUrl}/agentic_chat/agui`,
}),
shared_state: new SpringAiAgent({
- url: `${envVars.springAiUrl}/shared_state/agui`
+ url: `${envVars.springAiUrl}/shared_state/agui`,
}),
tool_based_generative_ui: new SpringAiAgent({
- url: `${envVars.springAiUrl}/tool_based_generative_ui/agui`
+ url: `${envVars.springAiUrl}/tool_based_generative_ui/agui`,
}),
human_in_the_loop: new SpringAiAgent({
- url: `${envVars.springAiUrl}/human_in_the_loop/agui`
+ url: `${envVars.springAiUrl}/human_in_the_loop/agui`,
}),
agentic_generative_ui: new SpringAiAgent({
- url: `${envVars.springAiUrl}/agentic_generative_ui/agui`
- })
- }
- }
+ url: `${envVars.springAiUrl}/agentic_generative_ui/agui`,
+ }),
+ };
+ },
},
{
id: "llama-index",
@@ -341,6 +345,19 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
};
},
},
+ {
+ id: "a2a-basic",
+ agents: async () => {
+ const a2aClient = new A2AClient(envVars.a2aUrl);
+ return {
+ agentic_chat: new A2AAgent({
+ description: "Direct A2A agent",
+ a2aClient,
+ debug: process.env.NODE_ENV !== "production",
+ }),
+ };
+ },
+ },
{
id: "a2a",
agents: async () => {
diff --git a/apps/dojo/src/app/[integrationId]/feature/vnext_chat/page.tsx b/apps/dojo/src/app/[integrationId]/feature/vnext_chat/page.tsx
new file mode 100644
index 000000000..b81ab7ecb
--- /dev/null
+++ b/apps/dojo/src/app/[integrationId]/feature/vnext_chat/page.tsx
@@ -0,0 +1,39 @@
+"use client";
+
+import React from "react";
+import "@copilotkitnext/react/styles.css";
+import { CopilotChat, CopilotKitProvider } from "@copilotkitnext/react";
+
+export const dynamic = "force-dynamic";
+
+interface PageProps {
+ params: Promise<{
+ integrationId: string;
+ }>;
+}
+
+export default function Page({ params }: PageProps) {
+ const { integrationId } = React.use(params);
+
+ return (
+
+
+
+
+
+ );
+}
+
+function Chat({ threadId }: { threadId: string }) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/dojo/src/app/api/copilotkit/[integrationId]/route.ts b/apps/dojo/src/app/api/copilotkit/[integrationId]/route.ts
index 5a0641441..a1c04b856 100644
--- a/apps/dojo/src/app/api/copilotkit/[integrationId]/route.ts
+++ b/apps/dojo/src/app/api/copilotkit/[integrationId]/route.ts
@@ -3,10 +3,10 @@ import {
ExperimentalEmptyAdapter,
copilotRuntimeNextJSAppRouterEndpoint,
} from "@copilotkit/runtime";
-import { agentsIntegrations } from "@/agents";
-
import { NextRequest } from "next/server";
+import { agentsIntegrations } from "@/agents";
+
export async function POST(request: NextRequest) {
const integrationId = request.url.split("/").pop();
@@ -14,6 +14,7 @@ export async function POST(request: NextRequest) {
if (!integration) {
return new Response("Integration not found", { status: 404 });
}
+
const agents = await integration.agents();
const runtime = new CopilotRuntime({
// @ts-ignore for now
@@ -27,3 +28,4 @@ export async function POST(request: NextRequest) {
return handleRequest(request);
}
+
diff --git a/apps/dojo/src/app/api/copilotkit/route.ts b/apps/dojo/src/app/api/copilotkit/route.ts
new file mode 100644
index 000000000..80f03f2c1
--- /dev/null
+++ b/apps/dojo/src/app/api/copilotkit/route.ts
@@ -0,0 +1,22 @@
+import {
+ CopilotRuntime,
+ InMemoryAgentRunner,
+ createCopilotEndpoint,
+} from "@copilotkitnext/runtime";
+import { handle } from "hono/vercel";
+
+const runtime = new CopilotRuntime({
+ agents: {
+ default: null as any,
+ },
+ runner: new InMemoryAgentRunner(),
+});
+
+const app = createCopilotEndpoint({
+ runtime,
+ basePath: "/api/copilotkit",
+});
+
+export const GET = handle(app);
+export const POST = handle(app);
+
diff --git a/apps/dojo/src/app/api/copilotkitnext/[integrationId]/[[...slug]]/route.ts b/apps/dojo/src/app/api/copilotkitnext/[integrationId]/[[...slug]]/route.ts
new file mode 100644
index 000000000..f1b05e9d3
--- /dev/null
+++ b/apps/dojo/src/app/api/copilotkitnext/[integrationId]/[[...slug]]/route.ts
@@ -0,0 +1,54 @@
+import {
+ CopilotRuntime,
+ InMemoryAgentRunner,
+ createCopilotEndpoint,
+} from "@copilotkitnext/runtime";
+import { handle } from "hono/vercel";
+import type { NextRequest } from "next/server";
+import { BasicAgent } from "@copilotkitnext/agent";
+
+type RouteParams = {
+ params: Promise<{
+ integrationId: string;
+ slug?: string[];
+ }>;
+};
+
+const handlerCache = new Map>();
+
+function getHandler(integrationId: string) {
+ const cached = handlerCache.get(integrationId);
+ if (cached) {
+ return cached;
+ }
+
+ const runtime = new CopilotRuntime({
+ agents: {
+ default: new BasicAgent({
+ model: "openai/gpt-4o",
+ }),
+ },
+ runner: new InMemoryAgentRunner(),
+ });
+
+ const app = createCopilotEndpoint({
+ runtime,
+ basePath: `/api/copilotkitnext/${integrationId}`,
+ });
+
+ const handler = handle(app);
+ handlerCache.set(integrationId, handler);
+ return handler;
+}
+
+export async function GET(request: NextRequest, context: RouteParams) {
+ const { integrationId } = await context.params;
+ const handler = getHandler(integrationId);
+ return handler(request);
+}
+
+export async function POST(request: NextRequest, context: RouteParams) {
+ const { integrationId } = await context.params;
+ const handler = getHandler(integrationId);
+ return handler(request);
+}
diff --git a/apps/dojo/src/config.ts b/apps/dojo/src/config.ts
index e27f3a4b8..df7e0f25e 100644
--- a/apps/dojo/src/config.ts
+++ b/apps/dojo/src/config.ts
@@ -32,13 +32,15 @@ export const featureConfig: FeatureConfig[] = [
createFeatureConfig({
id: "human_in_the_loop",
name: "Human in the loop",
- description: "Plan a task together and direct the Copilot to take the right steps",
+ description:
+ "Plan a task together and direct the Copilot to take the right steps",
tags: ["HITL", "Interactivity"],
}),
createFeatureConfig({
id: "agentic_generative_ui",
name: "Agentic Generative UI",
- description: "Assign a long running task to your Copilot and see how it performs!",
+ description:
+ "Assign a long running task to your Copilot and see how it performs!",
tags: ["Generative ui (agent)", "Long running task"],
}),
createFeatureConfig({
@@ -56,7 +58,8 @@ export const featureConfig: FeatureConfig[] = [
createFeatureConfig({
id: "predictive_state_updates",
name: "Predictive State Updates",
- description: "Use collaboration to edit a document in real time with your Copilot",
+ description:
+ "Use collaboration to edit a document in real time with your Copilot",
tags: ["State", "Streaming", "Tools"],
}),
createFeatureConfig({
@@ -68,7 +71,8 @@ export const featureConfig: FeatureConfig[] = [
createFeatureConfig({
id: "subgraphs",
name: "Subgraphs",
- description: "Have your tasks performed by multiple agents, working together",
+ description:
+ "Have your tasks performed by multiple agents, working together",
tags: ["Chat", "Multi-agent architecture", "Streaming", "Subgraphs"],
}),
createFeatureConfig({
@@ -77,6 +81,12 @@ export const featureConfig: FeatureConfig[] = [
description: "Chat with your Copilot and call frontend tools",
tags: ["Chat", "Tools", "Streaming"],
}),
+ createFeatureConfig({
+ id: "vnext_chat",
+ name: "VNext Chat",
+ description: "Chat based on CopilotKit vnext",
+ tags: ["Chat", "VNext", "Streaming"],
+ }),
];
export default featureConfig;
diff --git a/apps/dojo/src/env.ts b/apps/dojo/src/env.ts
index c63344897..569c6cfdc 100644
--- a/apps/dojo/src/env.ts
+++ b/apps/dojo/src/env.ts
@@ -11,6 +11,7 @@ type envVars = {
crewAiUrl: string;
pydanticAIUrl: string;
adkMiddlewareUrl: string;
+ a2aUrl: string;
a2aMiddlewareBuildingsManagementUrl: string;
a2aMiddlewareFinanceUrl: string;
a2aMiddlewareItUrl: string;
@@ -40,10 +41,11 @@ export default function getEnvVars(): envVars {
pydanticAIUrl: process.env.PYDANTIC_AI_URL || 'http://localhost:9000',
adkMiddlewareUrl: process.env.ADK_MIDDLEWARE_URL || 'http://localhost:8000',
springAiUrl: process.env.SPRING_AI_URL || 'http://localhost:8080',
+ a2aUrl: process.env.A2A_URL || 'http://localhost:10002',
a2aMiddlewareBuildingsManagementUrl: process.env.A2A_MIDDLEWARE_BUILDINGS_MANAGEMENT_URL || 'http://localhost:9001',
a2aMiddlewareFinanceUrl: process.env.A2A_MIDDLEWARE_FINANCE_URL || 'http://localhost:9002',
a2aMiddlewareItUrl: process.env.A2A_MIDDLEWARE_IT_URL || 'http://localhost:9003',
a2aMiddlewareOrchestratorUrl: process.env.A2A_MIDDLEWARE_ORCHESTRATOR_URL || 'http://localhost:9000',
customDomainTitle: customDomainTitle,
}
-}
\ No newline at end of file
+}
diff --git a/apps/dojo/src/menu.ts b/apps/dojo/src/menu.ts
index 25d02f283..36959a218 100644
--- a/apps/dojo/src/menu.ts
+++ b/apps/dojo/src/menu.ts
@@ -47,7 +47,11 @@ export const menuIntegrations: MenuIntegrationConfig[] = [
{
id: "mastra",
name: "Mastra",
- features: ["agentic_chat", "backend_tool_rendering", "tool_based_generative_ui"],
+ features: [
+ "agentic_chat",
+ "backend_tool_rendering",
+ "tool_based_generative_ui",
+ ],
},
{
id: "mastra-agent-local",
@@ -60,15 +64,15 @@ export const menuIntegrations: MenuIntegrationConfig[] = [
],
},
{
- id: 'spring-ai',
- name: 'Spring AI',
+ id: "spring-ai",
+ name: "Spring AI",
features: [
- 'agentic_chat',
- 'shared_state',
- 'tool_based_generative_ui',
- 'human_in_the_loop',
- 'agentic_generative_ui'
- ]
+ "agentic_chat",
+ "shared_state",
+ "tool_based_generative_ui",
+ "human_in_the_loop",
+ "agentic_generative_ui",
+ ],
},
{
id: "pydantic-ai",
@@ -99,7 +103,11 @@ export const menuIntegrations: MenuIntegrationConfig[] = [
{
id: "agno",
name: "Agno",
- features: ["agentic_chat", "backend_tool_rendering", "tool_based_generative_ui"],
+ features: [
+ "agentic_chat",
+ "backend_tool_rendering",
+ "tool_based_generative_ui",
+ ],
},
{
id: "llama-index",
@@ -125,6 +133,11 @@ export const menuIntegrations: MenuIntegrationConfig[] = [
"tool_based_generative_ui",
],
},
+ {
+ id: "a2a-basic",
+ name: "A2A (Direct)",
+ features: ["vnext_chat"],
+ },
// Disabled until we can support Vercel AI SDK v5
// {
// id: "vercel-ai-sdk",
diff --git a/apps/dojo/src/types/integration.ts b/apps/dojo/src/types/integration.ts
index 59d9b3adb..182933a6f 100644
--- a/apps/dojo/src/types/integration.ts
+++ b/apps/dojo/src/types/integration.ts
@@ -10,7 +10,8 @@ export type Feature =
| "backend_tool_rendering"
| "agentic_chat_reasoning"
| "subgraphs"
- | "a2a_chat";
+ | "a2a_chat"
+ | "vnext_chat";
export interface MenuIntegrationConfig {
id: string;
diff --git a/apps/dojo/tsconfig.json b/apps/dojo/tsconfig.json
index af157911a..a57e78c43 100644
--- a/apps/dojo/tsconfig.json
+++ b/apps/dojo/tsconfig.json
@@ -19,9 +19,9 @@
}
],
"paths": {
- "@/*": ["./src/*", "../../packages/client/src/*"],
- "@ag-ui/client": ["../../packages/client/src"],
- "@ag-ui/client/*": ["../../packages/client/src/*"]
+ "@/*": ["./src/*", "../../sdks/typescript/packages/client/src/*"],
+ "@ag-ui/client": ["../../sdks/typescript/packages/client/src"],
+ "@ag-ui/client/*": ["../../sdks/typescript/packages/client/src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
diff --git a/docs/concepts/events.mdx b/docs/concepts/events.mdx
index 920b203b9..6ba6c9cc4 100644
--- a/docs/concepts/events.mdx
+++ b/docs/concepts/events.mdx
@@ -19,6 +19,7 @@ Events in the protocol are categorized by their purpose:
| Text Message Events | Handle streaming textual content |
| Tool Call Events | Manage tool executions by agents |
| State Management Events | Synchronize state between agents and UI |
+| Activity Events | Represent ongoing activity progress |
| Special Events | Support custom functionality |
| Draft Events | Proposed events under development |
@@ -83,10 +84,12 @@ elements such as progress indicators or loading states. It also provides crucial
identifiers that can be used to associate subsequent events with this specific
run.
-| Property | Description |
-| ---------- | ----------------------------- |
-| `threadId` | ID of the conversation thread |
-| `runId` | ID of the agent run |
+| Property | Description |
+| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `threadId` | ID of the conversation thread |
+| `runId` | ID of the agent run |
+| `parentRunId` | (Optional) Lineage pointer for branching/time travel. If present, refers to a prior run within the same thread, creating a git-like append-only log |
+| `input` | (Optional) The exact agent input payload that was sent to the agent for this run. May omit messages already present in history; compactEvents() will normalize |
### RunFinished
@@ -445,6 +448,40 @@ displayed to users.
| ---------- | ------------------------ |
| `messages` | Array of message objects |
+## Activity Events
+
+Activity Events expose structured, in-progress activity updates that occur
+between chat messages. They follow the same snapshot/delta pattern as the state
+system so that UIs can render a complete activity view immediately and then
+incrementally update it as new information arrives.
+
+### ActivitySnapshot
+
+Delivers a complete snapshot of an activity message.
+
+| Property | Description |
+| --------------- | ---------------------------------------------------------------- |
+| `messageId` | Identifier for the `ActivityMessage` this event updates |
+| `activityType` | Activity discriminator (for example `"PLAN"`, `"SEARCH"`) |
+| `content` | Structured JSON payload representing the full activity state |
+
+Frontends should either create a new `ActivityMessage` or replace the existing
+one with the payload supplied by the snapshot.
+
+### ActivityDelta
+
+Applies incremental updates to an existing activity using JSON Patch operations.
+
+| Property | Description |
+| --------------- | -------------------------------------------------------------------- |
+| `messageId` | Identifier for the target activity message |
+| `activityType` | Activity discriminator (mirrors the value from the most recent snapshot) |
+| `patch` | Array of RFC 6902 JSON Patch operations to apply to the activity data |
+
+Activity deltas should be applied in order to the previously synchronized
+activity content. If an application detects divergence, it can request or emit a
+fresh `ActivitySnapshot` to resynchronize.
+
## Special Events
Special events provide flexibility in the protocol by allowing for
@@ -504,9 +541,10 @@ Provides the complete activity state at a point in time.
| Property | Description |
| -------------- | ---------------------------------------------------- |
-| `messageId` | Unique identifier for the ActivityMessage |
-| `activityType` | Activity type (e.g., "PLAN", "SEARCH", "SCRAPE") |
-| `content` | Complete activity state at this point |
+| `messageId` | Unique identifier for the ActivityMessage |
+| `activityType` | Activity type (e.g., "PLAN", "SEARCH", "SCRAPE") |
+| `content` | Complete activity state at this point |
+| `replace` | `true` (default) replaces an existing activity message; `false` only creates a new one if none exists |
#### ActivityDeltaEvent
diff --git a/docs/concepts/messages.mdx b/docs/concepts/messages.mdx
index 4777cbfe1..c37011d1b 100644
--- a/docs/concepts/messages.mdx
+++ b/docs/concepts/messages.mdx
@@ -28,6 +28,10 @@ interface BaseMessage {
}
```
+The `role` discriminator can be `"user"`, `"assistant"`, `"system"`,
+`"tool"`, `"developer"`, or `"activity"`. Concrete message types extend this
+shape with the fields they need.
+
## Message Types
AG-UI supports several message types to accommodate different participants in a
@@ -41,11 +45,31 @@ Messages from the end user to the agent:
interface UserMessage {
id: string
role: "user"
- content: string // Text input from the user
+ content: string | InputContent[] // Text or multimodal input from the user
name?: string // Optional user identifier
}
+
+type InputContent = TextInputContent | BinaryInputContent
+
+interface TextInputContent {
+ type: "text"
+ text: string
+}
+
+interface BinaryInputContent {
+ type: "binary"
+ mimeType: string
+ id?: string
+ url?: string
+ data?: string
+ filename?: string
+}
```
+> For `BinaryInputContent`, provide at least one of `id`, `url`, or `data` to reference the payload.
+
+This structure keeps traditional plain-text inputs working while enabling richer payloads such as images, audio clips, or uploaded files in the same message.
+
### Assistant Messages
Messages from the AI assistant to the user:
@@ -86,6 +110,24 @@ interface ToolMessage {
}
```
+### Activity Messages
+
+Structured progress updates that appear between chat messages:
+
+```typescript
+interface ActivityMessage {
+ id: string
+ role: "activity"
+ activityType: string // e.g. "PLAN", "SEARCH", "SCRAPE"
+ content: Record // Structured payload rendered by the frontend
+}
+```
+
+Activity messages are populated by `ACTIVITY_SNAPSHOT` and `ACTIVITY_DELTA`
+events. The structured `content` object gives frontends everything they need to
+render bespoke status views, such as checklists, workflow progress, or search
+results in flight.
+
### Developer Messages
Internal messages used for development or debugging:
diff --git a/docs/drafts/multimodal-messages.mdx b/docs/drafts/multimodal-messages.mdx
index 969032fc9..6b375ff89 100644
--- a/docs/drafts/multimodal-messages.mdx
+++ b/docs/drafts/multimodal-messages.mdx
@@ -21,7 +21,7 @@ apps. Inputs may include text, images, audio, and files.
## Status
-- **Status**: Draft
+- **Status**: Implemented — October 16, 2025
- **Author(s)**: Markus Ecker (mail@mme.xyz)
## Detailed Specification
diff --git a/docs/sdk/js/client/abstract-agent.mdx b/docs/sdk/js/client/abstract-agent.mdx
index 3738b0aae..cf322d214 100644
--- a/docs/sdk/js/client/abstract-agent.mdx
+++ b/docs/sdk/js/client/abstract-agent.mdx
@@ -111,6 +111,33 @@ Creates a deep copy of the agent instance.
clone(): AbstractAgent
```
+### connectAgent()
+
+Establishes a persistent connection with an agent that implements the
+`connect()` method.
+
+```typescript
+connectAgent(parameters?: RunAgentParameters, subscriber?: AgentSubscriber): Promise
+```
+
+Similar to `runAgent()` but uses the `connect()` method internally. The agent
+must implement `connect()` or this functionality must be provided by a framework
+like [CopilotKit](https://copilotkit.ai).
+
+## Observable Properties
+
+### events$
+
+An observable stream of all events emitted during agent execution.
+
+```typescript
+events$: Observable
+```
+
+This property provides direct access to the agent's event stream. Events are
+stored using a `ReplaySubject`, allowing late subscribers will receive all
+historical events.
+
## Properties
- `agentId`: Unique identifier for the agent instance
@@ -118,6 +145,8 @@ clone(): AbstractAgent
- `threadId`: Conversation thread identifier
- `messages`: Array of conversation messages
- `state`: Current agent state object
+- `events$`: Observable stream of all `BaseEvent` objects emitted during agent
+ execution (replayed for late subscribers)
## Protected Methods
@@ -131,6 +160,17 @@ Executes the agent and returns an observable event stream.
protected abstract run(input: RunAgentInput): RunAgent
```
+### connect()
+
+Establishes a persistent connection and returns an observable event stream.
+
+```typescript
+protected connect(input: RunAgentInput): RunAgent
+```
+
+Override this method to implement persistent connections. Default implementation
+throws `ConnectNotImplementedError`.
+
### apply()
Processes events from the run and updates the agent state.
diff --git a/docs/sdk/js/client/subscriber.mdx b/docs/sdk/js/client/subscriber.mdx
index ee010488f..f97ab1389 100644
--- a/docs/sdk/js/client/subscriber.mdx
+++ b/docs/sdk/js/client/subscriber.mdx
@@ -271,6 +271,32 @@ Called when a complete message history snapshot is provided.
onMessagesSnapshotEvent?(params: { event: MessagesSnapshotEvent } & AgentSubscriberParams): MaybePromise
```
+#### onActivitySnapshotEvent()
+
+Called when an activity snapshot is received. The handler receives both the raw
+event and any existing `ActivityMessage` (if present) so you can inspect or
+replace it before the default client logic runs.
+
+```typescript
+onActivitySnapshotEvent?(params: {
+ event: ActivitySnapshotEvent
+ activityMessage?: ActivityMessage
+ existingMessage?: Message
+} & AgentSubscriberParams): MaybePromise
+```
+
+#### onActivityDeltaEvent()
+
+Triggered for each activity delta. Use this hook to transform or debounce the
+incoming JSON Patch operations before they update the conversation transcript.
+
+```typescript
+onActivityDeltaEvent?(params: {
+ event: ActivityDeltaEvent
+ activityMessage?: ActivityMessage
+} & AgentSubscriberParams): MaybePromise
+```
+
#### onRawEvent()
Handler for raw, unprocessed events.
diff --git a/docs/sdk/js/core/events.mdx b/docs/sdk/js/core/events.mdx
index ea119090c..df4d8646e 100644
--- a/docs/sdk/js/core/events.mdx
+++ b/docs/sdk/js/core/events.mdx
@@ -26,6 +26,8 @@ enum EventType {
STATE_SNAPSHOT = "STATE_SNAPSHOT",
STATE_DELTA = "STATE_DELTA",
MESSAGES_SNAPSHOT = "MESSAGES_SNAPSHOT",
+ ACTIVITY_SNAPSHOT = "ACTIVITY_SNAPSHOT",
+ ACTIVITY_DELTA = "ACTIVITY_DELTA",
RAW = "RAW",
CUSTOM = "CUSTOM",
RUN_STARTED = "RUN_STARTED",
@@ -68,13 +70,17 @@ type RunStartedEvent = BaseEvent & {
type: EventType.RUN_STARTED
threadId: string
runId: string
+ parentRunId?: string
+ input?: RunAgentInput
}
```
-| Property | Type | Description |
-| ---------- | -------- | ----------------------------- |
-| `threadId` | `string` | ID of the conversation thread |
-| `runId` | `string` | ID of the agent run |
+| Property | Type | Description |
+| -------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| `threadId` | `string` | ID of the conversation thread |
+| `runId` | `string` | ID of the agent run |
+| `parentRunId` | `string` (optional) | (Optional) Lineage pointer for branching/time travel. If present, refers to a prior run within the same thread |
+| `input` | `RunAgentInput` (optional) | (Optional) The exact agent input payload sent to the agent for this run. May omit messages already in history |
### RunFinishedEvent
@@ -320,6 +326,46 @@ type MessagesSnapshotEvent = BaseEvent & {
| ---------- | ----------- | ------------------------ |
| `messages` | `Message[]` | Array of message objects |
+### ActivitySnapshotEvent
+
+Delivers a complete snapshot of an activity message.
+
+```typescript
+type ActivitySnapshotEvent = BaseEvent & {
+ type: EventType.ACTIVITY_SNAPSHOT
+ messageId: string
+ activityType: string
+ content: Record
+ replace?: boolean
+}
+```
+
+| Property | Type | Description |
+| -------------- | ---------------------- | ------------------------------------------------------- |
+| `messageId` | `string` | Identifier for the target `ActivityMessage` |
+| `activityType` | `string` | Activity discriminator such as `"PLAN"` or `"SEARCH"` |
+| `content` | `Record` | Structured payload describing the full activity state |
+| `replace` | `boolean` (optional) | Defaults to `true`; when `false` the snapshot is ignored if a message with the same ID already exists |
+
+### ActivityDeltaEvent
+
+Provides incremental updates to an activity snapshot using JSON Patch.
+
+```typescript
+type ActivityDeltaEvent = BaseEvent & {
+ type: EventType.ACTIVITY_DELTA
+ messageId: string
+ activityType: string
+ patch: any[] // RFC 6902 JSON Patch operations
+}
+```
+
+| Property | Type | Description |
+| -------------- | ------------- | ----------------------------------------------------------------- |
+| `messageId` | `string` | Identifier for the target `ActivityMessage` |
+| `activityType` | `string` | Activity discriminator mirroring the most recent snapshot |
+| `patch` | `any[]` | JSON Patch operations applied to the structured activity payload |
+
## Special Events
### RawEvent
diff --git a/docs/sdk/js/core/types.mdx b/docs/sdk/js/core/types.mdx
index afc011609..758549ed0 100644
--- a/docs/sdk/js/core/types.mdx
+++ b/docs/sdk/js/core/types.mdx
@@ -20,6 +20,7 @@ Input parameters for running an agent. In the HTTP API, this is the body of the
type RunAgentInput = {
threadId: string
runId: string
+ parentRunId?: string
state: any
messages: Message[]
tools: Tool[]
@@ -32,6 +33,7 @@ type RunAgentInput = {
| ---------------- | ----------- | ---------------------------------------------- |
| `threadId` | `string` | ID of the conversation thread |
| `runId` | `string` | ID of the current run |
+| `parentRunId` | `string (optional)` | ID of the run that spawned this run |
| `state` | `any` | Current state of the agent |
| `messages` | `Message[]` | Array of messages in the conversation |
| `tools` | `Tool[]` | Array of tools available to the agent |
@@ -48,7 +50,7 @@ messages in the system.
Represents the possible roles a message sender can have.
```typescript
-type Role = "developer" | "system" | "assistant" | "user" | "tool"
+type Role = "developer" | "system" | "assistant" | "user" | "tool" | "activity"
```
### DeveloperMessage
@@ -121,17 +123,49 @@ Represents a message from a user.
type UserMessage = {
id: string
role: "user"
- content: string
+ content: string | InputContent[]
name?: string
}
```
-| Property | Type | Description |
-| --------- | -------- | ------------------------------------------- |
-| `id` | `string` | Unique identifier for the message |
-| `role` | `"user"` | Role of the message sender, fixed as "user" |
-| `content` | `string` | Text content of the message (required) |
-| `name` | `string` | Optional name of the sender |
+| Property | Type | Description |
+| --------- | --------------------------- | --------------------------------------------------------------------- |
+| `id` | `string` | Unique identifier for the message |
+| `role` | `"user"` | Role of the message sender, fixed as "user" |
+| `content` | `string \| InputContent[]` | Either plain text or an ordered array of multimodal content fragments |
+| `name` | `string` | Optional name of the sender |
+
+### InputContent
+
+Union of supported multimodal fragments.
+
+```typescript
+type InputContent = TextInputContent | BinaryInputContent
+```
+
+### TextInputContent
+
+```typescript
+type TextInputContent = {
+ type: "text"
+ text: string
+}
+```
+
+### BinaryInputContent
+
+```typescript
+type BinaryInputContent = {
+ type: "binary"
+ mimeType: string
+ id?: string
+ url?: string
+ data?: string
+ filename?: string
+}
+```
+
+> At least one of `id`, `url`, or `data` must be provided.
### ToolMessage
@@ -155,6 +189,26 @@ type ToolMessage = {
| `toolCallId` | `string` | ID of the tool call this message responds to |
| `error` | `string` | Error message if the tool call failed |
+### ActivityMessage
+
+Represents structured activity progress emitted between chat messages.
+
+```typescript
+type ActivityMessage = {
+ id: string
+ role: "activity"
+ activityType: string
+ content: Record
+}
+```
+
+| Property | Type | Description |
+| -------------- | --------------------- | ------------------------------------------------------- |
+| `id` | `string` | Unique identifier for the activity message |
+| `role` | `"activity"` | Fixed discriminator identifying the message as activity |
+| `activityType` | `string` | Activity discriminator used for renderer selection |
+| `content` | `Record` | Structured payload representing the activity state |
+
### Message
A union type representing any type of message in the system.
@@ -166,6 +220,7 @@ type Message =
| AssistantMessage
| UserMessage
| ToolMessage
+ | ActivityMessage
```
### ToolCall
diff --git a/docs/sdk/python/core/events.mdx b/docs/sdk/python/core/events.mdx
index 6d5cdc934..463b21734 100644
--- a/docs/sdk/python/core/events.mdx
+++ b/docs/sdk/python/core/events.mdx
@@ -29,6 +29,8 @@ class EventType(str, Enum):
STATE_SNAPSHOT = "STATE_SNAPSHOT"
STATE_DELTA = "STATE_DELTA"
MESSAGES_SNAPSHOT = "MESSAGES_SNAPSHOT"
+ ACTIVITY_SNAPSHOT = "ACTIVITY_SNAPSHOT"
+ ACTIVITY_DELTA = "ACTIVITY_DELTA"
RAW = "RAW"
CUSTOM = "CUSTOM"
RUN_STARTED = "RUN_STARTED"
@@ -73,12 +75,16 @@ class RunStartedEvent(BaseEvent):
type: Literal[EventType.RUN_STARTED]
thread_id: str
run_id: str
+ parent_run_id: Optional[str] = None
+ input: Optional[RunAgentInput] = None
```
-| Property | Type | Description |
-| ----------- | ----- | ----------------------------- |
-| `thread_id` | `str` | ID of the conversation thread |
-| `run_id` | `str` | ID of the agent run |
+| Property | Type | Description |
+| ---------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
+| `thread_id` | `str` | ID of the conversation thread |
+| `run_id` | `str` | ID of the agent run |
+| `parent_run_id` | `Optional[str]` | (Optional) Lineage pointer for branching/time travel. If present, refers to a prior run within the same thread |
+| `input` | `Optional[RunAgentInput]` | (Optional) The exact agent input payload sent to the agent for this run. May omit messages already in history |
### RunFinishedEvent
@@ -342,6 +348,48 @@ class MessagesSnapshotEvent(BaseEvent):
| ---------- | --------------- | ------------------------ |
| `messages` | `List[Message]` | Array of message objects |
+### ActivitySnapshotEvent
+
+`from ag_ui.core import ActivitySnapshotEvent`
+
+Delivers a complete snapshot of an activity message.
+
+```python
+class ActivitySnapshotEvent(BaseEvent):
+ type: Literal[EventType.ACTIVITY_SNAPSHOT]
+ message_id: str
+ activity_type: str
+ content: Any
+ replace: bool = True
+```
+
+| Property | Type | Description |
+| --------------- | ----- | ----------------------------------------------------- |
+| `message_id` | `str` | Identifier for the target `ActivityMessage` |
+| `activity_type` | `str` | Activity discriminator such as `"PLAN"` or `"SEARCH"` |
+| `content` | `Any` | Structured payload describing the full activity state |
+| `replace` | `bool` (default `True`) | When `False`, the snapshot is ignored if a message with the same ID already exists |
+
+### ActivityDeltaEvent
+
+`from ag_ui.core import ActivityDeltaEvent`
+
+Provides incremental updates to an activity snapshot using JSON Patch.
+
+```python
+class ActivityDeltaEvent(BaseEvent):
+ type: Literal[EventType.ACTIVITY_DELTA]
+ message_id: str
+ activity_type: str
+ patch: List[Any]
+```
+
+| Property | Type | Description |
+| --------------- | ------------ | -------------------------------------------------------------------- |
+| `message_id` | `str` | Identifier for the target `ActivityMessage` |
+| `activity_type` | `str` | Activity discriminator mirroring the most recent snapshot |
+| `patch` | `List[Any]` | JSON Patch operations applied to the structured activity content |
+
## Special Events
### RawEvent
diff --git a/docs/sdk/python/core/types.mdx b/docs/sdk/python/core/types.mdx
index 4f9180455..70275c4e2 100644
--- a/docs/sdk/python/core/types.mdx
+++ b/docs/sdk/python/core/types.mdx
@@ -22,6 +22,7 @@ Input parameters for running an agent. In the HTTP API, this is the body of the
class RunAgentInput(ConfiguredBaseModel):
thread_id: str
run_id: str
+ parent_run_id: Optional[str] = None
state: Any
messages: List[Message]
tools: List[Tool]
@@ -33,6 +34,7 @@ class RunAgentInput(ConfiguredBaseModel):
| ----------------- | --------------- | --------------------------------------------- |
| `thread_id` | `str` | ID of the conversation thread |
| `run_id` | `str` | ID of the current run |
+| `parent_run_id` | `Optional[str]` | (Optional) ID of the run that spawned this run|
| `state` | `Any` | Current state of the agent |
| `messages` | `List[Message]` | List of messages in the conversation |
| `tools` | `List[Tool]` | List of tools available to the agent |
@@ -51,7 +53,7 @@ messages in the system.
Represents the possible roles a message sender can have.
```python
-Role = Literal["developer", "system", "assistant", "user", "tool"]
+Role = Literal["developer", "system", "assistant", "user", "tool", "activity"]
```
### DeveloperMessage
@@ -122,15 +124,55 @@ Represents a message from a user.
```python
class UserMessage(BaseMessage):
role: Literal["user"]
- content: str
+ content: Union[str, List["InputContent"]]
+```
+
+| Property | Type | Description |
+| --------- | ---------------------------------- | --------------------------------------------------------------------- |
+| `id` | `str` | Unique identifier for the message |
+| `role` | `Literal["user"]` | Role of the message sender, fixed as "user" |
+| `content` | `Union[str, List["InputContent"]]` | Either a plain text string or an ordered list of multimodal fragments |
+| `name` | `Optional[str]` | Optional name of the sender |
+
+### TextInputContent
+
+Represents a text fragment inside a multimodal user message.
+
+```python
+class TextInputContent(ConfiguredBaseModel):
+ type: Literal["text"]
+ text: str
+```
+
+| Property | Type | Description |
+| -------- | --------------- | ---------------------------- |
+| `type` | `Literal["text"]` | Identifies the fragment type |
+| `text` | `str` | Text content |
+
+### BinaryInputContent
+
+Represents binary data such as images, audio, or files.
+
+```python
+class BinaryInputContent(ConfiguredBaseModel):
+ type: Literal["binary"]
+ mime_type: str
+ id: Optional[str] = None
+ url: Optional[str] = None
+ data: Optional[str] = None
+ filename: Optional[str] = None
```
-| Property | Type | Description |
-| --------- | ----------------- | ------------------------------------------- |
-| `id` | `str` | Unique identifier for the message |
-| `role` | `Literal["user"]` | Role of the message sender, fixed as "user" |
-| `content` | `str` | Text content of the message (required) |
-| `name` | `Optional[str]` | Optional name of the sender |
+| Property | Type | Description |
+| ---------- | ----------------- | ------------------------------------------------------------- |
+| `type` | `Literal["binary"]` | Identifies the fragment type |
+| `mime_type`| `str` | MIME type, for example `"image/png"` |
+| `id` | `Optional[str]` | Reference to previously uploaded content |
+| `url` | `Optional[str]` | Remote URL where the content can be retrieved |
+| `data` | `Optional[str]` | Base64 encoded content |
+| `filename` | `Optional[str]` | Optional filename hint |
+
+> **Validation:** At least one of `id`, `url`, or `data` must be provided.
### ToolMessage
@@ -155,6 +197,27 @@ class ToolMessage(ConfiguredBaseModel):
| `tool_call_id` | `str` | ID of the tool call this message responds to |
| `error` | `Optional[str]` | Error message if the tool call failed |
+### ActivityMessage
+
+`from ag_ui.core import ActivityMessage`
+
+Represents structured activity progress emitted between chat messages.
+
+```python
+class ActivityMessage(ConfiguredBaseModel):
+ id: str
+ role: Literal["activity"]
+ activity_type: str
+ content: Dict[str, Any]
+```
+
+| Property | Type | Description |
+| --------------- | ----------------------- | ------------------------------------------------------- |
+| `id` | `str` | Unique identifier for the activity message |
+| `role` | `Literal["activity"]` | Fixed discriminator identifying the message as activity |
+| `activity_type` | `str` | Activity discriminator used for renderer selection |
+| `content` | `Dict[str, Any]` | Structured payload representing the activity state |
+
### Message
`from ag_ui.core import Message`
@@ -163,7 +226,14 @@ A union type representing any type of message in the system.
```python
Message = Annotated[
- Union[DeveloperMessage, SystemMessage, AssistantMessage, UserMessage, ToolMessage],
+ Union[
+ DeveloperMessage,
+ SystemMessage,
+ AssistantMessage,
+ UserMessage,
+ ToolMessage,
+ ActivityMessage,
+ ],
Field(discriminator="role")
]
```
diff --git a/integrations/a2a/typescript/.gitignore b/integrations/a2a/typescript/.gitignore
new file mode 100644
index 000000000..de4d1f007
--- /dev/null
+++ b/integrations/a2a/typescript/.gitignore
@@ -0,0 +1,2 @@
+dist
+node_modules
diff --git a/integrations/a2a/typescript/.npmrc b/integrations/a2a/typescript/.npmrc
new file mode 100644
index 000000000..3e775efb0
--- /dev/null
+++ b/integrations/a2a/typescript/.npmrc
@@ -0,0 +1 @@
+auto-install-peers=true
diff --git a/integrations/a2a/typescript/README.md b/integrations/a2a/typescript/README.md
new file mode 100644
index 000000000..e1f689eb1
--- /dev/null
+++ b/integrations/a2a/typescript/README.md
@@ -0,0 +1,86 @@
+# @ag-ui/a2a
+
+A TypeScript integration that connects AG-UI agents with remote services that expose the [A2A protocol](https://a2a.dev/). It converts AG-UI conversations into A2A payloads, forwards them through the official A2A SDK, and replays the responses back into AG-UI event streams.
+
+> **Status:** Experimental. APIs may change while the integration stabilises.
+
+## Features
+
+- Message conversion helpers between AG-UI and A2A formats (user, assistant, tool, binary payloads).
+- `A2AAgent` implementation that streams or performs blocking requests against A2A endpoints.
+- Optional fallback from streaming to blocking requests when an agent does not support SSE.
+- Event conversion utilities that surface A2A messages, task status updates, and artifact chunks as AG-UI events.
+- Helper tool schema (`send_message_to_a2a_agent`) for orchestration scenarios.
+- Example client and Jest tests to validate conversions and streaming flows.
+
+## Installation
+
+Once dependencies are installed in the monorepo:
+
+```bash
+pnpm install
+pnpm --filter @ag-ui/a2a build
+```
+
+## Quick start
+
+```ts
+import { A2AAgent } from "@ag-ui/a2a";
+
+import { A2AClient } from "@a2a-js/sdk/client";
+
+const client = new A2AClient("https://my-a2a-agent");
+
+const agent = new A2AAgent({
+ a2aClient: client,
+ initialMessages: [
+ { id: "user-1", role: "user", content: "Plan a team offsite" } as any,
+ ],
+});
+
+const { result, newMessages } = await agent.runAgent();
+console.log(result);
+console.log(newMessages);
+```
+
+You can inject your own `A2AClient` instance via the `client` option, override default instructions, or force blocking mode by setting `strategy: "blocking"`.
+
+## Configuration reference
+
+| Option | Description |
+| ------ | ----------- |
+| `a2aClient` | Required. Provide an `A2AClient` instance (with any auth headers or custom fetch logic you need). |
+
+## Environment variables & authentication
+
+The integration relies on the underlying A2A agent for authentication. Common patterns include:
+
+- `A2A_AGENT_URL` – set in deployment environments to point to the remote agent base URL.
+- `A2A_API_KEY` or `A2A_BEARER_TOKEN` – consumed by a wrapped `fetch` inside a custom `A2AClient` instance if the remote agent enforces API key or bearer authentication.
+
+Pass any credentials to the `A2AClient` you provide to `A2AAgent`, or configure an HTTP proxy that injects the correct headers.
+
+## Utilities
+
+- `convertAGUIMessagesToA2A(messages, options)` — reshapes AG-UI history into A2A message objects, forwarding only user/assistant/tool turns and preserving the tool payloads.
+- `convertA2AEventToAGUIEvents(event, options)` — maps an A2A stream event to AG-UI text and tool events (`TEXT_MESSAGE_CHUNK`, `TOOL_CALL_*`, `TOOL_CALL_RESULT`).
+- `sendMessageToA2AAgentTool` — JSON schema describing a `send_message_to_a2a_agent` tool for orchestration agents.
+
+## Testing
+
+```bash
+pnpm --filter @ag-ui/a2a test
+```
+
+The suite covers conversion edge cases and streaming / fallback behaviour using mocked A2A clients.
+
+## Examples
+
+- `examples/basic.ts` – minimal script. If you set `A2A_AGENT_URL`, it will connect to that agent through the real `A2AClient`. Otherwise it falls back to a tiny in-memory mock client so you can observe the integration without hitting a remote endpoint.
+
+## Release checklist
+
+1. `pnpm --filter @ag-ui/a2a build`
+2. `pnpm --filter @ag-ui/a2a test`
+3. Update CHANGELOG / release notes.
+4. Publish with `pnpm publish --filter @ag-ui/a2a`.
diff --git a/integrations/a2a/typescript/jest.config.js b/integrations/a2a/typescript/jest.config.js
new file mode 100644
index 000000000..0521f8d91
--- /dev/null
+++ b/integrations/a2a/typescript/jest.config.js
@@ -0,0 +1,10 @@
+/** @type {import('ts-jest').JestConfigWithTsJest} */
+module.exports = {
+ preset: "ts-jest",
+ testEnvironment: "node",
+ testMatch: ["**/*.test.ts"],
+ passWithNoTests: true,
+ moduleNameMapper: {
+ "^@/(.*)$": "/src/$1",
+ },
+};
diff --git a/integrations/a2a/typescript/package.json b/integrations/a2a/typescript/package.json
new file mode 100644
index 000000000..d939bf829
--- /dev/null
+++ b/integrations/a2a/typescript/package.json
@@ -0,0 +1,52 @@
+{
+ "name": "@ag-ui/a2a",
+ "author": "Markus Ecker ",
+ "version": "0.0.5",
+ "license": "Apache-2.0",
+ "main": "./dist/index.js",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "sideEffects": false,
+ "private": false,
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "dist/**",
+ "README.md"
+ ],
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.js"
+ }
+ },
+ "scripts": {
+ "build": "tsup",
+ "dev": "tsup --watch",
+ "clean": "rm -rf dist .turbo node_modules",
+ "typecheck": "tsc --noEmit",
+ "test": "jest",
+ "link:global": "pnpm link --global",
+ "unlink:global": "pnpm unlink --global"
+ },
+ "dependencies": {
+ "@a2a-js/sdk": "^0.2.2",
+ "rxjs": "7.8.1"
+ },
+ "peerDependencies": {
+ "@ag-ui/core": ">=0.0.40",
+ "@ag-ui/client": ">=0.0.40"
+ },
+ "devDependencies": {
+ "@ag-ui/core": "workspace:*",
+ "@ag-ui/client": "workspace:*",
+ "@types/jest": "^29.5.14",
+ "@types/node": "^20.11.19",
+ "jest": "^29.7.0",
+ "ts-jest": "^29.1.2",
+ "tsup": "^8.0.2",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/integrations/a2a/typescript/src/__tests__/agent.test.ts b/integrations/a2a/typescript/src/__tests__/agent.test.ts
new file mode 100644
index 000000000..9d9ab88b9
--- /dev/null
+++ b/integrations/a2a/typescript/src/__tests__/agent.test.ts
@@ -0,0 +1,143 @@
+import type { Message } from "@ag-ui/client";
+import { A2AAgent } from "../agent";
+import type { MessageSendParams } from "@a2a-js/sdk";
+
+const createMessage = (message: Partial): Message => message as Message;
+
+type SendMessageResponseSuccess = {
+ id: string | number | null;
+ jsonrpc: "2.0";
+ result: any;
+};
+
+type SendMessageResponseError = {
+ id: string | number | null;
+ jsonrpc: "2.0";
+ error: { code: number; message: string };
+};
+
+class FakeA2AClient {
+ constructor(
+ readonly behaviour: {
+ stream?: () => AsyncGenerator;
+ send?: () => Promise;
+ card?: () => Promise;
+ } = {},
+ ) {}
+
+ sendMessageStream(params: MessageSendParams) {
+ if (!this.behaviour.stream) {
+ throw new Error("Streaming not configured");
+ }
+ return this.behaviour.stream();
+ }
+
+ async sendMessage(params: MessageSendParams) {
+ if (!this.behaviour.send) {
+ throw new Error("sendMessage not configured");
+ }
+ return this.behaviour.send();
+ }
+
+ isErrorResponse(response: SendMessageResponseSuccess | SendMessageResponseError): response is SendMessageResponseError {
+ return "error" in response && Boolean(response.error);
+ }
+
+ async getAgentCard() {
+ if (this.behaviour.card) {
+ return this.behaviour.card();
+ }
+ return {
+ name: "Test Agent",
+ description: "",
+ capabilities: {},
+ };
+ }
+}
+
+describe("A2AAgent", () => {
+ it("streams responses and records run summary", async () => {
+ const fakeClient = new FakeA2AClient({
+ stream: async function* () {
+ yield {
+ kind: "message",
+ messageId: "resp-1",
+ role: "agent",
+ parts: [{ kind: "text", text: "Hello from stream" }],
+ };
+ },
+ });
+
+ const agent = new A2AAgent({
+ a2aClient: fakeClient as any,
+ initialMessages: [
+ createMessage({
+ id: "user-1",
+ role: "user",
+ content: "Hi there",
+ }),
+ ],
+ });
+
+ const result = await agent.runAgent();
+
+ expect(result.result).toBeUndefined();
+
+ expect(result.newMessages).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({ role: "assistant" }),
+ ]),
+ );
+ });
+
+ it("falls back to blocking when streaming fails", async () => {
+ const fakeClient = new FakeA2AClient({
+ stream: async function* () {
+ throw new Error("Streaming unsupported");
+ },
+ send: async () => ({
+ id: null,
+ jsonrpc: "2.0",
+ result: {
+ kind: "message",
+ messageId: "resp-2",
+ role: "agent",
+ parts: [{ kind: "text", text: "Blocking response" }],
+ },
+ }),
+ });
+
+ const agent = new A2AAgent({
+ a2aClient: fakeClient as any,
+ initialMessages: [
+ createMessage({ id: "user-1", role: "user", content: "Ping" }),
+ ],
+ });
+
+ const result = await agent.runAgent();
+
+ expect(result.result).toBeUndefined();
+ });
+
+ it("throws when the A2A service reports an error", async () => {
+ const fakeClient = new FakeA2AClient({
+ stream: async function* () {
+ throw new Error("Streaming unsupported");
+ },
+ send: async () => ({
+ id: null,
+ jsonrpc: "2.0",
+ error: { code: -32000, message: "Agent failure" },
+ }),
+ });
+
+ const agent = new A2AAgent({
+ a2aClient: fakeClient as any,
+ initialMessages: [
+ createMessage({ id: "user-1", role: "user", content: "Trouble" }),
+ ],
+ });
+
+ await expect(agent.runAgent()).rejects.toThrow("Agent failure");
+ });
+});
diff --git a/integrations/a2a/typescript/src/__tests__/utils.test.ts b/integrations/a2a/typescript/src/__tests__/utils.test.ts
new file mode 100644
index 000000000..2621e62df
--- /dev/null
+++ b/integrations/a2a/typescript/src/__tests__/utils.test.ts
@@ -0,0 +1,191 @@
+import { EventType } from "@ag-ui/client";
+import type { Message } from "@ag-ui/client";
+import {
+ convertAGUIMessagesToA2A,
+ convertA2AEventToAGUIEvents,
+ sendMessageToA2AAgentTool,
+} from "../utils";
+
+const createMessage = (message: Partial): Message => message as Message;
+
+describe("convertAGUIMessagesToA2A", () => {
+ it("converts AG-UI messages into A2A format while skipping system messages", () => {
+ const systemMessage = createMessage({
+ id: "sys-1",
+ role: "system",
+ content: "Follow project guidelines",
+ });
+
+ const userMessage = createMessage({
+ id: "user-1",
+ role: "user",
+ content: [
+ {
+ type: "text",
+ text: "Draft a project plan",
+ },
+ ],
+ });
+
+ const assistantMessage = createMessage({
+ id: "assistant-1",
+ role: "assistant",
+ content: "Sure, preparing a plan",
+ toolCalls: [
+ {
+ id: "tool-call-1",
+ type: "function",
+ function: {
+ name: "lookupRequirements",
+ arguments: JSON.stringify({ id: 123 }),
+ },
+ },
+ ],
+ });
+
+ const toolMessage = createMessage({
+ id: "tool-1",
+ role: "tool",
+ toolCallId: "tool-call-1",
+ content: JSON.stringify({ status: "ok" }),
+ });
+
+ const converted = convertAGUIMessagesToA2A([
+ systemMessage,
+ userMessage,
+ assistantMessage,
+ toolMessage,
+ ]);
+
+ expect(converted.contextId).toBeUndefined();
+ expect(converted.history).toHaveLength(3);
+
+ const assistantEntry = converted.history.find((entry) => entry.role === "agent");
+ expect(assistantEntry?.parts).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({ kind: "text", text: "Sure, preparing a plan" }),
+ expect.objectContaining({ kind: "data" }),
+ ]),
+ );
+
+ const toolEntry = converted.history.find((entry) =>
+ entry.parts.some((part) => part.kind === "data" && (part as any).data?.type === "tool-result"),
+ );
+ expect(toolEntry?.parts).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({ kind: "data", data: expect.objectContaining({ type: "tool-result" }) }),
+ ]),
+ );
+
+ expect(converted.latestUserMessage?.role).toBe("user");
+ expect(
+ converted.history.some((msg) =>
+ (msg.parts ?? []).some((part) =>
+ part.kind === "text" && (part as any).text?.includes("Follow project guidelines"),
+ ),
+ ),
+ ).toBe(false);
+ });
+});
+
+describe("convertA2AEventToAGUIEvents", () => {
+ it("produces AG-UI text chunks from A2A messages", () => {
+ const a2aEvent = {
+ kind: "message" as const,
+ messageId: "remote-1",
+ role: "agent" as const,
+ parts: [
+ { kind: "text" as const, text: "Hello from A2A" },
+ ],
+ };
+
+ const map = new Map();
+ const events = convertA2AEventToAGUIEvents(a2aEvent, {
+ messageIdMap: map,
+ });
+
+ expect(events).toHaveLength(1);
+ expect(events[0]).toEqual(
+ expect.objectContaining({
+ type: EventType.TEXT_MESSAGE_CHUNK,
+ delta: "Hello from A2A",
+ }),
+ );
+
+ expect(map.size).toBe(1);
+ });
+
+ it("maps tool-call payloads to tool events", () => {
+ const a2aEvent = {
+ kind: "message" as const,
+ messageId: "remote-call",
+ role: "agent" as const,
+ parts: [
+ {
+ kind: "data" as const,
+ data: { type: "tool-call", id: "tool-123", name: "lookup", arguments: { query: "hi" } },
+ },
+ {
+ kind: "data" as const,
+ data: { type: "tool-result", toolCallId: "tool-123", payload: { ok: true } },
+ },
+ ],
+ };
+
+ const events = convertA2AEventToAGUIEvents(a2aEvent, { messageIdMap: new Map() });
+
+ expect(events).toEqual([
+ expect.objectContaining({ type: EventType.TOOL_CALL_START, toolCallId: "tool-123" }),
+ expect.objectContaining({ type: EventType.TOOL_CALL_ARGS, toolCallId: "tool-123" }),
+ expect.objectContaining({ type: EventType.TOOL_CALL_RESULT, toolCallId: "tool-123" }),
+ expect.objectContaining({ type: EventType.TOOL_CALL_END, toolCallId: "tool-123" }),
+ ]);
+ });
+
+ it("maps tool-result payloads to ToolCallResult events", () => {
+ const a2aEvent = {
+ kind: "message" as const,
+ messageId: "remote-2",
+ role: "agent" as const,
+ parts: [
+ {
+ kind: "data" as const,
+ data: { type: "tool-result", toolCallId: "call-1", payload: { ok: true } },
+ },
+ ],
+ };
+
+ const events = convertA2AEventToAGUIEvents(a2aEvent, { messageIdMap: new Map() });
+
+ expect(events).toHaveLength(1);
+ expect(events[0]).toEqual(
+ expect.objectContaining({
+ type: EventType.TOOL_CALL_RESULT,
+ toolCallId: "call-1",
+ }),
+ );
+ });
+
+ it("maps task status updates to raw events", () => {
+ const statusEvent = {
+ kind: "status-update" as const,
+ contextId: "ctx",
+ final: false,
+ status: { state: "working", message: undefined },
+ taskId: "task-1",
+ };
+
+ const events = convertA2AEventToAGUIEvents(statusEvent as any, {
+ messageIdMap: new Map(),
+ });
+
+ expect(events).toHaveLength(0);
+ });
+});
+
+describe("sendMessageToA2AAgentTool", () => {
+ it("matches the expected schema", () => {
+ expect(sendMessageToA2AAgentTool.name).toBe("send_message_to_a2a_agent");
+ expect(sendMessageToA2AAgentTool.parameters.required).toContain("task");
+ });
+});
diff --git a/integrations/a2a/typescript/src/agent.ts b/integrations/a2a/typescript/src/agent.ts
new file mode 100644
index 000000000..e141763fc
--- /dev/null
+++ b/integrations/a2a/typescript/src/agent.ts
@@ -0,0 +1,360 @@
+import {
+ AbstractAgent,
+ AgentConfig,
+ BaseEvent,
+ EventType,
+ RunAgentInput,
+ RunErrorEvent,
+ RunFinishedEvent,
+ RunStartedEvent,
+} from "@ag-ui/client";
+import { Observable } from "rxjs";
+import { A2AClient } from "@a2a-js/sdk/client";
+import type {
+ MessageSendConfiguration,
+ MessageSendParams,
+ Message as A2AMessage,
+} from "@a2a-js/sdk";
+import { convertAGUIMessagesToA2A, convertA2AEventToAGUIEvents } from "./utils";
+import type {
+ A2AAgentRunResultSummary,
+ ConvertedA2AMessages,
+ A2AStreamEvent,
+ SurfaceTracker,
+} from "./types";
+import { randomUUID } from "@ag-ui/client";
+
+export interface A2AAgentConfig extends AgentConfig {
+ a2aClient: A2AClient;
+}
+
+const EXTENSION_URI = "https://a2ui.org/ext/a2a-ui/v0.1";
+
+export class A2AAgent extends AbstractAgent {
+ private readonly a2aClient: A2AClient;
+ private readonly messageIdMap = new Map();
+
+ constructor(config: A2AAgentConfig) {
+ const { a2aClient, ...rest } = config;
+ if (!a2aClient) {
+ throw new Error("A2AAgent requires a configured A2AClient instance.");
+ }
+
+ super(rest);
+
+ this.a2aClient = a2aClient;
+ this.initializeExtension(this.a2aClient);
+ }
+
+ clone() {
+ return new A2AAgent({ a2aClient: this.a2aClient, debug: this.debug });
+ }
+
+ protected run(input: RunAgentInput): Observable {
+ return new Observable((subscriber) => {
+ const run = async () => {
+ const runStarted: RunStartedEvent = {
+ type: EventType.RUN_STARTED,
+ threadId: input.threadId,
+ runId: input.runId,
+ };
+ subscriber.next(runStarted);
+
+ if (!input.messages?.length) {
+ const runFinished: RunFinishedEvent = {
+ type: EventType.RUN_FINISHED,
+ threadId: input.threadId,
+ runId: input.runId,
+ };
+ subscriber.next(runFinished);
+ subscriber.complete();
+ return;
+ }
+
+ try {
+ const converted = this.prepareConversation(input);
+
+ if (!converted.latestUserMessage) {
+ const runFinished: RunFinishedEvent = {
+ type: EventType.RUN_FINISHED,
+ threadId: input.threadId,
+ runId: input.runId,
+ } as unknown as RunFinishedEvent;
+ subscriber.next(runFinished);
+ subscriber.complete();
+ return;
+ }
+
+ const sendParams = await this.createSendParams(converted, input);
+
+ const surfaceTracker = this.createSurfaceTracker();
+
+ try {
+ await this.streamMessage(sendParams, subscriber, surfaceTracker);
+ } catch (error) {
+ await this.fallbackToBlocking(
+ sendParams,
+ subscriber,
+ error as Error,
+ surfaceTracker,
+ );
+ }
+
+ const runFinished: RunFinishedEvent = {
+ type: EventType.RUN_FINISHED,
+ threadId: input.threadId,
+ runId: input.runId,
+ };
+ subscriber.next(runFinished);
+ subscriber.complete();
+ } catch (error) {
+ const runError: RunErrorEvent = {
+ type: EventType.RUN_ERROR,
+ message: (error as Error).message ?? "Unknown A2A error",
+ };
+ subscriber.next(runError);
+ subscriber.error(error);
+ }
+ };
+
+ run();
+
+ return () => {};
+ });
+ }
+
+ private prepareConversation(input: RunAgentInput): ConvertedA2AMessages {
+ return convertAGUIMessagesToA2A(input.messages ?? [], {
+ contextId: input.threadId,
+ });
+ }
+
+ private async createSendParams(
+ converted: ConvertedA2AMessages,
+ input: RunAgentInput,
+ ): Promise {
+ const latest = converted.latestUserMessage as A2AMessage;
+
+ const message: A2AMessage = {
+ ...latest,
+ messageId: latest.messageId ?? randomUUID(),
+ contextId: converted.contextId ?? input.threadId,
+ };
+
+ const configuration: MessageSendConfiguration = {
+ acceptedOutputModes: ["text"],
+ } as MessageSendConfiguration;
+
+ return {
+ message,
+ configuration,
+ } as MessageSendParams;
+ }
+
+ private async streamMessage(
+ params: MessageSendParams,
+ subscriber: { next: (event: BaseEvent) => void },
+ surfaceTracker?: SurfaceTracker,
+ ): Promise {
+ const aggregatedText = new Map();
+ const rawEvents: A2AStreamEvent[] = [];
+ const tracker = surfaceTracker ?? this.createSurfaceTracker();
+
+ const stream = this.a2aClient.sendMessageStream(params);
+ for await (const chunk of stream) {
+ rawEvents.push(chunk as A2AStreamEvent);
+ const events = convertA2AEventToAGUIEvents(chunk as A2AStreamEvent, {
+ role: "assistant",
+ messageIdMap: this.messageIdMap,
+ onTextDelta: ({ messageId, delta }) => {
+ aggregatedText.set(
+ messageId,
+ (aggregatedText.get(messageId) ?? "") + delta,
+ );
+ },
+ getCurrentText: (messageId) => aggregatedText.get(messageId),
+ source: "a2a",
+ surfaceTracker: tracker,
+ });
+ for (const event of events) {
+ subscriber.next(event);
+ }
+ }
+
+ return {
+ messages: [],
+ rawEvents,
+ };
+ }
+
+ private async fallbackToBlocking(
+ params: MessageSendParams,
+ subscriber: { next: (event: BaseEvent) => void },
+ error: Error,
+ surfaceTracker?: SurfaceTracker,
+ ): Promise {
+ const configuration: MessageSendConfiguration = {
+ ...params.configuration,
+ acceptedOutputModes: params.configuration?.acceptedOutputModes ?? [
+ "text",
+ ],
+ blocking: true,
+ };
+
+ return this.blockingMessage(
+ {
+ ...params,
+ configuration,
+ },
+ subscriber,
+ surfaceTracker,
+ );
+ }
+
+ private async blockingMessage(
+ params: MessageSendParams,
+ subscriber: { next: (event: BaseEvent) => void },
+ surfaceTracker?: SurfaceTracker,
+ ): Promise {
+ const response = await this.a2aClient.sendMessage(params);
+
+ if (this.a2aClient.isErrorResponse(response)) {
+ const errorMessage =
+ response.error?.message ?? "Unknown error from A2A agent";
+ console.error("A2A sendMessage error", response.error);
+ throw new Error(errorMessage);
+ }
+
+ const aggregatedText = new Map();
+ const rawEvents: A2AStreamEvent[] = [];
+ const tracker = surfaceTracker ?? this.createSurfaceTracker();
+
+ const result = response.result as A2AStreamEvent;
+ rawEvents.push(result);
+
+ const events = convertA2AEventToAGUIEvents(result, {
+ role: "assistant",
+ messageIdMap: this.messageIdMap,
+ onTextDelta: ({ messageId, delta }) => {
+ aggregatedText.set(
+ messageId,
+ (aggregatedText.get(messageId) ?? "") + delta,
+ );
+ },
+ getCurrentText: (messageId) => aggregatedText.get(messageId),
+ source: "a2a",
+ surfaceTracker: tracker,
+ });
+
+ for (const event of events) {
+ subscriber.next(event);
+ }
+
+ return {
+ messages: [],
+ rawEvents,
+ };
+ }
+
+ private initializeExtension(client: A2AClient) {
+ const addExtensionHeader = (headers: Headers) => {
+ const existingValue = headers.get("X-A2A-Extensions") ?? "";
+ const values = existingValue
+ .split(",")
+ .map((value) => value.trim())
+ .filter(Boolean);
+
+ if (!values.includes(EXTENSION_URI)) {
+ values.push(EXTENSION_URI);
+ headers.set("X-A2A-Extensions", values.join(", "));
+ }
+ };
+
+ const patchFetch = () => {
+ const originalFetch = globalThis.fetch;
+ if (!originalFetch) {
+ return () => {};
+ }
+
+ const extensionFetch: typeof fetch = async (input, init) => {
+ const headers = new Headers(init?.headers);
+ addExtensionHeader(headers);
+ const nextInit: RequestInit = {
+ ...init,
+ headers,
+ };
+ return originalFetch(input, nextInit);
+ };
+
+ globalThis.fetch = extensionFetch;
+
+ return () => {
+ globalThis.fetch = originalFetch;
+ };
+ };
+
+ const wrapPromise = async (operation: () => Promise): Promise => {
+ const restore = patchFetch();
+ try {
+ return await operation();
+ } finally {
+ restore();
+ }
+ };
+
+ const wrapStream = (
+ original:
+ | ((...args: any[]) => AsyncGenerator)
+ | undefined,
+ ) => {
+ if (!original) {
+ return undefined;
+ }
+
+ return function wrapped(this: unknown, ...args: unknown[]) {
+ const restore = patchFetch();
+ const iterator = original.apply(this, args);
+
+ const wrappedIterator = (async function* () {
+ try {
+ for await (const value of iterator) {
+ yield value;
+ }
+ } finally {
+ restore();
+ }
+ })();
+
+ return wrappedIterator;
+ };
+ };
+
+ const originalSendMessage = client.sendMessage.bind(client);
+ client.sendMessage = (params) =>
+ wrapPromise(() => originalSendMessage(params));
+
+ const originalSendMessageStream = client.sendMessageStream?.bind(client);
+ const wrappedSendMessageStream = wrapStream(originalSendMessageStream);
+ if (wrappedSendMessageStream) {
+ client.sendMessageStream =
+ wrappedSendMessageStream as typeof client.sendMessageStream;
+ }
+
+ const originalResubscribeTask = client.resubscribeTask?.bind(client);
+ const wrappedResubscribeTask = wrapStream(originalResubscribeTask);
+ if (wrappedResubscribeTask) {
+ client.resubscribeTask =
+ wrappedResubscribeTask as typeof client.resubscribeTask;
+ }
+ }
+
+ private createSurfaceTracker(): SurfaceTracker {
+ const seenSurfaceIds = new Set();
+ return {
+ has: (surfaceId: string) => seenSurfaceIds.has(surfaceId),
+ add: (surfaceId: string) => {
+ seenSurfaceIds.add(surfaceId);
+ },
+ };
+ }
+}
diff --git a/integrations/a2a/typescript/src/index.ts b/integrations/a2a/typescript/src/index.ts
new file mode 100644
index 000000000..c06315272
--- /dev/null
+++ b/integrations/a2a/typescript/src/index.ts
@@ -0,0 +1,3 @@
+export * from "./agent";
+export * from "./utils";
+export * from "./types";
diff --git a/integrations/a2a/typescript/src/types.ts b/integrations/a2a/typescript/src/types.ts
new file mode 100644
index 000000000..b3e51181c
--- /dev/null
+++ b/integrations/a2a/typescript/src/types.ts
@@ -0,0 +1,60 @@
+import type {
+ MessageSendConfiguration,
+ MessageSendParams,
+ Message as A2AMessage,
+ Part as A2APart,
+ TextPart as A2ATextPart,
+ DataPart as A2ADataPart,
+ FilePart as A2AFilePart,
+ Task as A2ATask,
+ TaskStatusUpdateEvent as A2ATaskStatusUpdateEvent,
+ TaskArtifactUpdateEvent as A2ATaskArtifactUpdateEvent,
+} from "@a2a-js/sdk";
+import type { Message as AGUIMessage } from "@ag-ui/client";
+
+export type {
+ A2AMessage,
+ A2APart,
+ A2ATextPart,
+ A2ADataPart,
+ A2AFilePart,
+ MessageSendParams,
+ MessageSendConfiguration,
+ AGUIMessage as AGUIConversationMessage,
+};
+
+export interface SurfaceTracker {
+ has(surfaceId: string): boolean;
+ add(surfaceId: string): void;
+}
+
+export type A2AStreamEvent =
+ | A2AMessage
+ | A2ATask
+ | A2ATaskStatusUpdateEvent
+ | A2ATaskArtifactUpdateEvent;
+
+export interface ConvertAGUIMessagesOptions {
+ contextId?: string;
+ includeToolMessages?: boolean;
+}
+
+export interface ConvertedA2AMessages {
+ contextId?: string;
+ history: A2AMessage[];
+ latestUserMessage?: A2AMessage;
+}
+
+export interface ConvertA2AEventOptions {
+ role?: "assistant" | "user";
+ messageIdMap: Map;
+ onTextDelta?: (payload: { messageId: string; delta: string }) => void;
+ source?: string;
+ getCurrentText?: (messageId: string) => string | undefined;
+ surfaceTracker?: SurfaceTracker;
+}
+
+export interface A2AAgentRunResultSummary {
+ messages: Array<{ messageId: string; text: string }>;
+ rawEvents: A2AStreamEvent[];
+}
diff --git a/integrations/a2a/typescript/src/utils.ts b/integrations/a2a/typescript/src/utils.ts
new file mode 100644
index 000000000..8aba08fcd
--- /dev/null
+++ b/integrations/a2a/typescript/src/utils.ts
@@ -0,0 +1,488 @@
+import type {
+ BaseEvent,
+ InputContent,
+ Message,
+ TextMessageChunkEvent,
+ RawEvent,
+ ToolCallArgsEvent,
+ ToolCallEndEvent,
+ ToolCallStartEvent,
+ ToolCallResultEvent,
+} from "@ag-ui/client";
+import { EventType, randomUUID } from "@ag-ui/client";
+import type {
+ A2AMessage,
+ A2APart,
+ A2ATextPart,
+ A2ADataPart,
+ A2AFilePart,
+ A2AStreamEvent,
+ ConvertAGUIMessagesOptions,
+ ConvertedA2AMessages,
+ ConvertA2AEventOptions,
+} from "./types";
+
+const ROLE_MAP: Record = {
+ user: "user",
+ assistant: "agent",
+ tool: "agent",
+ system: "user",
+ developer: "user",
+};
+
+const TOOL_RESULT_PART_TYPE = "tool-result";
+const TOOL_CALL_PART_TYPE = "tool-call";
+const SURFACE_OPERATION_KEYS = [
+ "beginRendering",
+ "surfaceUpdate",
+ "dataModelUpdate",
+] as const;
+
+type SurfaceOperationKey = (typeof SURFACE_OPERATION_KEYS)[number];
+
+const isBinaryContent = (
+ content: InputContent,
+): content is Extract => content.type === "binary";
+
+const isTextContent = (content: InputContent): content is Extract =>
+ content.type === "text";
+
+const createTextPart = (text: string): A2ATextPart => ({
+ kind: "text",
+ text,
+});
+
+const createFilePart = (content: Extract): A2AFilePart | null => {
+ if (content.url) {
+ return {
+ kind: "file",
+ file: {
+ uri: content.url,
+ mimeType: content.mimeType,
+ name: content.filename,
+ },
+ };
+ }
+
+ if (content.data) {
+ return {
+ kind: "file",
+ file: {
+ bytes: content.data,
+ mimeType: content.mimeType,
+ name: content.filename,
+ },
+ };
+ }
+
+ return null;
+};
+
+const extractSurfaceOperation = (
+ payload: unknown,
+): { surfaceId: string; operation: Record } | null => {
+ if (!payload || typeof payload !== "object") {
+ return null;
+ }
+
+ const record = payload as Record;
+
+ for (const key of SURFACE_OPERATION_KEYS) {
+ const value = record[key as SurfaceOperationKey];
+ if (value && typeof value === "object" && (value as { surfaceId?: unknown }).surfaceId) {
+ const surfaceId = (value as { surfaceId?: unknown }).surfaceId;
+ if (typeof surfaceId === "string" && surfaceId.length > 0) {
+ return { surfaceId, operation: record };
+ }
+ }
+ }
+
+ return null;
+};
+
+const safeJsonParse = (value: string): unknown => {
+ try {
+ return JSON.parse(value);
+ } catch (error) {
+ return value;
+ }
+};
+
+const messageContentToParts = (message: Message): A2APart[] => {
+ const parts: A2APart[] = [];
+ const { content } = message as { content?: Message["content"] };
+
+ if (typeof content === "string") {
+ const trimmed = content.trim();
+ if (trimmed.length > 0) {
+ parts.push(createTextPart(trimmed));
+ }
+ } else if (Array.isArray(content)) {
+ for (const chunk of content) {
+ if (isTextContent(chunk)) {
+ const value = chunk.text.trim();
+ if (value.length > 0) {
+ parts.push(createTextPart(value));
+ }
+ } else if (isBinaryContent(chunk)) {
+ const filePart = createFilePart(chunk);
+ if (filePart) {
+ parts.push(filePart);
+ }
+ } else {
+ parts.push({ kind: "data", data: chunk } as A2ADataPart);
+ }
+ }
+ } else if (content && typeof content === "object") {
+ parts.push({
+ kind: "data",
+ data: content as Record,
+ });
+ }
+
+ if (message.role === "assistant" && "toolCalls" in message && message.toolCalls?.length) {
+ for (const toolCall of message.toolCalls) {
+ parts.push({
+ kind: "data",
+ data: {
+ type: TOOL_CALL_PART_TYPE,
+ id: toolCall.id,
+ name: toolCall.function.name,
+ arguments: safeJsonParse(toolCall.function.arguments),
+ rawArguments: toolCall.function.arguments,
+ },
+ });
+ }
+ }
+
+ if (message.role === "tool") {
+ const payload = typeof message.content === "string" ? safeJsonParse(message.content) : message.content;
+ parts.push({
+ kind: "data",
+ data: {
+ type: TOOL_RESULT_PART_TYPE,
+ toolCallId: message.toolCallId,
+ payload,
+ },
+ });
+ }
+
+ return parts;
+};
+
+const messageContentToText = (message: Message): string => {
+ const { content } = message as { content?: Message["content"] };
+ if (typeof content === "string") {
+ return content;
+ }
+ if (Array.isArray(content)) {
+ return content
+ .filter((part): part is Extract => isTextContent(part))
+ .map((part) => part.text)
+ .join("\n");
+ }
+ if (content && typeof content === "object") {
+ return JSON.stringify(content);
+ }
+ return "";
+};
+
+export function convertAGUIMessagesToA2A(
+ messages: Message[],
+ options: ConvertAGUIMessagesOptions = {},
+): ConvertedA2AMessages {
+ const history: A2AMessage[] = [];
+ const includeToolMessages = options.includeToolMessages ?? true;
+ const contextId = options.contextId;
+
+ for (const message of messages) {
+ if (message.role === "activity") {
+ continue;
+ }
+
+ if (message.role === "tool" && !includeToolMessages) {
+ continue;
+ }
+
+ if (message.role === "system" || message.role === "developer") {
+ continue;
+ }
+
+ const mappedRole = ROLE_MAP[message.role] ?? (message.role === "tool" ? "agent" : undefined);
+
+ if (!mappedRole) {
+ continue;
+ }
+
+ const parts = messageContentToParts(message);
+
+ if (parts.length === 0 && mappedRole !== "agent") {
+ continue;
+ }
+
+ const messageId = message.id ?? randomUUID();
+
+ history.push({
+ kind: "message",
+ messageId,
+ role: mappedRole,
+ parts,
+ contextId,
+ });
+ }
+
+ const latestUserMessage = [...history].reverse().find((msg) => msg.role === "user");
+
+ return {
+ contextId,
+ history,
+ latestUserMessage,
+ };
+}
+
+const isA2AMessage = (event: A2AStreamEvent): event is A2AMessage => event.kind === "message";
+
+const isA2ATask = (event: A2AStreamEvent): event is import("@a2a-js/sdk").Task => event.kind === "task";
+
+const isA2AStatusUpdate = (
+ event: A2AStreamEvent,
+): event is import("@a2a-js/sdk").TaskStatusUpdateEvent => event.kind === "status-update";
+
+function resolveMappedMessageId(
+ originalId: string,
+ options: ConvertA2AEventOptions,
+ aliasKey?: string,
+): string {
+ if (aliasKey) {
+ const existingAliasId = options.messageIdMap.get(aliasKey);
+ if (existingAliasId) {
+ options.messageIdMap.set(originalId, existingAliasId);
+ return existingAliasId;
+ }
+ }
+
+ const existingId = options.messageIdMap.get(originalId);
+ if (existingId) {
+ if (aliasKey) {
+ options.messageIdMap.set(aliasKey, existingId);
+ }
+ return existingId;
+ }
+
+ const newId = randomUUID();
+ options.messageIdMap.set(originalId, newId);
+ if (aliasKey) {
+ options.messageIdMap.set(aliasKey, newId);
+ }
+ return newId;
+}
+
+function convertMessageToEvents(
+ message: A2AMessage,
+ options: ConvertA2AEventOptions,
+ aliasKey?: string,
+): BaseEvent[] {
+ const role = options.role ?? "assistant";
+ const events: BaseEvent[] = [];
+
+ const originalId = message.messageId ?? randomUUID();
+ const mappedId = resolveMappedMessageId(originalId, options, aliasKey);
+
+ const openToolCalls = new Set();
+
+ for (const part of message.parts ?? []) {
+ if (part.kind === "text") {
+ const textPart = part as A2ATextPart;
+ const partText = textPart.text ?? "";
+ if (partText) {
+ const previousText = options.getCurrentText?.(mappedId) ?? "";
+
+ if (partText !== previousText) {
+ const deltaText = partText.startsWith(previousText)
+ ? partText.slice(previousText.length)
+ : partText;
+
+ if (deltaText.length > 0) {
+ const chunkEvent: TextMessageChunkEvent = {
+ type: EventType.TEXT_MESSAGE_CHUNK,
+ messageId: mappedId,
+ role,
+ delta: deltaText,
+ };
+ options.onTextDelta?.({ messageId: mappedId, delta: deltaText });
+ events.push(chunkEvent);
+ }
+ }
+ }
+ continue;
+ }
+
+ if (part.kind === "data") {
+ const dataPart = part as A2ADataPart;
+ const payload = dataPart.data;
+
+ if (payload && typeof payload === "object" && (payload as any).type === TOOL_CALL_PART_TYPE) {
+ const toolCallId = (payload as any).id ?? randomUUID();
+ const toolCallName = (payload as any).name ?? "unknown_tool";
+ const args = (payload as any).arguments;
+
+ const startEvent: ToolCallStartEvent = {
+ type: EventType.TOOL_CALL_START,
+ toolCallId,
+ toolCallName,
+ parentMessageId: mappedId,
+ };
+ events.push(startEvent);
+
+ if (args !== undefined) {
+ const argsEvent: ToolCallArgsEvent = {
+ type: EventType.TOOL_CALL_ARGS,
+ toolCallId,
+ delta: JSON.stringify(args),
+ };
+ events.push(argsEvent);
+ }
+
+ openToolCalls.add(toolCallId);
+ continue;
+ }
+
+ if (
+ payload &&
+ typeof payload === "object" &&
+ (payload as any).type === TOOL_RESULT_PART_TYPE &&
+ (payload as any).toolCallId
+ ) {
+ const toolCallId = (payload as any).toolCallId;
+ const toolResultEvent: ToolCallResultEvent = {
+ type: EventType.TOOL_CALL_RESULT,
+ toolCallId,
+ content: JSON.stringify((payload as any).payload ?? payload),
+ messageId: randomUUID(),
+ role: "tool",
+ };
+ events.push(toolResultEvent);
+
+ if (openToolCalls.has(toolCallId)) {
+ const endEvent: ToolCallEndEvent = {
+ type: EventType.TOOL_CALL_END,
+ toolCallId,
+ };
+ events.push(endEvent);
+ openToolCalls.delete(toolCallId);
+ }
+
+ continue;
+ }
+
+ const surfaceOperation = extractSurfaceOperation(payload);
+ if (surfaceOperation && options.surfaceTracker) {
+ const tracker = options.surfaceTracker;
+ const { surfaceId, operation } = surfaceOperation;
+ const hasSeenSurface = tracker.has(surfaceId);
+
+ if (!hasSeenSurface) {
+ tracker.add(surfaceId);
+ events.push({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: surfaceId,
+ activityType: "a2ui-surface",
+ content: { operations: [] },
+ replace: false,
+ } as BaseEvent);
+ }
+
+ events.push({
+ type: EventType.ACTIVITY_DELTA,
+ messageId: surfaceId,
+ activityType: "a2ui-surface",
+ patch: [
+ {
+ op: "add",
+ path: "/operations/-",
+ value: operation,
+ },
+ ],
+ } as BaseEvent);
+
+ continue;
+ }
+
+ continue;
+ }
+
+ // Ignore other part kinds for now.
+ }
+
+ for (const toolCallId of openToolCalls) {
+ const endEvent: ToolCallEndEvent = {
+ type: EventType.TOOL_CALL_END,
+ toolCallId,
+ };
+ events.push(endEvent);
+ }
+
+ return events;
+}
+
+export function convertA2AEventToAGUIEvents(
+ event: A2AStreamEvent,
+ options: ConvertA2AEventOptions,
+): BaseEvent[] {
+ const events: BaseEvent[] = [];
+ const source = options.source ?? "a2a";
+
+ if (isA2AMessage(event)) {
+ return convertMessageToEvents(event, options);
+ }
+
+ if (isA2AStatusUpdate(event)) {
+ const statusMessage = event.status?.message;
+ const statusState = event.status?.state;
+ const aliasKey = statusState && statusState !== "input-required" ? `${event.taskId}:status` : undefined;
+
+ if (statusMessage && statusMessage.kind === "message") {
+ return convertMessageToEvents(statusMessage as A2AMessage, options, aliasKey);
+ }
+ return events;
+ }
+
+ if (isA2ATask(event)) {
+ const rawEvent: RawEvent = {
+ type: EventType.RAW,
+ event,
+ source,
+ };
+ events.push(rawEvent);
+ return events;
+ }
+
+ const fallbackEvent: RawEvent = {
+ type: EventType.RAW,
+ event,
+ source,
+ };
+ events.push(fallbackEvent);
+ return events;
+}
+
+export const sendMessageToA2AAgentTool = {
+ name: "send_message_to_a2a_agent",
+ description:
+ "Sends a task to the agent named `agentName`, including the full conversation context and goal",
+ parameters: {
+ type: "object",
+ properties: {
+ agentName: {
+ type: "string",
+ description: "The name of the A2A agent to send the message to.",
+ },
+ task: {
+ type: "string",
+ description:
+ "The comprehensive conversation-context summary and goal to be achieved regarding the user inquiry.",
+ },
+ },
+ required: ["task"],
+ },
+} as const;
diff --git a/integrations/a2a/typescript/tsconfig.json b/integrations/a2a/typescript/tsconfig.json
new file mode 100644
index 000000000..ceecfd457
--- /dev/null
+++ b/integrations/a2a/typescript/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "es2017",
+ "module": "NodeNext",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "declaration": true,
+ "declarationMap": true,
+ "sourceMap": true,
+ "moduleResolution": "NodeNext",
+ "skipLibCheck": true,
+ "strict": true,
+ "jsx": "react-jsx",
+ "esModuleInterop": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "stripInternal": true
+ },
+ "include": ["src"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/integrations/a2a/typescript/tsup.config.ts b/integrations/a2a/typescript/tsup.config.ts
new file mode 100644
index 000000000..a0ee0a256
--- /dev/null
+++ b/integrations/a2a/typescript/tsup.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from "tsup";
+
+export default defineConfig({
+ entry: {
+ index: "src/index.ts",
+ },
+ format: ["cjs", "esm"],
+ dts: true,
+ splitting: false,
+ sourcemap: true,
+ clean: true,
+ minify: true,
+});
diff --git a/integrations/adk-middleware/python/src/ag_ui_adk/utils/converters.py b/integrations/adk-middleware/python/src/ag_ui_adk/utils/converters.py
index dd33b7b46..2cef5241d 100644
--- a/integrations/adk-middleware/python/src/ag_ui_adk/utils/converters.py
+++ b/integrations/adk-middleware/python/src/ag_ui_adk/utils/converters.py
@@ -8,7 +8,7 @@
from ag_ui.core import (
Message, UserMessage, AssistantMessage, SystemMessage, ToolMessage,
- ToolCall, FunctionCall
+ ToolCall, FunctionCall, TextInputContent, BinaryInputContent
)
from google.adk.events import Event as ADKEvent
from google.genai import types
@@ -38,18 +38,19 @@ def convert_ag_ui_messages_to_adk(messages: List[Message]) -> List[ADKEvent]:
# Convert content based on message type
if isinstance(message, (UserMessage, SystemMessage)):
- if message.content:
+ flattened_content = flatten_message_content(message.content)
+ if flattened_content:
event.content = types.Content(
role=message.role,
- parts=[types.Part(text=message.content)]
+ parts=[types.Part(text=flattened_content)]
)
-
+
elif isinstance(message, AssistantMessage):
parts = []
-
+
# Add text content if present
if message.content:
- parts.append(types.Part(text=message.content))
+ parts.append(types.Part(text=flatten_message_content(message.content)))
# Add tool calls if present
if message.tool_calls:
@@ -205,25 +206,32 @@ def convert_json_patch_to_state(patches: List[Dict[str, Any]]) -> Dict[str, Any]
def extract_text_from_content(content: types.Content) -> str:
- """Extract all text from ADK Content object.
-
- Args:
- content: ADK Content object
-
- Returns:
- Combined text from all text parts
- """
+ """Extract all text from ADK Content object."""
if not content or not content.parts:
return ""
-
+
text_parts = []
for part in content.parts:
if part.text:
text_parts.append(part.text)
-
+
return "\n".join(text_parts)
+def flatten_message_content(content: Any) -> str:
+ if content is None:
+ return ""
+
+ if isinstance(content, str):
+ return content
+
+ if isinstance(content, list):
+ text_parts = [part.text for part in content if isinstance(part, TextInputContent) and part.text]
+ return "\n".join(text_parts)
+
+ return str(content)
+
+
def create_error_message(error: Exception, context: str = "") -> str:
"""Create a user-friendly error message.
@@ -240,4 +248,4 @@ def create_error_message(error: Exception, context: str = "") -> str:
if context:
return f"{context}: {error_type} - {error_msg}"
else:
- return f"{error_type}: {error_msg}"
\ No newline at end of file
+ return f"{error_type}: {error_msg}"
diff --git a/integrations/langgraph/python/ag_ui_langgraph/utils.py b/integrations/langgraph/python/ag_ui_langgraph/utils.py
index ed5060887..4e0067512 100644
--- a/integrations/langgraph/python/ag_ui_langgraph/utils.py
+++ b/integrations/langgraph/python/ag_ui_langgraph/utils.py
@@ -13,6 +13,8 @@
ToolMessage as AGUIToolMessage,
ToolCall as AGUIToolCall,
FunctionCall as AGUIFunctionCall,
+ TextInputContent,
+ BinaryInputContent,
)
from .types import State, SchemaKeys, LangGraphReasoning
@@ -41,14 +43,56 @@ def stringify_if_needed(item: Any) -> str:
return item
return json.dumps(item)
+def convert_langchain_multimodal_to_agui(content: List[Dict[str, Any]]) -> List[Union[TextInputContent, BinaryInputContent]]:
+ """Convert LangChain's multimodal content to AG-UI format."""
+ agui_content = []
+ for item in content:
+ if isinstance(item, dict):
+ if item.get("type") == "text":
+ agui_content.append(TextInputContent(
+ type="text",
+ text=item.get("text", "")
+ ))
+ elif item.get("type") == "image_url":
+ image_url_data = item.get("image_url", {})
+ url = image_url_data.get("url", "") if isinstance(image_url_data, dict) else image_url_data
+
+ # Parse data URLs to extract base64 data
+ if url.startswith("data:"):
+ # Format: data:mime_type;base64,data
+ parts = url.split(",", 1)
+ header = parts[0]
+ data = parts[1] if len(parts) > 1 else ""
+ mime_type = header.split(":")[1].split(";")[0] if ":" in header else "image/png"
+
+ agui_content.append(BinaryInputContent(
+ type="binary",
+ mime_type=mime_type,
+ data=data
+ ))
+ else:
+ # Regular URL or ID
+ agui_content.append(BinaryInputContent(
+ type="binary",
+ mime_type="image/png", # Default MIME type
+ url=url
+ ))
+ return agui_content
+
def langchain_messages_to_agui(messages: List[BaseMessage]) -> List[AGUIMessage]:
agui_messages: List[AGUIMessage] = []
for message in messages:
if isinstance(message, HumanMessage):
+ # Handle multimodal content
+ if isinstance(message.content, list):
+ content = convert_langchain_multimodal_to_agui(message.content)
+ else:
+ content = stringify_if_needed(resolve_message_content(message.content))
+
agui_messages.append(AGUIUserMessage(
id=str(message.id),
role="user",
- content=stringify_if_needed(resolve_message_content(message.content)),
+ content=content,
name=message.name,
))
elif isinstance(message, AIMessage):
@@ -91,14 +135,49 @@ def langchain_messages_to_agui(messages: List[BaseMessage]) -> List[AGUIMessage]
raise TypeError(f"Unsupported message type: {type(message)}")
return agui_messages
+def convert_agui_multimodal_to_langchain(content: List[Union[TextInputContent, BinaryInputContent]]) -> List[Dict[str, Any]]:
+ """Convert AG-UI multimodal content to LangChain's multimodal format."""
+ langchain_content = []
+ for item in content:
+ if isinstance(item, TextInputContent):
+ langchain_content.append({
+ "type": "text",
+ "text": item.text
+ })
+ elif isinstance(item, BinaryInputContent):
+ # LangChain uses image_url format (OpenAI-style)
+ content_dict = {"type": "image_url"}
+
+ # Prioritize url, then data, then id
+ if item.url:
+ content_dict["image_url"] = {"url": item.url}
+ elif item.data:
+ # Construct data URL from base64 data
+ content_dict["image_url"] = {"url": f"data:{item.mime_type};base64,{item.data}"}
+ elif item.id:
+ # Use id as a reference (some providers may support this)
+ content_dict["image_url"] = {"url": item.id}
+
+ langchain_content.append(content_dict)
+
+ return langchain_content
+
def agui_messages_to_langchain(messages: List[AGUIMessage]) -> List[BaseMessage]:
langchain_messages = []
for message in messages:
role = message.role
if role == "user":
+ # Handle multimodal content
+ if isinstance(message.content, str):
+ content = message.content
+ elif isinstance(message.content, list):
+ content = convert_agui_multimodal_to_langchain(message.content)
+ else:
+ content = str(message.content)
+
langchain_messages.append(HumanMessage(
id=message.id,
- content=message.content,
+ content=content,
name=message.name,
))
elif role == "assistant":
@@ -177,6 +256,36 @@ def resolve_message_content(content: Any) -> str | None:
return None
+
+def flatten_user_content(content: Any) -> str:
+ """
+ Flatten multimodal content into plain text.
+ Used for backwards compatibility or when multimodal is not supported.
+ """
+ if content is None:
+ return ""
+
+ if isinstance(content, str):
+ return content
+
+ if isinstance(content, list):
+ parts = []
+ for item in content:
+ if isinstance(item, TextInputContent):
+ if item.text:
+ parts.append(item.text)
+ elif isinstance(item, BinaryInputContent):
+ # Add descriptive placeholder for binary content
+ if item.filename:
+ parts.append(f"[Binary content: {item.filename}]")
+ elif item.url:
+ parts.append(f"[Binary content: {item.url}]")
+ else:
+ parts.append(f"[Binary content: {item.mime_type}]")
+ return "\n".join(parts)
+
+ return str(content)
+
def camel_to_snake(name):
return re.sub(r'(? Any:
**make_json_safe(value.__dict__),
}
- return repr(value)
\ No newline at end of file
+ return repr(value)
diff --git a/integrations/langgraph/python/examples/agents/multimodal_messages/__init__.py b/integrations/langgraph/python/examples/agents/multimodal_messages/__init__.py
new file mode 100644
index 000000000..989a9b717
--- /dev/null
+++ b/integrations/langgraph/python/examples/agents/multimodal_messages/__init__.py
@@ -0,0 +1,51 @@
+"""
+Multimodal Messages Example
+
+This example demonstrates how to use AG-UI's multimodal message support
+to send and receive messages containing both text and images.
+
+Key features:
+- User messages can contain text and binary content (images, audio, files)
+- Automatic conversion between AG-UI and LangChain multimodal formats
+- Support for vision models like GPT-4o and Claude 3
+
+Example usage:
+
+```python
+from ag_ui.core import UserMessage, TextInputContent, BinaryInputContent
+
+# Create a multimodal user message
+message = UserMessage(
+ id="user-123",
+ content=[
+ TextInputContent(text="What's in this image?"),
+ BinaryInputContent(
+ mime_type="image/jpeg",
+ url="https://example.com/photo.jpg"
+ ),
+ ],
+)
+
+# Or with base64 encoded data
+message_with_data = UserMessage(
+ id="user-124",
+ content=[
+ TextInputContent(text="Describe this picture"),
+ BinaryInputContent(
+ mime_type="image/png",
+ data="iVBORw0KGgoAAAANSUhEUgAAAAUA...", # base64 encoded
+ filename="screenshot.png"
+ ),
+ ],
+)
+```
+
+The LangGraph integration automatically handles:
+1. Converting AG-UI multimodal format to LangChain's format
+2. Passing multimodal messages to vision models
+3. Converting responses back to AG-UI format
+"""
+
+from .agent import graph
+
+__all__ = ["graph"]
diff --git a/integrations/langgraph/python/examples/agents/multimodal_messages/agent.py b/integrations/langgraph/python/examples/agents/multimodal_messages/agent.py
new file mode 100644
index 000000000..067006f9b
--- /dev/null
+++ b/integrations/langgraph/python/examples/agents/multimodal_messages/agent.py
@@ -0,0 +1,90 @@
+"""
+An example demonstrating multimodal message support with images.
+
+This agent demonstrates how to:
+1. Receive user messages with images
+2. Process multimodal content (text + images)
+3. Use vision models to analyze images
+"""
+
+from typing import List, Any, Optional
+import os
+
+from langchain_core.runnables import RunnableConfig
+from langchain_core.messages import SystemMessage
+from langchain_openai import ChatOpenAI
+from langgraph.graph import StateGraph, END, START
+from langgraph.graph import MessagesState
+from langgraph.types import Command
+
+class AgentState(MessagesState):
+ """
+ State of our graph.
+ """
+ tools: List[Any]
+
+async def vision_chat_node(state: AgentState, config: Optional[RunnableConfig] = None):
+ """
+ Chat node that supports multimodal input including images.
+
+ The messages in state can contain multimodal content with text and images.
+ LangGraph will automatically handle the conversion from AG-UI format to
+ the format expected by the vision model.
+ """
+
+ # 1. Use a vision-capable model
+ # GPT-4o supports vision, as do other models like Claude 3
+ model = ChatOpenAI(model="gpt-4o")
+
+ # Define config for the model
+ if config is None:
+ config = RunnableConfig(recursion_limit=25)
+
+ # 2. Bind tools if needed
+ model_with_tools = model.bind_tools(
+ state.get("tools", []),
+ parallel_tool_calls=False,
+ )
+
+ # 3. Define the system message
+ system_message = SystemMessage(
+ content=(
+ "You are a helpful vision assistant. You can analyze images and "
+ "answer questions about them. Describe what you see in detail."
+ )
+ )
+
+ # 4. Run the model with multimodal messages
+ # The messages may contain both text and images
+ response = await model_with_tools.ainvoke([
+ system_message,
+ *state["messages"],
+ ], config)
+
+ # 5. Return the response
+ return Command(
+ goto=END,
+ update={
+ "messages": response
+ }
+ )
+
+# Define a new graph
+workflow = StateGraph(AgentState)
+workflow.add_node("vision_chat_node", vision_chat_node)
+workflow.set_entry_point("vision_chat_node")
+
+# Add edges
+workflow.add_edge(START, "vision_chat_node")
+workflow.add_edge("vision_chat_node", END)
+
+# Conditionally use a checkpointer based on the environment
+is_fast_api = os.environ.get("LANGGRAPH_FAST_API", "false").lower() == "true"
+
+# Compile the graph
+if is_fast_api:
+ from langgraph.checkpoint.memory import MemorySaver
+ memory = MemorySaver()
+ graph = workflow.compile(checkpointer=memory)
+else:
+ graph = workflow.compile()
diff --git a/integrations/langgraph/python/poetry.lock b/integrations/langgraph/python/poetry.lock
index d5ffe403a..aa5300483 100644
--- a/integrations/langgraph/python/poetry.lock
+++ b/integrations/langgraph/python/poetry.lock
@@ -1,19 +1,21 @@
-# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand.
+# This file is automatically @generated by Poetry 2.1.4 and should not be changed by hand.
[[package]]
name = "ag-ui-protocol"
-version = "0.1.7"
+version = "0.1.9"
description = ""
optional = false
-python-versions = "<4.0,>=3.9"
+python-versions = "^3.9"
groups = ["main"]
-files = [
- {file = "ag_ui_protocol-0.1.7-py3-none-any.whl", hash = "sha256:8c821662ca6e9852569022f449b9f7aeb3f16aa75390fa8c28ceae2cce642baa"},
- {file = "ag_ui_protocol-0.1.7.tar.gz", hash = "sha256:0e93fd9f7c74d52afbd824d6e9738bd3422e859503905ba7582481cbc3c67ab2"},
-]
+files = []
+develop = true
[package.dependencies]
-pydantic = ">=2.11.2,<3.0.0"
+pydantic = "^2.11.2"
+
+[package.source]
+type = "directory"
+url = "../../../sdks/python"
[[package]]
name = "annotated-types"
@@ -1473,4 +1475,4 @@ fastapi = ["fastapi"]
[metadata]
lock-version = "2.1"
python-versions = "<3.14,>=3.10"
-content-hash = "b4f98fd8fba22b450b106c36ab12b2dc3bdc656b060ac257b5d21b40b51b4f17"
+content-hash = "7b62eac41b70b284f5d430698b1e0c8dd23dca37f9178ee00f10edc774f006f5"
diff --git a/integrations/langgraph/python/pyproject.toml b/integrations/langgraph/python/pyproject.toml
index e64a0051e..26d515725 100644
--- a/integrations/langgraph/python/pyproject.toml
+++ b/integrations/langgraph/python/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ag-ui-langgraph"
-version = "0.0.17"
+version = "0.0.18a0"
description = "Implementation of the AG-UI protocol for LangGraph."
authors = ["Ran Shem Tov "]
readme = "README.md"
@@ -10,7 +10,7 @@ exclude = [
[tool.poetry.dependencies]
python = "<3.14,>=3.10"
-ag-ui-protocol = "==0.1.9"
+ag-ui-protocol = "==0.2.0a0"
fastapi = { version = "^0.115.12", optional = true }
langchain = ">=0.3.0"
langchain-core = ">=0.3.0"
diff --git a/integrations/langgraph/python/tests/test_multimodal.py b/integrations/langgraph/python/tests/test_multimodal.py
new file mode 100644
index 000000000..afc68fffc
--- /dev/null
+++ b/integrations/langgraph/python/tests/test_multimodal.py
@@ -0,0 +1,227 @@
+"""
+Tests for multimodal message conversion between AG-UI and LangChain formats.
+"""
+
+import unittest
+from ag_ui.core import (
+ UserMessage,
+ TextInputContent,
+ BinaryInputContent,
+)
+from langchain_core.messages import HumanMessage
+
+from ag_ui_langgraph.utils import (
+ agui_messages_to_langchain,
+ langchain_messages_to_agui,
+ convert_agui_multimodal_to_langchain,
+ convert_langchain_multimodal_to_agui,
+ flatten_user_content,
+)
+
+
+class TestMultimodalConversion(unittest.TestCase):
+ """Test multimodal message conversion between AG-UI and LangChain."""
+
+ def test_agui_text_only_to_langchain(self):
+ """Test converting a text-only AG-UI message to LangChain."""
+ agui_message = UserMessage(
+ id="test-1",
+ role="user",
+ content="Hello, world!"
+ )
+
+ lc_messages = agui_messages_to_langchain([agui_message])
+
+ self.assertEqual(len(lc_messages), 1)
+ self.assertIsInstance(lc_messages[0], HumanMessage)
+ self.assertEqual(lc_messages[0].content, "Hello, world!")
+ self.assertEqual(lc_messages[0].id, "test-1")
+
+ def test_agui_multimodal_to_langchain(self):
+ """Test converting a multimodal AG-UI message to LangChain."""
+ agui_message = UserMessage(
+ id="test-2",
+ role="user",
+ content=[
+ TextInputContent(type="text", text="What's in this image?"),
+ BinaryInputContent(
+ type="binary",
+ mime_type="image/jpeg",
+ url="https://example.com/photo.jpg"
+ ),
+ ]
+ )
+
+ lc_messages = agui_messages_to_langchain([agui_message])
+
+ self.assertEqual(len(lc_messages), 1)
+ self.assertIsInstance(lc_messages[0], HumanMessage)
+ self.assertIsInstance(lc_messages[0].content, list)
+ self.assertEqual(len(lc_messages[0].content), 2)
+
+ # Check text content
+ self.assertEqual(lc_messages[0].content[0]["type"], "text")
+ self.assertEqual(lc_messages[0].content[0]["text"], "What's in this image?")
+
+ # Check image content
+ self.assertEqual(lc_messages[0].content[1]["type"], "image_url")
+ self.assertEqual(
+ lc_messages[0].content[1]["image_url"]["url"],
+ "https://example.com/photo.jpg"
+ )
+
+ def test_agui_multimodal_with_data_to_langchain(self):
+ """Test converting AG-UI message with base64 data to LangChain."""
+ agui_message = UserMessage(
+ id="test-3",
+ role="user",
+ content=[
+ TextInputContent(type="text", text="Analyze this"),
+ BinaryInputContent(
+ type="binary",
+ mime_type="image/png",
+ data="iVBORw0KGgoAAAANSUhEUgAAAAUA",
+ filename="test.png"
+ ),
+ ]
+ )
+
+ lc_messages = agui_messages_to_langchain([agui_message])
+
+ self.assertEqual(len(lc_messages), 1)
+ self.assertIsInstance(lc_messages[0].content, list)
+ self.assertEqual(len(lc_messages[0].content), 2)
+
+ # Check that data URL is properly formatted
+ image_content = lc_messages[0].content[1]
+ self.assertEqual(image_content["type"], "image_url")
+ self.assertTrue(
+ image_content["image_url"]["url"].startswith("data:image/png;base64,")
+ )
+
+ def test_langchain_multimodal_to_agui(self):
+ """Test converting LangChain multimodal message to AG-UI."""
+ lc_message = HumanMessage(
+ id="test-4",
+ content=[
+ {"type": "text", "text": "What do you see?"},
+ {
+ "type": "image_url",
+ "image_url": {"url": "https://example.com/image.jpg"}
+ },
+ ]
+ )
+
+ agui_messages = langchain_messages_to_agui([lc_message])
+
+ self.assertEqual(len(agui_messages), 1)
+ self.assertEqual(agui_messages[0].role, "user")
+ self.assertIsInstance(agui_messages[0].content, list)
+ self.assertEqual(len(agui_messages[0].content), 2)
+
+ # Check text content
+ self.assertIsInstance(agui_messages[0].content[0], TextInputContent)
+ self.assertEqual(agui_messages[0].content[0].text, "What do you see?")
+
+ # Check binary content
+ self.assertIsInstance(agui_messages[0].content[1], BinaryInputContent)
+ self.assertEqual(agui_messages[0].content[1].mime_type, "image/png")
+ self.assertEqual(agui_messages[0].content[1].url, "https://example.com/image.jpg")
+
+ def test_langchain_data_url_to_agui(self):
+ """Test converting LangChain data URL to AG-UI."""
+ lc_message = HumanMessage(
+ id="test-5",
+ content=[
+ {"type": "text", "text": "Check this out"},
+ {
+ "type": "image_url",
+ "image_url": {"url": "data:image/png;base64,iVBORw0KGgo"}
+ },
+ ]
+ )
+
+ agui_messages = langchain_messages_to_agui([lc_message])
+
+ self.assertEqual(len(agui_messages), 1)
+ self.assertIsInstance(agui_messages[0].content, list)
+ self.assertEqual(len(agui_messages[0].content), 2)
+
+ # Check that data URL was parsed correctly
+ binary_content = agui_messages[0].content[1]
+ self.assertIsInstance(binary_content, BinaryInputContent)
+ self.assertEqual(binary_content.mime_type, "image/png")
+ self.assertEqual(binary_content.data, "iVBORw0KGgo")
+
+ def test_flatten_multimodal_content(self):
+ """Test flattening multimodal content to plain text."""
+ content = [
+ TextInputContent(type="text", text="Hello"),
+ BinaryInputContent(
+ type="binary",
+ mime_type="image/jpeg",
+ url="https://example.com/image.jpg"
+ ),
+ TextInputContent(type="text", text="World"),
+ ]
+
+ flattened = flatten_user_content(content)
+
+ self.assertIn("Hello", flattened)
+ self.assertIn("World", flattened)
+ self.assertIn("[Binary content: https://example.com/image.jpg]", flattened)
+
+ def test_flatten_with_filename(self):
+ """Test flattening binary content with filename."""
+ content = [
+ TextInputContent(type="text", text="Check this file"),
+ BinaryInputContent(
+ type="binary",
+ mime_type="application/pdf",
+ url="https://example.com/doc.pdf",
+ filename="report.pdf"
+ ),
+ ]
+
+ flattened = flatten_user_content(content)
+
+ self.assertIn("Check this file", flattened)
+ self.assertIn("[Binary content: report.pdf]", flattened)
+
+ def test_convert_agui_multimodal_to_langchain_helper(self):
+ """Test the convert_agui_multimodal_to_langchain helper function."""
+ agui_content = [
+ TextInputContent(type="text", text="Test text"),
+ BinaryInputContent(
+ type="binary",
+ mime_type="image/png",
+ url="https://example.com/test.png"
+ ),
+ ]
+
+ lc_content = convert_agui_multimodal_to_langchain(agui_content)
+
+ self.assertEqual(len(lc_content), 2)
+ self.assertEqual(lc_content[0]["type"], "text")
+ self.assertEqual(lc_content[0]["text"], "Test text")
+ self.assertEqual(lc_content[1]["type"], "image_url")
+ self.assertEqual(lc_content[1]["image_url"]["url"], "https://example.com/test.png")
+
+ def test_convert_langchain_multimodal_to_agui_helper(self):
+ """Test the convert_langchain_multimodal_to_agui helper function."""
+ lc_content = [
+ {"type": "text", "text": "Test text"},
+ {"type": "image_url", "image_url": {"url": "https://example.com/test.png"}},
+ ]
+
+ agui_content = convert_langchain_multimodal_to_agui(lc_content)
+
+ self.assertEqual(len(agui_content), 2)
+ self.assertIsInstance(agui_content[0], TextInputContent)
+ self.assertEqual(agui_content[0].text, "Test text")
+ self.assertIsInstance(agui_content[1], BinaryInputContent)
+ self.assertEqual(agui_content[1].url, "https://example.com/test.png")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/integrations/langgraph/typescript/examples/src/agents/multimodal_messages/agent.ts b/integrations/langgraph/typescript/examples/src/agents/multimodal_messages/agent.ts
new file mode 100644
index 000000000..f6c0ac7da
--- /dev/null
+++ b/integrations/langgraph/typescript/examples/src/agents/multimodal_messages/agent.ts
@@ -0,0 +1,120 @@
+/**
+ * An example demonstrating multimodal message support with images.
+ *
+ * This agent demonstrates how to:
+ * 1. Receive user messages with images
+ * 2. Process multimodal content (text + images)
+ * 3. Use vision models to analyze images
+ *
+ * Example usage:
+ *
+ * ```typescript
+ * import { UserMessage, TextInputContent, BinaryInputContent } from "@ag-ui/core";
+ *
+ * // Create a multimodal user message
+ * const message: UserMessage = {
+ * id: "user-123",
+ * role: "user",
+ * content: [
+ * { type: "text", text: "What's in this image?" },
+ * {
+ * type: "binary",
+ * mimeType: "image/jpeg",
+ * url: "https://example.com/photo.jpg"
+ * },
+ * ],
+ * };
+ *
+ * // Or with base64 encoded data
+ * const messageWithData: UserMessage = {
+ * id: "user-124",
+ * role: "user",
+ * content: [
+ * { type: "text", text: "Describe this picture" },
+ * {
+ * type: "binary",
+ * mimeType: "image/png",
+ * data: "iVBORw0KGgoAAAANSUhEUgAAAAUA...", // base64 encoded
+ * filename: "screenshot.png"
+ * },
+ * ],
+ * };
+ * ```
+ *
+ * The LangGraph integration automatically handles:
+ * 1. Converting AG-UI multimodal format to LangChain's format
+ * 2. Passing multimodal messages to vision models
+ * 3. Converting responses back to AG-UI format
+ */
+
+import { ChatOpenAI } from "@langchain/openai";
+import { SystemMessage } from "@langchain/core/messages";
+import { RunnableConfig } from "@langchain/core/runnables";
+import { Annotation, MessagesAnnotation, StateGraph, Command, START, END } from "@langchain/langgraph";
+
+const AgentStateAnnotation = Annotation.Root({
+ tools: Annotation({
+ reducer: (x, y) => y ?? x,
+ default: () => []
+ }),
+ ...MessagesAnnotation.spec,
+});
+
+type AgentState = typeof AgentStateAnnotation.State;
+
+async function visionChatNode(state: AgentState, config?: RunnableConfig) {
+ /**
+ * Chat node that supports multimodal input including images.
+ *
+ * The messages in state can contain multimodal content with text and images.
+ * LangGraph will automatically handle the conversion from AG-UI format to
+ * the format expected by the vision model.
+ */
+
+ // 1. Use a vision-capable model
+ // GPT-4o supports vision, as do other models like Claude 3
+ const model = new ChatOpenAI({ model: "gpt-4o" });
+
+ // Define config for the model
+ if (!config) {
+ config = { recursionLimit: 25 };
+ }
+
+ // 2. Bind tools if needed
+ const modelWithTools = model.bindTools(
+ state.tools ?? [],
+ {
+ parallel_tool_calls: false,
+ }
+ );
+
+ // 3. Define the system message
+ const systemMessage = new SystemMessage({
+ content: "You are a helpful vision assistant. You can analyze images and " +
+ "answer questions about them. Describe what you see in detail."
+ });
+
+ // 4. Run the model with multimodal messages
+ // The messages may contain both text and images
+ const response = await modelWithTools.invoke([
+ systemMessage,
+ ...state.messages,
+ ], config);
+
+ // 5. Return the response
+ return new Command({
+ goto: END,
+ update: {
+ messages: [response]
+ }
+ });
+}
+
+// Define a new graph
+const workflow = new StateGraph(AgentStateAnnotation)
+ .addNode("visionChatNode", visionChatNode)
+ .addEdge(START, "visionChatNode")
+ .addEdge("visionChatNode", END);
+
+// Compile the graph
+export const graph = workflow.compile();
diff --git a/integrations/langgraph/typescript/package.json b/integrations/langgraph/typescript/package.json
index e465fb9d5..e6e4cd5a1 100644
--- a/integrations/langgraph/typescript/package.json
+++ b/integrations/langgraph/typescript/package.json
@@ -1,6 +1,6 @@
{
"name": "@ag-ui/langgraph",
- "version": "0.0.18",
+ "version": "0.0.19-alpha.1",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
@@ -29,8 +29,8 @@
"rxjs": "7.8.1"
},
"peerDependencies": {
- "@ag-ui/core": ">=0.0.38",
- "@ag-ui/client": ">=0.0.38"
+ "@ag-ui/core": "0.0.40-alpha.7",
+ "@ag-ui/client": "0.0.40-alpha.7"
},
"devDependencies": {
"@ag-ui/core": "workspace:*",
diff --git a/integrations/langgraph/typescript/src/utils.test.ts b/integrations/langgraph/typescript/src/utils.test.ts
new file mode 100644
index 000000000..5042c6e20
--- /dev/null
+++ b/integrations/langgraph/typescript/src/utils.test.ts
@@ -0,0 +1,224 @@
+/**
+ * Tests for multimodal message conversion between AG-UI and LangChain formats.
+ */
+
+import { Message as LangGraphMessage } from "@langchain/langgraph-sdk";
+import { Message, UserMessage, TextInputContent, BinaryInputContent } from "@ag-ui/client";
+import { aguiMessagesToLangChain, langchainMessagesToAgui } from "./utils";
+
+describe("Multimodal Message Conversion", () => {
+ describe("aguiMessagesToLangChain", () => {
+ it("should convert text-only AG-UI message to LangChain", () => {
+ const aguiMessage: UserMessage = {
+ id: "test-1",
+ role: "user",
+ content: "Hello, world!",
+ };
+
+ const lcMessages = aguiMessagesToLangChain([aguiMessage]);
+
+ expect(lcMessages).toHaveLength(1);
+ expect(lcMessages[0].type).toBe("human");
+ expect(lcMessages[0].content).toBe("Hello, world!");
+ expect(lcMessages[0].id).toBe("test-1");
+ });
+
+ it("should convert multimodal AG-UI message to LangChain", () => {
+ const aguiMessage: UserMessage = {
+ id: "test-2",
+ role: "user",
+ content: [
+ { type: "text", text: "What's in this image?" },
+ {
+ type: "binary",
+ mimeType: "image/jpeg",
+ url: "https://example.com/photo.jpg",
+ },
+ ],
+ };
+
+ const lcMessages = aguiMessagesToLangChain([aguiMessage]);
+
+ expect(lcMessages).toHaveLength(1);
+ expect(lcMessages[0].type).toBe("human");
+ expect(Array.isArray(lcMessages[0].content)).toBe(true);
+
+ const content = lcMessages[0].content as Array;
+ expect(content).toHaveLength(2);
+
+ // Check text content
+ expect(content[0].type).toBe("text");
+ expect(content[0].text).toBe("What's in this image?");
+
+ // Check image content
+ expect(content[1].type).toBe("image_url");
+ expect(content[1].image_url.url).toBe("https://example.com/photo.jpg");
+ });
+
+ it("should convert AG-UI message with base64 data to LangChain", () => {
+ const aguiMessage: UserMessage = {
+ id: "test-3",
+ role: "user",
+ content: [
+ { type: "text", text: "Analyze this" },
+ {
+ type: "binary",
+ mimeType: "image/png",
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAUA",
+ filename: "test.png",
+ },
+ ],
+ };
+
+ const lcMessages = aguiMessagesToLangChain([aguiMessage]);
+
+ expect(lcMessages).toHaveLength(1);
+ expect(Array.isArray(lcMessages[0].content)).toBe(true);
+
+ const content = lcMessages[0].content as Array;
+ expect(content).toHaveLength(2);
+
+ // Check that data URL is properly formatted
+ const imageContent = content[1];
+ expect(imageContent.type).toBe("image_url");
+ expect(imageContent.image_url.url).toContain("data:image/png;base64,");
+ });
+ });
+
+ describe("langchainMessagesToAgui", () => {
+ it("should convert text-only LangChain message to AG-UI", () => {
+ const lcMessage: LangGraphMessage = {
+ id: "test-4",
+ type: "human",
+ content: "Hello from LangChain",
+ };
+
+ const aguiMessages = langchainMessagesToAgui([lcMessage]);
+
+ expect(aguiMessages).toHaveLength(1);
+ expect(aguiMessages[0].role).toBe("user");
+ expect(aguiMessages[0].content).toBe("Hello from LangChain");
+ });
+
+ it("should convert LangChain multimodal message to AG-UI", () => {
+ const lcMessage: LangGraphMessage = {
+ id: "test-5",
+ type: "human",
+ content: [
+ { type: "text", text: "What do you see?" },
+ {
+ type: "image_url",
+ image_url: { url: "https://example.com/image.jpg" },
+ },
+ ] as any,
+ };
+
+ const aguiMessages = langchainMessagesToAgui([lcMessage]);
+
+ expect(aguiMessages).toHaveLength(1);
+ expect(aguiMessages[0].role).toBe("user");
+ expect(Array.isArray(aguiMessages[0].content)).toBe(true);
+
+ const content = aguiMessages[0].content as Array;
+ expect(content).toHaveLength(2);
+
+ // Check text content
+ expect(content[0].type).toBe("text");
+ expect((content[0] as TextInputContent).text).toBe("What do you see?");
+
+ // Check binary content
+ expect(content[1].type).toBe("binary");
+ expect((content[1] as BinaryInputContent).mimeType).toBe("image/png");
+ expect((content[1] as BinaryInputContent).url).toBe("https://example.com/image.jpg");
+ });
+
+ it("should convert LangChain data URL to AG-UI", () => {
+ const lcMessage: LangGraphMessage = {
+ id: "test-6",
+ type: "human",
+ content: [
+ { type: "text", text: "Check this out" },
+ {
+ type: "image_url",
+ image_url: { url: "data:image/png;base64,iVBORw0KGgo" },
+ },
+ ] as any,
+ };
+
+ const aguiMessages = langchainMessagesToAgui([lcMessage]);
+
+ expect(aguiMessages).toHaveLength(1);
+ expect(Array.isArray(aguiMessages[0].content)).toBe(true);
+
+ const content = aguiMessages[0].content as Array;
+ expect(content).toHaveLength(2);
+
+ // Check that data URL was parsed correctly
+ const binaryContent = content[1] as BinaryInputContent;
+ expect(binaryContent.type).toBe("binary");
+ expect(binaryContent.mimeType).toBe("image/png");
+ expect(binaryContent.data).toBe("iVBORw0KGgo");
+ });
+ });
+
+ describe("Edge cases", () => {
+ it("should handle empty content arrays", () => {
+ const aguiMessage: UserMessage = {
+ id: "test-7",
+ role: "user",
+ content: [],
+ };
+
+ const lcMessages = aguiMessagesToLangChain([aguiMessage]);
+
+ expect(lcMessages).toHaveLength(1);
+ expect(Array.isArray(lcMessages[0].content)).toBe(true);
+ expect((lcMessages[0].content as Array)).toHaveLength(0);
+ });
+
+ it("should handle binary content with only id", () => {
+ const aguiMessage: UserMessage = {
+ id: "test-8",
+ role: "user",
+ content: [
+ {
+ type: "binary",
+ mimeType: "image/jpeg",
+ id: "img-123",
+ },
+ ],
+ };
+
+ const lcMessages = aguiMessagesToLangChain([aguiMessage]);
+
+ expect(lcMessages).toHaveLength(1);
+ const content = lcMessages[0].content as Array;
+ expect(content).toHaveLength(1);
+ expect(content[0].type).toBe("image_url");
+ expect(content[0].image_url.url).toBe("img-123");
+ });
+
+ it("should skip binary content without any source", () => {
+ const aguiMessage: UserMessage = {
+ id: "test-9",
+ role: "user",
+ content: [
+ { type: "text", text: "Hello" },
+ {
+ type: "binary",
+ mimeType: "image/jpeg",
+ // No url, data, or id
+ } as BinaryInputContent,
+ ],
+ };
+
+ const lcMessages = aguiMessagesToLangChain([aguiMessage]);
+
+ expect(lcMessages).toHaveLength(1);
+ const content = lcMessages[0].content as Array;
+ // Binary content should be skipped, only text remains
+ expect(content).toHaveLength(1);
+ expect(content[0].type).toBe("text");
+ });
+ });
+});
diff --git a/integrations/langgraph/typescript/src/utils.ts b/integrations/langgraph/typescript/src/utils.ts
index d69ac6778..8866a9d7f 100644
--- a/integrations/langgraph/typescript/src/utils.ts
+++ b/integrations/langgraph/typescript/src/utils.ts
@@ -1,6 +1,6 @@
import { Message as LangGraphMessage } from "@langchain/langgraph-sdk";
import { State, SchemaKeys, LangGraphReasoning } from "./types";
-import { Message, ToolCall } from "@ag-ui/client";
+import { Message, ToolCall, TextInputContent, BinaryInputContent, InputContent , UserMessage} from "@ag-ui/client";
export const DEFAULT_SCHEMA_KEYS = ["messages", "tools"];
@@ -26,21 +26,118 @@ export function getStreamPayloadInput({
return input;
}
+/**
+ * Convert LangChain's multimodal content to AG-UI format
+ */
+function convertLangchainMultimodalToAgui(
+ content: Array<{ type: string; text?: string; image_url?: any }>
+): InputContent[] {
+ const aguiContent: InputContent[] = [];
+
+ for (const item of content) {
+ if (item.type === "text" && item.text) {
+ aguiContent.push({
+ type: "text",
+ text: item.text,
+ });
+ } else if (item.type === "image_url") {
+ const imageUrl = typeof item.image_url === "string"
+ ? item.image_url
+ : item.image_url?.url;
+
+ if (!imageUrl) continue;
+
+ // Parse data URLs to extract base64 data
+ if (imageUrl.startsWith("data:")) {
+ // Format: data:mime_type;base64,data
+ const [header, data] = imageUrl.split(",", 2);
+ const mimeType = header.includes(":")
+ ? header.split(":")[1].split(";")[0]
+ : "image/png";
+
+ aguiContent.push({
+ type: "binary",
+ mimeType,
+ data: data || "",
+ });
+ } else {
+ // Regular URL or ID
+ aguiContent.push({
+ type: "binary",
+ mimeType: "image/png", // Default MIME type
+ url: imageUrl,
+ });
+ }
+ }
+ }
+
+ return aguiContent;
+}
+
+/**
+ * Convert AG-UI multimodal content to LangChain's format
+ */
+function convertAguiMultimodalToLangchain(
+ content: InputContent[]
+): Array<{ type: string; text?: string; image_url?: { url: string } }> {
+ const langchainContent: Array<{ type: string; text?: string; image_url?: { url: string } }> = [];
+
+ for (const item of content) {
+ if (item.type === "text") {
+ langchainContent.push({
+ type: "text",
+ text: item.text,
+ });
+ } else if (item.type === "binary") {
+ // LangChain uses image_url format (OpenAI-style)
+ let url: string;
+
+ // Prioritize url, then data, then id
+ if (item.url) {
+ url = item.url;
+ } else if (item.data) {
+ // Construct data URL from base64 data
+ url = `data:${item.mimeType};base64,${item.data}`;
+ } else if (item.id) {
+ // Use id as a reference
+ url = item.id;
+ } else {
+ continue; // Skip if no source is provided
+ }
+
+ langchainContent.push({
+ type: "image_url",
+ image_url: { url },
+ });
+ }
+ }
+
+ return langchainContent;
+}
+
export function langchainMessagesToAgui(messages: LangGraphMessage[]): Message[] {
return messages.map((message) => {
switch (message.type) {
case "human":
+ // Handle multimodal content
+ let userContent: string | InputContent[];
+ if (Array.isArray(message.content)) {
+ userContent = convertLangchainMultimodalToAgui(message.content as any);
+ } else {
+ userContent = stringifyIfNeeded(resolveMessageContent(message.content));
+ }
+
return {
id: message.id!,
role: "user",
- content: stringifyIfNeeded(resolveMessageContent(message.content)),
+ content: userContent,
};
case "ai":
- const content = resolveMessageContent(message.content)
+ const aiContent = resolveMessageContent(message.content)
return {
id: message.id!,
role: "assistant",
- content: content ? stringifyIfNeeded(content) : '',
+ content: aiContent ? stringifyIfNeeded(aiContent) : '',
toolCalls: message.tool_calls?.map((tc) => ({
id: tc.id!,
type: "function",
@@ -73,12 +170,22 @@ export function aguiMessagesToLangChain(messages: Message[]): LangGraphMessage[]
return messages.map((message, index) => {
switch (message.role) {
case "user":
+ // Handle multimodal content
+ let content: UserMessage['content'];
+ if (typeof message.content === "string") {
+ content = message.content;
+ } else if (Array.isArray(message.content)) {
+ content = convertAguiMultimodalToLangchain(message.content) as any;
+ } else {
+ content = String(message.content);
+ }
+
return {
id: message.id,
role: message.role,
- content: message.content,
+ content,
type: "human",
- };
+ } as LangGraphMessage;
case "assistant":
return {
id: message.id,
@@ -119,6 +226,42 @@ function stringifyIfNeeded(item: any) {
return JSON.stringify(item);
}
+/**
+ * Flatten multimodal content into plain text.
+ * Used for backwards compatibility or when multimodal is not supported.
+ */
+function flattenUserContent(content: Message["content"]): string {
+ if (typeof content === "string") {
+ return content;
+ }
+
+ if (!Array.isArray(content)) {
+ return "";
+ }
+
+ const parts: string[] = [];
+
+ for (const item of content) {
+ if (item.type === "text" && "text" in item) {
+ if (item.text) {
+ parts.push(item.text);
+ }
+ } else if (item.type === "binary" && "mimeType" in item) {
+ // Add descriptive placeholder for binary content
+ const binaryItem = item as BinaryInputContent;
+ if (binaryItem.filename) {
+ parts.push(`[Binary content: ${binaryItem.filename}]`);
+ } else if (binaryItem.url) {
+ parts.push(`[Binary content: ${binaryItem.url}]`);
+ } else {
+ parts.push(`[Binary content: ${binaryItem.mimeType}]`);
+ }
+ }
+ }
+
+ return parts.join("\n");
+}
+
export function resolveReasoningContent(eventData: any): LangGraphReasoning | null {
const content = eventData.chunk?.content
diff --git a/integrations/mastra/typescript/src/mastra.ts b/integrations/mastra/typescript/src/mastra.ts
index 63aab3f86..e6b9f95a8 100644
--- a/integrations/mastra/typescript/src/mastra.ts
+++ b/integrations/mastra/typescript/src/mastra.ts
@@ -51,14 +51,19 @@ export class MastraAgent extends AbstractAgent {
resourceId?: string;
runtimeContext?: RuntimeContext;
- constructor({ agent, resourceId, runtimeContext, ...rest }: MastraAgentConfig) {
+ constructor(private config: MastraAgentConfig) {
+ const { agent, resourceId, runtimeContext, ...rest } = config;
super(rest);
this.agent = agent;
this.resourceId = resourceId;
this.runtimeContext = runtimeContext ?? new RuntimeContext();
}
- run(input: RunAgentInput): Observable {
+ public clone() {
+ return new MastraAgent(this.config);
+ }
+
+ protected run(input: RunAgentInput): Observable {
let messageId = randomUUID();
return new Observable((subscriber) => {
diff --git a/integrations/mastra/typescript/src/utils.ts b/integrations/mastra/typescript/src/utils.ts
index 665e846fa..e775d353f 100644
--- a/integrations/mastra/typescript/src/utils.ts
+++ b/integrations/mastra/typescript/src/utils.ts
@@ -1,4 +1,4 @@
-import type { Message } from "@ag-ui/client";
+import type { InputContent, Message } from "@ag-ui/client";
import { AbstractAgent } from "@ag-ui/client";
import { MastraClient } from "@mastra/client-js";
import type { CoreMessage, Mastra } from "@mastra/core";
@@ -6,12 +6,39 @@ import { Agent as LocalMastraAgent } from "@mastra/core/agent";
import { RuntimeContext } from "@mastra/core/runtime-context";
import { MastraAgent } from "./mastra";
+const toMastraTextContent = (content: Message["content"]): string => {
+ if (!content) {
+ return "";
+ }
+
+ if (typeof content === "string") {
+ return content;
+ }
+
+ if (!Array.isArray(content)) {
+ return "";
+ }
+
+ type TextInput = Extract;
+
+ const textParts = content
+ .filter((part): part is TextInput => part.type === "text")
+ .map((part: TextInput) => part.text.trim())
+ .filter(Boolean);
+
+ return textParts.join("\n");
+};
+
export function convertAGUIMessagesToMastra(messages: Message[]): CoreMessage[] {
const result: CoreMessage[] = [];
for (const message of messages) {
if (message.role === "assistant") {
- const parts: any[] = message.content ? [{ type: "text", text: message.content }] : [];
+ const assistantContent = toMastraTextContent(message.content);
+ const parts: any[] = [];
+ if (assistantContent) {
+ parts.push({ type: "text", text: assistantContent });
+ }
for (const toolCall of message.toolCalls ?? []) {
parts.push({
type: "tool-call",
@@ -25,9 +52,10 @@ export function convertAGUIMessagesToMastra(messages: Message[]): CoreMessage[]
content: parts,
});
} else if (message.role === "user") {
+ const userContent = toMastraTextContent(message.content);
result.push({
role: "user",
- content: message.content || "",
+ content: userContent,
});
} else if (message.role === "tool") {
let toolName = "unknown";
diff --git a/integrations/vercel-ai-sdk/typescript/src/index.ts b/integrations/vercel-ai-sdk/typescript/src/index.ts
index e2d3436c4..80498d7c7 100644
--- a/integrations/vercel-ai-sdk/typescript/src/index.ts
+++ b/integrations/vercel-ai-sdk/typescript/src/index.ts
@@ -25,10 +25,54 @@ import {
tool as createVercelAISDKTool,
ToolChoice,
ToolSet,
+ FilePart,
+ ImagePart,
+ TextPart,
} from "ai";
import { randomUUID } from "@ag-ui/client";
import { z } from "zod";
+type VercelUserContent = Extract["content"];
+type VercelUserArrayContent = Extract;
+type VercelUserPart = VercelUserArrayContent extends Array ? Part : never;
+
+const toVercelUserParts = (inputContent: Message["content"]): VercelUserPart[] => {
+ if (!Array.isArray(inputContent)) {
+ return [];
+ }
+
+ const parts: VercelUserPart[] = [];
+
+ for (const part of inputContent) {
+ if (part.type === "text") {
+ parts.push({ type: "text", text: part.text } as VercelUserPart);
+ }
+ }
+
+ return parts;
+};
+
+const toVercelUserContent = (content: Message["content"]): VercelUserContent => {
+ if (!content) {
+ return "";
+ }
+
+ if (typeof content === "string") {
+ return content;
+ }
+
+ const parts = toVercelUserParts(content);
+ if (parts.length === 0) {
+ return "";
+ }
+
+ if (parts.length === 1 && parts[0].type === "text") {
+ return parts[0].text;
+ }
+
+ return parts;
+};
+
type ProcessedEvent =
| MessagesSnapshotEvent
| RunFinishedEvent
@@ -48,14 +92,19 @@ export class VercelAISDKAgent extends AbstractAgent {
model: LanguageModelV1;
maxSteps: number;
toolChoice: ToolChoice>;
- constructor({ model, maxSteps, toolChoice, ...rest }: VercelAISDKAgentConfig) {
+ constructor(private config: VercelAISDKAgentConfig) {
+ const { model, maxSteps, toolChoice, ...rest } = config;
super({ ...rest });
this.model = model;
this.maxSteps = maxSteps ?? 1;
this.toolChoice = toolChoice ?? "auto";
}
- run(input: RunAgentInput): Observable {
+ public clone() {
+ return new VercelAISDKAgent(this.config);
+ }
+
+ protected run(input: RunAgentInput): Observable {
const finalMessages: Message[] = input.messages;
return new Observable((subscriber) => {
@@ -188,7 +237,7 @@ export function convertMessagesToVercelAISDKMessages(messages: Message[]): CoreM
} else if (message.role === "user") {
result.push({
role: "user",
- content: message.content || "",
+ content: toVercelUserContent(message.content),
});
} else if (message.role === "tool") {
let toolName = "unknown";
diff --git a/middlewares/a2a-middleware/src/index.ts b/middlewares/a2a-middleware/src/index.ts
index 7e5b90e16..3a18dab43 100644
--- a/middlewares/a2a-middleware/src/index.ts
+++ b/middlewares/a2a-middleware/src/index.ts
@@ -304,6 +304,7 @@ export class A2AMiddlewareAgent extends AbstractAgent {
pendingA2ACalls: Set,
pendingTextMessages: Set,
): void {
+ // @ts-expect-error orchestrationAgent.run remains protected; middleware intentionally bypasses until public API exists.
const newRunStream = this.orchestrationAgent.run(input);
this.wrapStream(newRunStream, pendingA2ACalls, pendingTextMessages, observer, input);
}
diff --git a/package.json b/package.json
index 8e7e9a40c..ec3d2bda9 100644
--- a/package.json
+++ b/package.json
@@ -13,11 +13,11 @@
"check-types": "turbo run check-types",
"test": "turbo run test",
"create-integration": "pnpm dlx tsx create-integration.ts",
- "bump": "pnpm --filter './packages/*' exec -- pnpm version",
- "bump:alpha": "pnpm --filter './packages/*' exec -- pnpm version --preid alpha",
- "publish": "pnpm -r clean && pnpm install && turbo run build && pnpm publish -r --filter='./packages/*'",
+ "bump": "pnpm --filter './sdks/typescript/packages/*' exec -- pnpm version",
+ "bump:alpha": "pnpm --filter './sdks/typescript/packages/*' exec -- pnpm version --preid alpha",
+ "publish": "pnpm -r clean && pnpm install && turbo run build && pnpm publish -r --filter='./sdks/typescript/packages/*'",
"publish:integrations": "pnpm -r clean && pnpm install && turbo run build && pnpm publish -r --filter='./integrations/*'",
- "publish:alpha": "pnpm -r clean && pnpm install && turbo run build && pnpm publish -r --no-git-checks --filter='./packages/*' --tag alpha"
+ "publish:alpha": "pnpm -r clean && pnpm install && turbo run build && pnpm publish -r --no-git-checks --filter='./sdks/typescript/packages/*' --tag alpha"
},
"devDependencies": {
"prettier": "^3.5.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 70be6bfa3..f9ec0554c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -66,6 +66,12 @@ importers:
apps/dojo:
dependencies:
+ '@a2a-js/sdk':
+ specifier: 0.2.5
+ version: 0.2.5
+ '@ag-ui/a2a':
+ specifier: workspace:*
+ version: link:../../integrations/a2a/typescript
'@ag-ui/a2a-middleware':
specifier: workspace:*
version: link:../../middlewares/a2a-middleware
@@ -77,16 +83,16 @@ importers:
version: link:../../integrations/agno/typescript
'@ag-ui/client':
specifier: workspace:*
- version: link:../../sdks/typescript/packages/client
+ version: 0.0.40-alpha.10
'@ag-ui/core':
specifier: workspace:*
- version: link:../../sdks/typescript/packages/core
+ version: 0.0.40-alpha.10
'@ag-ui/crewai':
specifier: workspace:*
version: link:../../integrations/crew-ai/typescript
'@ag-ui/encoder':
specifier: workspace:*
- version: link:../../sdks/typescript/packages/encoder
+ version: 0.0.40-alpha.10
'@ag-ui/langgraph':
specifier: workspace:*
version: link:../../integrations/langgraph/typescript
@@ -101,7 +107,7 @@ importers:
version: link:../../middlewares/middleware-starter
'@ag-ui/proto':
specifier: workspace:*
- version: link:../../sdks/typescript/packages/proto
+ version: 0.0.40-alpha.10
'@ag-ui/pydantic-ai':
specifier: workspace:*
version: link:../../integrations/pydantic-ai/typescript
@@ -128,13 +134,22 @@ importers:
version: 1.10.6(@types/react@19.2.2)(graphql@16.11.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@copilotkit/runtime':
specifier: 1.10.6
- version: 1.10.6(43a54c62826e391639c20a8a0387b983)
+ version: 1.10.6(mowpvgryoykmzvzmtdo322deu4)
'@copilotkit/runtime-client-gql':
specifier: 1.10.6
version: 1.10.6(graphql@16.11.0)(react@19.2.0)
'@copilotkit/shared':
specifier: 1.10.6
version: 1.10.6
+ '@copilotkitnext/agent':
+ specifier: 0.0.19-alpha.0
+ version: 0.0.19-alpha.0
+ '@copilotkitnext/react':
+ specifier: 0.0.19-alpha.0
+ version: 0.0.19-alpha.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@copilotkitnext/runtime':
+ specifier: 0.0.19-alpha.0
+ version: 0.0.19-alpha.0(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
'@mastra/client-js':
specifier: ^0.15.2
version: 0.15.2(openapi-types@12.1.3)(react@19.2.0)(zod@3.25.76)
@@ -216,6 +231,9 @@ importers:
fast-json-patch:
specifier: ^3.1.1
version: 3.1.1
+ hono:
+ specifier: ^4.10.3
+ version: 4.10.3
lucide-react:
specifier: ^0.477.0
version: 0.477.0(react@19.2.0)
@@ -314,6 +332,40 @@ importers:
specifier: ^1.1.0
version: 1.1.0
+ integrations/a2a/typescript:
+ dependencies:
+ '@a2a-js/sdk':
+ specifier: ^0.2.2
+ version: 0.2.5
+ rxjs:
+ specifier: 7.8.1
+ version: 7.8.1
+ devDependencies:
+ '@ag-ui/client':
+ specifier: workspace:*
+ version: link:../../../sdks/typescript/packages/client
+ '@ag-ui/core':
+ specifier: workspace:*
+ version: link:../../../sdks/typescript/packages/core
+ '@types/jest':
+ specifier: ^29.5.14
+ version: 29.5.14
+ '@types/node':
+ specifier: ^20.11.19
+ version: 20.19.21
+ jest:
+ specifier: ^29.7.0
+ version: 29.7.0(@types/node@20.19.21)
+ ts-jest:
+ specifier: ^29.1.2
+ version: 29.4.5(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0)(typescript@5.9.3)
+ tsup:
+ specifier: ^8.0.2
+ version: 8.5.0(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)
+ typescript:
+ specifier: ^5.3.3
+ version: 5.9.3
+
integrations/adk-middleware/typescript:
dependencies:
rxjs:
@@ -507,7 +559,7 @@ importers:
version: 1.2.11(zod@3.25.76)
'@copilotkit/runtime':
specifier: ^1.10.5
- version: 1.10.6(2963fdc46a5185bf1f60e289781c45cd)
+ version: 1.10.6(oefq6yilp5bsxwbn3p2ta6g6we)
'@mastra/client-js':
specifier: ^0.15.2
version: 0.15.2(openapi-types@12.1.3)(react@19.2.0)(zod@3.25.76)
@@ -953,32 +1005,35 @@ packages:
'@ag-ui/client@0.0.35':
resolution: {integrity: sha512-rHtMQSU232dZeVx9qAGt1+j4ar4RWqwFanXcyNxAwbAh0XrY7VZeXFBDUeazy1LtBoViS7xehX8V1Ssf1a+bUw==}
+ '@ag-ui/client@0.0.40-alpha.10':
+ resolution: {integrity: sha512-b1hcS1+nMzsNSSt4E3tryFD4Bd9DQqcqYg0R1CeD9dx4bbDg2cF0sjtUBDpbAQ6N3l4438VranGE9Sj+UozUBg==}
+
'@ag-ui/core@0.0.35':
resolution: {integrity: sha512-YAqrln3S3fdo+Hs5FFQPODXiBttyilv/E3xSSHCuxqC0Y/Fp3+VqyDx97BorO3NVp2VKZ9cG2nsO3cbmcTwkQw==}
'@ag-ui/core@0.0.37':
resolution: {integrity: sha512-7bmjPn1Ol0Zo00F+MrPr0eOwH4AFZbhmq/ZMhCsrMILtVYBiBLcLU9QFBpBL3Zm9MCHha8b79N7JE2FzwcMaVA==}
- '@ag-ui/core@0.0.39':
- resolution: {integrity: sha512-T5Hp4oFkQ+H5MynWAvSwrX/rNYJOD+PJ4qPQ0o771oSZQAxoIvDDft47Cx5wRyBNNLXAe1RWqJjfWUUwJFNKqA==}
+ '@ag-ui/core@0.0.40-alpha.10':
+ resolution: {integrity: sha512-VczUym5UTwVdvJPD95z4cqSEnrygjINqrqZX4ru1gnNlf8PPmhElNPdE/ZiFEgmZAXaxzK6rI+LB6iDx5NltQA==}
'@ag-ui/encoder@0.0.35':
resolution: {integrity: sha512-Ym0h0ZKIiD1Ld3+e3v/WQSogY62xs72ysoEBW1kt+dDs79QazBsW5ZlcBBj2DelEs9NrczQLxTVEvrkcvhrHqA==}
- '@ag-ui/encoder@0.0.39':
- resolution: {integrity: sha512-6fsoFwPWkStK7Uyj3pwBn7+aQjUWf7pbDTSI43cD53sBLvTr5oEFNnoKOzRfC5UqvHc4JjUIuLKPQyjHRwWg4g==}
+ '@ag-ui/encoder@0.0.40-alpha.10':
+ resolution: {integrity: sha512-aoBhFIcX+SGWzvw/FAK4+mHY6NIz5YA7DchjRCBWAyAGWrdSEObKRgPRifahOrl3hhKgSZo0MYwOin9Q33B+rg==}
- '@ag-ui/langgraph@0.0.18':
- resolution: {integrity: sha512-soWSV8+xR91jMArZUJoRv85UCgTi3Zt3u3gTMZhvs1t6fGFpAi6+hEQ4AqP13Rgvg90IlmIU8MTWo2k0OZDnoA==}
+ '@ag-ui/langgraph@0.0.19-alpha.1':
+ resolution: {integrity: sha512-rX8Y4LSxTXWUMFzCspO0c42b6YWGTuciP69Okrh7Lw3kpGsmFq/zmXoBLFz654Yuii2zLHl5mZvkBJ5a3nI6lA==}
peerDependencies:
- '@ag-ui/client': '>=0.0.38'
- '@ag-ui/core': '>=0.0.38'
+ '@ag-ui/client': 0.0.40-alpha.7
+ '@ag-ui/core': 0.0.40-alpha.7
'@ag-ui/proto@0.0.35':
resolution: {integrity: sha512-+rz3LAYHcR3D2xVgRKa7QE5mp+cwmZs6j+1XxG5dT7HNdg51uKea12L57EVY2bxE3JzpAvCIgOjFEmQCNH82pw==}
- '@ag-ui/proto@0.0.39':
- resolution: {integrity: sha512-xlj/PzZHkJ3CgoQC5QP9g7DEl/78wUK1+A2rdkoLKoNAMOkM2g6jKw0N88iFIh5GZhtiCNN2wb8XwRWPYx9XQQ==}
+ '@ag-ui/proto@0.0.40-alpha.10':
+ resolution: {integrity: sha512-d7FzAIjWyQzaMEZyMkTMgIyW+qK7LUg2T/MpjAGqWjjcrWGk2Zh6DU/rNMwMbYnK/YlXS3Ljo5a5gI95SrLS+Q==}
'@ai-sdk/anthropic@2.0.23':
resolution: {integrity: sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw==}
@@ -1074,6 +1129,12 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
+ '@antfu/install-pkg@1.1.0':
+ resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
+
+ '@antfu/utils@9.3.0':
+ resolution: {integrity: sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==}
+
'@anthropic-ai/sdk@0.27.3':
resolution: {integrity: sha512-IjLt0gd3L4jlOfilxVXTifn42FnVffMgDC04RJK1KDZpmkBWLv0XC92MVVmkxrFZNS/7l3xWgP/I3nqtX1sQHw==}
@@ -1471,6 +1532,9 @@ packages:
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+ '@braintree/sanitize-url@7.1.1':
+ resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==}
+
'@browserbasehq/sdk@2.6.0':
resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==}
@@ -1489,6 +1553,21 @@ packages:
'@cfworker/json-schema@4.1.1':
resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==}
+ '@chevrotain/cst-dts-gen@11.0.3':
+ resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==}
+
+ '@chevrotain/gast@11.0.3':
+ resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==}
+
+ '@chevrotain/regexp-to-ast@11.0.3':
+ resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==}
+
+ '@chevrotain/types@11.0.3':
+ resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==}
+
+ '@chevrotain/utils@11.0.3':
+ resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==}
+
'@clack/core@0.5.0':
resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==}
@@ -1523,6 +1602,35 @@ packages:
'@copilotkit/shared@1.10.6':
resolution: {integrity: sha512-56Rltf4fDBqCpl1ZXARypt5NdE4LTg3tGPPLurZpgPmm31Lv5EAHpfjC7I55vt9A0mXWlTCHtCrpiaAlTyzGJw==}
+ '@copilotkitnext/agent@0.0.19-alpha.0':
+ resolution: {integrity: sha512-8S9Ds+9gHeNYyaGLA0luExqrqyEpJG4msQn5d5RM+Vw/mcQ4870b4JR5WG6QUi/pzvrsYhTKI//rWl8LrDAzLA==}
+ engines: {node: '>=18'}
+
+ '@copilotkitnext/core@0.0.19-alpha.0':
+ resolution: {integrity: sha512-pIv1mrAW2uNNpaWWgUtkZNhscQwu6dGAfL1BA9WYW0ipZPgrlSbR3OleKV0D5z+uV51XCknWTSNM49ioTcgZZQ==}
+ engines: {node: '>=18'}
+
+ '@copilotkitnext/react@0.0.19-alpha.0':
+ resolution: {integrity: sha512-ApVlwXDalzwYaVNki6srA4Ab9LDw5VcqECzoZK8wf+6aSYvdLIXLe1ZLxIFemzU/Cuks8rmhJ0Bwk0gbRSk9eA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+
+ '@copilotkitnext/runtime@0.0.19-alpha.0':
+ resolution: {integrity: sha512-QVlHOg/hJyg3i5J2Sm+OEHACeYJYYtBBmwzQpqz2o0j4B20Z4PPgeffMEBta0LjbPen+mdsDykTVRvupVTd+5g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ openai: ^5.9.0
+
+ '@copilotkitnext/shared@0.0.19-alpha.0':
+ resolution: {integrity: sha512-dfQCod+NmD0ttfpLOZxeEj3IQprlgsj8Z5+XCRAkXtKRWf+BaW53BzPZMPPKN1oUSJSK8g7jb3aoD+3Ek5w1AA==}
+ engines: {node: '>=18'}
+
+ '@copilotkitnext/web-inspector@0.0.19-alpha.0':
+ resolution: {integrity: sha512-em2OACHCarRXuZgpqpULshXGQpZhAeZVbwzkD5u60zy4ALmGDjFZ4Rb7FUhR+vwIpFQ/yZfOjvxoduVzu+wrBA==}
+ engines: {node: '>=18'}
+
'@emnapi/core@1.5.0':
resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==}
@@ -1852,6 +1960,12 @@ packages:
resolution: {integrity: sha512-TmLaoFXmLc7yVFJIQS25mzZcuWfju4JmRXcO62KthDKNENyPpXXJukrHN6gXfv1BotzFt0M2kyRnO1Vt8ZLlxQ==}
engines: {node: '>=18.0.0'}
+ '@iconify/types@2.0.0':
+ resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
+
+ '@iconify/utils@3.0.2':
+ resolution: {integrity: sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==}
+
'@img/sharp-darwin-arm64@0.33.5':
resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -2711,6 +2825,20 @@ packages:
cpu: [x64]
os: [win32]
+ '@lit-labs/react@2.1.3':
+ resolution: {integrity: sha512-OD9h2JynerBQUMNzb563jiVpxfvPF0HjQkKY2mx0lpVYvD7F+rtJpOGz6ek+6ufMidV3i+MPT9SX62OKWHFrQg==}
+
+ '@lit-labs/ssr-dom-shim@1.4.0':
+ resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==}
+
+ '@lit/react@1.0.8':
+ resolution: {integrity: sha512-p2+YcF+JE67SRX3mMlJ1TKCSTsgyOVdAwd/nxp3NuV1+Cb6MWALbN6nT7Ld4tpmYofcE5kcaSY1YBB9erY+6fw==}
+ peerDependencies:
+ '@types/react': 17 || 18 || 19
+
+ '@lit/reactive-element@2.1.1':
+ resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==}
+
'@lukeed/csprng@1.1.0':
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
engines: {node: '>=8'}
@@ -2824,6 +2952,9 @@ packages:
'@types/react': '>=16'
react: '>=16'
+ '@mermaid-js/parser@0.6.3':
+ resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==}
+
'@modelcontextprotocol/sdk@1.20.0':
resolution: {integrity: sha512-kOQ4+fHuT4KbR2iq2IjeV32HiihueuOf1vJkq18z08CLZ1UQrTc8BXJpVfxZkq45+inLLD+D4xx4nBjUelJa4Q==}
engines: {node: '>=18'}
@@ -3696,6 +3827,19 @@ packages:
'@types/react-dom':
optional: true
+ '@radix-ui/react-tooltip@1.2.8':
+ resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/react-use-callback-ref@1.1.1':
resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==}
peerDependencies:
@@ -3759,6 +3903,19 @@ packages:
'@types/react':
optional: true
+ '@radix-ui/react-visually-hidden@1.2.3':
+ resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
'@radix-ui/rect@1.1.1':
resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==}
@@ -4137,6 +4294,27 @@ packages:
resolution: {integrity: sha512-0dtu/5ApsOZ24qgaZwtif8jVwqol7a4m1x5AxPuM1k5wxhqU7t/qEfBGtaSki1R8VlbTQfCj5PAlO45NKCa7Gg==}
hasBin: true
+ '@shikijs/core@3.14.0':
+ resolution: {integrity: sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw==}
+
+ '@shikijs/engine-javascript@3.14.0':
+ resolution: {integrity: sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ==}
+
+ '@shikijs/engine-oniguruma@3.14.0':
+ resolution: {integrity: sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==}
+
+ '@shikijs/langs@3.14.0':
+ resolution: {integrity: sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==}
+
+ '@shikijs/themes@3.14.0':
+ resolution: {integrity: sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==}
+
+ '@shikijs/types@3.14.0':
+ resolution: {integrity: sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==}
+
+ '@shikijs/vscode-textmate@10.0.2':
+ resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
+
'@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
@@ -4644,6 +4822,99 @@ packages:
'@types/cors@2.8.19':
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
+ '@types/d3-array@3.2.2':
+ resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
+
+ '@types/d3-axis@3.0.6':
+ resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
+
+ '@types/d3-brush@3.0.6':
+ resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
+
+ '@types/d3-chord@3.0.6':
+ resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
+
+ '@types/d3-color@3.1.3':
+ resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
+
+ '@types/d3-contour@3.0.6':
+ resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
+
+ '@types/d3-delaunay@6.0.4':
+ resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
+
+ '@types/d3-dispatch@3.0.7':
+ resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
+
+ '@types/d3-drag@3.0.7':
+ resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
+
+ '@types/d3-dsv@3.0.7':
+ resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
+
+ '@types/d3-ease@3.0.2':
+ resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
+
+ '@types/d3-fetch@3.0.7':
+ resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
+
+ '@types/d3-force@3.0.10':
+ resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
+
+ '@types/d3-format@3.0.4':
+ resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
+
+ '@types/d3-geo@3.1.0':
+ resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
+
+ '@types/d3-hierarchy@3.1.7':
+ resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
+
+ '@types/d3-interpolate@3.0.4':
+ resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
+
+ '@types/d3-path@3.1.1':
+ resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
+
+ '@types/d3-polygon@3.0.2':
+ resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
+
+ '@types/d3-quadtree@3.0.6':
+ resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
+
+ '@types/d3-random@3.0.3':
+ resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
+
+ '@types/d3-scale-chromatic@3.1.0':
+ resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
+
+ '@types/d3-scale@4.0.9':
+ resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
+
+ '@types/d3-selection@3.0.11':
+ resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
+
+ '@types/d3-shape@3.1.7':
+ resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
+
+ '@types/d3-time-format@4.0.3':
+ resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
+
+ '@types/d3-time@3.0.4':
+ resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
+
+ '@types/d3-timer@3.0.2':
+ resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
+
+ '@types/d3-transition@3.0.9':
+ resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
+
+ '@types/d3-zoom@3.0.8':
+ resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
+
+ '@types/d3@7.4.3':
+ resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
+
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -4665,6 +4936,9 @@ packages:
'@types/express@4.17.23':
resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==}
+ '@types/geojson@7946.0.16':
+ resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
+
'@types/graceful-fs@4.1.9':
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
@@ -4802,6 +5076,9 @@ packages:
'@types/tough-cookie@4.0.5':
resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==}
+ '@types/trusted-types@2.0.7':
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
@@ -5390,6 +5667,14 @@ packages:
chardet@2.1.0:
resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==}
+ chevrotain-allstar@0.3.1:
+ resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==}
+ peerDependencies:
+ chevrotain: ^11.0.0
+
+ chevrotain@11.0.3:
+ resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==}
+
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
@@ -5501,6 +5786,10 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
@@ -5567,6 +5856,12 @@ packages:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
+ cose-base@1.0.3:
+ resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
+
+ cose-base@2.2.0:
+ resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
+
create-jest@29.7.0:
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -5594,6 +5889,162 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ cytoscape-cose-bilkent@4.1.0:
+ resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape-fcose@2.2.0:
+ resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+
+ cytoscape@3.33.1:
+ resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==}
+ engines: {node: '>=0.10'}
+
+ d3-array@2.12.1:
+ resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
+
+ d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+
+ d3-axis@3.0.0:
+ resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
+ engines: {node: '>=12'}
+
+ d3-brush@3.0.0:
+ resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
+ engines: {node: '>=12'}
+
+ d3-chord@3.0.1:
+ resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
+ engines: {node: '>=12'}
+
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-contour@4.0.2:
+ resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
+ engines: {node: '>=12'}
+
+ d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ engines: {node: '>=12'}
+
+ d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+
+ d3-drag@3.0.0:
+ resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
+ engines: {node: '>=12'}
+
+ d3-dsv@3.0.1:
+ resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-fetch@3.0.1:
+ resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+ engines: {node: '>=12'}
+
+ d3-force@3.0.0:
+ resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+ engines: {node: '>=12'}
+
+ d3-format@3.1.0:
+ resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+ engines: {node: '>=12'}
+
+ d3-geo@3.1.1:
+ resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+ engines: {node: '>=12'}
+
+ d3-hierarchy@3.1.2:
+ resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-path@1.0.9:
+ resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
+
+ d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+
+ d3-polygon@3.0.1:
+ resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
+ engines: {node: '>=12'}
+
+ d3-quadtree@3.0.1:
+ resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+ engines: {node: '>=12'}
+
+ d3-random@3.0.1:
+ resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+ engines: {node: '>=12'}
+
+ d3-sankey@0.12.3:
+ resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
+
+ d3-scale-chromatic@3.1.0:
+ resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+ engines: {node: '>=12'}
+
+ d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-shape@1.3.7:
+ resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
+
+ d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+
+ d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+
+ d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ d3-transition@3.0.1:
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+
+ d3-zoom@3.0.0:
+ resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
+ engines: {node: '>=12'}
+
+ d3@7.9.0:
+ resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
+ engines: {node: '>=12'}
+
+ dagre-d3-es@7.0.13:
+ resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==}
+
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -5622,6 +6073,9 @@ packages:
dateformat@4.6.3:
resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
+ dayjs@1.11.18:
+ resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==}
+
debug@2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
@@ -5704,6 +6158,9 @@ packages:
defu@6.1.4:
resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
+ delaunator@5.0.1:
+ resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
+
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -5765,6 +6222,9 @@ packages:
dompurify@3.1.7:
resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==}
+ dompurify@3.3.0:
+ resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
+
dotenv@16.6.1:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
engines: {node: '>=12'}
@@ -6397,6 +6857,10 @@ packages:
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
engines: {node: '>=18'}
+ globals@15.15.0:
+ resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
+ engines: {node: '>=18'}
+
globalthis@1.0.4:
resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
engines: {node: '>= 0.4'}
@@ -6458,6 +6922,9 @@ packages:
resolution: {integrity: sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==}
engines: {node: '>=12.0.0'}
+ hachure-fill@0.5.2:
+ resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
+
handlebars@4.7.8:
resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==}
engines: {node: '>=0.4.7'}
@@ -6490,9 +6957,21 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ hast-util-from-dom@5.0.1:
+ resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==}
+
+ hast-util-from-html-isomorphic@2.0.0:
+ resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==}
+
+ hast-util-from-html@2.0.3:
+ resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
+
hast-util-from-parse5@8.0.3:
resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
+ hast-util-is-element@3.0.0:
+ resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
+
hast-util-parse-selector@2.2.5:
resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==}
@@ -6505,12 +6984,18 @@ packages:
hast-util-to-estree@3.1.3:
resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
+ hast-util-to-html@9.0.5:
+ resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
+
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
hast-util-to-parse5@8.0.0:
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
+ hast-util-to-text@4.0.2:
+ resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==}
+
hast-util-whitespace@2.0.1:
resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==}
@@ -6577,6 +7062,10 @@ packages:
zod-openapi:
optional: true
+ hono@4.10.3:
+ resolution: {integrity: sha512-2LOYWUbnhdxdL8MNbNg9XZig6k+cZXm5IjHn2Aviv7honhBMOHb+jxrKIeJRZJRmn+htUCKhaicxwXuUDlchRA==}
+ engines: {node: '>=16.9.0'}
+
hono@4.9.12:
resolution: {integrity: sha512-SrTC0YxqPwnN7yKa8gg/giLyQ2pILCKoideIHbYbFQlWZjYt68D2A4Ae1hehO/aDQ6RmTcpqOV/O2yBtMzx/VQ==}
engines: {node: '>=16.9.0'}
@@ -6686,6 +7175,13 @@ packages:
resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
engines: {node: '>= 0.4'}
+ internmap@1.0.1:
+ resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
+
+ internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+
ip-regex@4.3.0:
resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
engines: {node: '>=8'}
@@ -7176,6 +7672,9 @@ packages:
keyv@4.5.4:
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ khroma@2.1.0:
+ resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+
kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
@@ -7184,6 +7683,9 @@ packages:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
+ kolorist@1.8.0:
+ resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
+
langchain@0.3.36:
resolution: {integrity: sha512-PqC19KChFF0QlTtYDFgfEbIg+SCnCXox29G8tY62QWfj9bOW7ew2kgWmPw5qoHLOTKOdQPvXET20/1Pdq8vAtQ==}
engines: {node: '>=18'}
@@ -7242,6 +7744,10 @@ packages:
typeorm:
optional: true
+ langium@3.3.1:
+ resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==}
+ engines: {node: '>=16.0.0'}
+
langsmith@0.3.74:
resolution: {integrity: sha512-ZuW3Qawz8w88XcuCRH91yTp6lsdGuwzRqZ5J0Hf5q/AjMz7DwcSv0MkE6V5W+8hFMI850QZN2Wlxwm3R9lHlZg==}
peerDependencies:
@@ -7266,6 +7772,12 @@ packages:
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
engines: {node: '>=0.10'}
+ layout-base@1.0.2:
+ resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
+
+ layout-base@2.0.1:
+ resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==}
+
leven@3.1.0:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
@@ -7279,7 +7791,6 @@ packages:
libsql@0.5.22:
resolution: {integrity: sha512-NscWthMQt7fpU8lqd7LXMvT9pi+KhhmTHAJWUB/Lj6MWa0MKFv0F2V4C6WKKpjCVZl0VwcDz4nOI3CyaT1DDiA==}
- cpu: [x64, arm64, wasm32, arm]
os: [darwin, linux, win32]
lightningcss-darwin-arm64@1.30.1:
@@ -7356,6 +7867,15 @@ packages:
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
+ lit-element@4.2.1:
+ resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==}
+
+ lit-html@3.3.1:
+ resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==}
+
+ lit@3.3.1:
+ resolution: {integrity: sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==}
+
load-tsconfig@0.2.5:
resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -7372,6 +7892,9 @@ packages:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
+ lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+
lodash.camelcase@4.3.0:
resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
@@ -7441,6 +7964,19 @@ packages:
peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ lucide-react@0.525.0:
+ resolution: {integrity: sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ lucide-react@0.542.0:
+ resolution: {integrity: sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ lucide@0.525.0:
+ resolution: {integrity: sha512-sfehWlaE/7NVkcEQ4T9JD3eID8RNMIGJBBUq9wF3UFiJIrcMKRbU3g1KGfDk4svcW7yw8BtDLXaXo02scDtUYQ==}
+
magic-string@0.30.19:
resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
@@ -7473,6 +8009,11 @@ packages:
engines: {node: '>= 18'}
hasBin: true
+ marked@16.4.1:
+ resolution: {integrity: sha512-ntROs7RaN3EvWfy3EZi14H4YxmT6A5YvywfhO+0pm+cH/dnSQRmdAmoFIc3B9aiwTehyk7pESH4ofyBY+V5hZg==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
mastra@0.15.1:
resolution: {integrity: sha512-8C+2/ANWRrDN82gBvWpvcsi0tuntgZvXu2QdPGuqLAoap/vinzMVQXMXDgLtvlQ8shW1E7x6Vwsr0UzoNPKrAg==}
hasBin: true
@@ -7572,6 +8113,9 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
+ mermaid@11.12.1:
+ resolution: {integrity: sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g==}
+
methods@1.1.2:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
@@ -8000,6 +8544,12 @@ packages:
resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
engines: {node: '>=12'}
+ oniguruma-parser@0.12.1:
+ resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
+
+ oniguruma-to-es@4.3.3:
+ resolution: {integrity: sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==}
+
open@10.2.0:
resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==}
engines: {node: '>=18'}
@@ -8089,6 +8639,9 @@ packages:
package-json-from-dist@1.0.1:
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+ package-manager-detector@1.5.0:
+ resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==}
+
parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -8117,6 +8670,9 @@ packages:
partial-json@0.1.7:
resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==}
+ path-data-parser@0.1.0:
+ resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -8244,6 +8800,12 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ points-on-curve@0.2.0:
+ resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==}
+
+ points-on-path@0.2.1:
+ resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
+
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -8585,10 +9147,25 @@ packages:
refractor@3.6.0:
resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==}
+ regex-recursion@6.0.2:
+ resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==}
+
+ regex-utilities@2.3.0:
+ resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==}
+
+ regex@6.0.1:
+ resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==}
+
regexp.prototype.flags@1.5.4:
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
engines: {node: '>= 0.4'}
+ rehype-harden@1.1.5:
+ resolution: {integrity: sha512-JrtBj5BVd/5vf3H3/blyJatXJbzQfRT9pJBmjafbTaPouQCAKxHwRyCc7dle9BXQKxv4z1OzZylz/tNamoiG3A==}
+
+ rehype-katex@7.0.1:
+ resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==}
+
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
@@ -8676,6 +9253,9 @@ packages:
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+ robust-predicates@3.0.2:
+ resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
+
rollup-plugin-esbuild@6.2.1:
resolution: {integrity: sha512-jTNOMGoMRhs0JuueJrJqbW8tOwxumaWYq+V5i+PD+8ecSCVkuX27tGW7BXqDgoULQ55rO7IdNxPcnsWtshz3AA==}
engines: {node: '>=14.18.0'}
@@ -8702,6 +9282,9 @@ packages:
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
+ roughjs@4.6.6:
+ resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==}
+
router@2.2.0:
resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
engines: {node: '>= 18'}
@@ -8717,6 +9300,9 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+
rxjs@7.8.1:
resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==}
@@ -8814,6 +9400,9 @@ packages:
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
engines: {node: '>= 0.4'}
+ shiki@3.14.0:
+ resolution: {integrity: sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g==}
+
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
@@ -8918,6 +9507,11 @@ packages:
resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
engines: {node: '>= 0.4'}
+ streamdown@1.4.0:
+ resolution: {integrity: sha512-ylhDSQ4HpK5/nAH9v7OgIIdGJxlJB2HoYrYkJNGrO8lMpnWuKUcrz/A8xAMwA6eILA27469vIavcOTjmxctrKg==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0
+
streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
@@ -9028,6 +9622,9 @@ packages:
babel-plugin-macros:
optional: true
+ stylis@4.3.6:
+ resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==}
+
sucrase@3.35.0:
resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -9147,6 +9744,14 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
+ ts-dedent@2.2.0:
+ resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
+ engines: {node: '>=6.10'}
+
+ ts-deepmerge@7.0.3:
+ resolution: {integrity: sha512-Du/ZW2RfwV/D4cmA5rXafYjBQVuvu4qGiEEla4EmEHVHgRdx68Gftx7i66jn2bzHPwSVZY36Ae6OuDn9el4ZKA==}
+ engines: {node: '>=14.13.1'}
+
ts-error@1.0.6:
resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==}
@@ -9254,6 +9859,9 @@ packages:
resolution: {integrity: sha512-5c9Fdsr9qfpT3hA0EyYSFRZj1dVVsb6KIWubA9JBYZ/9ZEAijgUEae0BBR/Xl/wekt4w65/lYLTFaP3JmwSO8w==}
hasBin: true
+ tw-animate-css@1.4.0:
+ resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==}
+
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -9354,6 +9962,9 @@ packages:
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+ unist-util-find-after@5.0.0:
+ resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
+
unist-util-generated@2.0.1:
resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
@@ -9456,6 +10067,11 @@ packages:
'@types/react':
optional: true
+ use-stick-to-bottom@1.1.1:
+ resolution: {integrity: sha512-JkDp0b0tSmv7HQOOpL1hT7t7QaoUBXkq045WWWOFDTlLGRzgIIyW7vyzOIJzY7L2XVIG7j1yUxeDj2LHm9Vwng==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
@@ -9512,6 +10128,26 @@ packages:
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ vscode-jsonrpc@8.2.0:
+ resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==}
+ engines: {node: '>=14.0.0'}
+
+ vscode-languageserver-protocol@3.17.5:
+ resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==}
+
+ vscode-languageserver-textdocument@1.0.12:
+ resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==}
+
+ vscode-languageserver-types@3.17.5:
+ resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==}
+
+ vscode-languageserver@9.0.1:
+ resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==}
+ hasBin: true
+
+ vscode-uri@3.0.8:
+ resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==}
+
w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
@@ -9721,6 +10357,18 @@ snapshots:
uuid: 11.1.0
zod: 3.25.76
+ '@ag-ui/client@0.0.40-alpha.10':
+ dependencies:
+ '@ag-ui/core': 0.0.40-alpha.10
+ '@ag-ui/encoder': 0.0.40-alpha.10
+ '@ag-ui/proto': 0.0.40-alpha.10
+ '@types/uuid': 10.0.0
+ fast-json-patch: 3.1.1
+ rxjs: 7.8.1
+ untruncate-json: 0.0.1
+ uuid: 11.1.0
+ zod: 3.25.76
+
'@ag-ui/core@0.0.35':
dependencies:
rxjs: 7.8.1
@@ -9731,7 +10379,7 @@ snapshots:
rxjs: 7.8.1
zod: 3.25.76
- '@ag-ui/core@0.0.39':
+ '@ag-ui/core@0.0.40-alpha.10':
dependencies:
rxjs: 7.8.1
zod: 3.25.76
@@ -9741,12 +10389,12 @@ snapshots:
'@ag-ui/core': 0.0.35
'@ag-ui/proto': 0.0.35
- '@ag-ui/encoder@0.0.39':
+ '@ag-ui/encoder@0.0.40-alpha.10':
dependencies:
- '@ag-ui/core': 0.0.39
- '@ag-ui/proto': 0.0.39
+ '@ag-ui/core': 0.0.40-alpha.10
+ '@ag-ui/proto': 0.0.40-alpha.10
- '@ag-ui/langgraph@0.0.18(@ag-ui/client@sdks+typescript+packages+client)(@ag-ui/core@sdks+typescript+packages+core)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ '@ag-ui/langgraph@0.0.19-alpha.1(@ag-ui/client@sdks+typescript+packages+client)(@ag-ui/core@sdks+typescript+packages+core)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@ag-ui/client': link:sdks/typescript/packages/client
'@ag-ui/core': link:sdks/typescript/packages/core
@@ -9767,9 +10415,9 @@ snapshots:
'@ag-ui/core': 0.0.35
'@bufbuild/protobuf': 2.9.0
- '@ag-ui/proto@0.0.39':
+ '@ag-ui/proto@0.0.40-alpha.10':
dependencies:
- '@ag-ui/core': 0.0.39
+ '@ag-ui/core': 0.0.40-alpha.10
'@bufbuild/protobuf': 2.9.0
'@protobuf-ts/protoc': 2.11.1
@@ -9871,6 +10519,13 @@ snapshots:
'@alloc/quick-lru@5.2.0': {}
+ '@antfu/install-pkg@1.1.0':
+ dependencies:
+ package-manager-detector: 1.5.0
+ tinyexec: 1.0.1
+
+ '@antfu/utils@9.3.0': {}
+
'@anthropic-ai/sdk@0.27.3':
dependencies:
'@types/node': 18.19.130
@@ -10732,6 +11387,8 @@ snapshots:
'@bcoe/v8-coverage@0.2.3': {}
+ '@braintree/sanitize-url@7.1.1': {}
+
'@browserbasehq/sdk@2.6.0':
dependencies:
'@types/node': 18.19.130
@@ -10780,6 +11437,23 @@ snapshots:
'@cfworker/json-schema@4.1.1': {}
+ '@chevrotain/cst-dts-gen@11.0.3':
+ dependencies:
+ '@chevrotain/gast': 11.0.3
+ '@chevrotain/types': 11.0.3
+ lodash-es: 4.17.21
+
+ '@chevrotain/gast@11.0.3':
+ dependencies:
+ '@chevrotain/types': 11.0.3
+ lodash-es: 4.17.21
+
+ '@chevrotain/regexp-to-ast@11.0.3': {}
+
+ '@chevrotain/types@11.0.3': {}
+
+ '@chevrotain/utils@11.0.3': {}
+
'@clack/core@0.5.0':
dependencies:
picocolors: 1.1.1
@@ -10836,22 +11510,22 @@ snapshots:
- encoding
- graphql
- '@copilotkit/runtime@1.10.6(2963fdc46a5185bf1f60e289781c45cd)':
+ '@copilotkit/runtime@1.10.6(mowpvgryoykmzvzmtdo322deu4)':
dependencies:
- '@ag-ui/client': link:sdks/typescript/packages/client
- '@ag-ui/core': link:sdks/typescript/packages/core
- '@ag-ui/encoder': 0.0.39
- '@ag-ui/langgraph': 0.0.18(@ag-ui/client@sdks+typescript+packages+client)(@ag-ui/core@sdks+typescript+packages+core)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
- '@ag-ui/proto': 0.0.39
+ '@ag-ui/client': 0.0.40-alpha.10
+ '@ag-ui/core': 0.0.40-alpha.10
+ '@ag-ui/encoder': 0.0.40-alpha.10
+ '@ag-ui/langgraph': link:integrations/langgraph/typescript
+ '@ag-ui/proto': 0.0.40-alpha.10
'@anthropic-ai/sdk': 0.57.0
'@copilotkit/shared': 1.10.6
'@graphql-yoga/plugin-defer-stream': 3.16.0(graphql-yoga@5.16.0(graphql@16.11.0))(graphql@16.11.0)
- '@langchain/aws': 0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))
- '@langchain/community': 0.3.57(8d705aac09841dc81e24dfe2c773558d)
+ '@langchain/aws': 0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))
+ '@langchain/community': 0.3.57(37emb7xvj5c4vxjobtfi323cve)
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
- '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)
- '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(react@19.2.0)
- '@langchain/openai': 0.4.9(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)
+ '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)
+ '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(react@19.2.0)
+ '@langchain/openai': 0.4.9(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)
'@scarf/scarf': 1.4.0
class-transformer: 0.5.1
class-validator: 0.14.2
@@ -10860,7 +11534,7 @@ snapshots:
graphql-scalars: 1.24.2(graphql@16.11.0)
graphql-yoga: 5.16.0(graphql@16.11.0)
groq-sdk: 0.5.0
- langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))))(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(axios@1.12.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(ws@8.18.3)
+ langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))))(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(axios@1.12.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(ws@8.18.3)
openai: 4.104.0(ws@8.18.3)(zod@3.25.76)
partial-json: 0.1.7
pino: 9.13.1
@@ -11018,18 +11692,18 @@ snapshots:
- ws
- youtubei.js
- '@copilotkit/runtime@1.10.6(43a54c62826e391639c20a8a0387b983)':
+ '@copilotkit/runtime@1.10.6(oefq6yilp5bsxwbn3p2ta6g6we)':
dependencies:
'@ag-ui/client': link:sdks/typescript/packages/client
'@ag-ui/core': link:sdks/typescript/packages/core
- '@ag-ui/encoder': link:sdks/typescript/packages/encoder
- '@ag-ui/langgraph': link:integrations/langgraph/typescript
- '@ag-ui/proto': link:sdks/typescript/packages/proto
+ '@ag-ui/encoder': 0.0.40-alpha.10
+ '@ag-ui/langgraph': 0.0.19-alpha.1(@ag-ui/client@sdks+typescript+packages+client)(@ag-ui/core@sdks+typescript+packages+core)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@ag-ui/proto': 0.0.40-alpha.10
'@anthropic-ai/sdk': 0.57.0
'@copilotkit/shared': 1.10.6
'@graphql-yoga/plugin-defer-stream': 3.16.0(graphql-yoga@5.16.0(graphql@16.11.0))(graphql@16.11.0)
'@langchain/aws': 0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))
- '@langchain/community': 0.3.57(a6f05470c76b31786172bd3244671918)
+ '@langchain/community': 0.3.57(75igdgciibrgswysse3hw62tgi)
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
'@langchain/google-gauth': 0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)
'@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(react@19.2.0)
@@ -11212,6 +11886,78 @@ snapshots:
transitivePeerDependencies:
- encoding
+ '@copilotkitnext/agent@0.0.19-alpha.0':
+ dependencies:
+ '@ag-ui/client': 0.0.40-alpha.10
+ '@ai-sdk/anthropic': 2.0.23(zod@3.25.76)
+ '@ai-sdk/google': 2.0.17(zod@3.25.76)
+ '@ai-sdk/openai': 2.0.52(zod@3.25.76)
+ '@modelcontextprotocol/sdk': 1.20.0
+ ai: 5.0.60(zod@3.25.76)
+ rxjs: 7.8.1
+ zod: 3.25.76
+ transitivePeerDependencies:
+ - supports-color
+
+ '@copilotkitnext/core@0.0.19-alpha.0':
+ dependencies:
+ '@ag-ui/client': 0.0.40-alpha.10
+ '@copilotkitnext/shared': 0.0.19-alpha.0
+ rxjs: 7.8.1
+ zod: 3.25.76
+ zod-to-json-schema: 3.24.6(zod@3.25.76)
+
+ '@copilotkitnext/react@0.0.19-alpha.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@ag-ui/client': 0.0.40-alpha.10
+ '@ag-ui/core': 0.0.40-alpha.10
+ '@copilotkitnext/core': 0.0.19-alpha.0
+ '@copilotkitnext/shared': 0.0.19-alpha.0
+ '@copilotkitnext/web-inspector': 0.0.19-alpha.0
+ '@lit-labs/react': 2.1.3(@types/react@19.2.2)
+ '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ class-variance-authority: 0.7.1
+ clsx: 2.1.1
+ katex: 0.16.25
+ lucide-react: 0.525.0(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ streamdown: 1.4.0(@types/react@19.2.2)(react@19.2.0)
+ tailwind-merge: 3.3.1
+ ts-deepmerge: 7.0.3
+ tw-animate-css: 1.4.0
+ use-stick-to-bottom: 1.1.1(react@19.2.0)
+ zod: 3.25.76
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ - supports-color
+
+ '@copilotkitnext/runtime@0.0.19-alpha.0(openai@4.104.0(ws@8.18.3)(zod@3.25.76))':
+ dependencies:
+ '@ag-ui/client': 0.0.40-alpha.10
+ '@ag-ui/core': 0.0.40-alpha.10
+ '@ag-ui/encoder': 0.0.40-alpha.10
+ '@copilotkitnext/shared': 0.0.19-alpha.0
+ hono: 4.10.3
+ openai: 4.104.0(ws@8.18.3)(zod@3.25.76)
+ rxjs: 7.8.1
+
+ '@copilotkitnext/shared@0.0.19-alpha.0':
+ dependencies:
+ '@ag-ui/client': 0.0.40-alpha.10
+ partial-json: 0.1.7
+ uuid: 11.1.0
+
+ '@copilotkitnext/web-inspector@0.0.19-alpha.0':
+ dependencies:
+ '@ag-ui/client': 0.0.40-alpha.10
+ '@copilotkitnext/core': 0.0.19-alpha.0
+ lit: 3.3.1
+ lucide: 0.525.0
+
'@emnapi/core@1.5.0':
dependencies:
'@emnapi/wasi-threads': 1.1.0
@@ -11506,6 +12252,21 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/utils@3.0.2':
+ dependencies:
+ '@antfu/install-pkg': 1.1.0
+ '@antfu/utils': 9.3.0
+ '@iconify/types': 2.0.0
+ debug: 4.4.3
+ globals: 15.15.0
+ kolorist: 1.8.0
+ local-pkg: 1.1.2
+ mlly: 1.8.0
+ transitivePeerDependencies:
+ - supports-color
+
'@img/sharp-darwin-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-darwin-arm64': 1.0.4
@@ -11926,29 +12687,19 @@ snapshots:
transitivePeerDependencies:
- aws-crt
- '@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))':
- dependencies:
- '@aws-sdk/client-bedrock-agent-runtime': 3.910.0
- '@aws-sdk/client-bedrock-runtime': 3.910.0
- '@aws-sdk/client-kendra': 3.910.0
- '@aws-sdk/credential-provider-node': 3.910.0
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- transitivePeerDependencies:
- - aws-crt
-
- '@langchain/community@0.3.57(8d705aac09841dc81e24dfe2c773558d)':
+ '@langchain/community@0.3.57(37emb7xvj5c4vxjobtfi323cve)':
dependencies:
- '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.56.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@5.12.2(ws@8.18.3)(zod@3.25.76))(zod@3.25.76)
+ '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.56.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(zod@3.25.76)
'@ibm-cloud/watsonx-ai': 1.7.0
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- '@langchain/openai': 0.6.16(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)
- '@langchain/weaviate': 0.2.3(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))
+ '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
+ '@langchain/openai': 0.6.16(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)
+ '@langchain/weaviate': 0.2.3(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))
binary-extensions: 2.3.0
expr-eval: 2.0.2
flat: 5.0.2
ibm-cloud-sdk-core: 5.4.3
js-yaml: 4.1.0
- langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))))(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(axios@1.12.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(ws@8.18.3)
+ langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))))(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(axios@1.12.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(ws@8.18.3)
langsmith: 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
openai: 4.104.0(ws@8.18.3)(zod@3.25.76)
uuid: 10.0.0
@@ -11993,9 +12744,9 @@ snapshots:
- handlebars
- peggy
- '@langchain/community@0.3.57(a6f05470c76b31786172bd3244671918)':
+ '@langchain/community@0.3.57(75igdgciibrgswysse3hw62tgi)':
dependencies:
- '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.56.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(zod@3.25.76)
+ '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.56.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@5.12.2(ws@8.18.3)(zod@3.25.76))(zod@3.25.76)
'@ibm-cloud/watsonx-ai': 1.7.0
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
'@langchain/openai': 0.6.16(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)
@@ -12098,14 +12849,6 @@ snapshots:
transitivePeerDependencies:
- zod
- '@langchain/google-common@0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)':
- dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- uuid: 10.0.0
- zod-to-json-schema: 3.24.6(zod@3.25.76)
- transitivePeerDependencies:
- - zod
-
'@langchain/google-gauth@0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)':
dependencies:
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
@@ -12116,16 +12859,6 @@ snapshots:
- supports-color
- zod
- '@langchain/google-gauth@0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)':
- dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- '@langchain/google-common': 0.1.8(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(zod@3.25.76)
- google-auth-library: 8.9.0
- transitivePeerDependencies:
- - encoding
- - supports-color
- - zod
-
'@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(react@19.2.0)':
dependencies:
'@types/json-schema': 7.0.15
@@ -12136,16 +12869,6 @@ snapshots:
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
react: 19.2.0
- '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(react@19.2.0)':
- dependencies:
- '@types/json-schema': 7.0.15
- p-queue: 6.6.2
- p-retry: 4.6.2
- uuid: 9.0.1
- optionalDependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- react: 19.2.0
-
'@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@types/json-schema': 7.0.15
@@ -12168,17 +12891,6 @@ snapshots:
- encoding
- ws
- '@langchain/openai@0.4.9(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)':
- dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- js-tiktoken: 1.0.21
- openai: 4.104.0(ws@8.18.3)(zod@3.25.76)
- zod: 3.25.76
- zod-to-json-schema: 3.24.6(zod@3.25.76)
- transitivePeerDependencies:
- - encoding
- - ws
-
'@langchain/openai@0.6.16(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)':
dependencies:
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
@@ -12188,25 +12900,11 @@ snapshots:
transitivePeerDependencies:
- ws
- '@langchain/openai@0.6.16(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)':
- dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- js-tiktoken: 1.0.21
- openai: 5.12.2(ws@8.18.3)(zod@3.25.76)
- zod: 3.25.76
- transitivePeerDependencies:
- - ws
-
'@langchain/textsplitters@0.1.0(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))':
dependencies:
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
js-tiktoken: 1.0.21
- '@langchain/textsplitters@0.1.0(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))':
- dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- js-tiktoken: 1.0.21
-
'@langchain/weaviate@0.2.3(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))':
dependencies:
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
@@ -12215,14 +12913,6 @@ snapshots:
transitivePeerDependencies:
- encoding
- '@langchain/weaviate@0.2.3(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))':
- dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- uuid: 10.0.0
- weaviate-client: 3.9.0
- transitivePeerDependencies:
- - encoding
-
'@libsql/client@0.15.15':
dependencies:
'@libsql/core': 0.15.15
@@ -12285,6 +12975,22 @@ snapshots:
'@libsql/win32-x64-msvc@0.5.22':
optional: true
+ '@lit-labs/react@2.1.3(@types/react@19.2.2)':
+ dependencies:
+ '@lit/react': 1.0.8(@types/react@19.2.2)
+ transitivePeerDependencies:
+ - '@types/react'
+
+ '@lit-labs/ssr-dom-shim@1.4.0': {}
+
+ '@lit/react@1.0.8(@types/react@19.2.2)':
+ dependencies:
+ '@types/react': 19.2.2
+
+ '@lit/reactive-element@2.1.1':
+ dependencies:
+ '@lit-labs/ssr-dom-shim': 1.4.0
+
'@lukeed/csprng@1.1.0': {}
'@lukeed/uuid@2.0.1':
@@ -12429,8 +13135,8 @@ snapshots:
ai-v5: ai@5.0.60(zod@3.25.76)
date-fns: 3.6.0
dotenv: 16.6.1
- hono: 4.9.12
- hono-openapi: 0.4.8(hono@4.9.12)(openapi-types@12.1.3)(zod@3.25.76)
+ hono: 4.10.3
+ hono-openapi: 0.4.8(hono@4.10.3)(openapi-types@12.1.3)(zod@3.25.76)
js-tiktoken: 1.0.21
json-schema: 0.4.0
json-schema-to-zod: 2.6.1
@@ -12654,6 +13360,10 @@ snapshots:
'@types/react': 19.2.2
react: 19.2.0
+ '@mermaid-js/parser@0.6.3':
+ dependencies:
+ langium: 3.3.1
+
'@modelcontextprotocol/sdk@1.20.0':
dependencies:
ajv: 6.12.6
@@ -13707,6 +14417,26 @@ snapshots:
'@types/react': 19.2.2
'@types/react-dom': 19.2.2(@types/react@19.2.2)
+ '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.2.0)
+ '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
'@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.2.0)':
dependencies:
react: 19.2.0
@@ -13755,6 +14485,15 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.2
+ '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
'@radix-ui/rect@1.1.1': {}
'@react-aria/focus@3.21.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
@@ -14052,6 +14791,39 @@ snapshots:
prompts: 2.4.2
zod: 3.25.76
+ '@shikijs/core@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+ hast-util-to-html: 9.0.5
+
+ '@shikijs/engine-javascript@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+ '@shikijs/vscode-textmate': 10.0.2
+ oniguruma-to-es: 4.3.3
+
+ '@shikijs/engine-oniguruma@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+ '@shikijs/vscode-textmate': 10.0.2
+
+ '@shikijs/langs@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+
+ '@shikijs/themes@3.14.0':
+ dependencies:
+ '@shikijs/types': 3.14.0
+
+ '@shikijs/types@3.14.0':
+ dependencies:
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
+ '@shikijs/vscode-textmate@10.0.2': {}
+
'@sinclair/typebox@0.27.8': {}
'@sindresorhus/merge-streams@4.0.0': {}
@@ -14690,6 +15462,123 @@ snapshots:
dependencies:
'@types/node': 20.19.21
+ '@types/d3-array@3.2.2': {}
+
+ '@types/d3-axis@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-brush@3.0.6':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-chord@3.0.6': {}
+
+ '@types/d3-color@3.1.3': {}
+
+ '@types/d3-contour@3.0.6':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-delaunay@6.0.4': {}
+
+ '@types/d3-dispatch@3.0.7': {}
+
+ '@types/d3-drag@3.0.7':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-dsv@3.0.7': {}
+
+ '@types/d3-ease@3.0.2': {}
+
+ '@types/d3-fetch@3.0.7':
+ dependencies:
+ '@types/d3-dsv': 3.0.7
+
+ '@types/d3-force@3.0.10': {}
+
+ '@types/d3-format@3.0.4': {}
+
+ '@types/d3-geo@3.1.0':
+ dependencies:
+ '@types/geojson': 7946.0.16
+
+ '@types/d3-hierarchy@3.1.7': {}
+
+ '@types/d3-interpolate@3.0.4':
+ dependencies:
+ '@types/d3-color': 3.1.3
+
+ '@types/d3-path@3.1.1': {}
+
+ '@types/d3-polygon@3.0.2': {}
+
+ '@types/d3-quadtree@3.0.6': {}
+
+ '@types/d3-random@3.0.3': {}
+
+ '@types/d3-scale-chromatic@3.1.0': {}
+
+ '@types/d3-scale@4.0.9':
+ dependencies:
+ '@types/d3-time': 3.0.4
+
+ '@types/d3-selection@3.0.11': {}
+
+ '@types/d3-shape@3.1.7':
+ dependencies:
+ '@types/d3-path': 3.1.1
+
+ '@types/d3-time-format@4.0.3': {}
+
+ '@types/d3-time@3.0.4': {}
+
+ '@types/d3-timer@3.0.2': {}
+
+ '@types/d3-transition@3.0.9':
+ dependencies:
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3-zoom@3.0.8':
+ dependencies:
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-selection': 3.0.11
+
+ '@types/d3@7.4.3':
+ dependencies:
+ '@types/d3-array': 3.2.2
+ '@types/d3-axis': 3.0.6
+ '@types/d3-brush': 3.0.6
+ '@types/d3-chord': 3.0.6
+ '@types/d3-color': 3.1.3
+ '@types/d3-contour': 3.0.6
+ '@types/d3-delaunay': 6.0.4
+ '@types/d3-dispatch': 3.0.7
+ '@types/d3-drag': 3.0.7
+ '@types/d3-dsv': 3.0.7
+ '@types/d3-ease': 3.0.2
+ '@types/d3-fetch': 3.0.7
+ '@types/d3-force': 3.0.10
+ '@types/d3-format': 3.0.4
+ '@types/d3-geo': 3.1.0
+ '@types/d3-hierarchy': 3.1.7
+ '@types/d3-interpolate': 3.0.4
+ '@types/d3-path': 3.1.1
+ '@types/d3-polygon': 3.0.2
+ '@types/d3-quadtree': 3.0.6
+ '@types/d3-random': 3.0.3
+ '@types/d3-scale': 4.0.9
+ '@types/d3-scale-chromatic': 3.1.0
+ '@types/d3-selection': 3.0.11
+ '@types/d3-shape': 3.1.7
+ '@types/d3-time': 3.0.4
+ '@types/d3-time-format': 4.0.3
+ '@types/d3-timer': 3.0.2
+ '@types/d3-transition': 3.0.9
+ '@types/d3-zoom': 3.0.8
+
'@types/debug@4.1.12':
dependencies:
'@types/ms': 2.1.0
@@ -14718,6 +15607,8 @@ snapshots:
'@types/qs': 6.14.0
'@types/serve-static': 1.15.9
+ '@types/geojson@7946.0.16': {}
+
'@types/graceful-fs@4.1.9':
dependencies:
'@types/node': 20.19.21
@@ -14871,6 +15762,8 @@ snapshots:
'@types/tough-cookie@4.0.5': {}
+ '@types/trusted-types@2.0.7': {}
+
'@types/unist@2.0.11': {}
'@types/unist@3.0.3': {}
@@ -15515,6 +16408,20 @@ snapshots:
chardet@2.1.0: {}
+ chevrotain-allstar@0.3.1(chevrotain@11.0.3):
+ dependencies:
+ chevrotain: 11.0.3
+ lodash-es: 4.17.21
+
+ chevrotain@11.0.3:
+ dependencies:
+ '@chevrotain/cst-dts-gen': 11.0.3
+ '@chevrotain/gast': 11.0.3
+ '@chevrotain/regexp-to-ast': 11.0.3
+ '@chevrotain/types': 11.0.3
+ '@chevrotain/utils': 11.0.3
+ lodash-es: 4.17.21
+
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
@@ -15605,6 +16512,8 @@ snapshots:
commander@4.1.1: {}
+ commander@7.2.0: {}
+
commander@8.3.0: {}
commander@9.5.0: {}
@@ -15657,6 +16566,14 @@ snapshots:
object-assign: 4.1.1
vary: 1.1.2
+ cose-base@1.0.3:
+ dependencies:
+ layout-base: 1.0.2
+
+ cose-base@2.2.0:
+ dependencies:
+ layout-base: 2.0.1
+
create-jest@29.7.0(@types/node@20.19.21):
dependencies:
'@jest/types': 29.6.3
@@ -15694,6 +16611,190 @@ snapshots:
csstype@3.1.3: {}
+ cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1):
+ dependencies:
+ cose-base: 1.0.3
+ cytoscape: 3.33.1
+
+ cytoscape-fcose@2.2.0(cytoscape@3.33.1):
+ dependencies:
+ cose-base: 2.2.0
+ cytoscape: 3.33.1
+
+ cytoscape@3.33.1: {}
+
+ d3-array@2.12.1:
+ dependencies:
+ internmap: 1.0.1
+
+ d3-array@3.2.4:
+ dependencies:
+ internmap: 2.0.3
+
+ d3-axis@3.0.0: {}
+
+ d3-brush@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3-chord@3.0.1:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-color@3.1.0: {}
+
+ d3-contour@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-delaunay@6.0.4:
+ dependencies:
+ delaunator: 5.0.1
+
+ d3-dispatch@3.0.1: {}
+
+ d3-drag@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+
+ d3-dsv@3.0.1:
+ dependencies:
+ commander: 7.2.0
+ iconv-lite: 0.6.3
+ rw: 1.3.3
+
+ d3-ease@3.0.1: {}
+
+ d3-fetch@3.0.1:
+ dependencies:
+ d3-dsv: 3.0.1
+
+ d3-force@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-timer: 3.0.1
+
+ d3-format@3.1.0: {}
+
+ d3-geo@3.1.1:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-hierarchy@3.1.2: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-path@1.0.9: {}
+
+ d3-path@3.1.0: {}
+
+ d3-polygon@3.0.1: {}
+
+ d3-quadtree@3.0.1: {}
+
+ d3-random@3.0.1: {}
+
+ d3-sankey@0.12.3:
+ dependencies:
+ d3-array: 2.12.1
+ d3-shape: 1.3.7
+
+ d3-scale-chromatic@3.1.0:
+ dependencies:
+ d3-color: 3.1.0
+ d3-interpolate: 3.0.1
+
+ d3-scale@4.0.2:
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.0
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-shape@1.3.7:
+ dependencies:
+ d3-path: 1.0.9
+
+ d3-shape@3.2.0:
+ dependencies:
+ d3-path: 3.1.0
+
+ d3-time-format@4.1.0:
+ dependencies:
+ d3-time: 3.1.0
+
+ d3-time@3.1.0:
+ dependencies:
+ d3-array: 3.2.4
+
+ d3-timer@3.0.1: {}
+
+ d3-transition@3.0.1(d3-selection@3.0.0):
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+
+ d3-zoom@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
+ d3@7.9.0:
+ dependencies:
+ d3-array: 3.2.4
+ d3-axis: 3.0.0
+ d3-brush: 3.0.0
+ d3-chord: 3.0.1
+ d3-color: 3.1.0
+ d3-contour: 4.0.2
+ d3-delaunay: 6.0.4
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-dsv: 3.0.1
+ d3-ease: 3.0.1
+ d3-fetch: 3.0.1
+ d3-force: 3.0.0
+ d3-format: 3.1.0
+ d3-geo: 3.1.1
+ d3-hierarchy: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-path: 3.1.0
+ d3-polygon: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-random: 3.0.1
+ d3-scale: 4.0.2
+ d3-scale-chromatic: 3.1.0
+ d3-selection: 3.0.0
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ d3-timer: 3.0.1
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3-zoom: 3.0.0
+
+ dagre-d3-es@7.0.13:
+ dependencies:
+ d3: 7.9.0
+ lodash-es: 4.17.21
+
damerau-levenshtein@1.0.8: {}
data-uri-to-buffer@4.0.1: {}
@@ -15722,6 +16823,8 @@ snapshots:
dateformat@4.6.3: {}
+ dayjs@1.11.18: {}
+
debug@2.6.9:
dependencies:
ms: 2.0.0
@@ -15777,6 +16880,10 @@ snapshots:
defu@6.1.4: {}
+ delaunator@5.0.1:
+ dependencies:
+ robust-predicates: 3.0.2
+
delayed-stream@1.0.0: {}
depd@2.0.0: {}
@@ -15813,6 +16920,10 @@ snapshots:
dompurify@3.1.7: {}
+ dompurify@3.3.0:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
dotenv@16.6.1: {}
dprint-node@1.0.8:
@@ -16085,7 +17196,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.6.1)):
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1)):
dependencies:
debug: 3.2.7
optionalDependencies:
@@ -16107,7 +17218,7 @@ snapshots:
doctrine: 2.1.0
eslint: 9.37.0(jiti@2.6.1)
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.6.1))
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.1(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.37.0(jiti@2.6.1)))(eslint@9.37.0(jiti@2.6.1))
hasown: 2.0.2
is-core-module: 2.16.1
is-glob: 4.0.3
@@ -16737,6 +17848,8 @@ snapshots:
globals@14.0.0: {}
+ globals@15.15.0: {}
+
globalthis@1.0.4:
dependencies:
define-properties: 1.2.1
@@ -16828,6 +17941,8 @@ snapshots:
- encoding
- supports-color
+ hachure-fill@0.5.2: {}
+
handlebars@4.7.8:
dependencies:
minimist: 1.2.8
@@ -16859,6 +17974,28 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ hast-util-from-dom@5.0.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ hastscript: 9.0.1
+ web-namespaces: 2.0.1
+
+ hast-util-from-html-isomorphic@2.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+ hast-util-from-dom: 5.0.1
+ hast-util-from-html: 2.0.3
+ unist-util-remove-position: 5.0.0
+
+ hast-util-from-html@2.0.3:
+ dependencies:
+ '@types/hast': 3.0.4
+ devlop: 1.1.0
+ hast-util-from-parse5: 8.0.3
+ parse5: 7.3.0
+ vfile: 6.0.3
+ vfile-message: 4.0.3
+
hast-util-from-parse5@8.0.3:
dependencies:
'@types/hast': 3.0.4
@@ -16870,6 +18007,10 @@ snapshots:
vfile-location: 5.0.3
web-namespaces: 2.0.1
+ hast-util-is-element@3.0.0:
+ dependencies:
+ '@types/hast': 3.0.4
+
hast-util-parse-selector@2.2.5: {}
hast-util-parse-selector@4.0.0:
@@ -16913,6 +18054,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ hast-util-to-html@9.0.5:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ ccount: 2.0.1
+ comma-separated-tokens: 2.0.3
+ hast-util-whitespace: 3.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.2.0
+ property-information: 7.1.0
+ space-separated-tokens: 2.0.2
+ stringify-entities: 4.0.4
+ zwitch: 2.0.4
+
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.8
@@ -16943,6 +18098,13 @@ snapshots:
web-namespaces: 2.0.1
zwitch: 2.0.4
+ hast-util-to-text@4.0.2:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.3
+ hast-util-is-element: 3.0.0
+ unist-util-find-after: 5.0.0
+
hast-util-whitespace@2.0.1: {}
hast-util-whitespace@3.0.0:
@@ -16971,6 +18133,14 @@ snapshots:
highlightjs-vue@1.0.0: {}
+ hono-openapi@0.4.8(hono@4.10.3)(openapi-types@12.1.3)(zod@3.25.76):
+ dependencies:
+ json-schema-walker: 2.0.0
+ openapi-types: 12.1.3
+ optionalDependencies:
+ hono: 4.10.3
+ zod: 3.25.76
+
hono-openapi@0.4.8(hono@4.9.12)(openapi-types@12.1.3)(zod@3.25.76):
dependencies:
json-schema-walker: 2.0.0
@@ -16979,6 +18149,8 @@ snapshots:
hono: 4.9.12
zod: 3.25.76
+ hono@4.10.3: {}
+
hono@4.9.12: {}
html-escaper@2.0.2: {}
@@ -17034,7 +18206,7 @@ snapshots:
isstream: 0.1.2
jsonwebtoken: 9.0.2
mime-types: 2.1.35
- retry-axios: 2.6.0(axios@1.12.2)
+ retry-axios: 2.6.0(axios@1.12.2(debug@4.4.3))
tough-cookie: 4.1.4
transitivePeerDependencies:
- supports-color
@@ -17105,6 +18277,10 @@ snapshots:
hasown: 2.0.2
side-channel: 1.1.0
+ internmap@1.0.1: {}
+
+ internmap@2.0.3: {}
+
ip-regex@4.3.0: {}
ipaddr.js@1.9.1: {}
@@ -17782,10 +18958,14 @@ snapshots:
dependencies:
json-buffer: 3.0.1
+ khroma@2.1.0: {}
+
kleur@3.0.3: {}
kleur@4.1.5: {}
+ kolorist@1.8.0: {}
+
langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))))(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(axios@1.12.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(ws@8.18.3):
dependencies:
'@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
@@ -17811,30 +18991,13 @@ snapshots:
- openai
- ws
- langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))))(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(axios@1.12.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3)(zod@3.25.76))(ws@8.18.3):
+ langium@3.3.1:
dependencies:
- '@langchain/core': 0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76))
- '@langchain/openai': 0.6.16(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))(ws@8.18.3)
- '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))
- js-tiktoken: 1.0.21
- js-yaml: 4.1.0
- jsonpointer: 5.0.1
- langsmith: 0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76))
- openapi-types: 12.1.3
- p-retry: 4.6.2
- uuid: 10.0.0
- yaml: 2.8.1
- zod: 3.25.76
- optionalDependencies:
- '@langchain/aws': 0.1.15(@langchain/core@0.3.78(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@5.12.2(ws@8.18.3)(zod@3.25.76)))
- axios: 1.12.2(debug@4.4.3)
- handlebars: 4.7.8
- transitivePeerDependencies:
- - '@opentelemetry/api'
- - '@opentelemetry/exporter-trace-otlp-proto'
- - '@opentelemetry/sdk-trace-base'
- - openai
- - ws
+ chevrotain: 11.0.3
+ chevrotain-allstar: 0.3.1(chevrotain@11.0.3)
+ vscode-languageserver: 9.0.1
+ vscode-languageserver-textdocument: 1.0.12
+ vscode-uri: 3.0.8
langsmith@0.3.74(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.1.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3)(zod@3.25.76)):
dependencies:
@@ -17872,6 +19035,10 @@ snapshots:
dependencies:
language-subtag-registry: 0.3.23
+ layout-base@1.0.2: {}
+
+ layout-base@2.0.1: {}
+
leven@3.1.0: {}
levn@0.4.1:
@@ -17949,6 +19116,22 @@ snapshots:
dependencies:
uc.micro: 2.1.0
+ lit-element@4.2.1:
+ dependencies:
+ '@lit-labs/ssr-dom-shim': 1.4.0
+ '@lit/reactive-element': 2.1.1
+ lit-html: 3.3.1
+
+ lit-html@3.3.1:
+ dependencies:
+ '@types/trusted-types': 2.0.7
+
+ lit@3.3.1:
+ dependencies:
+ '@lit/reactive-element': 2.1.1
+ lit-element: 4.2.1
+ lit-html: 3.3.1
+
load-tsconfig@0.2.5: {}
local-pkg@1.1.2:
@@ -17965,6 +19148,8 @@ snapshots:
dependencies:
p-locate: 5.0.0
+ lodash-es@4.17.21: {}
+
lodash.camelcase@4.3.0: {}
lodash.get@4.4.2: {}
@@ -18021,6 +19206,16 @@ snapshots:
dependencies:
react: 19.2.0
+ lucide-react@0.525.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ lucide-react@0.542.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
+ lucide@0.525.0: {}
+
magic-string@0.30.19:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -18052,6 +19247,8 @@ snapshots:
marked@14.0.0: {}
+ marked@16.4.1: {}
+
mastra@0.15.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@19.2.0)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(typescript@5.9.3)(zod@3.25.76):
dependencies:
'@clack/prompts': 0.11.0
@@ -18322,6 +19519,31 @@ snapshots:
merge2@1.4.1: {}
+ mermaid@11.12.1:
+ dependencies:
+ '@braintree/sanitize-url': 7.1.1
+ '@iconify/utils': 3.0.2
+ '@mermaid-js/parser': 0.6.3
+ '@types/d3': 7.4.3
+ cytoscape: 3.33.1
+ cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1)
+ cytoscape-fcose: 2.2.0(cytoscape@3.33.1)
+ d3: 7.9.0
+ d3-sankey: 0.12.3
+ dagre-d3-es: 7.0.13
+ dayjs: 1.11.18
+ dompurify: 3.3.0
+ katex: 0.16.25
+ khroma: 2.1.0
+ lodash-es: 4.17.21
+ marked: 16.4.1
+ roughjs: 4.6.6
+ stylis: 4.3.6
+ ts-dedent: 2.2.0
+ uuid: 11.1.0
+ transitivePeerDependencies:
+ - supports-color
+
methods@1.1.2: {}
micromark-core-commonmark@1.1.0:
@@ -18970,6 +20192,14 @@ snapshots:
dependencies:
mimic-fn: 4.0.0
+ oniguruma-parser@0.12.1: {}
+
+ oniguruma-to-es@4.3.3:
+ dependencies:
+ oniguruma-parser: 0.12.1
+ regex: 6.0.1
+ regex-recursion: 6.0.2
+
open@10.2.0:
dependencies:
default-browser: 5.2.1
@@ -19066,6 +20296,8 @@ snapshots:
package-json-from-dist@1.0.1: {}
+ package-manager-detector@1.5.0: {}
+
parent-module@1.0.1:
dependencies:
callsites: 3.1.0
@@ -19106,6 +20338,8 @@ snapshots:
partial-json@0.1.7: {}
+ path-data-parser@0.1.0: {}
+
path-exists@4.0.0: {}
path-is-absolute@1.0.1: {}
@@ -19251,6 +20485,13 @@ snapshots:
optionalDependencies:
fsevents: 2.3.2
+ points-on-curve@0.2.0: {}
+
+ points-on-path@0.2.1:
+ dependencies:
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+
possible-typed-array-names@1.1.0: {}
postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.20.6)(yaml@2.8.1):
@@ -19680,6 +20921,16 @@ snapshots:
parse-entities: 2.0.0
prismjs: 1.27.0
+ regex-recursion@6.0.2:
+ dependencies:
+ regex-utilities: 2.3.0
+
+ regex-utilities@2.3.0: {}
+
+ regex@6.0.1:
+ dependencies:
+ regex-utilities: 2.3.0
+
regexp.prototype.flags@1.5.4:
dependencies:
call-bind: 1.0.8
@@ -19689,6 +20940,18 @@ snapshots:
gopd: 1.2.0
set-function-name: 2.0.2
+ rehype-harden@1.1.5: {}
+
+ rehype-katex@7.0.1:
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/katex': 0.16.7
+ hast-util-from-html-isomorphic: 2.0.0
+ hast-util-to-text: 4.0.2
+ katex: 0.16.25
+ unist-util-visit-parents: 6.0.1
+ vfile: 6.0.3
+
rehype-raw@7.0.0:
dependencies:
'@types/hast': 3.0.4
@@ -19809,7 +21072,7 @@ snapshots:
onetime: 5.1.2
signal-exit: 3.0.7
- retry-axios@2.6.0(axios@1.12.2):
+ retry-axios@2.6.0(axios@1.12.2(debug@4.4.3)):
dependencies:
axios: 1.12.2(debug@4.4.3)
@@ -19817,6 +21080,8 @@ snapshots:
reusify@1.1.0: {}
+ robust-predicates@3.0.2: {}
+
rollup-plugin-esbuild@6.2.1(esbuild@0.25.10)(rollup@4.50.2):
dependencies:
debug: 4.4.3
@@ -19889,6 +21154,13 @@ snapshots:
rope-sequence@1.3.4: {}
+ roughjs@4.6.6:
+ dependencies:
+ hachure-fill: 0.5.2
+ path-data-parser: 0.1.0
+ points-on-curve: 0.2.0
+ points-on-path: 0.2.1
+
router@2.2.0:
dependencies:
debug: 4.4.3
@@ -19907,6 +21179,8 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
+ rw@1.3.3: {}
+
rxjs@7.8.1:
dependencies:
tslib: 2.8.1
@@ -20065,6 +21339,17 @@ snapshots:
shell-quote@1.8.3: {}
+ shiki@3.14.0:
+ dependencies:
+ '@shikijs/core': 3.14.0
+ '@shikijs/engine-javascript': 3.14.0
+ '@shikijs/engine-oniguruma': 3.14.0
+ '@shikijs/langs': 3.14.0
+ '@shikijs/themes': 3.14.0
+ '@shikijs/types': 3.14.0
+ '@shikijs/vscode-textmate': 10.0.2
+ '@types/hast': 3.0.4
+
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -20160,6 +21445,26 @@ snapshots:
es-errors: 1.3.0
internal-slot: 1.1.0
+ streamdown@1.4.0(@types/react@19.2.2)(react@19.2.0):
+ dependencies:
+ clsx: 2.1.1
+ katex: 0.16.25
+ lucide-react: 0.542.0(react@19.2.0)
+ marked: 16.4.1
+ mermaid: 11.12.1
+ react: 19.2.0
+ react-markdown: 10.1.0(@types/react@19.2.2)(react@19.2.0)
+ rehype-harden: 1.1.5
+ rehype-katex: 7.0.1
+ rehype-raw: 7.0.0
+ remark-gfm: 4.0.1
+ remark-math: 6.0.0
+ shiki: 3.14.0
+ tailwind-merge: 3.3.1
+ transitivePeerDependencies:
+ - '@types/react'
+ - supports-color
+
streamsearch@1.1.0: {}
string-length@4.0.2:
@@ -20284,6 +21589,8 @@ snapshots:
client-only: 0.0.1
react: 19.2.0
+ stylis@4.3.6: {}
+
sucrase@3.35.0:
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@@ -20406,6 +21713,10 @@ snapshots:
dependencies:
typescript: 5.9.3
+ ts-dedent@2.2.0: {}
+
+ ts-deepmerge@7.0.3: {}
+
ts-error@1.0.6: {}
ts-interface-checker@0.1.13: {}
@@ -20516,6 +21827,8 @@ snapshots:
turbo-windows-64: 2.5.8
turbo-windows-arm64: 2.5.8
+ tw-animate-css@1.4.0: {}
+
type-check@0.4.0:
dependencies:
prelude-ls: 1.2.1
@@ -20633,6 +21946,11 @@ snapshots:
trough: 2.2.0
vfile: 6.0.3
+ unist-util-find-after@5.0.0:
+ dependencies:
+ '@types/unist': 3.0.3
+ unist-util-is: 6.0.0
+
unist-util-generated@2.0.1: {}
unist-util-is@5.2.1:
@@ -20765,6 +22083,10 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.2
+ use-stick-to-bottom@1.1.1(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
use-sync-external-store@1.6.0(react@19.2.0):
dependencies:
react: 19.2.0
@@ -20823,6 +22145,23 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
+ vscode-jsonrpc@8.2.0: {}
+
+ vscode-languageserver-protocol@3.17.5:
+ dependencies:
+ vscode-jsonrpc: 8.2.0
+ vscode-languageserver-types: 3.17.5
+
+ vscode-languageserver-textdocument@1.0.12: {}
+
+ vscode-languageserver-types@3.17.5: {}
+
+ vscode-languageserver@9.0.1:
+ dependencies:
+ vscode-languageserver-protocol: 3.17.5
+
+ vscode-uri@3.0.8: {}
+
w3c-keyname@2.2.8: {}
wait-port@1.1.0:
diff --git a/sdks/python/README.md b/sdks/python/README.md
index 843d9d028..d9e48e26a 100644
--- a/sdks/python/README.md
+++ b/sdks/python/README.md
@@ -38,6 +38,23 @@ sse_data = encoder.encode(event)
# Output: data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"msg_123","delta":"Hello from Python!"}\n\n
```
+### Multimodal user message
+
+```python
+from ag_ui.core import UserMessage, TextInputContent, BinaryInputContent
+
+message = UserMessage(
+ id="user-123",
+ content=[
+ TextInputContent(text="Please describe this image"),
+ BinaryInputContent(mime_type="image/png", url="https://example.com/cat.png"),
+ ],
+)
+
+payload = message.model_dump(by_alias=True)
+# {"id": "user-123", "role": "user", "content": [...]}
+```
+
## Packages
- **`ag_ui.core`** – Types, events, and data models for AG-UI protocol
diff --git a/sdks/python/ag_ui/core/__init__.py b/sdks/python/ag_ui/core/__init__.py
index 7e909ad5b..248ff1005 100644
--- a/sdks/python/ag_ui/core/__init__.py
+++ b/sdks/python/ag_ui/core/__init__.py
@@ -22,6 +22,8 @@
StateSnapshotEvent,
StateDeltaEvent,
MessagesSnapshotEvent,
+ ActivitySnapshotEvent,
+ ActivityDeltaEvent,
RawEvent,
CustomEvent,
RunStartedEvent,
@@ -41,12 +43,16 @@
AssistantMessage,
UserMessage,
ToolMessage,
+ ActivityMessage,
Message,
Role,
Context,
Tool,
RunAgentInput,
- State
+ State,
+ TextInputContent,
+ BinaryInputContent,
+ InputContent,
)
__all__ = [
@@ -70,6 +76,8 @@
"StateSnapshotEvent",
"StateDeltaEvent",
"MessagesSnapshotEvent",
+ "ActivitySnapshotEvent",
+ "ActivityDeltaEvent",
"RawEvent",
"CustomEvent",
"RunStartedEvent",
@@ -87,10 +95,14 @@
"AssistantMessage",
"UserMessage",
"ToolMessage",
+ "ActivityMessage",
"Message",
"Role",
"Context",
"Tool",
"RunAgentInput",
- "State"
+ "State",
+ "TextInputContent",
+ "BinaryInputContent",
+ "InputContent",
]
diff --git a/sdks/python/ag_ui/core/events.py b/sdks/python/ag_ui/core/events.py
index 2a54a9c8e..94fb63c75 100644
--- a/sdks/python/ag_ui/core/events.py
+++ b/sdks/python/ag_ui/core/events.py
@@ -7,7 +7,7 @@
from pydantic import Field
-from .types import ConfiguredBaseModel, Message, State, Role
+from .types import ConfiguredBaseModel, Message, State, Role, RunAgentInput
# Text messages can have any role except "tool"
TextMessageRole = Literal["developer", "system", "assistant", "user"]
@@ -34,6 +34,8 @@ class EventType(str, Enum):
STATE_SNAPSHOT = "STATE_SNAPSHOT"
STATE_DELTA = "STATE_DELTA"
MESSAGES_SNAPSHOT = "MESSAGES_SNAPSHOT"
+ ACTIVITY_SNAPSHOT = "ACTIVITY_SNAPSHOT"
+ ACTIVITY_DELTA = "ACTIVITY_DELTA"
RAW = "RAW"
CUSTOM = "CUSTOM"
RUN_STARTED = "RUN_STARTED"
@@ -188,6 +190,25 @@ class MessagesSnapshotEvent(BaseEvent):
messages: List[Message]
+class ActivitySnapshotEvent(BaseEvent):
+ """Event containing a snapshot of an activity message."""
+
+ type: Literal[EventType.ACTIVITY_SNAPSHOT] = EventType.ACTIVITY_SNAPSHOT # pyright: ignore[reportIncompatibleVariableOverride]
+ message_id: str
+ activity_type: str
+ content: Any
+ replace: bool = True
+
+
+class ActivityDeltaEvent(BaseEvent):
+ """Event containing a JSON Patch delta for an activity message."""
+
+ type: Literal[EventType.ACTIVITY_DELTA] = EventType.ACTIVITY_DELTA # pyright: ignore[reportIncompatibleVariableOverride]
+ message_id: str
+ activity_type: str
+ patch: List[Any]
+
+
class RawEvent(BaseEvent):
"""
Event containing a raw event.
@@ -213,6 +234,8 @@ class RunStartedEvent(BaseEvent):
type: Literal[EventType.RUN_STARTED] = EventType.RUN_STARTED # pyright: ignore[reportIncompatibleVariableOverride]
thread_id: str
run_id: str
+ parent_run_id: Optional[str] = None
+ input: Optional[RunAgentInput] = None
class RunFinishedEvent(BaseEvent):
@@ -264,6 +287,8 @@ class StepFinishedEvent(BaseEvent):
StateSnapshotEvent,
StateDeltaEvent,
MessagesSnapshotEvent,
+ ActivitySnapshotEvent,
+ ActivityDeltaEvent,
RawEvent,
CustomEvent,
RunStartedEvent,
diff --git a/sdks/python/ag_ui/core/types.py b/sdks/python/ag_ui/core/types.py
index 47b7ae182..e4e358caf 100644
--- a/sdks/python/ag_ui/core/types.py
+++ b/sdks/python/ag_ui/core/types.py
@@ -2,9 +2,9 @@
This module contains the types for the Agent User Interaction Protocol Python SDK.
"""
-from typing import Annotated, Any, List, Literal, Optional, Union
+from typing import Annotated, Any, Dict, List, Literal, Optional, Union
-from pydantic import BaseModel, ConfigDict, Field
+from pydantic import BaseModel, ConfigDict, Field, model_validator
from pydantic.alias_generators import to_camel
@@ -13,7 +13,7 @@ class ConfiguredBaseModel(BaseModel):
A configurable base model.
"""
model_config = ConfigDict(
- extra="forbid",
+ extra="allow",
alias_generator=to_camel,
populate_by_name=True,
)
@@ -70,12 +70,44 @@ class AssistantMessage(BaseMessage):
tool_calls: Optional[List[ToolCall]] = None
+class TextInputContent(ConfiguredBaseModel):
+ """A text fragment in a multimodal user message."""
+
+ type: Literal["text"] = "text"
+ text: str
+
+
+class BinaryInputContent(ConfiguredBaseModel):
+ """A binary payload reference in a multimodal user message."""
+
+ type: Literal["binary"] = "binary" # pyright: ignore[reportIncompatibleVariableOverride]
+ mime_type: str
+ id: Optional[str] = None
+ url: Optional[str] = None
+ data: Optional[str] = None
+ filename: Optional[str] = None
+
+ @model_validator(mode="after")
+ def validate_source(self) -> "BinaryInputContent":
+ """Ensure at least one binary payload source is provided."""
+ if not any([self.id, self.url, self.data]):
+ raise ValueError("BinaryInputContent requires id, url, or data to be provided.")
+ return self
+
+
+InputContent = Annotated[
+ Union[TextInputContent, BinaryInputContent],
+ Field(discriminator="type"),
+]
+
+
class UserMessage(BaseMessage):
"""
- A user message.
+ A user message supporting text or multimodal content.
"""
- role: Literal["user"] = "user" # pyright: ignore[reportIncompatibleVariableOverride]
- content: str
+
+ role: Literal["user"] = "user" # pyright: ignore[reportIncompatibleVariableOverride]
+ content: Union[str, List[InputContent]]
class ToolMessage(ConfiguredBaseModel):
@@ -89,12 +121,30 @@ class ToolMessage(ConfiguredBaseModel):
error: Optional[str] = None
+class ActivityMessage(ConfiguredBaseModel):
+ """
+ An activity progress message emitted between chat messages.
+ """
+
+ id: str
+ role: Literal["activity"] = "activity" # pyright: ignore[reportIncompatibleVariableOverride]
+ activity_type: str
+ content: Dict[str, Any]
+
+
Message = Annotated[
- Union[DeveloperMessage, SystemMessage, AssistantMessage, UserMessage, ToolMessage],
+ Union[
+ DeveloperMessage,
+ SystemMessage,
+ AssistantMessage,
+ UserMessage,
+ ToolMessage,
+ ActivityMessage,
+ ],
Field(discriminator="role")
]
-Role = Literal["developer", "system", "assistant", "user", "tool"]
+Role = Literal["developer", "system", "assistant", "user", "tool", "activity"]
class Context(ConfiguredBaseModel):
@@ -120,6 +170,7 @@ class RunAgentInput(ConfiguredBaseModel):
"""
thread_id: str
run_id: str
+ parent_run_id: Optional[str] = None
state: Any
messages: List[Message]
tools: List[Tool]
diff --git a/sdks/python/pyproject.toml b/sdks/python/pyproject.toml
index a8de00217..f38eb2dff 100644
--- a/sdks/python/pyproject.toml
+++ b/sdks/python/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "ag-ui-protocol"
-version = "0.1.9"
+version = "0.2.0a0"
description = ""
authors = ["Markus Ecker "]
readme = "README.md"
diff --git a/sdks/python/tests/test_events.py b/sdks/python/tests/test_events.py
index c73a2537c..7245d88aa 100644
--- a/sdks/python/tests/test_events.py
+++ b/sdks/python/tests/test_events.py
@@ -16,6 +16,8 @@
StateSnapshotEvent,
StateDeltaEvent,
MessagesSnapshotEvent,
+ ActivitySnapshotEvent,
+ ActivityDeltaEvent,
RawEvent,
CustomEvent,
RunStartedEvent,
@@ -202,6 +204,58 @@ def test_messages_snapshot(self):
self.assertEqual(serialized["messages"][0]["role"], "user")
self.assertEqual(serialized["messages"][1]["toolCalls"][0]["function"]["name"], "get_weather")
+ def test_activity_snapshot(self):
+ """Test creating and serializing an ActivitySnapshotEvent"""
+ content = {"tasks": ["search", "summarize"]}
+ event = ActivitySnapshotEvent(
+ message_id="msg_activity",
+ activity_type="PLAN",
+ content=content,
+ timestamp=1648214400000,
+ )
+
+ self.assertEqual(event.message_id, "msg_activity")
+ self.assertEqual(event.activity_type, "PLAN")
+ self.assertEqual(event.content, content)
+ self.assertTrue(event.replace)
+
+ serialized = event.model_dump(by_alias=True)
+ self.assertEqual(serialized["type"], "ACTIVITY_SNAPSHOT")
+ self.assertEqual(serialized["messageId"], "msg_activity")
+ self.assertEqual(serialized["activityType"], "PLAN")
+ self.assertEqual(serialized["content"], content)
+ self.assertTrue(serialized["replace"])
+
+ event_replace_false = ActivitySnapshotEvent(
+ message_id="msg_activity",
+ activity_type="PLAN",
+ content=content,
+ replace=False,
+ )
+ self.assertFalse(event_replace_false.replace)
+ serialized_false = event_replace_false.model_dump(by_alias=True)
+ self.assertFalse(serialized_false["replace"])
+
+ def test_activity_delta(self):
+ """Test creating and serializing an ActivityDeltaEvent"""
+ patch = [{"op": "replace", "path": "/tasks/0", "value": "✓ search"}]
+ event = ActivityDeltaEvent(
+ message_id="msg_activity",
+ activity_type="PLAN",
+ patch=patch,
+ timestamp=1648214400000,
+ )
+
+ self.assertEqual(event.message_id, "msg_activity")
+ self.assertEqual(event.activity_type, "PLAN")
+ self.assertEqual(event.patch, patch)
+
+ serialized = event.model_dump(by_alias=True)
+ self.assertEqual(serialized["type"], "ACTIVITY_DELTA")
+ self.assertEqual(serialized["messageId"], "msg_activity")
+ self.assertEqual(serialized["activityType"], "PLAN")
+ self.assertEqual(serialized["patch"], patch)
+
def test_raw_event(self):
"""Test creating and serializing a RawEvent"""
raw_data = {"origin": "server", "data": {"key": "value"}}
@@ -338,6 +392,13 @@ def test_event_union_deserialization(self):
"snapshot": {"status": "active"},
"timestamp": 1648214400000
},
+ {
+ "type": "ACTIVITY_SNAPSHOT",
+ "messageId": "msg_activity",
+ "activityType": "PLAN",
+ "content": {"tasks": []},
+ "timestamp": 1648214400000,
+ },
{
"type": "RUN_ERROR",
"message": "Error occurred",
@@ -345,12 +406,13 @@ def test_event_union_deserialization(self):
"timestamp": 1648214400000
}
]
-
+
expected_types = [
TextMessageStartEvent,
TextMessageContentEvent,
ToolCallStartEvent,
StateSnapshotEvent,
+ ActivitySnapshotEvent,
RunErrorEvent
]
@@ -392,6 +454,16 @@ def test_serialization_round_trip(self):
UserMessage(id="user_1", content="Hello")
]
),
+ ActivitySnapshotEvent(
+ message_id="msg_activity",
+ activity_type="PLAN",
+ content={"tasks": []},
+ ),
+ ActivityDeltaEvent(
+ message_id="msg_activity",
+ activity_type="PLAN",
+ patch=[{"op": "add", "path": "/tasks/-", "value": "search"}],
+ ),
RunStartedEvent(
thread_id="thread_123",
run_id="run_456"
diff --git a/sdks/python/tests/test_types.py b/sdks/python/tests/test_types.py
index e534aa5ab..843a5a977 100644
--- a/sdks/python/tests/test_types.py
+++ b/sdks/python/tests/test_types.py
@@ -10,8 +10,11 @@
AssistantMessage,
UserMessage,
ToolMessage,
+ ActivityMessage,
Message,
- RunAgentInput
+ RunAgentInput,
+ TextInputContent,
+ BinaryInputContent,
)
@@ -56,6 +59,24 @@ def test_tool_message_camel_case(self):
self.assertIn("toolCallId", serialized)
self.assertEqual(serialized["toolCallId"], "call_456")
+ def test_activity_message(self):
+ """Test creating and serializing an activity message"""
+ content = {"steps": ["search", "summarize"]}
+ msg = ActivityMessage(
+ id="activity_123",
+ activity_type="PLAN",
+ content=content,
+ )
+
+ self.assertEqual(msg.role, "activity")
+ self.assertEqual(msg.activity_type, "PLAN")
+ self.assertEqual(msg.content, content)
+
+ serialized = msg.model_dump(by_alias=True)
+ self.assertEqual(serialized["role"], "activity")
+ self.assertEqual(serialized["activityType"], "PLAN")
+ self.assertEqual(serialized["content"], content)
+
def test_parse_camel_case_json_tool_message(self):
"""Test parsing JSON with camelCase field names"""
# JSON data with camelCase field names
@@ -143,6 +164,31 @@ def test_user_message(self):
self.assertEqual(serialized["role"], "user")
self.assertEqual(serialized["content"], "User query")
+ def test_user_message_multimodal_content(self):
+ """Test creating and serializing a multimodal user message"""
+ contents = [
+ TextInputContent(text="Check this out"),
+ BinaryInputContent(mime_type="image/png", url="https://example.com/image.png"),
+ ]
+ msg = UserMessage(
+ id="user_multi",
+ content=contents,
+ )
+ self.assertIsInstance(msg.content, list)
+ self.assertEqual(len(msg.content), 2)
+ serialized = msg.model_dump(by_alias=True)
+ self.assertIsInstance(serialized["content"], list)
+ self.assertEqual(serialized["content"][0]["type"], "text")
+ self.assertEqual(serialized["content"][0]["text"], "Check this out")
+ self.assertEqual(serialized["content"][1]["mimeType"], "image/png")
+ self.assertEqual(serialized["content"][1]["url"], "https://example.com/image.png")
+
+ def test_binary_input_requires_payload_source(self):
+ """Binary content must specify at least one delivery channel"""
+ with self.assertRaises(ValidationError):
+ BinaryInputContent(mime_type="image/png")
+
+
def test_message_union_deserialization(self):
"""Test that the Message union correctly deserializes to the appropriate type"""
# Create type adapter for the union
@@ -159,7 +205,13 @@ def test_message_union_deserialization(self):
"role": "tool",
"content": "Tool result",
"toolCallId": "call_303"
- }
+ },
+ {
+ "id": "activity_404",
+ "role": "activity",
+ "activityType": "PLAN",
+ "content": {"steps": []},
+ },
]
expected_types = [
@@ -167,7 +219,8 @@ def test_message_union_deserialization(self):
SystemMessage,
AssistantMessage,
UserMessage,
- ToolMessage
+ ToolMessage,
+ ActivityMessage,
]
for data, expected_type in zip(message_data, expected_types):
@@ -209,6 +262,7 @@ def test_run_agent_input_deserialization(self):
run_agent_input_data = {
"threadId": "thread_12345",
"runId": "run_67890",
+ "parentRunId": "run_parent_123",
"state": {"conversation_state": "active", "custom_data": {"key": "value"}},
"messages": [
# System message
@@ -256,7 +310,14 @@ def test_run_agent_input_deserialization(self):
{
"id": "user_002",
"role": "user",
- "content": "Can you explain these results?"
+ "content": [
+ {"type": "text", "text": "Can you explain these results?"},
+ {
+ "type": "binary",
+ "mimeType": "image/png",
+ "url": "https://example.com/results-chart.png"
+ }
+ ]
}
],
"tools": [
@@ -307,6 +368,7 @@ def test_run_agent_input_deserialization(self):
# Verify basic fields
self.assertEqual(run_agent_input.thread_id, "thread_12345")
self.assertEqual(run_agent_input.run_id, "run_67890")
+ self.assertEqual(run_agent_input.parent_run_id, "run_parent_123")
self.assertEqual(run_agent_input.state["conversation_state"], "active")
# Verify messages count and types
@@ -321,6 +383,12 @@ def test_run_agent_input_deserialization(self):
# Verify specific message content
self.assertEqual(run_agent_input.messages[0].content, "You are a helpful assistant.")
self.assertEqual(run_agent_input.messages[1].content, "Can you help me analyze this data?")
+ multimodal_content = run_agent_input.messages[5].content
+ self.assertIsInstance(multimodal_content, list)
+ self.assertEqual(multimodal_content[0].type, "text")
+ self.assertEqual(multimodal_content[0].text, "Can you explain these results?")
+ self.assertEqual(multimodal_content[1].mime_type, "image/png")
+ self.assertEqual(multimodal_content[1].url, "https://example.com/results-chart.png")
# Verify assistant message with tool call
assistant_msg = run_agent_input.messages[3]
@@ -368,15 +436,17 @@ def test_validation_errors(self):
with self.assertRaises(ValidationError):
UserMessage.model_validate(missing_id_data)
- # Test extra fields
+ # Test extra fields are now allowed for backwards compatibility
extra_field_data = {
"id": "msg_456",
"role": "user",
"content": "Hello",
- "extra_field": "This shouldn't be here" # Extra field
+ "extra_field": "This is allowed for backwards compatibility" # Extra field
}
- with self.assertRaises(ValidationError):
- UserMessage.model_validate(extra_field_data)
+ # Should not raise an error - extra fields are allowed
+ msg = UserMessage.model_validate(extra_field_data)
+ self.assertEqual(msg.id, "msg_456")
+ self.assertEqual(msg.content, "Hello")
# Test invalid tool_call_id in ToolMessage
invalid_tool_data = {
diff --git a/sdks/typescript/README.md b/sdks/typescript/README.md
index 64b9a5f4a..1de4bd2f4 100644
--- a/sdks/typescript/README.md
+++ b/sdks/typescript/README.md
@@ -3,3 +3,21 @@
The TypeScript SDK for the [Agent User Interaction Protocol](https://ag-ui.com).
For more information visit the [official documentation](https://docs.ag-ui.com/).
+
+## Multimodal user messages
+
+```ts
+import { UserMessageSchema } from "@ag-ui/core";
+
+const message = UserMessageSchema.parse({
+ id: "user-123",
+ role: "user" as const,
+ content: [
+ { type: "text", text: "Please describe this image" },
+ { type: "binary", mimeType: "image/png", url: "https://example.com/cat.png" },
+ ],
+});
+
+console.log(message);
+// { id: "user-123", role: "user", content: [...] }
+```
diff --git a/sdks/typescript/packages/cli/package.json b/sdks/typescript/packages/cli/package.json
index 08d7a16d6..b3a480309 100644
--- a/sdks/typescript/packages/cli/package.json
+++ b/sdks/typescript/packages/cli/package.json
@@ -1,7 +1,7 @@
{
"name": "create-ag-ui-app",
"author": "Markus Ecker ",
- "version": "0.0.40",
+ "version": "0.0.40-alpha.11",
"private": false,
"publishConfig": {
"access": "public"
diff --git a/sdks/typescript/packages/client/jest.config.js b/sdks/typescript/packages/client/jest.config.js
index 0521f8d91..919fd78b9 100644
--- a/sdks/typescript/packages/client/jest.config.js
+++ b/sdks/typescript/packages/client/jest.config.js
@@ -6,5 +6,11 @@ module.exports = {
passWithNoTests: true,
moduleNameMapper: {
"^@/(.*)$": "/src/$1",
+ "^@ag-ui/core$": "/../core/src/index.ts",
+ "^@ag-ui/core/(.*)$": "/../core/src/$1",
+ "^@ag-ui/proto$": "/../proto/src/index.ts",
+ "^@ag-ui/proto/(.*)$": "/../proto/src/$1",
+ "^@ag-ui/encoder$": "/../encoder/src/index.ts",
+ "^@ag-ui/encoder/(.*)$": "/../encoder/src/$1",
},
};
diff --git a/sdks/typescript/packages/client/package.json b/sdks/typescript/packages/client/package.json
index c963ffe1a..ef6eefbba 100644
--- a/sdks/typescript/packages/client/package.json
+++ b/sdks/typescript/packages/client/package.json
@@ -1,7 +1,7 @@
{
"name": "@ag-ui/client",
"author": "Markus Ecker ",
- "version": "0.0.40",
+ "version": "0.0.40-alpha.11",
"private": false,
"publishConfig": {
"access": "public"
diff --git a/sdks/typescript/packages/client/src/agent/__tests__/agent-clone.test.ts b/sdks/typescript/packages/client/src/agent/__tests__/agent-clone.test.ts
new file mode 100644
index 000000000..3fb00e8a0
--- /dev/null
+++ b/sdks/typescript/packages/client/src/agent/__tests__/agent-clone.test.ts
@@ -0,0 +1,81 @@
+import { AbstractAgent } from "../agent";
+import { HttpAgent } from "../http";
+import { BaseEvent, Message, RunAgentInput } from "@ag-ui/core";
+import { EMPTY, Observable } from "rxjs";
+
+class CloneableTestAgent extends AbstractAgent {
+ constructor() {
+ super({
+ agentId: "test-agent",
+ description: "Cloneable test agent",
+ threadId: "thread-test",
+ initialMessages: [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "Hello world",
+ toolCalls: [],
+ } as Message,
+ ],
+ initialState: { stage: "initial" },
+ });
+ }
+
+ protected run(_: RunAgentInput): Observable {
+ return EMPTY as Observable;
+ }
+}
+
+describe("AbstractAgent cloning", () => {
+ it("clones subclass instances with independent state", () => {
+ const agent = new CloneableTestAgent();
+
+ const cloned = agent.clone() as CloneableTestAgent;
+
+ expect(cloned).toBeInstanceOf(CloneableTestAgent);
+ expect(cloned).not.toBe(agent);
+ expect(cloned.agentId).toBe(agent.agentId);
+ expect(cloned.threadId).toBe(agent.threadId);
+ expect(cloned.messages).toEqual(agent.messages);
+ expect(cloned.messages).not.toBe(agent.messages);
+ expect(cloned.state).toEqual(agent.state);
+ expect(cloned.state).not.toBe(agent.state);
+ });
+});
+
+describe("HttpAgent cloning", () => {
+ it("produces a new HttpAgent with cloned configuration and abort controller", () => {
+ const httpAgent = new HttpAgent({
+ url: "https://example.com/agent",
+ headers: { Authorization: "Bearer token" },
+ threadId: "thread-http",
+ initialMessages: [
+ {
+ id: "msg-http",
+ role: "assistant",
+ content: "response",
+ toolCalls: [],
+ } as Message,
+ ],
+ initialState: { status: "ready" },
+ });
+
+ httpAgent.abortController.abort("cancelled");
+
+ const cloned = httpAgent.clone() as HttpAgent;
+
+ expect(cloned).toBeInstanceOf(HttpAgent);
+ expect(cloned).not.toBe(httpAgent);
+ expect(cloned.url).toBe(httpAgent.url);
+ expect(cloned.headers).toEqual(httpAgent.headers);
+ expect(cloned.headers).not.toBe(httpAgent.headers);
+ expect(cloned.messages).toEqual(httpAgent.messages);
+ expect(cloned.messages).not.toBe(httpAgent.messages);
+ expect(cloned.state).toEqual(httpAgent.state);
+ expect(cloned.state).not.toBe(httpAgent.state);
+ expect(cloned.abortController).not.toBe(httpAgent.abortController);
+ expect(cloned.abortController).toBeInstanceOf(AbortController);
+ expect(cloned.abortController.signal.aborted).toBe(true);
+ expect(cloned.abortController.signal.reason).toBe("cancelled");
+ });
+});
diff --git a/sdks/typescript/packages/client/src/agent/__tests__/agent-multiple-runs.test.ts b/sdks/typescript/packages/client/src/agent/__tests__/agent-multiple-runs.test.ts
index 4f3633d6a..009df2cb7 100644
--- a/sdks/typescript/packages/client/src/agent/__tests__/agent-multiple-runs.test.ts
+++ b/sdks/typescript/packages/client/src/agent/__tests__/agent-multiple-runs.test.ts
@@ -1,5 +1,5 @@
-import { AbstractAgent, RunAgentResult } from "../agent";
-import { BaseEvent, EventType, Message, RunAgentInput, TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent, RunStartedEvent, RunFinishedEvent } from "@ag-ui/core";
+import { AbstractAgent } from "../agent";
+import { BaseEvent, EventType, Message, RunAgentInput, TextMessageStartEvent, TextMessageContentEvent, TextMessageEndEvent, RunStartedEvent, RunFinishedEvent, ActivitySnapshotEvent } from "@ag-ui/core";
import { Observable, of } from "rxjs";
describe("AbstractAgent multiple runs", () => {
@@ -273,4 +273,65 @@ describe("AbstractAgent multiple runs", () => {
expect(agent.messages[0].content).toBe("Initial message");
expect(agent.messages[1].content).toBe("Response message");
});
-});
\ No newline at end of file
+
+ it("should retain activity messages across runs", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ const firstRunEvents: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunStartedEvent,
+ {
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["task 1"] },
+ } as ActivitySnapshotEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(firstRunEvents);
+ await agent.runAgent({ runId: "run-1" });
+
+ expect(agent.messages.length).toBe(1);
+ expect(agent.messages[0].role).toBe("activity");
+
+ const secondRunEvents: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-2",
+ } as RunStartedEvent,
+ {
+ type: EventType.TEXT_MESSAGE_START,
+ messageId: "msg-2",
+ role: "assistant",
+ } as TextMessageStartEvent,
+ {
+ type: EventType.TEXT_MESSAGE_CONTENT,
+ messageId: "msg-2",
+ delta: "Hello from run 2",
+ } as TextMessageContentEvent,
+ {
+ type: EventType.TEXT_MESSAGE_END,
+ messageId: "msg-2",
+ } as TextMessageEndEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(secondRunEvents);
+ await agent.runAgent({ runId: "run-2" });
+
+ expect(agent.messages.length).toBe(2);
+ expect(agent.messages.some((message) => message.role === "activity" && message.id === "activity-1")).toBe(true);
+ });
+});
diff --git a/sdks/typescript/packages/client/src/agent/__tests__/agent-result.test.ts b/sdks/typescript/packages/client/src/agent/__tests__/agent-result.test.ts
index 5df3a5cf1..bcc39d219 100644
--- a/sdks/typescript/packages/client/src/agent/__tests__/agent-result.test.ts
+++ b/sdks/typescript/packages/client/src/agent/__tests__/agent-result.test.ts
@@ -1,11 +1,12 @@
import { AbstractAgent } from "../agent";
import { AgentSubscriber } from "../subscriber";
import {
+ ActivityDeltaEvent,
+ ActivitySnapshotEvent,
BaseEvent,
EventType,
Message,
RunAgentInput,
- State,
MessagesSnapshotEvent,
RunFinishedEvent,
RunStartedEvent,
@@ -307,6 +308,60 @@ describe("Agent Result", () => {
expect(result.newMessages[1].id).toBe("new-2");
expect(result.newMessages[2].id).toBe("new-3");
});
+
+ it("should retain appended activity operations in agent messages", async () => {
+ const firstOperation = { id: "op-1", status: "PENDING" };
+ const secondOperation = { id: "op-2", status: "COMPLETE" };
+
+ agent.setEventsToEmit([
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-ops",
+ } as RunStartedEvent,
+ {
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-ops",
+ activityType: "PLAN",
+ content: { operations: [] },
+ replace: false,
+ } as ActivitySnapshotEvent,
+ {
+ type: EventType.ACTIVITY_DELTA,
+ messageId: "activity-ops",
+ activityType: "PLAN",
+ patch: [{ op: "add", path: "/operations/-", value: firstOperation }],
+ } as ActivityDeltaEvent,
+ {
+ type: EventType.ACTIVITY_DELTA,
+ messageId: "activity-ops",
+ activityType: "PLAN",
+ patch: [{ op: "add", path: "/operations/-", value: secondOperation }],
+ } as ActivityDeltaEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-ops",
+ } as RunFinishedEvent,
+ ]);
+
+ const result = await agent.runAgent({ runId: "run-ops" });
+
+ const activityMessage = agent.messages.find((message) => message.id === "activity-ops");
+
+ expect(activityMessage).toBeTruthy();
+ expect(activityMessage?.role).toBe("activity");
+ expect(activityMessage?.activityType).toBe("PLAN");
+ expect(activityMessage?.content).toEqual({
+ operations: [firstOperation, secondOperation],
+ });
+
+ expect(result.newMessages).toHaveLength(1);
+ expect(result.newMessages[0].id).toBe("activity-ops");
+ expect(result.newMessages[0].content).toEqual({
+ operations: [firstOperation, secondOperation],
+ });
+ });
});
describe("combined result and newMessages", () => {
diff --git a/sdks/typescript/packages/client/src/agent/agent.ts b/sdks/typescript/packages/client/src/agent/agent.ts
index 8e2f95610..a85af6765 100644
--- a/sdks/typescript/packages/client/src/agent/agent.ts
+++ b/sdks/typescript/packages/client/src/agent/agent.ts
@@ -6,13 +6,14 @@ import { v4 as uuidv4 } from "uuid";
import { structuredClone_ } from "@/utils";
import { catchError, map, tap } from "rxjs/operators";
import { finalize } from "rxjs/operators";
-import { pipe, Observable, from, of } from "rxjs";
+import { pipe, Observable, from, of, EMPTY } from "rxjs";
import { verifyEvents } from "@/verify";
import { convertToLegacyEvents } from "@/legacy/convert";
import { LegacyRuntimeProtocolEvent } from "@/legacy/types";
import { lastValueFrom } from "rxjs";
import { transformChunks } from "@/chunks";
import { AgentStateMutation, AgentSubscriber, runSubscribersWithMutation } from "./subscriber";
+import { AGUIConnectNotImplementedError } from "@ag-ui/core";
export interface RunAgentResult {
result: any;
@@ -27,6 +28,7 @@ export abstract class AbstractAgent {
public state: State;
public debug: boolean = false;
public subscribers: AgentSubscriber[] = [];
+ public isRunning: boolean = false;
constructor({
agentId,
@@ -53,49 +55,110 @@ export abstract class AbstractAgent {
};
}
- abstract run(input: RunAgentInput): Observable;
+ protected abstract run(input: RunAgentInput): Observable;
public async runAgent(
parameters?: RunAgentParameters,
subscriber?: AgentSubscriber,
): Promise {
- this.agentId = this.agentId ?? uuidv4();
- const input = this.prepareRunAgentInput(parameters);
- let result: any = undefined;
- const currentMessageIds = new Set(this.messages.map((message) => message.id));
-
- const subscribers: AgentSubscriber[] = [
- {
- onRunFinishedEvent: (params) => {
- result = params.result;
+ try {
+ this.isRunning = true;
+ this.agentId = this.agentId ?? uuidv4();
+ const input = this.prepareRunAgentInput(parameters);
+ let result: any = undefined;
+ const currentMessageIds = new Set(this.messages.map((message) => message.id));
+
+ const subscribers: AgentSubscriber[] = [
+ {
+ onRunFinishedEvent: (params) => {
+ result = params.result;
+ },
},
- },
- ...this.subscribers,
- subscriber ?? {},
- ];
+ ...this.subscribers,
+ subscriber ?? {},
+ ];
+
+ await this.onInitialize(input, subscribers);
+
+ const pipeline = pipe(
+ () => this.run(input),
+ transformChunks(this.debug),
+ verifyEvents(this.debug),
+ (source$) => this.apply(input, source$, subscribers),
+ (source$) => this.processApplyEvents(input, source$, subscribers),
+ catchError((error) => {
+ this.isRunning = false;
+ return this.onError(input, error, subscribers);
+ }),
+ finalize(() => {
+ this.isRunning = false;
+ void this.onFinalize(input, subscribers);
+ }),
+ );
- await this.onInitialize(input, subscribers);
+ await lastValueFrom(pipeline(of(null)));
+ const newMessages = structuredClone_(this.messages).filter(
+ (message: Message) => !currentMessageIds.has(message.id),
+ );
+ return { result, newMessages };
+ } finally {
+ this.isRunning = false;
+ }
+ }
- const pipeline = pipe(
- () => this.run(input),
- transformChunks(this.debug),
- verifyEvents(this.debug),
- (source$) => this.apply(input, source$, subscribers),
- (source$) => this.processApplyEvents(input, source$, subscribers),
- catchError((error) => {
- return this.onError(input, error, subscribers);
- }),
- finalize(() => {
- void this.onFinalize(input, subscribers);
- }),
- );
+ protected connect(input: RunAgentInput): Observable {
+ throw new AGUIConnectNotImplementedError();
+ }
+ public async connectAgent(
+ parameters?: RunAgentParameters,
+ subscriber?: AgentSubscriber,
+ ): Promise {
+ try {
+ this.isRunning = true;
+ this.agentId = this.agentId ?? uuidv4();
+ const input = this.prepareRunAgentInput(parameters);
+ let result: any = undefined;
+ const currentMessageIds = new Set(this.messages.map((message) => message.id));
+
+ const subscribers: AgentSubscriber[] = [
+ {
+ onRunFinishedEvent: (params) => {
+ result = params.result;
+ },
+ },
+ ...this.subscribers,
+ subscriber ?? {},
+ ];
+
+ await this.onInitialize(input, subscribers);
+
+ const pipeline = pipe(
+ () => this.connect(input),
+ transformChunks(this.debug),
+ verifyEvents(this.debug),
+ (source$) => this.apply(input, source$, subscribers),
+ (source$) => this.processApplyEvents(input, source$, subscribers),
+ catchError((error) => {
+ this.isRunning = false;
+ if (!(error instanceof AGUIConnectNotImplementedError)) {
+ return this.onError(input, error, subscribers);
+ }
+ return EMPTY;
+ }),
+ finalize(() => {
+ this.isRunning = false;
+ void this.onFinalize(input, subscribers);
+ }),
+ );
- return lastValueFrom(pipeline(of(null))).then(() => {
+ await lastValueFrom(pipeline(of(null))); // wait for stream completion before toggling isRunning
const newMessages = structuredClone_(this.messages).filter(
(message: Message) => !currentMessageIds.has(message.id),
);
return { result, newMessages };
- });
+ } finally {
+ this.isRunning = false;
+ }
}
public abortRun() {}
@@ -142,6 +205,11 @@ export abstract class AbstractAgent {
}
protected prepareRunAgentInput(parameters?: RunAgentParameters): RunAgentInput {
+ const clonedMessages = structuredClone_(this.messages) as Message[];
+ const messagesWithoutActivity = clonedMessages.filter(
+ (message) => message.role !== "activity",
+ );
+
return {
threadId: this.threadId,
runId: parameters?.runId || uuidv4(),
@@ -149,7 +217,7 @@ export abstract class AbstractAgent {
context: structuredClone_(parameters?.context ?? []),
forwardedProps: structuredClone_(parameters?.forwardedProps ?? {}),
state: structuredClone_(this.state),
- messages: structuredClone_(this.messages),
+ messages: messagesWithoutActivity,
};
}
@@ -281,12 +349,14 @@ export abstract class AbstractAgent {
public clone() {
const cloned = Object.create(Object.getPrototypeOf(this));
- for (const key of Object.getOwnPropertyNames(this)) {
- const value = (this as any)[key];
- if (typeof value !== "function") {
- cloned[key] = structuredClone_(value);
- }
- }
+ cloned.agentId = this.agentId;
+ cloned.description = this.description;
+ cloned.threadId = this.threadId;
+ cloned.messages = structuredClone_(this.messages);
+ cloned.state = structuredClone_(this.state);
+ cloned.debug = this.debug;
+ cloned.isRunning = this.isRunning;
+ cloned.subscribers = [...this.subscribers];
return cloned;
}
diff --git a/sdks/typescript/packages/client/src/agent/http.ts b/sdks/typescript/packages/client/src/agent/http.ts
index 49fae2173..f9d9c3002 100644
--- a/sdks/typescript/packages/client/src/agent/http.ts
+++ b/sdks/typescript/packages/client/src/agent/http.ts
@@ -58,4 +58,19 @@ export class HttpAgent extends AbstractAgent {
const httpEvents = runHttpRequest(this.url, this.requestInit(input));
return transformHttpEventStream(httpEvents);
}
+
+ public clone(): HttpAgent {
+ const cloned = super.clone() as HttpAgent;
+ cloned.url = this.url;
+ cloned.headers = structuredClone_(this.headers ?? {});
+
+ const newController = new AbortController();
+ const originalSignal = this.abortController.signal as AbortSignal & { reason?: unknown };
+ if (originalSignal.aborted) {
+ newController.abort(originalSignal.reason);
+ }
+ cloned.abortController = newController;
+
+ return cloned;
+ }
}
diff --git a/sdks/typescript/packages/client/src/agent/index.ts b/sdks/typescript/packages/client/src/agent/index.ts
index e1a25b101..046bfa90b 100644
--- a/sdks/typescript/packages/client/src/agent/index.ts
+++ b/sdks/typescript/packages/client/src/agent/index.ts
@@ -2,4 +2,4 @@ export { AbstractAgent } from "./agent";
export type { RunAgentResult } from "./agent";
export { HttpAgent } from "./http";
export type { AgentConfig, HttpAgentConfig, RunAgentParameters } from "./types";
-export type { AgentSubscriber, AgentStateMutation, AgentSubscriberParams} from "./subscriber";
\ No newline at end of file
+export type { AgentSubscriber, AgentStateMutation, AgentSubscriberParams } from "./subscriber";
diff --git a/sdks/typescript/packages/client/src/agent/subscriber.ts b/sdks/typescript/packages/client/src/agent/subscriber.ts
index ab7d09e9f..204038e62 100644
--- a/sdks/typescript/packages/client/src/agent/subscriber.ts
+++ b/sdks/typescript/packages/client/src/agent/subscriber.ts
@@ -21,6 +21,9 @@ import {
RawEvent,
CustomEvent,
ToolCall,
+ ActivitySnapshotEvent,
+ ActivityDeltaEvent,
+ ActivityMessage,
} from "@ag-ui/core";
import { AbstractAgent } from "./agent";
import { structuredClone_ } from "@/utils";
@@ -123,6 +126,21 @@ export interface AgentSubscriber {
params: { event: MessagesSnapshotEvent } & AgentSubscriberParams,
): MaybePromise;
+ onActivitySnapshotEvent?(
+ params: {
+ event: ActivitySnapshotEvent;
+ activityMessage?: ActivityMessage;
+ existingMessage?: Message;
+ } & AgentSubscriberParams,
+ ): MaybePromise;
+
+ onActivityDeltaEvent?(
+ params: {
+ event: ActivityDeltaEvent;
+ activityMessage?: ActivityMessage;
+ } & AgentSubscriberParams,
+ ): MaybePromise;
+
onRawEvent?(
params: { event: RawEvent } & AgentSubscriberParams,
): MaybePromise;
diff --git a/sdks/typescript/packages/client/src/apply/__tests__/default.activity.test.ts b/sdks/typescript/packages/client/src/apply/__tests__/default.activity.test.ts
new file mode 100644
index 000000000..e6d0c8263
--- /dev/null
+++ b/sdks/typescript/packages/client/src/apply/__tests__/default.activity.test.ts
@@ -0,0 +1,381 @@
+import { Subject } from "rxjs";
+import { toArray } from "rxjs/operators";
+import { firstValueFrom } from "rxjs";
+import {
+ ActivityDeltaEvent,
+ ActivitySnapshotEvent,
+ BaseEvent,
+ EventType,
+ Message,
+ RunAgentInput,
+} from "@ag-ui/core";
+import { defaultApplyEvents } from "../default";
+import { AbstractAgent } from "@/agent";
+
+const createAgent = (messages: Message[] = []) =>
+ ({
+ messages: messages.map((message) => ({ ...message })),
+ state: {},
+ } as unknown as AbstractAgent);
+
+describe("defaultApplyEvents with activity events", () => {
+ it("creates and updates activity messages via snapshot and delta", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["search"] },
+ } as ActivitySnapshotEvent);
+
+ events$.next({
+ type: EventType.ACTIVITY_DELTA,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ patch: [{ op: "replace", path: "/tasks/0", value: "✓ search" }],
+ } as ActivityDeltaEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+
+ expect(stateUpdates.length).toBe(2);
+
+ const snapshotUpdate = stateUpdates[0];
+ expect(snapshotUpdate?.messages?.[0]?.role).toBe("activity");
+ expect(snapshotUpdate?.messages?.[0]?.activityType).toBe("PLAN");
+ expect(snapshotUpdate?.messages?.[0]?.content).toEqual({ tasks: ["search"] });
+
+ const deltaUpdate = stateUpdates[1];
+ expect(deltaUpdate?.messages?.[0]?.content).toEqual({ tasks: ["✓ search"] });
+ });
+
+ it("appends operations via delta when snapshot starts with an empty array", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ const firstOperation = { id: "op-1", status: "PENDING" };
+ const secondOperation = { id: "op-2", status: "COMPLETED" };
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-ops",
+ activityType: "PLAN",
+ content: { operations: [] },
+ } as ActivitySnapshotEvent);
+
+ events$.next({
+ type: EventType.ACTIVITY_DELTA,
+ messageId: "activity-ops",
+ activityType: "PLAN",
+ patch: [
+ { op: "add", path: "/operations/-", value: firstOperation },
+ ],
+ } as ActivityDeltaEvent);
+
+ events$.next({
+ type: EventType.ACTIVITY_DELTA,
+ messageId: "activity-ops",
+ activityType: "PLAN",
+ patch: [
+ { op: "add", path: "/operations/-", value: secondOperation },
+ ],
+ } as ActivityDeltaEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+
+ expect(stateUpdates.length).toBe(3);
+
+ const snapshotUpdate = stateUpdates[0];
+ expect(snapshotUpdate?.messages?.[0]?.content).toEqual({ operations: [] });
+
+ const firstDeltaUpdate = stateUpdates[1];
+ expect(firstDeltaUpdate?.messages?.[0]?.content?.operations).toEqual([
+ firstOperation,
+ ]);
+
+ const secondDeltaUpdate = stateUpdates[2];
+ expect(secondDeltaUpdate?.messages?.[0]?.content?.operations).toEqual([
+ firstOperation,
+ secondOperation,
+ ]);
+ });
+
+ it("does not replace existing activity message when replace is false", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [
+ {
+ id: "activity-1",
+ role: "activity",
+ activityType: "PLAN",
+ content: { tasks: ["initial"] },
+ },
+ ],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["updated"] },
+ replace: false,
+ } as ActivitySnapshotEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+ expect(stateUpdates.length).toBe(1);
+ const update = stateUpdates[0];
+ expect(update?.messages?.[0]?.content).toEqual({ tasks: ["initial"] });
+ });
+
+ it("adds activity message when replace is false and none exists", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["first"] },
+ replace: false,
+ } as ActivitySnapshotEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+ expect(stateUpdates.length).toBe(1);
+ const update = stateUpdates[0];
+ expect(update?.messages?.[0]?.content).toEqual({ tasks: ["first"] });
+ expect(update?.messages?.[0]?.role).toBe("activity");
+ });
+
+ it("replaces existing activity message when replace is true", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [
+ {
+ id: "activity-1",
+ role: "activity" as const,
+ activityType: "PLAN",
+ content: { tasks: ["initial"] },
+ },
+ ],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["updated"] },
+ replace: true,
+ } as ActivitySnapshotEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+ expect(stateUpdates.length).toBe(1);
+ const update = stateUpdates[0];
+ expect(update?.messages?.[0]?.content).toEqual({ tasks: ["updated"] });
+ });
+
+ it("replaces non-activity message when replace is true", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [
+ {
+ id: "activity-1",
+ role: "user" as const,
+ content: "placeholder",
+ },
+ ],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["first"] },
+ replace: true,
+ } as ActivitySnapshotEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+ expect(stateUpdates.length).toBe(1);
+ const update = stateUpdates[0];
+ expect(update?.messages?.[0]?.role).toBe("activity");
+ expect(update?.messages?.[0]?.content).toEqual({ tasks: ["first"] });
+ });
+
+ it("does not alter non-activity message when replace is false", async () => {
+ const events$ = new Subject();
+ const initialState: RunAgentInput = {
+ messages: [
+ {
+ id: "activity-1",
+ role: "user" as const,
+ content: "placeholder",
+ },
+ ],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
+ const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
+
+ events$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["first"] },
+ replace: false,
+ } as ActivitySnapshotEvent);
+
+ events$.complete();
+
+ const stateUpdates = await stateUpdatesPromise;
+ expect(stateUpdates.length).toBe(1);
+ const update = stateUpdates[0];
+ expect(update?.messages?.[0]?.role).toBe("user");
+ expect(update?.messages?.[0]?.content).toBe("placeholder");
+ });
+
+ it("maintains replace semantics across runs", async () => {
+ const firstRunEvents$ = new Subject();
+ const baseInput: RunAgentInput = {
+ messages: [],
+ state: {},
+ threadId: "thread-activity",
+ runId: "run-activity",
+ tools: [],
+ context: [],
+ };
+
+ const baseAgent = createAgent(baseInput.messages);
+ const firstResult$ = defaultApplyEvents(baseInput, firstRunEvents$, baseAgent, []);
+ const firstUpdatesPromise = firstValueFrom(firstResult$.pipe(toArray()));
+
+ firstRunEvents$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["initial"] },
+ replace: true,
+ } as ActivitySnapshotEvent);
+ firstRunEvents$.complete();
+
+ const firstUpdates = await firstUpdatesPromise;
+ const nextMessages = firstUpdates[0]?.messages ?? [];
+
+ const secondRunEvents$ = new Subject();
+ const secondInput: RunAgentInput = {
+ ...baseInput,
+ messages: nextMessages,
+ };
+
+ const secondAgent = createAgent(secondInput.messages);
+ const secondResult$ = defaultApplyEvents(
+ secondInput,
+ secondRunEvents$,
+ secondAgent,
+ [],
+ );
+ const secondUpdatesPromise = firstValueFrom(secondResult$.pipe(toArray()));
+
+ secondRunEvents$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["updated"] },
+ replace: false,
+ } as ActivitySnapshotEvent);
+
+ secondRunEvents$.next({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "activity-1",
+ activityType: "PLAN",
+ content: { tasks: ["final"] },
+ replace: true,
+ } as ActivitySnapshotEvent);
+
+ secondRunEvents$.complete();
+
+ const secondUpdates = await secondUpdatesPromise;
+ expect(secondUpdates.length).toBe(2);
+ const afterReplaceFalse = secondUpdates[0];
+ expect(afterReplaceFalse?.messages?.[0]?.content).toEqual({ tasks: ["initial"] });
+ const afterReplaceTrue = secondUpdates[1];
+ expect(afterReplaceTrue?.messages?.[0]?.content).toEqual({ tasks: ["final"] });
+ });
+});
diff --git a/sdks/typescript/packages/client/src/apply/__tests__/default.concurrent.test.ts b/sdks/typescript/packages/client/src/apply/__tests__/default.concurrent.test.ts
index 49cad4552..322ada5e8 100644
--- a/sdks/typescript/packages/client/src/apply/__tests__/default.concurrent.test.ts
+++ b/sdks/typescript/packages/client/src/apply/__tests__/default.concurrent.test.ts
@@ -19,12 +19,12 @@ import {
} from "@ag-ui/core";
import { AbstractAgent } from "../../agent";
-// Mock agent for testing
-const FAKE_AGENT = {
- messages: [],
- state: {},
- agentId: "test-agent",
-} as unknown as AbstractAgent;
+const createAgent = (messages: Message[] = []) =>
+ ({
+ messages: messages.map((message) => ({ ...message })),
+ state: {},
+ agentId: "test-agent",
+ } as unknown as AbstractAgent);
describe("defaultApplyEvents concurrent operations", () => {
// Test: Concurrent text messages should create separate messages
@@ -41,7 +41,8 @@ describe("defaultApplyEvents concurrent operations", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -125,7 +126,8 @@ describe("defaultApplyEvents concurrent operations", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -218,7 +220,8 @@ describe("defaultApplyEvents concurrent operations", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -333,7 +336,8 @@ describe("defaultApplyEvents concurrent operations", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -444,7 +448,8 @@ describe("defaultApplyEvents concurrent operations", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -552,7 +557,8 @@ describe("defaultApplyEvents concurrent operations", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
diff --git a/sdks/typescript/packages/client/src/apply/__tests__/default.state.test.ts b/sdks/typescript/packages/client/src/apply/__tests__/default.state.test.ts
index 71e64efc1..41627ee2e 100644
--- a/sdks/typescript/packages/client/src/apply/__tests__/default.state.test.ts
+++ b/sdks/typescript/packages/client/src/apply/__tests__/default.state.test.ts
@@ -1,10 +1,14 @@
import { AbstractAgent } from "@/agent";
import { defaultApplyEvents } from "../default";
-import { EventType, StateDeltaEvent } from "@ag-ui/core";
+import { EventType, Message, StateDeltaEvent } from "@ag-ui/core";
import { of } from "rxjs";
import { AgentStateMutation } from "@/agent/subscriber";
-const FAKE_AGENT = null as unknown as AbstractAgent;
+const createAgent = (messages: Message[] = []) =>
+ ({
+ messages: messages.map((message) => ({ ...message })),
+ state: {},
+ } as unknown as AbstractAgent);
describe("defaultApplyEvents - State Patching", () => {
it("should apply state delta patch correctly", (done) => {
@@ -30,7 +34,8 @@ describe("defaultApplyEvents - State Patching", () => {
const events$ = of(stateDelta);
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
result$.subscribe((update: AgentStateMutation) => {
expect(update.state).toEqual({
@@ -65,7 +70,8 @@ describe("defaultApplyEvents - State Patching", () => {
const events$ = of(stateDelta);
// Cast to any to bypass strict type checking
- const result$ = defaultApplyEvents(initialState as any, events$, FAKE_AGENT, []);
+ const agent = createAgent((initialState as any).messages as Message[]);
+ const result$ = defaultApplyEvents(initialState as any, events$, agent, []);
result$.subscribe((update: AgentStateMutation) => {
expect(update.state).toEqual({
@@ -102,7 +108,8 @@ describe("defaultApplyEvents - State Patching", () => {
const events$ = of(stateDelta);
// Cast to any to bypass strict type checking
- const result$ = defaultApplyEvents(initialState as any, events$, FAKE_AGENT, []);
+ const agent = createAgent((initialState as any).messages as Message[]);
+ const result$ = defaultApplyEvents(initialState as any, events$, agent, []);
result$.subscribe((update: AgentStateMutation) => {
expect(update.state).toEqual({
@@ -137,7 +144,8 @@ describe("defaultApplyEvents - State Patching", () => {
const events$ = of(...stateDeltas);
// Cast to any to bypass strict type checking
- const result$ = defaultApplyEvents(initialState as any, events$, FAKE_AGENT, []);
+ const agent = createAgent((initialState as any).messages as Message[]);
+ const result$ = defaultApplyEvents(initialState as any, events$, agent, []);
let updateCount = 0;
result$.subscribe((update: AgentStateMutation) => {
@@ -176,7 +184,8 @@ describe("defaultApplyEvents - State Patching", () => {
const events$ = of(stateDelta);
// Cast to any to bypass strict type checking
- const result$ = defaultApplyEvents(initialState as any, events$, FAKE_AGENT, []);
+ const agent = createAgent((initialState as any).messages as Message[]);
+ const result$ = defaultApplyEvents(initialState as any, events$, agent, []);
let updateCount = 0;
result$.subscribe({
diff --git a/sdks/typescript/packages/client/src/apply/__tests__/default.text-message.test.ts b/sdks/typescript/packages/client/src/apply/__tests__/default.text-message.test.ts
index 581f418df..031713f29 100644
--- a/sdks/typescript/packages/client/src/apply/__tests__/default.text-message.test.ts
+++ b/sdks/typescript/packages/client/src/apply/__tests__/default.text-message.test.ts
@@ -4,6 +4,7 @@ import { firstValueFrom } from "rxjs";
import {
BaseEvent,
EventType,
+ Message,
RunStartedEvent,
TextMessageStartEvent,
TextMessageContentEvent,
@@ -13,7 +14,11 @@ import {
import { defaultApplyEvents } from "../default";
import { AbstractAgent } from "@/agent";
-const FAKE_AGENT = null as unknown as AbstractAgent;
+const createAgent = (messages: Message[] = []) =>
+ ({
+ messages: messages.map((message) => ({ ...message })),
+ state: {},
+ } as unknown as AbstractAgent);
describe("defaultApplyEvents with text messages", () => {
it("should handle text message events correctly", async () => {
@@ -29,7 +34,8 @@ describe("defaultApplyEvents with text messages", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -102,7 +108,8 @@ describe("defaultApplyEvents with text messages", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
diff --git a/sdks/typescript/packages/client/src/apply/__tests__/default.tool-calls.test.ts b/sdks/typescript/packages/client/src/apply/__tests__/default.tool-calls.test.ts
index bcce0ab40..261057677 100644
--- a/sdks/typescript/packages/client/src/apply/__tests__/default.tool-calls.test.ts
+++ b/sdks/typescript/packages/client/src/apply/__tests__/default.tool-calls.test.ts
@@ -2,19 +2,24 @@ import { Subject } from "rxjs";
import { toArray } from "rxjs/operators";
import { firstValueFrom } from "rxjs";
import {
+ AssistantMessage,
BaseEvent,
EventType,
+ Message,
+ RunAgentInput,
RunStartedEvent,
- ToolCallStartEvent,
ToolCallArgsEvent,
ToolCallEndEvent,
- RunAgentInput,
- AssistantMessage,
+ ToolCallStartEvent,
} from "@ag-ui/core";
import { defaultApplyEvents } from "../default";
import { AbstractAgent } from "@/agent";
-const FAKE_AGENT = null as unknown as AbstractAgent;
+const createAgent = (messages: Message[] = []) =>
+ ({
+ messages: messages.map((message) => ({ ...message })),
+ state: {},
+ } as unknown as AbstractAgent);
describe("defaultApplyEvents with tool calls", () => {
it("should handle a single tool call correctly", async () => {
@@ -33,7 +38,8 @@ describe("defaultApplyEvents with tool calls", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -120,7 +126,8 @@ describe("defaultApplyEvents with tool calls", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -224,7 +231,8 @@ describe("defaultApplyEvents with tool calls", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages as Message[]);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -287,7 +295,8 @@ describe("defaultApplyEvents with tool calls", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
@@ -346,7 +355,8 @@ describe("defaultApplyEvents with tool calls", () => {
};
// Create the observable stream
- const result$ = defaultApplyEvents(initialState, events$, FAKE_AGENT, []);
+ const agent = createAgent(initialState.messages);
+ const result$ = defaultApplyEvents(initialState, events$, agent, []);
// Collect all emitted state updates in an array
const stateUpdatesPromise = firstValueFrom(result$.pipe(toArray()));
diff --git a/sdks/typescript/packages/client/src/apply/__tests__/run-started-input.test.ts b/sdks/typescript/packages/client/src/apply/__tests__/run-started-input.test.ts
new file mode 100644
index 000000000..9be1a0133
--- /dev/null
+++ b/sdks/typescript/packages/client/src/apply/__tests__/run-started-input.test.ts
@@ -0,0 +1,416 @@
+import { AbstractAgent } from "../../agent/agent";
+import {
+ BaseEvent,
+ EventType,
+ Message,
+ RunAgentInput,
+ RunStartedEvent,
+ RunFinishedEvent,
+ TextMessageStartEvent,
+ TextMessageContentEvent,
+ TextMessageEndEvent,
+} from "@ag-ui/core";
+import { Observable, of } from "rxjs";
+import { AgentSubscriber } from "../../agent/subscriber";
+
+describe("RunStartedEvent with input.messages", () => {
+ class TestAgent extends AbstractAgent {
+ private events: BaseEvent[] = [];
+
+ setEvents(events: BaseEvent[]) {
+ this.events = events;
+ }
+
+ protected run(input: RunAgentInput): Observable {
+ return of(...this.events);
+ }
+ }
+
+ it("should add messages from RunStartedEvent.input that are not already present", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ const events: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ input: {
+ threadId: "test-thread",
+ runId: "run-1",
+ messages: [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "Hello",
+ },
+ {
+ id: "msg-2",
+ role: "user",
+ content: "How are you?",
+ },
+ ],
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(events);
+ const result = await agent.runAgent({ runId: "run-1" });
+
+ // Verify both messages were added
+ expect(agent.messages.length).toBe(2);
+ expect(agent.messages[0].id).toBe("msg-1");
+ expect(agent.messages[0].content).toBe("Hello");
+ expect(agent.messages[1].id).toBe("msg-2");
+ expect(agent.messages[1].content).toBe("How are you?");
+
+ // Verify they appear in newMessages
+ expect(result.newMessages.length).toBe(2);
+ });
+
+ it("should not duplicate messages that already exist (by ID)", async () => {
+ const initialMessages: Message[] = [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "Existing message",
+ },
+ ];
+
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages,
+ });
+
+ const events: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ input: {
+ threadId: "test-thread",
+ runId: "run-1",
+ messages: [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "Duplicate message (should be ignored)",
+ },
+ {
+ id: "msg-2",
+ role: "user",
+ content: "New message",
+ },
+ ],
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(events);
+ const result = await agent.runAgent({ runId: "run-1" });
+
+ // Verify only the new message was added
+ expect(agent.messages.length).toBe(2);
+ expect(agent.messages[0].id).toBe("msg-1");
+ expect(agent.messages[0].content).toBe("Existing message"); // Original content preserved
+ expect(agent.messages[1].id).toBe("msg-2");
+ expect(agent.messages[1].content).toBe("New message");
+
+ // Verify only the new message appears in newMessages
+ expect(result.newMessages.length).toBe(1);
+ expect(result.newMessages[0].id).toBe("msg-2");
+ });
+
+ it("should handle RunStartedEvent without input field", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ const events: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ // No input field
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(events);
+ const result = await agent.runAgent({ runId: "run-1" });
+
+ // Verify no errors and messages remain empty
+ expect(agent.messages.length).toBe(0);
+ expect(result.newMessages.length).toBe(0);
+ });
+
+ it("should handle RunStartedEvent with input but no messages", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ const events: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ input: {
+ threadId: "test-thread",
+ runId: "run-1",
+ messages: [], // Empty messages array
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(events);
+ const result = await agent.runAgent({ runId: "run-1" });
+
+ // Verify no errors and messages remain empty
+ expect(agent.messages.length).toBe(0);
+ expect(result.newMessages.length).toBe(0);
+ });
+
+ it("should respect stopPropagation from subscribers", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ // Create a subscriber that stops propagation
+ const stopPropagationSubscriber: AgentSubscriber = {
+ onRunStartedEvent: () => {
+ return { stopPropagation: true };
+ },
+ };
+
+ const events: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ input: {
+ threadId: "test-thread",
+ runId: "run-1",
+ messages: [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "Should not be added",
+ },
+ ],
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(events);
+ const result = await agent.runAgent({ runId: "run-1" }, stopPropagationSubscriber);
+
+ // Verify messages were NOT added due to stopPropagation
+ expect(agent.messages.length).toBe(0);
+ expect(result.newMessages.length).toBe(0);
+ });
+
+ it("should add messages before other events in the same run", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ const events: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ input: {
+ threadId: "test-thread",
+ runId: "run-1",
+ messages: [
+ {
+ id: "msg-from-input",
+ role: "user",
+ content: "From input",
+ },
+ ],
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.TEXT_MESSAGE_START,
+ messageId: "msg-streamed",
+ role: "assistant",
+ } as TextMessageStartEvent,
+ {
+ type: EventType.TEXT_MESSAGE_CONTENT,
+ messageId: "msg-streamed",
+ delta: "Streamed response",
+ } as TextMessageContentEvent,
+ {
+ type: EventType.TEXT_MESSAGE_END,
+ messageId: "msg-streamed",
+ } as TextMessageEndEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(events);
+ const result = await agent.runAgent({ runId: "run-1" });
+
+ // Verify message order: input message first, then streamed message
+ expect(agent.messages.length).toBe(2);
+ expect(agent.messages[0].id).toBe("msg-from-input");
+ expect(agent.messages[0].content).toBe("From input");
+ expect(agent.messages[1].id).toBe("msg-streamed");
+ expect(agent.messages[1].content).toBe("Streamed response");
+
+ expect(result.newMessages.length).toBe(2);
+ });
+
+ it("should handle multiple runs with input.messages", async () => {
+ const agent = new TestAgent({
+ threadId: "test-thread",
+ initialMessages: [],
+ });
+
+ // First run with one message
+ const firstRunEvents: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-1",
+ input: {
+ threadId: "test-thread",
+ runId: "run-1",
+ messages: [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "First message",
+ },
+ ],
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-1",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(firstRunEvents);
+ const result1 = await agent.runAgent({ runId: "run-1" });
+
+ expect(agent.messages.length).toBe(1);
+ expect(agent.messages[0].id).toBe("msg-1");
+ expect(result1.newMessages.length).toBe(1);
+
+ // Second run with three messages (one duplicate, two new)
+ const secondRunEvents: BaseEvent[] = [
+ {
+ type: EventType.RUN_STARTED,
+ threadId: "test-thread",
+ runId: "run-2",
+ input: {
+ threadId: "test-thread",
+ runId: "run-2",
+ messages: [
+ {
+ id: "msg-1",
+ role: "user",
+ content: "First message (duplicate)",
+ },
+ {
+ id: "msg-2",
+ role: "user",
+ content: "Second message",
+ },
+ {
+ id: "msg-3",
+ role: "user",
+ content: "Third message",
+ },
+ ],
+ tools: [],
+ context: [],
+ state: {},
+ forwardedProps: {},
+ },
+ } as RunStartedEvent,
+ {
+ type: EventType.RUN_FINISHED,
+ threadId: "test-thread",
+ runId: "run-2",
+ } as RunFinishedEvent,
+ ];
+
+ agent.setEvents(secondRunEvents);
+ const result2 = await agent.runAgent({ runId: "run-2" });
+
+ // Verify only new messages were added
+ expect(agent.messages.length).toBe(3);
+ expect(agent.messages[0].id).toBe("msg-1");
+ expect(agent.messages[0].content).toBe("First message"); // Original content preserved
+ expect(agent.messages[1].id).toBe("msg-2");
+ expect(agent.messages[1].content).toBe("Second message");
+ expect(agent.messages[2].id).toBe("msg-3");
+ expect(agent.messages[2].content).toBe("Third message");
+
+ // Verify only the two new messages appear in newMessages for the second run
+ expect(result2.newMessages.length).toBe(2);
+ expect(result2.newMessages[0].id).toBe("msg-2");
+ expect(result2.newMessages[1].id).toBe("msg-3");
+ });
+});
diff --git a/sdks/typescript/packages/client/src/apply/default.ts b/sdks/typescript/packages/client/src/apply/default.ts
index 8f720c7a0..6cd3dcd8b 100644
--- a/sdks/typescript/packages/client/src/apply/default.ts
+++ b/sdks/typescript/packages/client/src/apply/default.ts
@@ -25,6 +25,9 @@ import {
RunErrorEvent,
StepStartedEvent,
StepFinishedEvent,
+ ActivitySnapshotEvent,
+ ActivityDeltaEvent,
+ ActivityMessage,
} from "@ag-ui/core";
import { mergeMap, mergeAll, defaultIfEmpty, concatMap } from "rxjs/operators";
import { of, EMPTY } from "rxjs";
@@ -45,7 +48,7 @@ export const defaultApplyEvents = (
agent: AbstractAgent,
subscribers: AgentSubscriber[],
): Observable => {
- let messages = structuredClone_(input.messages);
+ let messages = structuredClone_(agent.messages);
let state = structuredClone_(input.state);
let currentMutation: AgentStateMutation = {};
@@ -140,14 +143,17 @@ export const defaultApplyEvents = (
state,
agent,
input,
- textMessageBuffer: targetMessage.content ?? "",
+ textMessageBuffer:
+ typeof targetMessage.content === "string" ? targetMessage.content : "",
}),
);
applyMutation(mutation);
if (mutation.stopPropagation !== true) {
// Append content to the correct message by ID
- targetMessage.content = (targetMessage.content || "") + delta;
+ const existingContent =
+ typeof targetMessage.content === "string" ? targetMessage.content : "";
+ targetMessage.content = `${existingContent}${delta}`;
applyMutation({ messages });
}
@@ -175,7 +181,8 @@ export const defaultApplyEvents = (
state,
agent,
input,
- textMessageBuffer: targetMessage.content ?? "",
+ textMessageBuffer:
+ typeof targetMessage.content === "string" ? targetMessage.content : "",
}),
);
applyMutation(mutation);
@@ -513,6 +520,143 @@ export const defaultApplyEvents = (
return emitUpdates();
}
+ case EventType.ACTIVITY_SNAPSHOT: {
+ const activityEvent = event as ActivitySnapshotEvent;
+ const existingIndex = messages.findIndex((m) => m.id === activityEvent.messageId);
+ const existingMessage = existingIndex >= 0 ? messages[existingIndex] : undefined;
+ const existingActivityMessage =
+ existingMessage?.role === "activity" ? (existingMessage as ActivityMessage) : undefined;
+ const replace = activityEvent.replace ?? true;
+
+ const mutation = await runSubscribersWithMutation(
+ subscribers,
+ messages,
+ state,
+ (subscriber, messages, state) =>
+ subscriber.onActivitySnapshotEvent?.({
+ event: activityEvent,
+ messages,
+ state,
+ agent,
+ input,
+ activityMessage: existingActivityMessage,
+ existingMessage,
+ }),
+ );
+ applyMutation(mutation);
+
+ if (mutation.stopPropagation !== true) {
+ const activityMessage: ActivityMessage = {
+ id: activityEvent.messageId,
+ role: "activity",
+ activityType: activityEvent.activityType,
+ content: structuredClone_(activityEvent.content),
+ };
+
+ let createdMessage: ActivityMessage | undefined;
+
+ if (existingIndex === -1) {
+ messages.push(activityMessage);
+ createdMessage = activityMessage;
+ } else if (existingActivityMessage) {
+ if (replace) {
+ messages[existingIndex] = {
+ ...existingActivityMessage,
+ activityType: activityEvent.activityType,
+ content: structuredClone_(activityEvent.content),
+ };
+ }
+ } else if (replace) {
+ messages[existingIndex] = activityMessage;
+ createdMessage = activityMessage;
+ }
+
+ applyMutation({ messages });
+
+ if (createdMessage) {
+ await Promise.all(
+ subscribers.map((subscriber) =>
+ subscriber.onNewMessage?.({
+ message: createdMessage,
+ messages,
+ state,
+ agent,
+ input,
+ }),
+ ),
+ );
+ }
+ }
+
+ return emitUpdates();
+ }
+
+ case EventType.ACTIVITY_DELTA: {
+ const activityEvent = event as ActivityDeltaEvent;
+ const existingIndex = messages.findIndex((m) => m.id === activityEvent.messageId);
+ if (existingIndex === -1) {
+ console.warn(
+ `ACTIVITY_DELTA: No message found with ID '${activityEvent.messageId}' to apply patch`,
+ );
+ return emitUpdates();
+ }
+
+ const existingMessage = messages[existingIndex];
+ if (existingMessage.role !== "activity") {
+ console.warn(
+ `ACTIVITY_DELTA: Message '${activityEvent.messageId}' is not an activity message`,
+ );
+ return emitUpdates();
+ }
+
+ const existingActivityMessage = existingMessage as ActivityMessage;
+
+ const mutation = await runSubscribersWithMutation(
+ subscribers,
+ messages,
+ state,
+ (subscriber, messages, state) =>
+ subscriber.onActivityDeltaEvent?.({
+ event: activityEvent,
+ messages,
+ state,
+ agent,
+ input,
+ activityMessage: existingActivityMessage,
+ }),
+ );
+ applyMutation(mutation);
+
+ if (mutation.stopPropagation !== true) {
+ try {
+ const baseContent = structuredClone_(existingActivityMessage.content ?? {});
+
+ const result = applyPatch(
+ baseContent,
+ activityEvent.patch ?? [],
+ true,
+ false,
+ );
+ const updatedContent = result.newDocument as ActivityMessage["content"];
+
+ messages[existingIndex] = {
+ ...existingActivityMessage,
+ content: structuredClone_(updatedContent),
+ activityType: activityEvent.activityType,
+ };
+
+ applyMutation({ messages });
+ } catch (error: unknown) {
+ const errorMessage = error instanceof Error ? error.message : String(error);
+ console.warn(
+ `Failed to apply activity patch for '${activityEvent.messageId}': ${errorMessage}`,
+ );
+ }
+ }
+
+ return emitUpdates();
+ }
+
case EventType.RAW: {
const mutation = await runSubscribersWithMutation(
subscribers,
@@ -567,6 +711,25 @@ export const defaultApplyEvents = (
);
applyMutation(mutation);
+ // Handle input.messages if present and stopPropagation is not set
+ if (mutation.stopPropagation !== true) {
+ const runStartedEvent = event as RunStartedEvent;
+
+ // Check if the event contains input with messages
+ if (runStartedEvent.input?.messages) {
+ // Add messages that aren't already present (checked by ID)
+ for (const message of runStartedEvent.input.messages) {
+ const existingMessage = messages.find((m) => m.id === message.id);
+ if (!existingMessage) {
+ messages.push(message);
+ }
+ }
+
+ // Apply mutation to emit the updated messages
+ applyMutation({ messages });
+ }
+ }
+
return emitUpdates();
}
diff --git a/sdks/typescript/packages/client/src/chunks/transform.ts b/sdks/typescript/packages/client/src/chunks/transform.ts
index bb0ec6ba4..0f2f4e15c 100644
--- a/sdks/typescript/packages/client/src/chunks/transform.ts
+++ b/sdks/typescript/packages/client/src/chunks/transform.ts
@@ -101,6 +101,8 @@ export const transformChunks =
case EventType.THINKING_TEXT_MESSAGE_END:
return [...closePendingEvent(), event];
case EventType.RAW:
+ case EventType.ACTIVITY_SNAPSHOT:
+ case EventType.ACTIVITY_DELTA:
return [event];
case EventType.TEXT_MESSAGE_CHUNK:
const messageChunkEvent = event as TextMessageChunkEvent;
@@ -220,10 +222,11 @@ export const transformChunks =
return toolMessageResult;
}
const _exhaustiveCheck: never = event.type;
+ return [];
}),
finalize(() => {
// This ensures that we close any pending events when the source observable completes
- return closePendingEvent();
+ closePendingEvent();
}),
);
};
diff --git a/sdks/typescript/packages/client/src/compact/__tests__/compact.test.ts b/sdks/typescript/packages/client/src/compact/__tests__/compact.test.ts
new file mode 100644
index 000000000..17ccb017c
--- /dev/null
+++ b/sdks/typescript/packages/client/src/compact/__tests__/compact.test.ts
@@ -0,0 +1,294 @@
+import { compactEvents } from "../compact";
+import {
+ EventType,
+ TextMessageStartEvent,
+ TextMessageContentEvent,
+ ToolCallStartEvent,
+ ToolCallArgsEvent,
+ CustomEvent,
+} from "@ag-ui/core";
+
+describe("Event Compaction", () => {
+ describe("Text Message Compaction", () => {
+ it("should compact multiple text message content events into one", () => {
+ const events = [
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg1", role: "user" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "Hello" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: " " },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "world" },
+ { type: EventType.TEXT_MESSAGE_END, messageId: "msg1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(3);
+ expect(compacted[0].type).toBe(EventType.TEXT_MESSAGE_START);
+ expect(compacted[1].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
+ expect((compacted[1] as TextMessageContentEvent).delta).toBe("Hello world");
+ expect(compacted[2].type).toBe(EventType.TEXT_MESSAGE_END);
+ });
+
+ it("should move interleaved events to after text message events", () => {
+ const events = [
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg1", role: "assistant" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "Processing" },
+ { type: EventType.CUSTOM, id: "custom1", name: "thinking" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "..." },
+ { type: EventType.CUSTOM, id: "custom2", name: "done-thinking" },
+ { type: EventType.TEXT_MESSAGE_END, messageId: "msg1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(5);
+ // Text message events should come first
+ expect(compacted[0].type).toBe(EventType.TEXT_MESSAGE_START);
+ expect(compacted[1].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
+ expect((compacted[1] as TextMessageContentEvent).delta).toBe("Processing...");
+ expect(compacted[2].type).toBe(EventType.TEXT_MESSAGE_END);
+ // Other events should come after
+ expect(compacted[3].type).toBe(EventType.CUSTOM);
+ expect((compacted[3] as CustomEvent & { id: string }).id).toBe("custom1");
+ expect(compacted[4].type).toBe(EventType.CUSTOM);
+ expect((compacted[4] as CustomEvent & { id: string }).id).toBe("custom2");
+ });
+
+ it("should handle multiple messages independently", () => {
+ const events = [
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg1", role: "user" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "Hi" },
+ { type: EventType.TEXT_MESSAGE_END, messageId: "msg1" },
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg2", role: "assistant" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg2", delta: "Hello" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg2", delta: " there" },
+ { type: EventType.TEXT_MESSAGE_END, messageId: "msg2" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(6);
+ // First message
+ expect(compacted[0].type).toBe(EventType.TEXT_MESSAGE_START);
+ expect((compacted[0] as TextMessageStartEvent).messageId).toBe("msg1");
+ expect(compacted[1].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
+ expect((compacted[1] as TextMessageContentEvent).delta).toBe("Hi");
+ expect(compacted[2].type).toBe(EventType.TEXT_MESSAGE_END);
+ // Second message
+ expect(compacted[3].type).toBe(EventType.TEXT_MESSAGE_START);
+ expect((compacted[3] as TextMessageStartEvent).messageId).toBe("msg2");
+ expect(compacted[4].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
+ expect((compacted[4] as TextMessageContentEvent).delta).toBe("Hello there");
+ expect(compacted[5].type).toBe(EventType.TEXT_MESSAGE_END);
+ });
+
+ it("should handle incomplete messages", () => {
+ const events = [
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg1", role: "user" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "Incomplete" },
+ // No END event
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(2);
+ expect(compacted[0].type).toBe(EventType.TEXT_MESSAGE_START);
+ expect(compacted[1].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
+ expect((compacted[1] as TextMessageContentEvent).delta).toBe("Incomplete");
+ });
+
+ it("should pass through non-text-message events unchanged", () => {
+ const events = [
+ { type: EventType.CUSTOM, id: "custom1", name: "event1" },
+ { type: EventType.TOOL_CALL_START, toolCallId: "tool1", toolCallName: "search" },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toEqual(events);
+ });
+
+ it("should handle empty content deltas", () => {
+ const events = [
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg1", role: "user" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "Hello" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "" },
+ { type: EventType.TEXT_MESSAGE_END, messageId: "msg1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(3);
+ expect((compacted[1] as TextMessageContentEvent).delta).toBe("Hello");
+ });
+ });
+
+ describe("Tool Call Compaction", () => {
+ it("should compact multiple tool call args events into one", () => {
+ const events = [
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool1",
+ toolCallName: "search",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '{"query": "' },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: "weather" },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: ' today"' },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: "}" },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(3);
+ expect(compacted[0].type).toBe(EventType.TOOL_CALL_START);
+ expect(compacted[1].type).toBe(EventType.TOOL_CALL_ARGS);
+ expect((compacted[1] as ToolCallArgsEvent).delta).toBe('{"query": "weather today"}');
+ expect(compacted[2].type).toBe(EventType.TOOL_CALL_END);
+ });
+
+ it("should move interleaved events to after tool call events", () => {
+ const events = [
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool1",
+ toolCallName: "calculate",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '{"a": ' },
+ { type: EventType.CUSTOM, id: "custom1", name: "processing" },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '10, "b": 20}' },
+ { type: EventType.CUSTOM, id: "custom2", name: "calculating" },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(5);
+ // Tool call events should come first
+ expect(compacted[0].type).toBe(EventType.TOOL_CALL_START);
+ expect(compacted[1].type).toBe(EventType.TOOL_CALL_ARGS);
+ expect((compacted[1] as ToolCallArgsEvent).delta).toBe('{"a": 10, "b": 20}');
+ expect(compacted[2].type).toBe(EventType.TOOL_CALL_END);
+ // Other events should come after
+ expect(compacted[3].type).toBe(EventType.CUSTOM);
+ expect((compacted[3] as CustomEvent & { id: string }).id).toBe("custom1");
+ expect(compacted[4].type).toBe(EventType.CUSTOM);
+ expect((compacted[4] as CustomEvent & { id: string }).id).toBe("custom2");
+ });
+
+ it("should handle multiple tool calls independently", () => {
+ const events = [
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool1",
+ toolCallName: "search",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '{"query": "test"}' },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool1" },
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool2",
+ toolCallName: "calculate",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool2", delta: '{"a": ' },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool2", delta: "5}" },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool2" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(6);
+ // First tool call
+ expect(compacted[0].type).toBe(EventType.TOOL_CALL_START);
+ expect((compacted[0] as ToolCallStartEvent).toolCallId).toBe("tool1");
+ expect(compacted[1].type).toBe(EventType.TOOL_CALL_ARGS);
+ expect((compacted[1] as ToolCallArgsEvent).delta).toBe('{"query": "test"}');
+ expect(compacted[2].type).toBe(EventType.TOOL_CALL_END);
+ // Second tool call
+ expect(compacted[3].type).toBe(EventType.TOOL_CALL_START);
+ expect((compacted[3] as ToolCallStartEvent).toolCallId).toBe("tool2");
+ expect(compacted[4].type).toBe(EventType.TOOL_CALL_ARGS);
+ expect((compacted[4] as ToolCallArgsEvent).delta).toBe('{"a": 5}');
+ expect(compacted[5].type).toBe(EventType.TOOL_CALL_END);
+ });
+
+ it("should handle incomplete tool calls", () => {
+ const events = [
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool1",
+ toolCallName: "search",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '{"incomplete": ' },
+ // No END event
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(2);
+ expect(compacted[0].type).toBe(EventType.TOOL_CALL_START);
+ expect(compacted[1].type).toBe(EventType.TOOL_CALL_ARGS);
+ expect((compacted[1] as ToolCallArgsEvent).delta).toBe('{"incomplete": ');
+ });
+
+ it("should handle empty args deltas", () => {
+ const events = [
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool1",
+ toolCallName: "search",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: "" },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '{"test": true}' },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: "" },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(3);
+ expect((compacted[1] as ToolCallArgsEvent).delta).toBe('{"test": true}');
+ });
+ });
+
+ describe("Mixed Compaction", () => {
+ it("should handle text messages and tool calls together", () => {
+ const events = [
+ { type: EventType.TEXT_MESSAGE_START, messageId: "msg1", role: "assistant" },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "Let me " },
+ { type: EventType.TEXT_MESSAGE_CONTENT, messageId: "msg1", delta: "search for that" },
+ { type: EventType.TEXT_MESSAGE_END, messageId: "msg1" },
+ {
+ type: EventType.TOOL_CALL_START,
+ toolCallId: "tool1",
+ toolCallName: "search",
+ parentMessageId: "msg1",
+ },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: '{"q": "' },
+ { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool1", delta: 'test"}' },
+ { type: EventType.TOOL_CALL_END, toolCallId: "tool1" },
+ ];
+
+ const compacted = compactEvents(events);
+
+ expect(compacted).toHaveLength(6);
+ // Text message
+ expect(compacted[0].type).toBe(EventType.TEXT_MESSAGE_START);
+ expect(compacted[1].type).toBe(EventType.TEXT_MESSAGE_CONTENT);
+ expect((compacted[1] as TextMessageContentEvent).delta).toBe("Let me search for that");
+ expect(compacted[2].type).toBe(EventType.TEXT_MESSAGE_END);
+ // Tool call
+ expect(compacted[3].type).toBe(EventType.TOOL_CALL_START);
+ expect(compacted[4].type).toBe(EventType.TOOL_CALL_ARGS);
+ expect((compacted[4] as ToolCallArgsEvent).delta).toBe('{"q": "test"}');
+ expect(compacted[5].type).toBe(EventType.TOOL_CALL_END);
+ });
+ });
+});
diff --git a/sdks/typescript/packages/client/src/compact/compact.ts b/sdks/typescript/packages/client/src/compact/compact.ts
new file mode 100644
index 000000000..b34c08e6a
--- /dev/null
+++ b/sdks/typescript/packages/client/src/compact/compact.ts
@@ -0,0 +1,252 @@
+import {
+ BaseEvent,
+ EventType,
+ TextMessageStartEvent,
+ TextMessageContentEvent,
+ TextMessageEndEvent,
+ ToolCallStartEvent,
+ ToolCallArgsEvent,
+ ToolCallEndEvent,
+} from "@ag-ui/core";
+
+/**
+ * Compacts streaming events by consolidating multiple deltas into single events.
+ * For text messages: multiple content deltas become one concatenated delta.
+ * For tool calls: multiple args deltas become one concatenated delta.
+ * Events between related streaming events are reordered to keep streaming events together.
+ *
+ * @param events - Array of events to compact
+ * @returns Compacted array of events
+ */
+export function compactEvents(events: BaseEvent[]): BaseEvent[] {
+ const compacted: BaseEvent[] = [];
+ const pendingTextMessages = new Map<
+ string,
+ {
+ start?: TextMessageStartEvent;
+ contents: TextMessageContentEvent[];
+ end?: TextMessageEndEvent;
+ otherEvents: BaseEvent[];
+ }
+ >();
+ const pendingToolCalls = new Map<
+ string,
+ {
+ start?: ToolCallStartEvent;
+ args: ToolCallArgsEvent[];
+ end?: ToolCallEndEvent;
+ otherEvents: BaseEvent[];
+ }
+ >();
+
+ for (const event of events) {
+ // Handle text message streaming events
+ if (event.type === EventType.TEXT_MESSAGE_START) {
+ const startEvent = event as TextMessageStartEvent;
+ const messageId = startEvent.messageId;
+
+ if (!pendingTextMessages.has(messageId)) {
+ pendingTextMessages.set(messageId, {
+ contents: [],
+ otherEvents: [],
+ });
+ }
+
+ const pending = pendingTextMessages.get(messageId)!;
+ pending.start = startEvent;
+ } else if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
+ const contentEvent = event as TextMessageContentEvent;
+ const messageId = contentEvent.messageId;
+
+ if (!pendingTextMessages.has(messageId)) {
+ pendingTextMessages.set(messageId, {
+ contents: [],
+ otherEvents: [],
+ });
+ }
+
+ const pending = pendingTextMessages.get(messageId)!;
+ pending.contents.push(contentEvent);
+ } else if (event.type === EventType.TEXT_MESSAGE_END) {
+ const endEvent = event as TextMessageEndEvent;
+ const messageId = endEvent.messageId;
+
+ if (!pendingTextMessages.has(messageId)) {
+ pendingTextMessages.set(messageId, {
+ contents: [],
+ otherEvents: [],
+ });
+ }
+
+ const pending = pendingTextMessages.get(messageId)!;
+ pending.end = endEvent;
+
+ // Flush this message's events
+ flushTextMessage(messageId, pending, compacted);
+ pendingTextMessages.delete(messageId);
+ } else if (event.type === EventType.TOOL_CALL_START) {
+ const startEvent = event as ToolCallStartEvent;
+ const toolCallId = startEvent.toolCallId;
+
+ if (!pendingToolCalls.has(toolCallId)) {
+ pendingToolCalls.set(toolCallId, {
+ args: [],
+ otherEvents: [],
+ });
+ }
+
+ const pending = pendingToolCalls.get(toolCallId)!;
+ pending.start = startEvent;
+ } else if (event.type === EventType.TOOL_CALL_ARGS) {
+ const argsEvent = event as ToolCallArgsEvent;
+ const toolCallId = argsEvent.toolCallId;
+
+ if (!pendingToolCalls.has(toolCallId)) {
+ pendingToolCalls.set(toolCallId, {
+ args: [],
+ otherEvents: [],
+ });
+ }
+
+ const pending = pendingToolCalls.get(toolCallId)!;
+ pending.args.push(argsEvent);
+ } else if (event.type === EventType.TOOL_CALL_END) {
+ const endEvent = event as ToolCallEndEvent;
+ const toolCallId = endEvent.toolCallId;
+
+ if (!pendingToolCalls.has(toolCallId)) {
+ pendingToolCalls.set(toolCallId, {
+ args: [],
+ otherEvents: [],
+ });
+ }
+
+ const pending = pendingToolCalls.get(toolCallId)!;
+ pending.end = endEvent;
+
+ // Flush this tool call's events
+ flushToolCall(toolCallId, pending, compacted);
+ pendingToolCalls.delete(toolCallId);
+ } else {
+ // For non-streaming events, check if we're in the middle of any streaming sequences
+ let addedToBuffer = false;
+
+ // Check text messages
+ for (const [messageId, pending] of pendingTextMessages) {
+ // If we have a start but no end yet, this event is "in between"
+ if (pending.start && !pending.end) {
+ pending.otherEvents.push(event);
+ addedToBuffer = true;
+ break;
+ }
+ }
+
+ // Check tool calls if not already buffered
+ if (!addedToBuffer) {
+ for (const [toolCallId, pending] of pendingToolCalls) {
+ // If we have a start but no end yet, this event is "in between"
+ if (pending.start && !pending.end) {
+ pending.otherEvents.push(event);
+ addedToBuffer = true;
+ break;
+ }
+ }
+ }
+
+ // If not in the middle of any streaming sequence, add directly to compacted
+ if (!addedToBuffer) {
+ compacted.push(event);
+ }
+ }
+ }
+
+ // Flush any remaining incomplete messages
+ for (const [messageId, pending] of pendingTextMessages) {
+ flushTextMessage(messageId, pending, compacted);
+ }
+
+ // Flush any remaining incomplete tool calls
+ for (const [toolCallId, pending] of pendingToolCalls) {
+ flushToolCall(toolCallId, pending, compacted);
+ }
+
+ return compacted;
+}
+
+function flushTextMessage(
+ messageId: string,
+ pending: {
+ start?: TextMessageStartEvent;
+ contents: TextMessageContentEvent[];
+ end?: TextMessageEndEvent;
+ otherEvents: BaseEvent[];
+ },
+ compacted: BaseEvent[],
+): void {
+ // Add start event if present
+ if (pending.start) {
+ compacted.push(pending.start);
+ }
+
+ // Compact all content events into one
+ if (pending.contents.length > 0) {
+ const concatenatedDelta = pending.contents.map((c) => c.delta).join("");
+
+ const compactedContent: TextMessageContentEvent = {
+ type: EventType.TEXT_MESSAGE_CONTENT,
+ messageId: messageId,
+ delta: concatenatedDelta,
+ };
+
+ compacted.push(compactedContent);
+ }
+
+ // Add end event if present
+ if (pending.end) {
+ compacted.push(pending.end);
+ }
+
+ // Add any events that were in between
+ for (const otherEvent of pending.otherEvents) {
+ compacted.push(otherEvent);
+ }
+}
+
+function flushToolCall(
+ toolCallId: string,
+ pending: {
+ start?: ToolCallStartEvent;
+ args: ToolCallArgsEvent[];
+ end?: ToolCallEndEvent;
+ otherEvents: BaseEvent[];
+ },
+ compacted: BaseEvent[],
+): void {
+ // Add start event if present
+ if (pending.start) {
+ compacted.push(pending.start);
+ }
+
+ // Compact all args events into one
+ if (pending.args.length > 0) {
+ const concatenatedArgs = pending.args.map((a) => a.delta).join("");
+
+ const compactedArgs: ToolCallArgsEvent = {
+ type: EventType.TOOL_CALL_ARGS,
+ toolCallId: toolCallId,
+ delta: concatenatedArgs,
+ };
+
+ compacted.push(compactedArgs);
+ }
+
+ // Add end event if present
+ if (pending.end) {
+ compacted.push(pending.end);
+ }
+
+ // Add any events that were in between
+ for (const otherEvent of pending.otherEvents) {
+ compacted.push(otherEvent);
+ }
+}
diff --git a/sdks/typescript/packages/client/src/compact/index.ts b/sdks/typescript/packages/client/src/compact/index.ts
new file mode 100644
index 000000000..bb036168b
--- /dev/null
+++ b/sdks/typescript/packages/client/src/compact/index.ts
@@ -0,0 +1 @@
+export { compactEvents } from "./compact";
diff --git a/sdks/typescript/packages/client/src/index.ts b/sdks/typescript/packages/client/src/index.ts
index aa4a0a5ef..a6300de99 100644
--- a/sdks/typescript/packages/client/src/index.ts
+++ b/sdks/typescript/packages/client/src/index.ts
@@ -5,5 +5,6 @@ export * from "./run";
export * from "./legacy";
export * from "./agent";
export * from "./utils";
+export * from "./compact";
export * from "@ag-ui/core";
export * from "./chunks";
diff --git a/sdks/typescript/packages/client/src/legacy/convert.ts b/sdks/typescript/packages/client/src/legacy/convert.ts
index aa3ac5e6f..2f4cd6761 100644
--- a/sdks/typescript/packages/client/src/legacy/convert.ts
+++ b/sdks/typescript/packages/client/src/legacy/convert.ts
@@ -41,6 +41,27 @@ import {
} from "./types";
import untruncateJson from "untruncate-json";
+const flattenMessageContentToText = (content: Message["content"]) => {
+ if (typeof content === "string") {
+ return content;
+ }
+
+ if (!Array.isArray(content)) {
+ return undefined;
+ }
+
+ const textParts = content
+ .filter((part): part is { type: "text"; text: string } => part.type === "text")
+ .map((part) => part.text)
+ .filter((text) => text.length > 0);
+
+ if (textParts.length === 0) {
+ return undefined;
+ }
+
+ return textParts.join("\n");
+};
+
interface PredictStateValue {
state_key: string;
tool: string;
@@ -392,11 +413,12 @@ export function convertMessagesToLegacyFormat(messages: Message[]): LegacyMessag
for (const message of messages) {
if (message.role === "assistant" || message.role === "user" || message.role === "system") {
- if (message.content) {
+ const textContent = flattenMessageContentToText(message.content);
+ if (textContent) {
const textMessage: LegacyTextMessage = {
id: message.id,
role: message.role,
- content: message.content,
+ content: textContent,
};
result.push(textMessage);
}
diff --git a/sdks/typescript/packages/core/package.json b/sdks/typescript/packages/core/package.json
index d3626f998..5e5aba046 100644
--- a/sdks/typescript/packages/core/package.json
+++ b/sdks/typescript/packages/core/package.json
@@ -1,7 +1,7 @@
{
"name": "@ag-ui/core",
"author": "Markus Ecker ",
- "version": "0.0.39",
+ "version": "0.0.40-alpha.11",
"private": false,
"publishConfig": {
"access": "public"
diff --git a/sdks/typescript/packages/core/src/__tests__/activity-events.test.ts b/sdks/typescript/packages/core/src/__tests__/activity-events.test.ts
new file mode 100644
index 000000000..78b31d3a3
--- /dev/null
+++ b/sdks/typescript/packages/core/src/__tests__/activity-events.test.ts
@@ -0,0 +1,54 @@
+import { ActivitySnapshotEventSchema, ActivityDeltaEventSchema, EventType } from "../events";
+import { ActivityMessageSchema } from "../types";
+
+describe("Activity events", () => {
+ it("parses ActivitySnapshotEvent", () => {
+ const result = ActivitySnapshotEventSchema.parse({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "msg_activity",
+ activityType: "PLAN",
+ content: { tasks: ["search"] },
+ });
+
+ expect(result.type).toBe(EventType.ACTIVITY_SNAPSHOT);
+ expect(result.messageId).toBe("msg_activity");
+ expect(result.content.tasks).toEqual(["search"]);
+ expect(result.replace).toBe(true);
+ });
+
+ it("respects replace flag in ActivitySnapshotEvent", () => {
+ const result = ActivitySnapshotEventSchema.parse({
+ type: EventType.ACTIVITY_SNAPSHOT,
+ messageId: "msg_activity",
+ activityType: "PLAN",
+ content: { tasks: [] },
+ replace: false,
+ });
+
+ expect(result.replace).toBe(false);
+ });
+
+ it("parses ActivityDeltaEvent", () => {
+ const result = ActivityDeltaEventSchema.parse({
+ type: EventType.ACTIVITY_DELTA,
+ messageId: "msg_activity",
+ activityType: "PLAN",
+ patch: [{ op: "replace", path: "/tasks/0", value: "✓ search" }],
+ });
+
+ expect(result.type).toBe(EventType.ACTIVITY_DELTA);
+ expect(result.patch).toHaveLength(1);
+ });
+
+ it("parses ActivityMessage", () => {
+ const result = ActivityMessageSchema.parse({
+ id: "activity_1",
+ role: "activity" as const,
+ activityType: "PLAN",
+ content: { tasks: [] },
+ });
+
+ expect(result.activityType).toBe("PLAN");
+ expect(result.content).toEqual({ tasks: [] });
+ });
+});
diff --git a/sdks/typescript/packages/core/src/__tests__/backwards-compatibility.test.ts b/sdks/typescript/packages/core/src/__tests__/backwards-compatibility.test.ts
new file mode 100644
index 000000000..34c47961f
--- /dev/null
+++ b/sdks/typescript/packages/core/src/__tests__/backwards-compatibility.test.ts
@@ -0,0 +1,252 @@
+import {
+ UserMessageSchema,
+ AssistantMessageSchema,
+ RunAgentInputSchema,
+ TextMessageStartEventSchema,
+ RunStartedEventSchema,
+ ToolSchema,
+ ContextSchema,
+ EventType,
+} from "../index";
+
+describe("Backwards Compatibility", () => {
+ describe("Message Schemas", () => {
+ it("should accept UserMessage with extra fields from future versions", () => {
+ const messageWithExtraFields = {
+ id: "msg_1",
+ role: "user" as const,
+ content: "Hello",
+ futureField: "This is from a future version",
+ anotherNewProp: { nested: "data" },
+ };
+
+ const result = UserMessageSchema.safeParse(messageWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.id).toBe("msg_1");
+ expect(result.data.role).toBe("user");
+ expect(result.data.content).toBe("Hello");
+ // Extra fields should be stripped (Zod default behavior)
+ expect('futureField' in result.data).toBe(false);
+ expect('anotherNewProp' in result.data).toBe(false);
+ }
+ });
+
+ it("should accept AssistantMessage with extra fields", () => {
+ const messageWithExtraFields = {
+ id: "msg_2",
+ role: "assistant" as const,
+ content: "Response",
+ newFeatureFlag: true,
+ experimentalData: [1, 2, 3],
+ };
+
+ const result = AssistantMessageSchema.safeParse(messageWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.id).toBe("msg_2");
+ expect(result.data.content).toBe("Response");
+ }
+ });
+ });
+
+ describe("RunAgentInput Schema", () => {
+ it("should accept RunAgentInput with extra fields at top level", () => {
+ const inputWithExtraFields = {
+ threadId: "thread_1",
+ runId: "run_1",
+ parentRunId: "parent_run_1",
+ state: {},
+ messages: [],
+ tools: [],
+ context: [],
+ forwardedProps: {},
+ // Extra fields from future version
+ newFeatureFlag: true,
+ experimentalTimeout: 5000,
+ futureConfig: { option: "value" },
+ };
+
+ const result = RunAgentInputSchema.safeParse(inputWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.threadId).toBe("thread_1");
+ expect(result.data.runId).toBe("run_1");
+ expect(result.data.parentRunId).toBe("parent_run_1");
+ }
+ });
+
+ it("should accept RunAgentInput with messages containing extra fields", () => {
+ const inputWithExtraFieldsInMessages = {
+ threadId: "thread_2",
+ runId: "run_2",
+ state: {},
+ messages: [
+ {
+ id: "m1",
+ role: "user" as const,
+ content: "Hi",
+ extraProp: "value",
+ metadata: { source: "web" },
+ },
+ ],
+ tools: [],
+ context: [],
+ forwardedProps: {},
+ };
+
+ const result = RunAgentInputSchema.safeParse(inputWithExtraFieldsInMessages);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.messages.length).toBe(1);
+ expect(result.data.messages[0].content).toBe("Hi");
+ }
+ });
+ });
+
+ describe("Event Schemas", () => {
+ it("should accept TextMessageStartEvent with extra fields", () => {
+ const eventWithExtraFields = {
+ type: EventType.TEXT_MESSAGE_START,
+ messageId: "msg_1",
+ role: "assistant" as const,
+ // Extra fields from future version
+ metadata: { tokenCount: 10 },
+ experimentalFeature: true,
+ };
+
+ const result = TextMessageStartEventSchema.safeParse(eventWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.type).toBe(EventType.TEXT_MESSAGE_START);
+ expect(result.data.messageId).toBe("msg_1");
+ }
+ });
+
+ it("should accept RunStartedEvent with extra fields", () => {
+ const eventWithExtraFields = {
+ type: EventType.RUN_STARTED,
+ threadId: "thread_1",
+ runId: "run_1",
+ // Extra fields from future version
+ startTime: Date.now(),
+ priority: "high",
+ };
+
+ const result = RunStartedEventSchema.safeParse(eventWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.threadId).toBe("thread_1");
+ expect(result.data.runId).toBe("run_1");
+ }
+ });
+ });
+
+ describe("Tool and Context Schemas", () => {
+ it("should accept Tool with extra fields", () => {
+ const toolWithExtraFields = {
+ name: "calculator",
+ description: "Performs calculations",
+ parameters: { type: "object" },
+ // Extra fields from future version
+ version: "2.0",
+ deprecationWarning: null,
+ };
+
+ const result = ToolSchema.safeParse(toolWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.name).toBe("calculator");
+ expect(result.data.description).toBe("Performs calculations");
+ }
+ });
+
+ it("should accept Context with extra fields", () => {
+ const contextWithExtraFields = {
+ description: "User preferences",
+ value: '{"theme":"dark"}',
+ // Extra fields from future version
+ priority: 1,
+ expiresAt: Date.now() + 3600000,
+ };
+
+ const result = ContextSchema.safeParse(contextWithExtraFields);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.description).toBe("User preferences");
+ expect(result.data.value).toBe('{"theme":"dark"}');
+ }
+ });
+ });
+
+ describe("Complex nested structures", () => {
+ it("should handle deeply nested objects with extra fields at multiple levels", () => {
+ const complexInput = {
+ threadId: "thread_complex",
+ runId: "run_complex",
+ state: { currentStep: 1 },
+ messages: [
+ {
+ id: "m1",
+ role: "user" as const,
+ content: "Hello",
+ extraUserProp: "value1",
+ },
+ {
+ id: "m2",
+ role: "assistant" as const,
+ content: "Hi there",
+ toolCalls: [
+ {
+ id: "tc1",
+ type: "function" as const,
+ function: {
+ name: "search",
+ arguments: "{}",
+ extraFunctionProp: "value2",
+ },
+ extraToolCallProp: "value3",
+ },
+ ],
+ extraAssistantProp: "value4",
+ },
+ ],
+ tools: [
+ {
+ name: "search",
+ description: "Search tool",
+ parameters: {},
+ extraToolProp: "value5",
+ },
+ ],
+ context: [
+ {
+ description: "ctx",
+ value: "val",
+ extraContextProp: "value6",
+ },
+ ],
+ forwardedProps: { custom: true },
+ extraTopLevelProp: "value7",
+ };
+
+ const result = RunAgentInputSchema.safeParse(complexInput);
+
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.messages.length).toBe(2);
+ expect(result.data.messages[1].toolCalls?.length).toBe(1);
+ expect(result.data.tools.length).toBe(1);
+ expect(result.data.context.length).toBe(1);
+ }
+ });
+ });
+});
diff --git a/sdks/typescript/packages/core/src/__tests__/multimodal-messages.test.ts b/sdks/typescript/packages/core/src/__tests__/multimodal-messages.test.ts
new file mode 100644
index 000000000..3bcca30e8
--- /dev/null
+++ b/sdks/typescript/packages/core/src/__tests__/multimodal-messages.test.ts
@@ -0,0 +1,52 @@
+import {
+ UserMessageSchema,
+ BinaryInputContentSchema,
+} from "../types";
+
+describe("Multimodal messages", () => {
+ it("parses user message with content array", () => {
+ const result = UserMessageSchema.parse({
+ id: "user_multimodal",
+ role: "user" as const,
+ content: [
+ { type: "text" as const, text: "Check this out" },
+ { type: "binary" as const, mimeType: "image/png", url: "https://example.com/image.png" },
+ ],
+ });
+
+ expect(Array.isArray(result.content)).toBe(true);
+ if (Array.isArray(result.content)) {
+ expect(result.content[0].type).toBe("text");
+ expect(result.content[0].text).toBe("Check this out");
+ expect(result.content[1].type).toBe("binary");
+ expect(result.content[1].mimeType).toBe("image/png");
+ expect(result.content[1].url).toBe("https://example.com/image.png");
+ }
+ });
+
+ it("rejects binary content without payload source", () => {
+ const result = UserMessageSchema.safeParse({
+ id: "user_invalid",
+ role: "user" as const,
+ content: [{ type: "binary" as const, mimeType: "image/png" }],
+ });
+
+ expect(result.success).toBe(false);
+ });
+
+ it("parses binary input with embedded data", () => {
+ const binary = BinaryInputContentSchema.parse({
+ type: "binary" as const,
+ mimeType: "image/png",
+ data: "base64",
+ });
+
+ expect(binary.data).toBe("base64");
+ });
+
+ it("requires binary payload source", () => {
+ expect(() =>
+ BinaryInputContentSchema.parse({ type: "binary" as const, mimeType: "image/png" }),
+ ).toThrow(/id, url, or data/);
+ });
+});
diff --git a/sdks/typescript/packages/core/src/events.ts b/sdks/typescript/packages/core/src/events.ts
index a95fc8e15..36076896c 100644
--- a/sdks/typescript/packages/core/src/events.ts
+++ b/sdks/typescript/packages/core/src/events.ts
@@ -1,5 +1,5 @@
import { z } from "zod";
-import { MessageSchema, StateSchema } from "./types";
+import { MessageSchema, StateSchema, RunAgentInputSchema } from "./types";
// Text messages can have any role except "tool"
const TextMessageRoleSchema = z.union([
@@ -27,6 +27,8 @@ export enum EventType {
STATE_SNAPSHOT = "STATE_SNAPSHOT",
STATE_DELTA = "STATE_DELTA",
MESSAGES_SNAPSHOT = "MESSAGES_SNAPSHOT",
+ ACTIVITY_SNAPSHOT = "ACTIVITY_SNAPSHOT",
+ ACTIVITY_DELTA = "ACTIVITY_DELTA",
RAW = "RAW",
CUSTOM = "CUSTOM",
RUN_STARTED = "RUN_STARTED",
@@ -139,6 +141,21 @@ export const MessagesSnapshotEventSchema = BaseEventSchema.extend({
messages: z.array(MessageSchema),
});
+export const ActivitySnapshotEventSchema = BaseEventSchema.extend({
+ type: z.literal(EventType.ACTIVITY_SNAPSHOT),
+ messageId: z.string(),
+ activityType: z.string(),
+ content: z.record(z.any()),
+ replace: z.boolean().optional().default(true),
+});
+
+export const ActivityDeltaEventSchema = BaseEventSchema.extend({
+ type: z.literal(EventType.ACTIVITY_DELTA),
+ messageId: z.string(),
+ activityType: z.string(),
+ patch: z.array(z.any()),
+});
+
export const RawEventSchema = BaseEventSchema.extend({
type: z.literal(EventType.RAW),
event: z.any(),
@@ -155,6 +172,8 @@ export const RunStartedEventSchema = BaseEventSchema.extend({
type: z.literal(EventType.RUN_STARTED),
threadId: z.string(),
runId: z.string(),
+ parentRunId: z.string().optional(),
+ input: RunAgentInputSchema.optional(),
});
export const RunFinishedEventSchema = BaseEventSchema.extend({
@@ -198,6 +217,8 @@ export const EventSchemas = z.discriminatedUnion("type", [
StateSnapshotEventSchema,
StateDeltaEventSchema,
MessagesSnapshotEventSchema,
+ ActivitySnapshotEventSchema,
+ ActivityDeltaEventSchema,
RawEventSchema,
CustomEventSchema,
RunStartedEventSchema,
@@ -225,6 +246,8 @@ export type ThinkingEndEvent = z.infer;
export type StateSnapshotEvent = z.infer;
export type StateDeltaEvent = z.infer;
export type MessagesSnapshotEvent = z.infer;
+export type ActivitySnapshotEvent = z.infer;
+export type ActivityDeltaEvent = z.infer;
export type RawEvent = z.infer;
export type CustomEvent = z.infer;
export type RunStartedEvent = z.infer;
diff --git a/sdks/typescript/packages/core/src/types.ts b/sdks/typescript/packages/core/src/types.ts
index 1abb31a0b..6a8560293 100644
--- a/sdks/typescript/packages/core/src/types.ts
+++ b/sdks/typescript/packages/core/src/types.ts
@@ -18,6 +18,48 @@ export const BaseMessageSchema = z.object({
name: z.string().optional(),
});
+export const TextInputContentSchema = z.object({
+ type: z.literal("text"),
+ text: z.string(),
+});
+
+const BinaryInputContentObjectSchema = z.object({
+ type: z.literal("binary"),
+ mimeType: z.string(),
+ id: z.string().optional(),
+ url: z.string().optional(),
+ data: z.string().optional(),
+ filename: z.string().optional(),
+});
+
+const ensureBinaryPayload = (
+ value: { id?: string; url?: string; data?: string },
+ ctx: z.RefinementCtx,
+) => {
+ if (!value.id && !value.url && !value.data) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: "BinaryInputContent requires at least one of id, url, or data.",
+ path: ["id"],
+ });
+ }
+};
+
+export const BinaryInputContentSchema = BinaryInputContentObjectSchema.superRefine((value, ctx) => {
+ ensureBinaryPayload(value, ctx);
+});
+
+const InputContentBaseSchema = z.discriminatedUnion("type", [
+ TextInputContentSchema,
+ BinaryInputContentObjectSchema,
+]);
+
+export const InputContentSchema = InputContentBaseSchema.superRefine((value, ctx) => {
+ if (value.type === "binary") {
+ ensureBinaryPayload(value, ctx);
+ }
+});
+
export const DeveloperMessageSchema = BaseMessageSchema.extend({
role: z.literal("developer"),
content: z.string(),
@@ -36,7 +78,7 @@ export const AssistantMessageSchema = BaseMessageSchema.extend({
export const UserMessageSchema = BaseMessageSchema.extend({
role: z.literal("user"),
- content: z.string(),
+ content: z.union([z.string(), z.array(InputContentSchema)]),
});
export const ToolMessageSchema = z.object({
@@ -47,12 +89,20 @@ export const ToolMessageSchema = z.object({
error: z.string().optional(),
});
+export const ActivityMessageSchema = z.object({
+ id: z.string(),
+ role: z.literal("activity"),
+ activityType: z.string(),
+ content: z.record(z.any()),
+});
+
export const MessageSchema = z.discriminatedUnion("role", [
DeveloperMessageSchema,
SystemMessageSchema,
AssistantMessageSchema,
UserMessageSchema,
ToolMessageSchema,
+ ActivityMessageSchema,
]);
export const RoleSchema = z.union([
@@ -61,6 +111,7 @@ export const RoleSchema = z.union([
z.literal("assistant"),
z.literal("user"),
z.literal("tool"),
+ z.literal("activity"),
]);
export const ContextSchema = z.object({
@@ -77,6 +128,7 @@ export const ToolSchema = z.object({
export const RunAgentInputSchema = z.object({
threadId: z.string(),
runId: z.string(),
+ parentRunId: z.string().optional(),
state: z.any(),
messages: z.array(MessageSchema),
tools: z.array(ToolSchema),
@@ -88,11 +140,15 @@ export const StateSchema = z.any();
export type ToolCall = z.infer;
export type FunctionCall = z.infer;
+export type TextInputContent = z.infer;
+export type BinaryInputContent = z.infer;
+export type InputContent = z.infer;
export type DeveloperMessage = z.infer;
export type SystemMessage = z.infer;
export type AssistantMessage = z.infer;
export type UserMessage = z.infer;
export type ToolMessage = z.infer;
+export type ActivityMessage = z.infer;
export type Message = z.infer;
export type Context = z.infer;
export type Tool = z.infer;
@@ -105,3 +161,9 @@ export class AGUIError extends Error {
super(message);
}
}
+
+export class AGUIConnectNotImplementedError extends AGUIError {
+ constructor() {
+ super("Connect not implemented. This method is not supported by the current agent.");
+ }
+}
diff --git a/sdks/typescript/packages/encoder/package.json b/sdks/typescript/packages/encoder/package.json
index b95c5ee70..2167df068 100644
--- a/sdks/typescript/packages/encoder/package.json
+++ b/sdks/typescript/packages/encoder/package.json
@@ -1,7 +1,7 @@
{
"name": "@ag-ui/encoder",
"author": "Markus Ecker ",
- "version": "0.0.39",
+ "version": "0.0.40-alpha.11",
"private": false,
"publishConfig": {
"access": "public"
diff --git a/sdks/typescript/packages/proto/package.json b/sdks/typescript/packages/proto/package.json
index 7c1fed72b..f485dcfea 100644
--- a/sdks/typescript/packages/proto/package.json
+++ b/sdks/typescript/packages/proto/package.json
@@ -1,7 +1,7 @@
{
"name": "@ag-ui/proto",
"author": "Markus Ecker ",
- "version": "0.0.39",
+ "version": "0.0.40-alpha.11",
"private": false,
"publishConfig": {
"access": "public"