dsh-ag-ui

DSH PLUGIN模型与账号接入

仓库 CaiZongyuan/dsh-ag-ui

模型与账号接入 L4 · 沙盒安装通过 安全审查:低风险 可信度:银 可安装插件
安装这个插件

dsh-ag-ui 是 DeepSeek Harness 的 AG-UI 协议网关插件,通过 HTTP/SSE 网关将 DSH Agent 暴露给浏览器,支持流式文本、工具事件及浏览器工具代理。

一句话选型结论

按资料字段整理

dsh-ag-ui 是模型与账号接入类的 DSH 可安装插件,验证等级 L4 · 沙盒安装通过,安全审查为低风险,列出 5 项功能,有可直接复制的安装命令。验证通过不等于安全:插件会读写文件、执行命令或联网,装前先到仓库确认它做了什么;涉及密钥或系统权限的,建议先在隔离环境试装。

分类模型与账号接入 收录类型可安装插件 验证等级L4 · 沙盒安装通过 安全审查低风险
验证等级 L4 L4 · 沙盒安装通过
GitHub 星标 6 复刻 3 · 未关闭 issue 0
最近更新 2026-10-06 创建于 2026-08-23
README 38.6 KB 正文体量,越大说明说明书写得越细

安装

可安装插件
在 DSH 里执行,装完用 dsh plugin list 确认已挂上;标了 L4 / L5 的命令已在沙盒里真实跑通
dsh plugin --profile web add github:CaiZongyuan/dsh-ag-ui
  1. 1
    确认版本与运行环境

    兼容的 DSH 版本为 0.1.1-rc.2;版本不匹配时安装会报错。

  2. 2
    执行安装命令

    在 DSH 里执行上面的命令;多包仓库要注意区分具体子包名,别装错包。

  3. 3
    确认已挂载

    用 dsh plugin list 查看列表里是否出现这个插件,出现即挂载成功。

  4. 4
    试跑并检查权限

    加载后跑一个最小用例,确认它能正常工作;同时看它申请了哪些文件、命令或网络权限。

兼容 DSH 版本0.1.1-rc.2版本不匹配时安装会报错
Node.js 要求未标注来自仓库 package.json 的 engines 字段
前置依赖无需要同时安装的同级依赖
npm 包名未发布到 npm可用 npm view 查看包信息

能做什么

5 项功能
字段来自项目仓库与公开索引,中文说明由本站标注

dsh-ag-ui:DeepSeek Harness 的 AG-UI 协议网关插件

  • AG-UI 协议网关
  • 浏览器工具代理
  • 流式文本与工具事件
  • 线程绑定与状态同步
  • 工具卡片渲染

上游标签

ag-uicordisdeepseek-harnessdsh-plugin

验证等级与安全审查

L4 · 沙盒安装通过
自动化流程结论,不是人工审核
验证等级L4 · 沙盒安装通过安装命令在隔离沙盒内真实执行成功,无依赖错误
安全审查低风险静态分析结论,未标注不代表已通过审查
仓库健康度活跃按仓库活跃度与最近更新时间给出
可信度银综合仓库活跃度与验证结果

上面的结论来自上游最近一次自动化验证(2026-10-08): 同一套检查会在插件发新版后重跑,页面显示的是最新一次的逐项结果。

逐项检查结果

L1 · 已找到 ✓ 通过
  • L1.0_DEFAULTED_FROM_L3 由 L3 推定 默认通过——L3 安装规范已通过(全库 L3 扫描策略)
L2 · 结构化 ✓ 通过
  • L2.0_DEFAULTED_FROM_L3 由 L3 推定 默认通过——L3 安装规范已通过(全库 L3 扫描策略)
L3 · 安装规范 ✓ 通过
  • L3.1_INSTALL_COMMAND_PARSED 安装命令已解析 方式:安装规范解析
  • L3.2_DSH_VERSION_DECLARED DSH 版本已声明 方式:DSH 版本声明
L4 · 安装已测 ✓ 通过
  • L4.1_INSTALL_EXECUTED_CONFINED 受限沙盒内执行安装 方式:沙盒安装执行
  • L4.2_INSTALL_SUCCESS 沙盒安装成功 方式:沙盒安装执行
  • L4.3_ARTIFACT_CONFIRMED 安装产物已确认 方式:沙盒安装执行
L5 · 运行已测 ✕ 未通过
  • L5.1_INSTALL_PASSED_SANDBOXED 沙盒安装通过 方式:沙盒安装执行
  • L5.2_WEB_BOOT_READY Web 启动就绪 boot failed
  • L5.3_HTTP_SERVED HTTP 服务响应
— · 其它检查项
  • L4_0_SANDBOX 沙盒安装判定 方式:沙盒安装执行

风险详情

  • 审查结论 通过
  • 结论说明 vet 静态分析未发现可疑行为,判定为干净(clean)。
  • 风险计数 严重 0 / 高 0 / 中 0
  • 风险详情 未发现严重或警告级问题。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 验证等级 L4:在沙盒里真实装过,依赖能装上,不是纸面声明。
  • 静态安全审查为低风险,没有发现明显可疑行为。
  • 有可直接复制的安装命令,不用自己翻文档找入口。
  • 标注了兼容的 DSH 版本(0.1.1-rc.2),按版本装不容易报错。
  • 列出了 5 项功能,能直接看出它解决什么问题。

