20 KiB
description, kind
| description | kind |
|---|---|
| 面向让进程外 SDK 客户端在 DeepSeek Harness 运行时中打开会话并驱动 agent 的部署的 stdio JSON-RPC 服务插件。 | package-reference |
@deepseek-ai/dsh-sdk-jsonrpc-server
English | 中文
概述
dsh-sdk-jsonrpc-server 通过 stdio 服务 SDK 协议格式(wire format),使进程外客户端能够驱动 harness agent(智能体):它为每个 sessionId 打开一个会话、把用户提示词排入队列,并把每个会话事件与 agent 状态转换流式发回客户端。把它作为 jsonrpc 插件挂载到 Loader 组合中;外围插件树提供其余一切——agent、模型适配器、持久化与工具。Stdout 只承载 JSON-RPC 帧,因此部署不得组合 stdout logger。它通过 dispose(资源释放)根运行时并以 0 退出应答 shutdown;EOF 与信号退出归 app bin 负责。
目录
使用本包
当运行时必须服务 SDK 客户端时挂载本插件:把它加入组合了 agent 服务的 cordis.yml,启动运行时,客户端即可通过 stdio 连接。常用路径是显式的——插件需要 agents 服务;其余每个能力都来自外围插件树。
将 initialize.approvalRequests 设为 true,即可把内部审批作为服务端到客户端的 approval/request 请求发送。桥接通过 agents.isOwnedBy() 运行时归属识别 SDK 拥有的 Agent 及其仍在运行的进程内后代,持久化的 parentSession 历史关系不决定审批归属;其他 Agent,以及未提供该标志的客户端,继续使用已组合的应答链。审批服务仍在分发前执行 never 策略,并记录询问与决定的审计事件。桥接仅接受 allowed-once、rejected 或 cancelled;无效响应、客户端错误或传输断开会产生审批服务的拒绝放行结果 unavailable。请求取消时释放待处理的协议等待项;shutdown 在等待 Agent 结束前释放所有此类等待项。迟到响应不能授权已放弃的操作。载荷见协议,客户端处理见 Python 示例。
session/resume 在初始化后接受持久化的 { sessionId },待其 Agent 就绪后返回 {}。它通过 agents.resume() 加载历史、修复最后一个被中断的轮次,并保留存储的起始目录和当前有效目录,同时将初始化时选定的模型路由记录为 Session 选择。省略推理强度时,仅在路由相同的情况下保留已保存的显式强度。它不发送新输入。会话缺失、不可读、不兼容或被占用时,请求失败且不创建替代会话。对正在恢复的标识发送提示词时,会等待恢复完成;shutdown 会等待进行中的恢复并释放其 Agent。历史事件保留在日志中,不会作为新通知重新发送。恢复期间记录的模型选择会在恢复响应前发出新的 session.event 通知。
插件在注册提醒激活处理方后发布 sdkSessionActivation 就绪标记;SDK 组合让 Schedule 依赖此标记,而非 Web Session controller。定时提醒也通过同一个 SDK Session 所有者投递。启动时已到期的提醒等待 SDK 成功初始化,再使用初始化指定的提供方、模型、推理强度和输出上限恢复会话。客户端可以首次接管提醒恢复的 Session;并发的提醒与客户端恢复共用一个 handle。关闭时释放等待初始化的提醒,并等待已经接纳的恢复完成。
下列支持范围是桌面已有、无需图形界面即可执行的原生命令。/permission <preset> 使用这条原生调用路径;Desktop 的 /model 选择器使用独立的模型选择 API。
session/query 是高层 SDK run() 发送字符串和内容块数组时使用的协议入口。普通输入保留内容块,使用既有的提示词队列。完整 SDK 识别裸 /compact、带非空 preset 的 /permission <preset>、/plan [off|message] 和 /goal [objective|clear|edit objective|pause|resume],这些命令按官方定义 ID 识别。裸 /permission 或 /权限、/compact extra 以及 /model 等其余输入仍是普通提示词。命令缺失时也保留提示词路径;选中的 handler 失败后绝不回退。未知 preset 是原生命令错误。随已识别命令提交的编码图片块经过原生附件接纳。session/prompt 保留为低层排队输入入口,不经过原生 slash command 路由。
Host 与桌面适配器共用 dsh-commands/builtin 的定义身份及中英文别名解析。/压缩、/权限、/计划 和 /目标 跟随重命名后的官方定义,执行使用当前注册名。精确名称优先,但无关的同名定义不是 SDK 内置命令,仍作为普通提示词。其他内置能力仍不属于 SDK 命令集合。
第一个文本块提供完整命令行;编码图片可位于它之前或之后。该行匹配到受支持命令时,它必须是唯一的文本块,其余块必须为编码图片。额外文本块、持久图片引用及其他内容类型会在执行或入队前返回 JSON-RPC 错误,不回退为普通提示词。普通提示词保留全部内容块及其顺序。空数组和只包含空白文本的数组会被拒绝;允许仅图片输入。
已识别的命令通过接收 Agent 的原生命令注册器执行。Handler 校验命令参数和附件。压缩负责其辅助模型请求、历史替换和 checkpoint,并拒绝忙碌 Agent。权限变更保留原生状态和通知行为。Shutdown 取消待决命令,在释放 Agent 前排空 SDK 命令等待者。超时不等于回滚,客户端不能自动重试。结果和错误见协议。
权限命令只修改接收命令的 Session。后续输入和恢复的 Session 保留其持久化权限事实;其他 Session 保留各自状态,新 Session 使用配置的默认值。调用方的启动配置和已保存的用户默认值均不会被重写。模型选择不是 SDK slash 命令;恢复时的模型覆盖仍按上文描述的独立行为执行。
不带附件时,/plan 和 /plan off 只改变原生计划状态。带图片或正文的 /plan 通过原生 steer 提交,图片位于去除首尾空白的正文前;/plan off 带图片时返回错误且不改变状态。高层 run() 等待提交的模型活动、审阅及获批后的后续执行,也包括等待先前取消收尾后才开始执行的输入。原生命令响应确认 handler 完成;对应的 session.query.completed 通知确认高层活动结束。问答响应保留批准和反馈语义;{ cancelled: true } 转为原生 ASK_CANCELLED,使关闭审阅结束 turn 而不退出计划模式。
/goal 将语法委托给原生目标命令。高层 run() 创建或恢复目标时跨越自动轮次等待,直到目标终止及收尾活动结束,也包括压缩期间排队的参考输入。完成返回 completed,暂停或清除返回 aborted,阻塞或非终态失去激活状态报告失败。等待收尾活动结束时保留首次持久化的目标终止状态,后续 clear 或 resume 操作不会替换它。完成通知携带最终助手文本,未输出助手文本时为空字符串;目标状态由结束原因和事件表达。仅改变状态的控制命令返回原生 handler 文本,不等待无关活动。底层 session/wait 仍可在轮次之间返回。恢复会话保留目标状态,直到 /goal resume 才重新激活。目标命令不改变权限或 SDK 启动设置。
创建和编辑接受图片,作为下一轮前的参考材料;查看、暂停、恢复和清除拒绝附件,不修改目标状态。压缩和权限命令也拒绝附件。处理器拒绝后绝不回退为普通提示词;与 Desktop 相同,图片可能在处理器拒绝前已被存储。这些 SDK 输入接受栅格图片,不接受一般文件上传或客户端提供的附件引用。
session/working-directory/get 接收 { sessionId };session/working-directory/set 接收 { sessionId, path }。两者均返回 { cwd },并可创建未知 Session,而不启动模型轮次。读取通过工作目录服务恢复缺失目录;修改保留起始目录元数据与权限,并通过已记录的用户上下文告知模型。
组装
插件在首次使用时为每个 sessionId 创建一个 agent。已注册的模型适配器优先用于该路由;尚无适配器负责的 deepseek-official 路由会挂载 DeepSeek 适配器,任何其他尚无适配器负责的提供方都会导致初始化失败。初始化成功前,所选适配器会解析确切模型与可选推理强度。
配置
| 字段 | 默认值 | 含义 |
|---|---|---|
maxTokensAsSuccess |
false |
把 max-token 轮次或 subagent 终止报告为成功的 SDK 结果 |
profile 组合拥有每个根 agent 的工具。input、output 与 exit 是仅供测试的运行时传输钩子;生产环境使用进程 stdio 与 process.exit。生成的配置目录是每个受支持字段的穷尽式真源。
stdout 即协议
Stdout 只承载 JSON-RPC 帧,客户端可以逐字节解析;诊断信息应写入 stderr。请勿在组合的插件树中加入 stdout logger。
SDK 客户端可以做什么
initialize 是运行时就绪边界:服务器由 Loader 组合挂载时,会等待当前插件树完成所有加载任务后再响应,因此首次提示词能够看到 MCP 初始工具发现等异步同级能力。握手返回协议稳定标识 deepseek-harness-sdk-runtime。服务器会通过所选适配器校验提供方/模型路由与可选的非空 reasoningEffort,再保存这些值;省略时不会保存推理强度,因此模型保留自身默认值。可选的正整数 maxTokens 会成为每个 SDK 创建的 agent 及其进程内后代的请求输出上限,省略时则应用所选适配器或提供方路由的默认值。JSON-RPC 请求可能并发分派,因此在一次 initialize 成功完成之前,session/prompt 会拒绝;客户端必须等待握手完成后再发送提示词。已接受的提示词会把一条带标识的用户消息排入队列,并立即返回 { messageId };服务器随后把每个持久事实作为 session.event、把整个 agent 生命周期的每次状态转换作为 session.status 流式发出。它不会把某条助手消息或 turn/end 归属于某个提示词,同一会话上的独立请求可以继续排入更多工作。持久化根目录与 persona 来自外围组合。
session/wait 观察已有 SDK 会话,直到受管理后代及其触发的根 Agent 后续轮次完成。它拒绝未知或已释放的根 Agent,不会创建会话,并在后代检查期间根 Agent 持续空闲后响应。收件箱消息处于停放状态的空闲子级继续驻留,但不延迟此响应;后续唤醒输入可使其继续运行。完成前发出的会话通知在 stdio 传输上先于响应到达。Agent 的运行时错误会保留在对应的确切会话中,直到后续提交的 turn/end 记录结果;否则等待请求会失败,即使错误发生在等待请求之前。终止事件已经记录的错误仍通过会话事件表达,后续成功的活动会清除较早的运行时错误。
原生用户问答
已初始化且声明 capabilities.userQuestions 的客户端负责此服务器所拥有的确切根 Agent 的问题。服务器把共享的 user-questions/request 事件转为 user-question/ask RPC,并在工具或审批消费答案前校验。其他 Agent 和未声明此能力的客户端仍使用原生回答方。工具取消和服务器关闭会中止待决请求并通知宿主;连接失败则返回错误。协议定义载荷结构和错误码。
关闭与退出
插件应答 shutdown,刷新响应并 dispose 根上下文,使 SDK 持有的 agent、订阅与持久化达到完全停稳,然后以 0 退出。EOF 与信号退出归 app bin 负责,后者也会 dispose 根上下文。仅卸载此插件会停止服务,但不会退出进程。
理解实现
实现细节——点击展开
本节解释服务插件背后的设计;可观察行为已在使用本包中完整说明。
设计理念
本插件是轻量的展示适配器:HarnessSdkJsonRpcServer 负责协议方法与通知,传输与具名协议类型来自 dsh-sdk-protocol,与客户端 SDK 共享。它订阅会话、agent 与 subagent 生命周期事件,并把它们作为协议通知转发;只有当服务在生命周期建立快照时记录的 local 标志为 true 时才转发 subagent 完成事件——提供方名称、子级 id 与持久化谱系均不能证明本地性。 subagent.started 要求 origin: subagent 和直接创建者 ID;普通 fork(包括保留父 ID 的历史会话)不产生此通知。
源码地图
| 文件 | 职责 |
|---|---|
src/index.ts |
插件入口:Config schema、stdio 接线、请求分发、共享关闭与退出任务 |
src/user-questions.ts |
原生问答适配器:宿主转发、答案校验、取消 |
src/server.ts |
HarnessSdkJsonRpcServer:协议方法、逐会话 agent 创建、生命周期订阅、清理 |
请求流程
每个协议方法在执行前都会校验输入并解析负责该请求的状态——initialize 保存 SDK 路由,session/prompt 解析存活的 agent 与会话配对并排入消息,shutdown 刷新响应,再 dispose 根上下文使其达到完全停稳,最后以 0 退出——共享退出任务确保竞争的 shutdown 请求绝不会重复 dispose 或退出。分发逻辑位于 src/index.ts 与 src/server.ts。
清理
server.shutdown() 关闭其精确根 Agent 下的后代准入,按子级优先顺序排空托管后代,再释放根句柄与适配器。它移除自身订阅,而仅卸载本插件时,无关父级树与外围上下文保持运行。后代排空失败会在其余根句柄与适配器的清理完成后报告。协议 shutdown 则 dispose 根 fiber,使持久化与整个运行时在进程退出前达到完全停稳。
进一步探索
当插件约定不够用时阅读以下页面。它们从协议格式进入客户端与可运行应用。
- SDK 协议格式 — 本插件服务的协议方法与载荷结构。
- TypeScript SDK 客户端 — 驱动本插件的客户端。
- SDK 应用组合包 — 启动本插件的
dsh --profile sdk应用。 - Python SDK — 驱动同一服务器的 Python 客户端。
- SDK 运行时分发决策 — 打包运行时为何服务封闭插件树。
模型体验
SDK 用户消息
模型看到什么
普通 session/query 输入和已接受的 session/prompt 内容按原始内容块顺序进入用户消息。普通输入进入提示队列,不会重新应用权限默认值。内联 SdkEncodedImageBlock 会先通过组合中的附件存储完成校验与提交,因此会话日志保留内容寻址的图片引用而不是 base64 字节。此包不会添加系统提示词文本或工具 schema;这些内容来自组合中的其他插件。
Token 影响
依数据而定的用户消息 token 会进入保留的会话历史,并在后续轮次中重复发送,直至另一个包将其压缩(compaction)。JSON-RPC 帧、会话通知与服务器内部记录不会增加模型上下文 token。
KV Cache 影响
仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
手动压缩
模型看到什么
命令行和结果不进入模型消息。现有compact 命令发起辅助摘要请求,并将选定历史替换为已记录的 checkpoint,不增加普通 turn。
Token 影响
摘要请求成本和后续历史 token 减少由压缩所有者决定,SDK 分帧不增加模型 token。
KV Cache 影响
历史替换按原生压缩器规则改变可复用前缀,SDK 不增加独立失效。
Session 权限变更
模型看到什么
命令文本和结果不进入模型消息。权限预设所有者更新 runtime-context 用户快照,并可能注入审批策略通知。运行中 turn 的通知可能触发额外 step;默认预设不会改变 system 文本或工具 schema。
Token 影响
原生策略快照和通知增加其正常历史 token;SDK 分帧不增加 token。
KV Cache 影响
原生上下文更新追加在保留历史之后,不重写 system 前缀。
计划命令
模型看到什么
原生 plan-mode 指导生效,权限状态保持不变。命令正文通过 steer 成为普通用户消息;裸命令和命令结果文本不进入模型历史。退出工具在首次进入时加入,退出后仍保留声明。批准、反馈和关闭沿用原生工具结果与下一步行为。
Token 影响
计划指导、退出工具声明、提交的计划 Markdown 和审查结果具有各自的原生 token 成本。
KV Cache 影响
原生系统与工具更新决定前缀复用;SDK 不添加额外计划提示词或失效操作。
目标命令
模型看到什么
原生目标驱动器 接纳已记录的 <goal_round> 用户消息。目标工具保留其状态与完成策略,包括 <goal_complete> 或 <goal_blocked> 收尾上下文。命令文本与结果不进入模型历史。
Token 影响
每个接纳的轮次和收尾步骤具有各自的原生模型成本;SDK 路由不增加模型输入。
KV Cache 影响
目标轮次消息和工具结果按原生驱动器的行为追加到保留历史。
已知限制与延期工作
这些限制说明本插件何时需要特别的运维注意。它们是当前包约束,不是与其他服务方式的对比或任务积压。
- 协议没有逐会话关闭或提示词取消方法——SDK 创建的 agent 会一直存活到进程关闭。
- 没有逐提示词结果——
MessageId只标识 inbox 准入;拥有自动化活动区间的客户端必须自行定义并观察该区间。 - stdout 纯净性由部署保证——外围配置仍可能加载 stdout logger 并破坏 JSON-RPC 通道;此插件不会检查或否决同级 logger。
- 自动挂载适配器仅支持 DeepSeek——
initialize可以复用任何预先注册的模型适配器,但唯一的回退行为是挂载 DeepSeek 适配器。
开发备注
维护者的工作上下文——点击展开
本开发备注是维护者的工作上下文,明确不具权威性——已交付的行为与限制见上文各节与代码。单文件可执行运行时分发将本插件与打包的 jsonrpc-demo bin 配对;请让关闭与退出约定与负责 EOF 和信号退出的 app bin 保持一致。没有记录其他未解决的开放设计问题。