完整的 AG-UI + CopilotKit 前端 example,演示 agent-adaptor 的四条纵向通路:
- Streaming:文本、thinking、tool_call 通过一条
stream.Events()通道流式传递,终局由一次stream.Result()收口。若 Driver 没有发布 assistant delta 但终局Result.Text非空,后端会在 terminal 前投影一次完整 assistant message;已有流式文本时绝不重复。 - HITL:没有安装
OnApproval回调时,approval 以自带 responder 的*adaptor.ApprovalRequest事件出现在同一条流上。宿主停放请求后直接调用req.Approve(ctx)/req.Deny(ctx, reason)/req.Answer(ctx, option);ErrApprovalResolved映射 HTTP 410,ErrApprovalKindMismatch映射 400。NoticeApprovalResolved会自动清掉 pending。 - Recovery:每个浏览器有稳定
thread_id,右侧面板通过/session/events+/decision/pending恢复历史与未决决策。EventMeta.Sequence只在单次 run 内排序;跨 run 重放使用hosttools/sessionrecorder分配的HostSeq。 - User-prompt persistence:user prompt 不会自动进入 SDK 持久层(thread store 只存 resume 元数据;driver 只 emit assistant 侧)。
handleAgent用userTurnEvents(lastUserMessageID(input), prompt)构造三段adaptor.TextDelta{Role: RoleUser, Phase: Start/Content/End},让它经过和 driver 事件相同的 fan-out。刷新页面拉/session/events即可恢复含 user 气泡的完整 transcript。
实现入口:server.go::handleAgent+agui_run_session.go::recordUserTurn;验证用例:agui_run_session_test.go::TestAGUIRunSessionRecordsUserTurnBeforeAssistant。
后端只调用真实本机 CLI:
AGUI_AGENT |
行为 |
|---|---|
codex |
本地 codex,streaming 走 codex app-server |
claude |
本地 claude / trpc-claudecode,PlanReview / Question 可进入 HITL 交互 |
cursor |
本地 Cursor Agent CLI(默认命令 agent,也会尝试 cursor-agent);没有 token-level assistant delta 时仍会通过终局 Result.Text 显示完整回复。 |
./examples/web-chat/copilotkit/start-all.sh codex
./examples/web-chat/copilotkit/start-all.sh claude
./examples/web-chat/copilotkit/start-all.sh cursor打开 http://localhost:3000 。Go backend 和 Next.js dev server 默认都只绑定 127.0.0.1;这个示例会调用已登录的真实 CLI,不会默认暴露到局域网。
团队委托 showcase 复用同一套前端,但由它自己的脚本启动正确的 Go backend 并切换为团队工作流页面(不显示该 backend 不提供的 HITL 恢复侧栏):
./examples/showcases/team-agent-workflow/start-all.sh claudecursor 在没有正式 assistant delta 协议时不会伪造 token 流;后端只把 Driver 已确认的最终 Result.Text 恰好投影一次,并同步写入恢复历史。
也可以只起后端:
./examples/web-chat/copilotkit/start.sh claudeBrowser
├─ <CopilotChat/> (React, @copilotkit/react-ui)
│ │ POST /api/copilotkit
│ ▼
│ Next.js CopilotRuntime + HttpAgent
│ │ POST /agent (AG-UI RunAgentInput)
│ ▼
│ Go backend
│ ├─ ai.Thread(key).Stream(ctx, prompt) → adaptor.Stream
│ └─ for ev := range stream.Events() → 一条通道,两个去处
│ ├─ recorder.Record(...) (历史,HostSeq 游标)
│ ├─ translator.Translate(ev) → SSE (实时)
│ └─ *adaptor.ApprovalRequest → pending store (自带 responder)
│ stream.Result() → Result.Text fallback(仅无 assistant delta)
│ → translator.CloseResult(...) (终局判决)
│
└─ 直接 fetch (旁路) —— 用于 HITL & 恢复
GET /session/events?thread_id=T&after=N
GET /decision/pending?thread_id=T
POST /decision/resolve
- Go 1.26.5+
- Node.js 20+ + npm
- 选用的
codex/claude/cursorCLI 已安装、已登录,并且--help可运行
前端依赖固定使用 CopilotKit 1.63.2 与 next@16.3.0-preview.8。选择该
Next preview 是因为截至冻结日最新 stable 16.2.12 仍落在已公开的 high
advisory 范围内;同时以局部 overrides 将 Hono、PostCSS、fast-uri 提升到
兼容的安全补丁版本,并更新 MCP SDK、DOMPurify、Mermaid 等仍在兼容范围内的
传递依赖。lockfile 只保存 npm 官方 registry URL,CI 会执行 fresh npm ci、
lint、build 与 npm audit --omit=dev --audit-level=high。冻结时 production tree
没有 high/critical;剩余 moderate/low 来自 CopilotKit Runtime 尚未接受的
@hono/node-server/uuid major 以及暂无已发布修复的上游链路,因此没有用
npm audit fix --force 或不兼容的全局 override 掩盖风险。
| 路径 | 方法 | 用途 |
|---|---|---|
/agent |
POST | AG-UI RunAgentInput -> SSE 流 |
/session/events |
GET | 历史事件重放 |
/decision/pending |
GET | 未解决的决策请求 |
/decision/resolve |
POST | 宿主回填 HITL 决策 |
/health |
GET | 就绪检查 |
| 名称 | 默认 | 说明 |
|---|---|---|
AGUI_AGENT |
codex |
codex / claude / cursor |
AGUI_MODEL |
agent 默认模型 | 覆盖 AG-UI 后端模型 |
CODEX_COMMAND / CLAUDE_COMMAND / CURSOR_COMMAND |
自动探测 | 覆盖本机 CLI 命令 |
CODEX_MODEL / CLAUDE_MODEL / CURSOR_MODEL |
agent 默认模型 | 覆盖对应 agent 模型 |
ADDR |
127.0.0.1:8080 |
backend 监听地址;远程部署必须显式覆盖 |
CORS_ORIGIN |
http://localhost:3000 |
仅允许本机 CopilotKit UI;可显式覆盖为部署后的准确 Origin |
THREAD_STORE_DIR |
unset | 设置后使用 typed Event JSONL 持久化;目录创建、写入或同步失败会显式终止启动/本次请求,绝不静默退回内存。未设置时明确使用 NewMemoryEventBackend() |
前端:
| 名称 | 默认 | 说明 |
|---|---|---|
AGENT_BACKEND_URL |
http://localhost:8080/agent |
CopilotRuntime 转发的 AG-UI 端点 |
NEXT_PUBLIC_AGENT_BACKEND_BASE |
http://localhost:8080 |
浏览器旁路请求 base URL |
如需从其他机器访问,必须显式设置监听地址与准确 Origin,例如 ADDR=0.0.0.0:8080 CORS_ORIGIN=https://chat.example.com;不要在不受信网络中使用 CORS_ORIGIN=*,因为每个请求都可能触发真实模型调用。
The backend can render remote A2A delegation progress in the same CopilotKit
AG-UI stream. Create an a2adelegation.Service from the remote Agent Cards and
pass team.Option() to adaptor.New (or pass the service through
adaptor.WithRunServices(team)). The service creates an authenticated,
run-scoped delegate_to_agent MCP sidecar, exposes it through the typed MCP
contract, binds its cleanup to the run, and folds progress into the Agent's
single event stream as adaptor.SubagentUpdate. The AG-UI bridge projects those
updates into one incrementally patched AG-UI Activity message per delegation
(activityType="subagent", messageId=delegationId); Claude/Codex/Cursor
receives only the final structured MCP result. In team-workflow mode the
CopilotKit frontend renders these Activity messages as live cards in the right
sidebar. A role's human-facing name carries its provider base, so the card tag
shows Claude Code, Codex, Cursor, or CodeBuddy instead of the common
A2A transport. Structured plan output shaped as a file value is rendered as a
previewable and downloadable PLAN.md attachment.
This example's default main path starts one local parent CLI. Hosts that enable
delegation should run an A2A bridge for each remote agent (see
examples/a2a-server and docs/a2a.md), construct the service once, keep it
alive for the equipped Agent, and close it during host shutdown. No stringly
runtime metadata or bridge-specific run handle is involved.
The important boundary for UI authors: render activityType="subagent" as a
nested visual group, but do not turn it into a parent assistant message. The
Activity content may include parentToolCallId when the host can correlate the
delegation sidecar with a parent provider tool-use ID, but UI code should not
require it. subagentId is the stable nested group key and runId is its run
scope. The parent transcript should contain the normal tool call plus the
concise final delegation result only.