先想清楚的地方

  • 没有声明 Node.js 版本要求,版本不匹配可能装不上。
  • 验证通过不等于安全:插件会读写文件、执行命令或联网,装前先到仓库确认它做了什么。

更适合

  • 需要在模型与账号接入场景里给 DSH 补能力的人。
  • 想装完就能用、不想自己排依赖的人。
  • 能接受在本地装依赖、愿意自己确认权限的人。

不太适合

  • 只使用 DSH 官方内置能力、不接受第三方插件的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
插件验证等级安全审查星标兼容版本安装命令
dsh-ag-ui当前 L4 · 沙盒安装通过 低风险 6 0.1.1-rc.2 有
archify L4 · 沙盒安装通过 低风险 8.0 万 未标注 有
DeepSeek-Reasonix L4 · 沙盒安装通过 低风险 3.6 万 未标注 有
nocobase L3 · 安装规范通过 低风险 2.5 万 0.1.1-rc.2 无
learn-harness-engineering L4 · 沙盒安装通过 低风险 2.0 万 未标注 有
dsh-our-free-model 未做沙盒验证 未标注 8,064 未标注 无

仓库档案

模型与账号接入
字段来自项目仓库与公开索引,中文说明由本站标注
作者 / 维护方CaiZongyuan仓库 dsh-ag-ui · 默认分支 main
分类模型与账号接入ai
收录类型可安装插件声明了 dsh.bundle、可用 dsh plugin add 安装的条目
开发语言TypeScript仓库主语言
许可证MIT商用或二次分发前请确认条款原文
星标 / 复刻 / issue6 / 3 / 0收录时的数值,非实时
创建 / 更新2026-08-23 · 2026-10-06最后推送 2026-10-06
本站收录2026-08-23首次进入本站目录的时间

README 全文

38.6 KB
作者原文,中文版优先;只做安全转义,不改内容

dsh-ag-ui

English | 简体中文



A community DeepSeek Harness Host plugin that exposes DSH Agents through the AG-UI protocol. It provides an authenticated HTTP/SSE Gateway, AG-UI thread-to-DSH Agent bindings, streamed text and Tool events, browser-owned Tools, and continuation of the same DSH turn after a browser Tool result returns. The same projection core is also available in an embedding form: the separate dsh-ag-ui-adapter package spawns a private loopback micro-host behind an AG-UI AbstractAgent.

> This is a community project. It is not an official DeepSeek or AG-UI package.

Features

  • Standard Cordis Service plugin exposed as ctx.agUi
  • Transport-neutral Agent-scoped browser Tool broker exposed as ctx.browserTools
  • Installable DSH Profile Bundle through dsh plugin add
  • Floored AG-UI protocol range (~0.0.59)
  • Authenticated BFF-to-Gateway requests with trusted tenant and user headers
  • Streamed per-thread file upload and authenticated download routes
  • Process-local (tenantId, userId, threadId) bindings to DSH Agents
  • AG-UI text streaming and backend Tool result projection
  • Agent-scoped browser Tools supplied by RunAgentInput.tools
  • Frontend Tool Promise parking and ToolMessage continuation across HTTP runs
  • Bidirectional shared state through RunAgentInput.state, ag_ui_update_state, and STATE_SNAPSHOT
  • Presenter cards for backend Tool calls as versioned dsh:tool:view CUSTOM events, live and on cold replay
  • React renderers for every card kind in the separate dsh-ag-ui-cards package, with component tests against recorded gateway events
  • An embedding adapter in the separate dsh-ag-ui-adapter package that spawns a loopback DSH micro-host and serves it as an AG-UI AbstractAgent
  • A keyless Dojo-compatible example for five standard AG-UI features
  • Run and message idempotency
  • Bounded requests, context, Tool schemas, event buffers, threads, and run ledgers
  • Complete Cordis disposal of routes, Agents, Tools, timers, and pending calls

Requirements

  • Node.js ^22.19.0 or >=24.0.0
  • A DSH Profile containing the standard Host services
  • A registered DSH model provider and model
  • An authenticated Backend-for-Frontend that keeps the Gateway secret away from browsers

Installation

Install the bundle into a DSH Profile:

dsh plugin --profile web add dsh-ag-ui

For the GitHub checkout before an npm release:

dsh plugin --profile web add github:CaiZongyuan/dsh-ag-ui

The bundle always mounts the lightweight browser-tools row. The AG-UI Gateway row stays dormant until all required environment variables are present, so native DSH integrations can lease browser-owned Tools without configuring a second model route or Gateway secret.

export DSH_AG_UI_PROVIDER='openai'
export DSH_AG_UI_MODEL='gpt-5.6-sol'
export DSH_AG_UI_SHARED_SECRET="$(openssl rand -hex 32)"
export DSH_AG_UI_PATH='/ag-ui' # optional

dsh --profile web

The bundle inserts an always-on browser-tools row and a conditional Host-plane ag-ui row. The first never creates an Agent: another integration selects an existing Agent and supplies a browser transport.

Browser Tool broker

dsh-ag-ui/browser-tools hides provider-safe name checks, object-rooted schema validation, exact Agent-scope registration, catalog replacement, collisions, cancellation, timeout, and lease teardown behind one interface. A caller owns Agent selection and transport:

