Skip to content

Commit 17831b2

Browse files
authored
Upgrade to CopilotKit 1.77 and AG-UI 1.0 (#750)
@copilotkit/runtime, react-core and a2ui-renderer move from 1.73.1 to 1.77.0, and @copilotkit/aimock from 1.39.0 to 1.43.0. CopilotKit 1.76 moved onto AG-UI 1.0 and pins @ag-ui/core and @ag-ui/client 1.0.1, so OpenBot's own imports move to 1.0.1 to match, and @ag-ui/mastra to 1.1.6, whose peers accept it. AG-UI 1.0 changes three things OpenBot used: - The zod schemas left the main entry for @ag-ui/core/schemas, which the server now depends on directly. - The binary content part is replaced by image, audio, video and document parts with a source. The plan model now builds the image part itself, so the shim that converted binary parts for the Python endpoint is gone. - A tool message's content may be content parts, which the hand-off resume now reads as text. TOOL_CALL_RESULT no longer carries an error field, since 1.0 does not define one and validation strips it; the refusal stays in the content and on the tool message. A test fixture that built a remote agent with no standing message now gives it one, as every registered agent has.
1 parent 7229c68 commit 17831b2

15 files changed

Lines changed: 153 additions & 103 deletions

‎CHANGELOG.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,22 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged.
88

99
## Unreleased
1010

11+
### CopilotKit 1.77 and AG-UI 1.0
12+
13+
OpenBot now runs on CopilotKit 1.77.0 (`@copilotkit/runtime`, `react-core` and `a2ui-renderer`,
14+
from 1.73.1), which carries the Intelligence client, and on AG-UI 1.0 (`@ag-ui/core` and
15+
`@ag-ui/client` 1.0.1, from 0.0.59). A managed Intelligence project needs nothing changed; it is the
16+
client in the server that moved. Among what comes with it: thread naming falls back to the first
17+
message, the realtime connection keeps its place across a repeated checkpoint, an interrupt resume
18+
gets a run id of its own, and generated A2UI controls no longer submit the page's own forms.
19+
20+
Bots served from their own endpoint keep working as they are. AG-UI 1.0 still accepts the earlier
21+
event shapes, so `agent-bot`, `agent-langgraph` and the Python Bots were not changed.
22+
23+
A tool result's `error` field is gone from the `TOOL_CALL_RESULT` event, because AG-UI 1.0 does not
24+
define one. The refusal is still in the result's text and on the tool message, which is where a
25+
Bot's next turn reads it.
26+
1127
## 0.1.1
1228

1329
- A password given to `curl` as `-uuser:password`, `--user=user:password` or `-u "user:pass word"` is

‎app/package.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
"typecheck": "tsc --noEmit"
1515
},
1616
"dependencies": {
17-
"@ag-ui/core": "0.0.59",
17+
"@ag-ui/core": "1.0.1",
1818
"@base-ui/react": "^1.6.0",
1919
"@better-auth/sso": "^1.7.1",
20-
"@copilotkit/a2ui-renderer": "1.73.1",
21-
"@copilotkit/react-core": "1.73.1",
20+
"@copilotkit/a2ui-renderer": "1.77.0",
21+
"@copilotkit/react-core": "1.77.0",
2222
"@fontsource-variable/inter": "^5.3.0",
2323
"@shadcn/react": "^0.3.0",
2424
"@tabler/icons-react": "^3.36.1",

‎app/src/lib/copilot/handoff-resume.ts‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,25 @@ export type HandoffAgent = {
1414
setMessages: (messages: Message[]) => void;
1515
};
1616

17+
/**
18+
* A tool result as text. AG-UI 1.0 lets a tool message carry content parts as well as a string;
19+
* the placeholders and transport failures this file looks for are only ever text.
20+
*/
21+
function toolText(message: Extract<Message, { role: "tool" }>): string {
22+
return typeof message.content === "string"
23+
? message.content
24+
: message.content
25+
.map((part) => (part.type === "text" ? part.text : ""))
26+
.join("");
27+
}
28+
1729
/** These are transport placeholders / retryable transport failures, never accepted results. */
1830
function needsAnswer(message: Message): boolean {
1931
if (message.role !== "tool") return false;
20-
if (message.content.trim() === "Forwarded to client") return true;
32+
const content = toolText(message);
33+
if (content.trim() === "Forwarded to client") return true;
2134
try {
22-
const result: unknown = JSON.parse(message.content);
35+
const result: unknown = JSON.parse(content);
2336
if (result === "Forwarded to client") return true;
2437
return Boolean(
2538
result &&

‎app/src/lib/copilot/thread-messages.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { type Message, MessageSchema } from "@ag-ui/core";
1+
import type { Message } from "@ag-ui/core";
2+
import { MessageSchema } from "@ag-ui/core/schemas";
23
import { tryClient } from "@/lib/client";
34

45
/**

‎app/tests/channel-history-refresh.test.tsx‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { afterAll, afterEach, beforeAll, expect, test } from "bun:test";
2-
import {
3-
type Message,
4-
type RunAgentInput,
5-
RunAgentInputSchema,
6-
} from "@ag-ui/core";
2+
import type { Message, RunAgentInput } from "@ag-ui/core";
3+
import { RunAgentInputSchema } from "@ag-ui/core/schemas";
74
import { CopilotKitProvider, useCopilotKit } from "@copilotkit/react-core/v2";
85
import { GlobalRegistrator } from "@happy-dom/global-registrator";
96
import { type InfiniteData, QueryClientProvider } from "@tanstack/react-query";

‎app/tests/failed-send-attachments.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
test,
88
} from "bun:test";
99
import type { Message, RunAgentInput } from "@ag-ui/core";
10-
import { RunAgentInputSchema } from "@ag-ui/core";
10+
import { RunAgentInputSchema } from "@ag-ui/core/schemas";
1111
import { CopilotKitProvider, useCopilotKit } from "@copilotkit/react-core/v2";
1212
import { GlobalRegistrator } from "@happy-dom/global-registrator";
1313
import { type InfiniteData, QueryClientProvider } from "@tanstack/react-query";

‎bun.lock‎

Lines changed: 82 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
},
3030
"devDependencies": {
3131
"@biomejs/biome": "2.5.10",
32-
"@copilotkit/aimock": "1.39.0",
32+
"@copilotkit/aimock": "1.43.0",
3333
"@types/bun": "^1.3.3",
3434
"roughjs": "^4.6.6",
3535
"typescript": "^5.9.3",

‎server/package.json‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,16 @@
1212
"typecheck": "tsc --noEmit"
1313
},
1414
"dependencies": {
15-
"@ag-ui/client": "0.0.59",
16-
"@ag-ui/mastra": "1.1.2",
15+
"@ag-ui/client": "1.0.1",
16+
"@ag-ui/core": "1.0.1",
17+
"@ag-ui/mastra": "1.1.6",
1718
"@better-auth/drizzle-adapter": "^1.7.1",
1819
"@better-auth/electron": "1.7.1",
1920
"@better-auth/expo": "1.7.1",
2021
"@better-auth/scim": "1.7.1",
2122
"@better-auth/sso": "^1.7.1",
2223
"@composio/core": "^0.18.1",
23-
"@copilotkit/runtime": "1.73.1",
24+
"@copilotkit/runtime": "1.77.0",
2425
"@mastra/client-js": "^1.43.0",
2526
"@mastra/core": "^1.64.0",
2627
"@modelcontextprotocol/sdk": "^1.30.0",
@@ -41,7 +42,7 @@
4142
"zod": "^4.4.3"
4243
},
4344
"devDependencies": {
44-
"@copilotkit/aimock": "1.39.0",
45+
"@copilotkit/aimock": "1.43.0",
4546
"drizzle-kit": "^0.31.10"
4647
}
4748
}

‎server/src/agents/plan-model.ts‎

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
import { createHash } from "node:crypto";
2-
import {
3-
EventSchemas,
4-
HttpAgent,
5-
type Message,
6-
type RunAgentInput,
7-
} from "@ag-ui/client";
2+
import { HttpAgent, type Message, type RunAgentInput } from "@ag-ui/client";
3+
import { EventSchemas } from "@ag-ui/core/schemas";
84
import type { BuiltInAgentClassicConfig } from "@copilotkit/runtime/v2";
95
import type { Subscription } from "rxjs";
106
import {
@@ -97,11 +93,20 @@ export function planMessages(prompt: Options["prompt"]): Message[] {
9793
`Plan models cannot receive ${part.mediaType} attachments.`,
9894
);
9995
const data = part.data;
100-
const url =
101-
data instanceof URL
102-
? data.href
103-
: `data:${part.mediaType};base64,${typeof data === "string" ? data : Buffer.from(data).toString("base64")}`;
104-
return { type: "binary", mimeType: part.mediaType, url };
96+
return {
97+
type: "image",
98+
source:
99+
data instanceof URL
100+
? { type: "url", value: data.href, mimeType: part.mediaType }
101+
: {
102+
type: "data",
103+
value:
104+
typeof data === "string"
105+
? data
106+
: Buffer.from(data).toString("base64"),
107+
mimeType: part.mediaType,
108+
},
109+
};
105110
}),
106111
});
107112
} else if (message.role === "assistant") {
@@ -152,37 +157,6 @@ export function planMessages(prompt: Options["prompt"]): Message[] {
152157
return messages;
153158
}
154159

155-
/** The pinned JS client uses binary parts; current Python AG-UI uses image/source. */
156-
function planWireBody(
157-
body: BodyInit | null | undefined,
158-
): BodyInit | null | undefined {
159-
if (typeof body !== "string") return body;
160-
const input = JSON.parse(body) as RunAgentInput;
161-
return JSON.stringify({
162-
...input,
163-
messages: input.messages.map((message) => {
164-
if (message.role !== "user" || typeof message.content === "string")
165-
return message;
166-
return {
167-
...message,
168-
content: message.content.map((part) => {
169-
if (part.type !== "binary") return part;
170-
if (!part.mimeType.startsWith("image/"))
171-
throw new Error("The plan model only supports image media parts.");
172-
const encoded = part.data ?? part.url?.split(";base64,", 2)[1];
173-
return {
174-
type: "image",
175-
source:
176-
encoded !== undefined
177-
? { type: "data", value: encoded, mimeType: part.mimeType }
178-
: { type: "url", value: part.url, mimeType: part.mimeType },
179-
};
180-
}),
181-
};
182-
}),
183-
});
184-
}
185-
186160
/** Model transport only. BuiltInAgent still validates and executes every server tool. */
187161
export class PlanModel implements PlanLanguageModel {
188162
readonly specificationVersion = "v3";
@@ -296,7 +270,6 @@ export class PlanModel implements PlanLanguageModel {
296270
fetch: (url, init) =>
297271
fetch(url, {
298272
...init,
299-
body: planWireBody(init?.body),
300273
redirect: "error",
301274
signal: AbortSignal.any([
302275
...(init?.signal ? [init.signal] : []),

0 commit comments

Comments
 (0)