const lease = ctx.browserTools.bind(agent, owner, tools, {
invoke: (call, signal) => browserTransport.invoke(call, signal),
})

lease.update(nextTools)
lease.dispose()

Browser context and Tool results are capability data, not authorization. Durable actions still require server-owned identity, resource checks, and any domain confirmation flow.

Profile configuration

Environment variables are the shortest setup path. A Profile can instead override the bundle row in its own cordis.patch.yml:

- id: ag-ui
disabled: false
config:
provider: openai
model: gpt-5.6-sol
sharedSecret: !!js process.env.DSH_AG_UI_SHARED_SECRET
path: /ag-ui
maxThreads: 100
frontendToolTimeoutMs: 300000

A later Profile patch replaces the bundle row's complete config; include every value that deployment needs.

Presented deliverables

Harness present declarations become standard AG-UI ACTIVITY_SNAPSHOT events with
activityType: "dsh-deliverables". Their activity messages survive history reads and
restart with the same id, derived from the native session and event sequence. Nested
present calls are included even if their enclosing tool later fails.

Activity content preserves native turn, callId, and files: [{ path, description? }],
adding a relative url to each file. A trusted BFF must proxy this URL with the same
authenticated tenant and user headers as agent runs:

GET /ag-ui/threads/:threadId/deliverables/:eventSeq/files/:fileIndex

The route reads only a declaration from that authenticated thread. It uses the Session's
native filesystem and persisted cwd, including absolute paths the provider permits.
Preset-isolated filesystems are resolved through the native preset roster; the host
filesystem is used only when that preset supplies none.
The host must supply @deepseek-ai/dsh-fs in that Agent scope and mount
@deepseek-ai/dsh-tool-present where the tool should be available. No attachment store
or upload receipt is needed for a deliverable. The response downloads the current file
as an attachment with Cache-Control: no-store; it does not archive the original bytes.
Deleted files and non-regular files return 404, provider access denials return 403,
and files exceeding maxFileBytes return 413. Reads are bounded and cancelled when the
client disconnects. The URLs are authenticated references, not public sharing links.

Render dsh-deliverables activities in the client's transcript. They do not turn generic
file tool results or client-supplied tool messages into declared deliverables.

Configuration

provider, model, and sharedSecret are required. sharedSecret must contain at least 16 UTF-8 bytes.

Field Default Purpose
path /ag-ui Base Host HTTP route for runs and files
provider required Registered DSH model provider route
model required Model ID owned by the provider
workspaceRoot /workspaces Root for per-thread workspace directories, named by durable session id
agentPreset none Deployment-default agent preset id composed into every thread
tenantPresets {} Per-tenant preset ids taking precedence over agentPreset
selectableAgentPresets {} Canonical preset ids each authenticated tenant may select for a blank thread
sharedSecret required Bearer secret shared only with the trusted BFF
tenantHeader x-dsh-tenant-id Trusted tenant identity header
userHeader x-dsh-user-id Trusted user identity header
allowNonLoopback false Permit a non-loopback Host bind explicitly
maxRequestBytes 262144 Maximum request body bytes
maxFileBytes 104857600 Maximum bytes per uploaded file or deliverable download
maxIdentityBytes 256 Maximum bytes per protocol or identity ID
maxMessages 256 Maximum message count per request
maxMessageBytes 524288 Maximum combined message JSON bytes
maxFilesPerMessage 8 Maximum non-text parts in one user message
maxContexts 32 Maximum context entry count
maxContextBytes 131072 Maximum combined context JSON bytes
maxTools 32 Maximum browser Tool count
maxToolBytes 131072 Maximum browser Tool JSON bytes
maxToolSchemaDepth 16 Maximum browser Tool schema depth
maxForwardedPropsBytes 65536 Maximum forwardedProps JSON bytes
maxStateBytes 65536 Maximum state JSON bytes
maxThreads 100 Maximum process-local live threads
threadIdleMs 1800000 Idle thread lifetime
frontendToolTimeoutMs 300000 Maximum browser Tool result wait
humanInteractionTimeoutMs 300000 Maximum wait for each native human request; integer from 1 to 2147483647 ms
maxPendingInterrupts 16 Maximum live human requests per thread
maxRunEvents 4096 Maximum events retained per run
maxRunEventBytes 2097152 Maximum retained event bytes per run
maxRunsPerThread 32 Maximum retained run ledger entries and, separately, waiting requests per thread

agentPreset composes each thread's agent from the host's agent-preset registry (mount the roster plugin before this Gateway); an unresolvable id fails Gateway activation loudly, a per-tenant entry overrides the deployment default for that tenant's threads, and a resumed thread keeps the composition its own durable session recorded. Without agentPreset, threads keep the host composition unchanged.

Each thread uses / as its DSH working directory. The directory is named by the durable session id, so client thread ids stay off disk. When the Host provides workspaceRegistry, the Gateway registers new workspaces for DSH Web.

File routes require the official fileUploads and attachments services, already mounted by @deepseek-ai/dsh-web-app. POST /threads//files streams the raw body with content-length, optional content-type, and percent-encoded x-file-name. Harness owns streamed storage, content hashes, temporary-file cleanup, and staged receipts. The response retains its AG-UI URL source and filename/size/sha256 metadata.

Clients must preserve the returned URL query when changing a proxy prefix. The gateway signs the native file reference and receipt for the authenticated session. GET verifies the signature and principal/thread mapping before calling the official streamed reader. Same-name uploads keep their display name and receive distinct receipt URLs. Downloads remain authorized after cold resume; rotating the shared secret invalidates old URLs. Pre-native unsigned upload URLs require a fresh upload.

User messages accept ordered text and signed thread-file URL parts. Images use official image admission; other files become native file content parts. Harness owns receipt binding, successful admission retirement, and rollback when queue delivery fails. Rejected admission can retry its still-staged receipt. A consumed, explicitly retired, or cold unsent receipt returns FILE_NOT_STAGED and requires re-upload; the gateway never restores expired authority. MESSAGES_SNAPSHOT preserves the exact accepted AG-UI parts. Shared-state and frontend Tool admission are revalidated after asynchronous file processing, before publishing those parts. Inline data parts are not accepted.

For multiple presets within one tenant, the host can grant selection with selectableAgentPresets: { "tenant-1": ["alpha", "beta"] }. A run may then request forwardedProps: { agentPreset: "beta" }. The Gateway validates grants against the roster at activation and calls native agentPresets.select under the thread's run reservation before its first turn. The roster alone grants no authority, and the BFF must still authenticate the tenant and authorize access to its application features.

Selection is optional. Omitting it preserves the current composition; repeating the effective canonical id is a no-op, including after restart. A different ungranted id fails with HTTP 403 PRESET_NOT_ALLOWED; a granted change after the first turn fails with HTTP 409 PRESET_LOCKED. A history-only request never selects a preset, so a session created by a history read can still choose one on its first work run. The native session log owns the selected composition and restores it after restart. Tool names are validated against the selected composition before SSE starts. A successful selection remains recorded if that validation rejects the run or its client disconnects; no user turn is started, and the blank session can select again.

maxRunEvents must retain at least the mandatory opening and terminal events. maxRunEventBytes bounds the complete retained Run record, including RUN_STARTED and its terminal event, and must be large enough for the configured maximum identity length. A non-loopback DSH WebServer requires allowNonLoopback: true. Prefer a loopback Gateway behind a same-host authenticated BFF.

Opening and final durable history snapshots both count toward this bound. Buffer overflow ends the HTTP run and cancels only its currently claimed native turn. An overflowing history-only read does not cancel another active turn. Completed duplicate requests still replay the exact retained events.

Architecture

One projection core, two supported shapes. The core is the dsh-ag-ui Host service: it binds AG-UI threads to DSH Agents and translates runs, events, tools, shared state, and presenter cards in both directions. Everything around it is packaging.

Deployment form — BFF Gateway            Embedding form — dsh-ag-ui-adapter

Browser                                   Node.js application
-> authenticated application BFF         -> DshAgent (an AG-UI AbstractAgent)
bearer secret and trusted                spawns a private micro-host child:
identity headers                         - loopback webserver, ephemeral port
-> POST /ag-ui on the Host                    - the same published dsh-ag-ui
-> dsh-ag-ui Host Service                       gateway row, per-process secret
-> DSH Agent / Session / Tool runtime         - the application's Agent core and
-> model provider and backend Tools              model plugin rows
-> run() over loopback HTTP to the
same gateway service

The deployment form fronts a shared Host with an authenticated BFF for browser clients. The embedding form (dsh-ag-ui-adapter) composes a throwaway Host per application process — nothing is spawned before the first run, and the child never outlives the process. Both shapes speak the same protocol to the same projection core, so run semantics, browser Tools, shared state, presenter cards, idempotency, and disposal behave identically.

In both forms the gateway binding key is the exact (tenantId, userId, threadId) tuple supplied through trusted identity headers.

Trust posture

  • The gateway listens on the Host webserver. Keep that webserver loopback and behind a same-host authenticated BFF; a non-loopback bind requires the explicit allowNonLoopback setting and is almost always a mistake.
  • The bearer secret authenticates one service-to-service hop — the BFF (or, in the embedding form, the adapter process) to the gateway. It is not end-user authentication: the gateway never sees a user credential and by itself grants nothing user-scoped.
  • End-user identity travels in the trusted tenantHeader/userHeader headers. Whoever holds the secret can assert any identity, so the secret holder must itself be trustworthy — in the deployment form, authenticating the user before injecting those headers is the BFF's whole job; in the embedding form the adapter process is the trusted principal.
  • Browser-supplied identity, permission, patient ID, resource ID, context, state, forwardedProps, Tool schemas, and IDs inside messages are untrusted wire input and never grant backend authority.
  • Backend Tools can derive the authenticated thread identity from the Agent:
const identity = ctx.agUi.identityFor(exec.agent)
if (identity === undefined) {
throw new Error('This Tool requires an authenticated AG-UI thread.')
}

const { principal, threadId } = identity

The application should map this tuple to its server-owned resource authorization state.

BFF proxy

The browser must not call the private Gateway directly. A BFF should authenticate the user, authorize the application resource, retain the browser request body exactly, and inject trusted identity headers.

app.post('/api/agent', async (c) => {
const user = await authenticateApplicationRequest(c.req.raw)
const body = new Uint8Array(await c.req.raw.arrayBuffer())

const upstream = await fetch('http://127.0.0.1:3080/ag-ui', {
method: 'POST',
headers: {
authorization: `Bearer ${process.env.DSH_AG_UI_SHARED_SECRET}`,
'content-type': 'application/json',
'x-dsh-tenant-id': user.tenantId,
'x-dsh-user-id': user.userId,
},
body,
})

return new Response(upstream.body, {
status: upstream.status,
headers: upstream.headers,
})
})

The BFF owns login, sessions, CSRF protection, tenant policy, resource authorization, audit, and rate limits. Do not treat the Gateway bearer secret as end-user authentication.

Proxying Host service-plugin remotes

The AG-UI gateway is one Host-plane service with an HTTP remote; other DSH service plugins can mount routes on the same loopback webserver. The same rule covers every one of them: the browser never reaches the Host directly. Expose each remote through the application backend under an application-owned route, with the authenticate → authorize → forward shape above and the credentials that service expects. The Host port itself stays loopback and unadvertised to clients.

AG-UI client

The Gateway wire protocol accepts official clients in the supported range (>=0.0.59 <0.1.0) and does not require an exact pin. The Gateway-owned DshHttpAgent companion is tested and peered with @ag-ui/client ~0.0.59:

pnpm add dsh-ag-ui @ag-ui/client@~0.0.59

The optional Gateway-owned client companion omits presentation messages from HTTP input while retaining every user and Tool message. It preserves the final synthetic pair added by A2UI middleware. The agent keeps its complete local history for rendering and middleware; standard HttpAgent with full history is also supported.

Send page-specific browser Tools and current context on every run:

import { randomUUID } from '@ag-ui/client'
import { DshHttpAgent } from 'dsh-ag-ui/client'

const agent = new DshHttpAgent({
url: '/api/agent',
threadId: 'application-thread-123',
})

agent.addMessage({
id: randomUUID(),
role: 'user',
content: 'Review the current draft.',
})

await agent.runAgent({
runId: randomUUID(),
tools: browserTools,
context: [{
description: 'Current page state',
value: JSON.stringify(readPageSnapshot()),
}],
forwardedProps: {},
})

Assistant messages do not acknowledge earlier input, so the companion never discards user messages based on their position. The Gateway deduplicates accepted messages by ID. Large user and Tool histories still count toward the configured HTTP request-body limit; this companion does not guarantee bounded request size.

If the model calls a browser-owned Tool, the current HTTP run finishes successfully while the DSH Tool Promise remains pending. The browser executes the Tool, appends one standard AG-UI ToolMessage with the same toolCallId, and starts another run. The Gateway resolves the original Promise and continues the same DSH turn.

The official @ag-ui/a2ui-middleware renders from the streamed Tool arguments and never sends a browser result. The Gateway therefore does not park the render Tool the middleware flags in forwardedProps.injectA2UITool: the call settles at once with {"status":"rendered"}, its result streams in the same run, and the DSH turn continues. A render Tool a client registers itself still parks like any browser-owned Tool. A later forwardedProps.a2uiAction starts the next turn as durable plugin context. That context keeps the readable middleware result plus the complete validated action JSON, including its optional timestamp, with recursively sorted object keys. The Gateway accepts only the middleware's exact bounded action envelope and matching final log_a2ui_event assistant/Tool pair; it does not import arbitrary assistant history into DSH.

The synthetic result message ID identifies the action in the native inbox and durable log. Redelivery of the same formed pair is idempotent across HTTP run IDs and restarts; reusing that identity with changed action content is rejected. Each new click needs a new result ID, even when its payload matches an earlier click. Middleware retries must preserve the formed pair identities.

Do not send ordinary browser Tool results through AG-UI resume[]; that field is reserved for explicit interrupt/HITL flows.

Native questions and approvals

Mount the native @deepseek-ai/dsh-user-questions and/or @deepseek-ai/dsh-user-approval services in the Host profile. A business preset can mount the official @deepseek-ai/dsh-tool-ask-user Tool. The Gateway answers requests only for its exact live root Agent; it does not install the services, replace their policies, or handle subagent questions.

A native human request ends the HTTP run with RUN_FINISHED and outcome: {type: "interrupt", interrupts: [...]}. The Harness turn continues waiting on its original Promise. Submit a fresh runId in the same thread with resume[] to answer it:

{
"threadId": "thread-1",
"runId": "answer-1",
"messages": [], "tools": [], "context": [], "state": {}, "forwardedProps": {},
"resume": [{"interruptId": "", "status": "resolved", "payload": {"approved": true}}]
}

Approval interrupts use reason: "approval", optional native toolCallId, and {approved: boolean} as their response. false rejects this action; status: "cancelled" withdraws the request. Only the native service can grant allowed-once, and its never policy still rejects without prompting. Gateway responses never grant lasting permission or manufacture backend Tool results.

Question interrupts use reason: "user_question". metadata.dsh.questions contains the native questions, options and optional intent; responseSchema describes the answer structure. Respond with {answers: [{id, selected: ["option label"], custom?: "text"}]}. Answer every question exactly once. Single-select questions accept one option or custom text; multi-select permits both. A cancelled question rejects through the native ASK_ABORTED error.

Every published interrupt must appear once in a resume batch. Validation precedes SSE, message admission, context/state changes and answer consumption. Invalid batches leave the pending work untouched. New user messages cannot share a run with human responses; already-ready frontend Tool results of the same turn can. Unknown interrupt ids are logged and ignored within the authenticated thread. Retained identical run ids replay without answering twice.

A history-only run repeats the same published interrupt ids and shared-state snapshot. Later native questions wait for the next accepted continuation; reload does not enlarge a form another tab already received. A newly discovered question may follow a frontend Tool's completed HTTP run; the next continuation or history read publishes it.

Each request has a finite server deadline, unaffected by reconnect. Timeout, native abort, overflow or disposal cancels the waiting work. A known expired resolved response returns INTERRUPT_UNAVAILABLE; send status: "cancelled" to clear that stale client gate without executing anything. expiresAt is omitted because current clients also reject cancellation of expired interrupts.

Shared state

Activate shared state by setting a non-empty initial value before the first run:

agent.setState({
recipe: {
title: 'Draft',
ingredients: [],
},
})

The Gateway injects the accepted state into the DSH Session, registers the reserved ag_ui_update_state Tool in the exact Agent scope, and emits STATE_SNAPSHOT. The official client replaces agent.state when each snapshot arrives.

The state Tool accepts:

{
"state_updates": {
"recipe": {
"title": "Pasta Primavera"
}
}
}

Updates use a shallow top-level merge: omitted top-level keys remain, while supplied nested values replace the previous nested value. The Gateway measures the complete merged state against maxStateBytes. It commits a model update and emits its snapshot only after DSH appends the durable tool/result; equal updates retain the Tool result but emit no redundant changed-state snapshot.

Initial activation ignores the default empty state sent by clients that do not use shared state. After activation, later empty objects, arrays, or null are valid complete baselines. Omitting state retains the current thread state.

Shared state is model/UI collaboration data. It never grants backend authority and should not replace an application's durable database state. STATE_DELTA is not implemented yet.

Dojo-compatible example

The package ships the framework-free BFF plugin as dsh-ag-ui/dojo-host. The keyless scripted model, launcher, and five-feature suite remain source-checkout fixtures. See examples/dojo/README.md for commands, routes, upstream Dojo compatibility, real-model configuration, and security limitations.

The upstream Dojo integration registry is static and has no deepseek-harness entry yet, so local upstream testing temporarily reuses the Claude Agent SDK TypeScript menu entry purely as a URL/path alias. The alias disappears once the upstream integration PR registering a DeepSeek Harness entry is accepted; no Claude runtime, model, or credential is involved.

The recording below shows the shared-state feature on the upstream Dojo demo viewer against this repository's keyless fixture. Both chat turns flow through the gateway: the first reads the shared state, the second emits STATE_SNAPSHOT events that rewrite the recipe form. Playback is sped up 3x and carries English captions.

If the inline player does not render on this host, download docs/demo/dojo-shared-state.mp4 (captions: docs/demo/dojo-shared-state.vtt).

Embedded adapter

The separate dsh-ag-ui-adapter package is the embedded counterpart of this deployment-form Gateway. A DshAgent (AbstractAgent subclass) spawns a DSH micro-host child — a Cordis overlay composing the loopback webserver on an ephemeral port, this Gateway with a per-process generated secret, and the caller's explicit Agent-core and model rows — and passes run() through loopback HTTP using the official client primitives, adding no protocol translation code. The host starts lazily on the first run, can idle-shut-down, and never outlives the embedding process. See its README for usage, plugin row resolution, environment fallback, lifecycle, and the trust posture of the embedded shape.

HTTP and run semantics

  • Requests must be POST application/json and match AG-UI RunAgentInput.
  • A normal run accepts one or more new user messages with text or supported content parts; they join one DSH turn in arrival order. A run without new messages, including a full already-accepted transcript, only returns the history snapshot; it never waits behind an active run.
  • A continuation accepts one or more new frontend ToolMessages for one pending DSH turn.
  • An official A2UI user-action run accepts its validated a2uiAction envelope and matching synthetic log_a2ui_event pair; it may also carry the result of a client-owned pending render_a2ui call.
  • A render Tool flagged by the middleware in forwardedProps.injectA2UITool settles inside its run with {"status":"rendered"} and never parks.
  • Successful frontend Tool results retain their accepted AG-UI message id, optional encryptedValue and subagentRunId, and empty-versus-absent metadata across durable history and cold resume. The Gateway reserves @dsh-ag-ui/frontend-result-id in native presentation metadata, overwrites client attempts to set it, and removes it from wire metadata and presenter inputs. Resending either the original result or its projected snapshot is idempotent; changing public result fields conflicts even on the first request after restart because admission digests are recovered from the durable results. Older results and server-owned results retain deterministic call-based ids. Failed frontend results keep native DSH rejection semantics and the deterministic fallback because rejection has no presentation-metadata hook.
  • Standard object metadata on an authenticated pending frontend Tool result is persisted through native DSH presentation metadata and returned by later message snapshots; results without metadata remain unchanged on the wire.
  • One DSH turn can cross multiple AG-UI HTTP runs.
  • Each run emits one RUN_STARTED and exactly one RUN_FINISHED or RUN_ERROR.
  • Native turns can outlive their HTTP response while frontend calls are parked. Their durable results, shared-state updates, and completion continue to update the thread projection. Invalid continuations leave pending calls available for a corrected request.
  • A failed user batch cancels its pending native inbox input. Discarded, unclaimed messages can be retried with their original message IDs; already claimed messages remain accepted, including after restart. A failed run ID still replays its recorded error, so retry input with a new run ID.
  • runId is an exact-request idempotency key. Completed identical requests, including history-only runs, replay retained events without driving DSH again. All runs share the bounded ledger; active records are never evicted, and a full ledger rejects admission with 429 RUN_LEDGER_FULL.
  • One thread drives one HTTP run at a time. A run that arrives while another is active waits for it and for the Agent turn to settle, so the runs of one thread are served in arrival order; a waiting client that disconnects is never admitted. Waiting and reservation happen together, so several queued runs all get their turn. At most maxRunsPerThread requests may wait per thread; excess requests receive 429 RUN_QUEUE_FULL, and disconnect frees a queue slot. A waiting request keeps the thread alive until admission or disconnect, including while a cancelled native turn settles. Identical requests that queued together replay the same retained result.
  • An active shared-state run emits its synchronization snapshot before model events.
  • One DSH step can park multiple frontend Tool calls; continuation runs may answer a subset.

Client-provided Tools

Browser Tool names must match:

[A-Za-z_][A-Za-z0-9_-]{0,63}

This conservative subset follows common model-provider function-name limits; AG-UI itself does not require this exact regular expression. The name ag_ui_update_state is reserved for protocol shared state. Browser Tool parameters must use the object-rooted JSON Schema subset enforced by DSH Tools. The Gateway rejects collisions with inherited or global Tools and registers each accepted definition only in the exact Agent's Tool scope.

Backend Tool results are emitted as TOOL_CALL_RESULT. Frontend Tool results are not echoed on the AG-UI wire because the browser already added the ToolMessage; DSH still records the real durable tool/result.

Tool view cards

Every backend Tool call carries its DSH render-intent card next to the standard tool events, as a CUSTOM event named dsh:tool:view:

{
"version": 1,
"callId": "call-42",
"toolName": "read_file",
"phase": "call",
"card": { "card": "generic", "title": "Reading src/index.ts", "kind": "read" }
}
  • The Gateway resolves the Tool definition in the Agent scope that executed the call, then evaluates its presentCall (pending state, emitted after TOOL_CALL_END) and presentResult (completed state, emitted after TOOL_CALL_RESULT) intents. Both are pure functions of the arguments and the durable result, including the presentation metadata the Tool's output.presentationMeta projected into its session log.
  • A Tool without intents, a returning intent, or a throwing intent soft-falls to the generic card: { "card": "generic", "title": "", "rawInput": } for the pending state and { "card": "generic" } (keep the pending title, render the raw result) for the completed state.
  • The card vocabulary is DSH's provider-neutral ToolCallView/ToolResultView union (generic, terminal, diff, search, read, web cards), so a UI renders cards without special-casing Tool names.
  • The reserved ag_ui_update_state Tool and client-provided frontend Tools are excluded: the state Tool projects through STATE_SNAPSHOT, and the client already knows how to present its own Tools.
  • At each run start, the Gateway re-derives the settled cards of the whole transcript from the durable session log — the same evaluator and inputs as the live path — and emits them right after MESSAGES_SNAPSHOT, so a client that missed the live stream renders identical cards. A cold read only re-derives cards for Tools that still resolve in the thread's scope, so a crash-materialized frontend Tool call after a restart stays cardless. Cards count against the per-run event budget.

The reserved ag_ui_update_state call and result remain protocol-only in live events and restored message history.

The separate dsh-ag-ui-cards React package renders every card kind from these envelopes with no DSH runtime dependency, and documents the event-wiring recipe. Its component tests render events recorded from this Gateway, and the recording scenario stays guarded by this package's test suite.

Lifecycle

All effects belong to the Cordis plugin fiber. Route removal, idle expiry, timeout, and plugin disposal unregister browser Tools, reject pending calls, cancel active work, dispose Agent handles, and wait for quiescence.

An unexpected HTTP disconnect cancels the Gateway-owned DSH turn. HttpAgent does not implement partial SSE reconnect. A frontend Tool handoff is an intentional completed run and does not cancel the parked turn.

Compatibility

Component Supported version
AG-UI core/client/encoder >=0.0.59 <0.1.0 (~0.0.59; tested with 0.0.59)
dsh-ag-ui/client companion @ag-ui/client ~0.0.59
Node.js ^22.19.0 or >=24.0.0
DeepSeek Harness 0.2.1-rc.2 (exact developer-preview peers)

DSH 0.2.1-rc.2 uses session log v4. Live text arrives through agent/assistant-stream; settled history is read with snapshotEvents(). With the JSONL persistence plugin configured, DSH migrates older logs on resume (tested with a 0.1.1-rc.2 recording). Image and file Tool results are represented by [image result] and [file result] placeholders; attachment bytes are not transported.

DSH is in developer preview and can introduce breaking changes. This package uses exact DSH peer versions until those APIs stabilize.

Model Experience

Injected AG-UI context

What the model sees

Each non-empty RunAgentInput.context becomes one user-role snapshot containing ordered ## sections. The source is { kind: "ag-ui", form: "snapshot", sections }.

Token effect

Conditional and retained. Every accepted normal or continuation run appends its bounded context snapshot to the DSH Session and later model history.

KV cache effect

Append-only context preserves earlier reusable history. Changed current context adds a new suffix; provider cache availability is outside this package.

Shared application state

What the model sees

Once activated, the complete bounded state appears in a Current Shared State section and the reserved ag_ui_update_state Tool joins the Agent schema. A successful state update returns the complete merged state as a durable DSH Tool result.

Token effect

Conditional and retained. Every accepted run with active shared state appends the full state baseline. A model update additionally appends one Tool call/result pair containing the complete merged state.

KV cache effect

An unchanged earlier history remains reusable, but each current state baseline and changed Tool result adds a suffix. Large or frequently changing state reduces cache reuse and increases retained Session tokens.

Client-provided capabilities

What the model sees

The current Agent-scoped browser Tool definitions join the ordinary DSH Tool schema list. Their names, descriptions, and validated parameter schemas come from the authenticated client request; execution remains browser-owned.

Token effect

Conditional and replacing. The visible Tool schema list is sent on every model request and changes when the page advertises a different capability set.

KV cache effect

An unchanged Tool set preserves the Tool-schema prefix. Adding, removing, or changing a Tool may invalidate provider reuse from that portion onward.

Known limitations

  • Live thread bindings, run replay buffers, and shared state are process-local; session history can persist through the Host persistence plugin.
  • Host restart resumes stored sessions with agents.resume(). Parked browser Tools and human request Promises are not recovered: an interrupted turn reports THREAD_INTERRUPTED, and shared state needs a new client baseline.
  • Partial SSE reconnect is not supported. Human questions reconnect through a fresh history-only run.
  • Human interaction supports root Agents only. If the embedding adapter enables idleShutdownMs, its independent child-process shutdown can interrupt a human wait; leave auto-shutdown disabled when live resumption is required.
  • STATE_DELTA and reasoning events are not adapted yet.
  • Shared-state updates use shallow top-level merge and do not provide versions, deep merge, or conflict resolution.

Development

git clone https://github.com/CaiZongyuan/dsh-ag-ui.git
cd dsh-ag-ui
corepack enable
pnpm install
pnpm -r --workspace-concurrency=1 --include-workspace-root check

The repository is a pnpm workspace: the root package is the Gateway, and packages/ holds the dsh-ag-ui-cards React card renderers and the dsh-ag-ui-adapter embedding adapter. pnpm -r --workspace-concurrency=1 --include-workspace-root check runs lint, strict TypeScript checking, per-file coverage, runtime/type builds, and publint in every workspace project. The Dojo fixture is intentionally source-checkout-only and is not included in the npm tarball.

See CONTRIBUTING.md for contribution and release requirements.

License

MIT. Portions are adapted from DeepSeek Harness; see NOTICE.

常见问题

8 问
答案由本站依据条目字段整理,不引用上游原文
dsh-ag-ui 怎么安装?

在 DSH 里执行 `dsh plugin --profile web add github:CaiZongyuan/dsh-ag-ui` 即可。

dsh-ag-ui 的验证等级是什么意思?

L4 · 沙盒安装通过。安装命令在隔离沙盒内真实执行成功,无依赖错误

dsh-ag-ui 安全吗?

低风险。vet 静态分析未发现可疑行为,判定为干净(clean)。

dsh-ag-ui 装之前要先确认什么?

先看它会读写哪些文件、执行哪些命令、连接哪些网络地址;验证等级只说明装得上、跑得起来,不等于安全。涉及密钥或系统权限的,建议先在隔离环境试装。

dsh-ag-ui 支持哪些 DSH 版本?

上游记录的兼容版本包括 0.1.1-rc.2。

dsh-ag-ui 用的是什么许可证?

上游标注为 MIT。商用或二次分发前请到仓库确认许可证原文。

dsh-ag-ui 有哪些功能?

AG-UI 协议网关、浏览器工具代理、流式文本与工具事件、线程绑定与状态同步、工具卡片渲染。

这条记录的数据可靠吗?

本站按仓库名去重后合并整理,字段以作者仓库与 npm 元数据为准;安装命令、仓库地址与 README 保持原文,带 L4 / L5 标记的安装命令已在隔离沙盒里真实跑通。

模型与账号接入
按星标排序,可作替代方案

archify

archify

从仓库或系统描述生成经过校验的自包含交互式架构图、流程图、时序图、数据流图和生命周期图。

L4 · 沙盒安装通过 8.0 万 星

nocobase

nocobase

L3 · 安装规范通过 2.5 万 星

dsh-our-free-model

dsh-our-free-model

为 DeepSeek Harness 挂载免登录、免 API Key 的免费前沿模型通道,装完即用,并附用量看板与 OpenAI 兼容本地转

未标注验证等级 8,064 星

dsh-purge

dsh-purge

dsh-purge 是一款 DeepSeek Harness 设置插件,可修改默认副本、权限策略和工具限制,并添加规则卡片以分组应用补丁,支

L5 · 运行验证通过 4,573 星

CaiZongyuan 的其它插件

1 个

插件的元信息来自作者仓库;分类、验证等级与安全结论的中文说明为中文整理,最近核验 2026-10-08。