@deepseek-ai/dsh-sdk-jsonrpc-server 0.1.1-rc.2 → 0.1.2-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/sdk/server/README.md
5
- README.md: 29ad5840b9d70c9c22ecd387730ba21ce89cbe07
6
- README.zh.md: c5e170beb70e3cd887865d653f49f0810bc07d48
5
+ README.md: d5653543c5ca92f523470fd3b2360497e836f3e5
6
+ README.zh.md: 6c30f274f666b83063df42d2c33253040ff9faf6
package/README.md CHANGED
@@ -1,36 +1,111 @@
1
+ ---
2
+ description: "The stdio JSON-RPC serving plugin for deployments that let out-of-process SDK clients open sessions and drive agents in a DeepSeek Harness runtime."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-sdk-jsonrpc-server
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- The `jsonrpc` plugin serves newline-delimited JSON-RPC over stdio so out-of-process SDK clients can drive harness agents. [`HarnessSdkJsonRpcServer`](src/server.ts) owns the protocol methods and notifications; the transport and the named wire types live in [`dsh-sdk-protocol`](../protocol/README.md), shared with the client SDKs; [`jsonrpc-demo`](../../examples/jsonrpc-demo/README.md) supplies the surrounding `cordis.yml` application.
10
+ ## Summary
11
+
12
+ `dsh-sdk-jsonrpc-server` serves the SDK wire protocol over stdio so out-of-process clients can drive harness agents: it opens one session per `sessionId`, queues user prompts, and streams every session event and agent status transition back to the client. Mount it as the `jsonrpc` plugin in a Loader composition; the surrounding tree supplies everything else — agents, model adapters, persistence, and tools. Stdout carries only JSON-RPC frames, so a deployment must not compose a stdout logger. It answers `shutdown` by disposing the root runtime and exiting 0; the app bin owns EOF and signal exits.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount this plugin when a runtime must serve SDK clients: add it to a `cordis.yml` that composes the agent service, boot the runtime, and clients connect over stdio. The common path is explicit — the plugin needs the `agents` service; every other capability comes from the surrounding tree.
29
+
30
+ ### Wiring
31
+
32
+ The plugin creates one agent per `sessionId` on first use. A registered model adapter wins the route; an unowned `deepseek-official` route mounts the DeepSeek adapter, and any other unowned provider fails initialization. The selected adapter resolves the exact model and optional reasoning effort before initialization succeeds.
33
+
34
+ ### Configuration
35
+
36
+ | Field | Default | Meaning |
37
+ |---|---|---|
38
+ | `maxTokensAsSuccess` | `false` | Report max-token turn/subagent termination as a successful SDK result |
39
+
40
+ The profile composition owns each root agent's tools. `input`, `output`, and `exit` are runtime-only transport hooks for tests; production uses process stdio and `process.exit`. The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-sdk-jsonrpc-server) is the exhaustive source for every accepted field.
41
+
42
+ ### stdout is the protocol
43
+
44
+ Stdout carries only JSON-RPC frames, so clients can parse every byte; diagnostics belong on stderr. Keep stdout loggers out of the composed tree.
45
+
46
+ ### What SDK clients can do
47
+
48
+ `initialize` is the runtime-readiness boundary: when the server is mounted by a Loader composition, it waits for the current plugin tree to settle before replying, so async sibling capabilities such as initial MCP tool discovery are visible to the first prompt. The handshake returns the wire-stable identity `deepseek-harness-sdk-runtime`. The server validates the provider/model route and optional non-empty `reasoningEffort` through the selected adapter before it stores them; omission stores no effort, so the model retains its own default. An optional positive `maxTokens` becomes the request output cap of each SDK-created agent and its in-process descendants, while omission applies the selected adapter or provider route default. JSON-RPC requests may dispatch concurrently, so `session/prompt` rejects until one `initialize` has completed successfully; clients must await the handshake before sending prompts. An accepted prompt queues one identified user message and immediately returns `{ messageId }`; the server then streams every durable fact as `session.event` and every whole-agent lifecycle transition as `session.status`. It does not assign an assistant message or `turn/end` to a prompt, and independent requests may enqueue more work on the same session. Persistence roots and persona come from the surrounding composition.
49
+
50
+ ### Shutdown and exit
6
51
 
7
- ## Wiring
52
+ The plugin answers `shutdown`, flushes the response, disposes the root context so SDK-owned agents, subscriptions, and persistence reach quiescence, then exits 0. EOF and signal exits belong to the app bin, which also disposes the root context. Unloading only this plugin stops serving without exiting the process.
8
53
 
9
- `inject: ['agents']`. The server gets or creates one agent per `sessionId`. It forwards subagent completions only when the service-snapshotted lifecycle `local` flag is true; provider names, child ids, and durable lineage never establish locality. A registered adapter wins, an unowned `deepseek-official` route mounts `dsh-llm-deepseek`, and any other unowned provider fails initialization. Other capabilities come from the surrounding `cordis.yml`.
54
+ -----
10
55
 
11
- ## Config
56
+ <a id="understand-the-implementation"></a>
57
+ ## Understand the implementation
12
58
 
13
- `maxTokensAsSuccess` defaults to `false` and affects only the deployment-mapped status on `subagent.finished`; root-session prompts have no prompt-level status. `JsonRpcConfig.input`, `output`, and `exit` are runtime-only transport hooks; production uses process stdio and `process.exit`.
59
+ <details>
60
+ <summary>Implementation internals — click to expand</summary>
14
61
 
15
- ## stdout is the protocol
62
+ This section explains the design behind the serving plugin; the observable behavior is fully covered in [Use this package](#use-this-package).
16
63
 
17
- Stdout carries only JSON-RPC frames. The deployment must not compose a stdout logger; diagnostics belong on stderr.
64
+ ### Design concept
18
65
 
19
- ## Shutdown and exit semantics
66
+ The plugin is a thin presentation adapter: [`HarnessSdkJsonRpcServer`](src/server.ts) owns the protocol methods and notifications, while the transport and the named wire types come from `dsh-sdk-protocol`, shared with the client SDKs. It subscribes to session, agent, and subagent lifecycle events and forwards them as wire notifications; subagent completions are forwarded only when the service-snapshotted lifecycle `local` flag is true — provider names, child ids, and durable lineage never establish locality.
20
67
 
21
- The plugin answers `shutdown`, flushes the response, disposes the root context so SDK-owned agents, subscriptions, and persistence reach quiescence, then exits with code 0. EOF and signal exits belong to the app bin, which also disposes the root context. Unloading only this plugin stops serving without exiting the process.
68
+ ### Source map
22
69
 
23
- ## Wire notes
70
+ | File | Role |
71
+ |---|---|
72
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `Config` schema, stdio wiring, request dispatch, shared shutdown/exit task |
73
+ | [`src/server.ts`](src/server.ts) | `HarnessSdkJsonRpcServer`: protocol methods, per-session agent creation, lifecycle subscriptions, teardown |
74
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant — boundary and replay tests cover the protocol mapping) |
24
75
 
25
- `initialize` is the runtime-readiness boundary: when the server is mounted by a Loader composition, it waits for the current plugin tree to settle before replying, so async sibling capabilities such as initial MCP tool discovery are visible to the first prompt. Hand-built contexts without Loader remain immediately usable. `initialize.serverInfo.name` is the wire-stable `deepseek-harness-sdk-runtime`. An optional positive `initialize.maxTokens` becomes the request output cap of each SDK-created agent and its in-process descendants; invalid values reject initialization, while omission sends no SDK cap and allows the selected adapter or provider route default to apply. `session/prompt` queues one identified user message and immediately returns `{ messageId }`. The server streams every durable fact as `session.event` and every whole-agent lifecycle transition as `session.status`; it does not assign an assistant message or `turn/end` to that prompt. Independent requests may enqueue more work on the same session. Persistence roots and persona come from `cordis.yml`.
76
+ ### Request flow
26
77
 
78
+ Each protocol method validates its inputs and resolves the owning state before acting — `initialize` stores the SDK route, `session/prompt` resolves the live agent+session pair and queues the message, and `shutdown` disposes server-owned state to quiescence before flushing the response and exiting 0 — and a shared exit task guarantees that racing `shutdown` requests never dispose or exit twice. The dispatch lives in [src/index.ts](src/index.ts) and [src/server.ts](src/server.ts).
79
+
80
+ ### Teardown
81
+
82
+ `server.shutdown()` disposes only what the server owns — the surrounding context stays running when just this plugin is unloaded. Protocol `shutdown` instead disposes the root fiber so persistence and the whole runtime reach quiescence before the process exits.
83
+
84
+ </details>
85
+
86
+ -----
87
+
88
+ <a id="further-exploration"></a>
89
+ ## Further Exploration
90
+
91
+ Read these pages when the plugin contract is not enough. They move from the wire protocol to the clients and the runnable application.
92
+
93
+ - [SDK wire protocol](../protocol/README.md) — the methods and payload shapes this plugin serves.
94
+ - [TypeScript SDK client](../client/README.md) — the client that drives this plugin.
95
+ - [SDK application bundle](../../bundle/sdk-app/README.md) — the `dsh --profile sdk` application that boots this plugin.
96
+ - [Python SDK](../../../python/README.md) — the Python client that drives the same server.
97
+ - [SDK runtime distribution decision](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md) — why the packaged runtime serves a closed plugin tree.
98
+
99
+ -----
100
+
101
+ <a id="model-experience"></a>
27
102
  ## Model Experience
28
103
 
29
104
  ### SDK user message
30
105
 
31
106
  #### What the model sees
32
107
 
33
- For each accepted `session/prompt`, the conversation model receives the caller-supplied `contentBlocks` verbatim as one user message in that SDK session. This package adds no system-prompt prose or tool schema; those come from the plugins in the surrounding `cordis.yml`.
108
+ For each accepted `session/prompt`, text and durable content references enter one user message verbatim. Inline `SdkEncodedImageBlock` values are validated and committed through the composition's attachment store first, so the session log retains content-addressed image references rather than base64 bytes. This package adds no system-prompt prose or tool schema; those come from the other plugins in the composition.
34
109
 
35
110
  #### Token effect
36
111
 
@@ -42,7 +117,22 @@ Append-only; newly visible content follows the reusable request prefix and does
42
117
 
43
118
  ## Known Limitations and Deferred Work
44
119
 
120
+ <a id="known-limitations-and-deferred-work"></a>
121
+
122
+
123
+ These limits define when the plugin needs special operational care. They are current package constraints, not a comparison with other serving approaches or a task backlog.
124
+
45
125
  - **The wire has no per-session close or prompt-cancel method** — SDK-created agents remain live until process shutdown.
46
126
  - **There is no per-prompt result** — `MessageId` identifies inbox admission only; clients that own an automation interval must define and observe that interval themselves.
47
127
  - **stdout purity is deployment-enforced** — a surrounding config can still load a stdout logger and corrupt the JSON-RPC channel; this plugin does not inspect or veto sibling loggers.
48
- - **Automatic adapter mounting is DeepSeek-specific** — `initialize` can reuse any pre-registered model adapter, but its only fallback mounts `dsh-llm-deepseek`.
128
+ - **Automatic adapter mounting is DeepSeek-specific** — `initialize` can reuse any pre-registered model adapter, but its only fallback mounts the DeepSeek adapter.
129
+
130
+ <a id="dev-note"></a>
131
+ ### Dev Note
132
+
133
+ <details>
134
+ <summary>Working context for maintainers — click to expand</summary>
135
+
136
+ This Dev Note is working context for maintainers and is explicitly non-authoritative — shipped behavior and limits live in the sections above and in the code. The single-executable runtime distribution pairs this plugin with the packaged `jsonrpc-demo` bin; keep the shutdown/exit contract consistent with the app bin, which owns EOF and signal exits. No other unresolved design questions are recorded.
137
+
138
+ </details>
package/README.zh.md CHANGED
@@ -1,48 +1,138 @@
1
+ ---
2
+ description: "面向让进程外 SDK 客户端在 DeepSeek Harness 运行时中打开会话并驱动 agent 的部署的 stdio JSON-RPC 服务插件。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-sdk-jsonrpc-server
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- `jsonrpc` 插件通过 stdio 提供以换行符分隔的 JSON-RPC,使进程外 SDK 客户端能够驱动 harness agent(智能体)。[`HarnessSdkJsonRpcServer`](src/server.ts) 负责协议方法和通知;传输与具名协议类型位于 [`dsh-sdk-protocol`](../protocol/README.zh.md),与客户端 SDK 共享;[`jsonrpc-demo`](../../examples/jsonrpc-demo/README.zh.md) 提供外围的 `cordis.yml` 应用。
10
+ ## 概述
11
+
12
+ `dsh-sdk-jsonrpc-server` 通过 stdio 服务 SDK 协议格式,使进程外客户端能够驱动 harness agent(智能体):它为每个 `sessionId` 打开一个会话、把用户提示词排入队列,并把每个会话事件与 agent 状态转换实时流回客户端。把它作为 `jsonrpc` 插件挂载到 Loader 组合中;外围插件树提供其余一切——agent、模型适配器、持久化与工具。Stdout 只承载 JSON-RPC 帧,因此部署不得组合 stdout logger。它通过 dispose(资源释放)根运行时并以 0 退出应答 `shutdown`;EOF 与信号退出归 app bin 负责。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 当运行时必须服务 SDK 客户端时挂载本插件:把它加入组合了 agent 服务的 `cordis.yml`,启动运行时,客户端即可通过 stdio 连接。常用路径是显式的——插件需要 `agents` 服务;其余每个能力都来自外围插件树。
29
+
30
+ ### 组装
31
+
32
+ 插件在首次使用时为每个 `sessionId` 创建一个 agent。已注册的模型适配器赢得路由;尚无适配器负责的 `deepseek-official` 路由会挂载 DeepSeek 适配器,任何其他尚无适配器负责的提供方都会导致初始化失败。初始化成功前,所选适配器会解析确切模型与可选推理强度。
33
+
34
+ ### 配置
35
+
36
+ | 字段 | 默认值 | 含义 |
37
+ |---|---|---|
38
+ | `maxTokensAsSuccess` | `false` | 把 max-token 轮次/subagent 终止报告为成功的 SDK 结果 |
39
+
40
+ profile 组合拥有每个根 agent 的工具。`input`、`output` 与 `exit` 是仅供测试的运行时传输钩子;生产环境使用进程 stdio 与 `process.exit`。生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-sdk-jsonrpc-server)是每个受支持字段的穷尽式真源。
41
+
42
+ ### stdout 即协议
43
+
44
+ Stdout 只承载 JSON-RPC 帧,客户端可以逐字节解析;诊断信息应写入 stderr。请勿在组合的插件树中加入 stdout logger。
45
+
46
+ ### SDK 客户端可以做什么
47
+
48
+ `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 来自外围组合。
49
+
50
+ ### 关闭与退出
6
51
 
7
- ## 组装
52
+ 插件应答 `shutdown`,刷新响应并 dispose 根上下文,使 SDK 持有的 agent、订阅与持久化达到完全停稳,然后以 0 退出。EOF 与信号退出归 app bin 负责,后者也会 dispose 根上下文。仅卸载此插件会停止服务,但不会退出进程。
8
53
 
9
- `inject: ['agents']`。服务器按 `sessionId` 获取或创建一个 agent。只有服务对生命周期建立快照时记录的 `local` 标志为 true,服务器才会转发 subagent 完成事件;提供方名称、子级 id 和持久化谱系均不能证明本地性。已注册的适配器优先;尚无适配器负责的 `deepseek-official` 路由会挂载 `dsh-llm-deepseek`,任何其他尚无适配器负责的提供方都会导致初始化失败。其他能力由外围 `cordis.yml` 提供。
54
+ -----
10
55
 
11
- ## 配置
56
+ <a id="understand-the-implementation"></a>
57
+ ## 理解实现
12
58
 
13
- `maxTokensAsSuccess` 默认为 `false`,且只影响 `subagent.finished` 上由部署映射的状态;根会话提示词没有提示词级状态。`JsonRpcConfig.input`、`output` 和 `exit` 是仅供运行时使用的传输钩子;生产环境使用进程 stdio 和 `process.exit`。
59
+ <details>
60
+ <summary>实现细节——点击展开</summary>
14
61
 
15
- ## stdout 即协议
62
+ 本节解释服务插件背后的设计;可观察行为已在[使用本包](#use-this-package)中完整说明。
16
63
 
17
- Stdout 只承载 JSON-RPC 帧。部署不得组合 stdout logger;诊断应写入 stderr。
64
+ ### 设计理念
18
65
 
19
- ## 关闭与退出语义
66
+ 本插件是薄薄的展示适配器:[`HarnessSdkJsonRpcServer`](src/server.ts) 负责协议方法与通知,传输与具名协议类型来自 `dsh-sdk-protocol`,与客户端 SDK 共享。它订阅会话、agent 与 subagent 生命周期事件,并把它们作为协议通知转发;只有当服务在生命周期建立快照时记录的 `local` 标志为 true 时才转发 subagent 完成事件——提供方名称、子级 id 与持久化谱系均不能证明本地性。
20
67
 
21
- 插件响应 `shutdown`,刷新响应并 dispose(资源释放)根上下文,使 SDK 持有的 agent、订阅和持久化达到完全停稳,然后以代码 0 退出。EOF 和信号退出由 app bin 处理,后者也会 dispose 根上下文。仅卸载此插件会停止服务,但不会退出进程。
68
+ ### 源码地图
22
69
 
23
- ## 协议说明
70
+ | 文件 | 职责 |
71
+ |---|---|
72
+ | [`src/index.ts`](src/index.ts) | 插件入口:`Config` schema、stdio 接线、请求分发、共享关闭/退出任务 |
73
+ | [`src/server.ts`](src/server.ts) | `HarnessSdkJsonRpcServer`:协议方法、逐会话 agent 创建、生命周期订阅、清理 |
74
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式配套插件(无运行时不变式——边界与回放测试覆盖协议映射) |
24
75
 
25
- `initialize` 是运行时就绪边界:服务器由 Loader 组合挂载时,会等待当前插件树完成所有加载任务后再响应,因此首次提示词能够看到 MCP 初始工具发现等异步同级能力。没有 Loader 的手工组装上下文仍可立即使用。`initialize.serverInfo.name` 的协议稳定值为 `deepseek-harness-sdk-runtime`。可选的正整数 `initialize.maxTokens` 会成为每个 SDK 创建的 agent 及其进程内后代的请求输出上限;非法值会使初始化失败,省略时则不发送 SDK 上限,并应用所选适配器或提供方路由的默认值。`session/prompt` 将一条带标识的用户消息排入队列,并立即返回 `{ messageId }`。服务器将每个持久事实作为 `session.event` 流式发出,并将整个 agent 生命周期的每次状态转换作为 `session.status` 发出;它不会把某条助手消息或 `turn/end` 归属于该提示词。同一会话上的独立请求可以继续排入更多工作。持久化根目录和 persona 由 `cordis.yml` 提供。
76
+ ### 请求流程
26
77
 
78
+ 每个协议方法在行动前都会校验输入并解析其拥有的状态——`initialize` 保存 SDK 路由,`session/prompt` 解析存活的 agent+会话对并排入消息,`shutdown` 在刷新响应并以 0 退出前把服务器持有的状态 dispose 到完全停稳——共享退出任务确保竞争的 shutdown 请求绝不会重复 dispose 或退出。分发逻辑位于 [src/index.ts](src/index.ts) 与 [src/server.ts](src/server.ts)。
79
+
80
+ ### 清理
81
+
82
+ `server.shutdown()` 只 dispose 服务器自身持有的内容——仅卸载本插件时,外围上下文保持运行。协议 `shutdown` 则 dispose 根 fiber,使持久化与整个运行时在进程退出前达到完全停稳。
83
+
84
+ </details>
85
+
86
+ -----
87
+
88
+ <a id="further-exploration"></a>
89
+ ## 进一步探索
90
+
91
+ 当插件约定不够用时阅读以下页面。它们从协议格式进入客户端与可运行应用。
92
+
93
+ - [SDK 协议格式](../protocol/README.zh.md) — 本插件服务的协议方法与载荷结构。
94
+ - [TypeScript SDK 客户端](../client/README.zh.md) — 驱动本插件的客户端。
95
+ - [SDK 应用组合包](../../bundle/sdk-app/README.zh.md) — 启动本插件的 `dsh --profile sdk` 应用。
96
+ - [Python SDK](../../../python/README.zh.md) — 驱动同一服务器的 Python 客户端。
97
+ - [SDK 运行时分发决策](../../../.agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.zh.md) — 打包运行时为何服务封闭插件树。
98
+
99
+ -----
100
+
101
+ <a id="model-experience"></a>
27
102
  ## 模型体验
28
103
 
29
104
  ### SDK 用户消息
30
105
 
31
- #### 模型看到的内容
106
+ #### 模型看到什么
32
107
 
33
- 对于每个已接受的 `session/prompt`,对话模型会将调用方提供的 `contentBlocks` 原样作为该 SDK 会话中的一条用户消息接收。此包不会添加系统提示词文本或工具 schema;这些内容来自外围 `cordis.yml` 中的插件。
108
+ 对于每个已接受的 `session/prompt`,文本和持久内容引用会原样进入一条用户消息。内联 `SdkEncodedImageBlock` 会先通过组合中的附件存储完成校验与提交,因此会话日志保留内容寻址的图片引用而不是 base64 字节。此包不会添加系统提示词文本或工具 schema;这些内容来自组合中的其他插件。
34
109
 
35
110
  #### Token 影响
36
111
 
37
- 依数据而定的用户消息 token 会进入保留的会话历史,并在后续轮次中重复发送,直至另一个包将其压缩(compaction)。JSON-RPC 帧、会话通知和服务器内部记录不会增加模型上下文 token。
112
+ 依数据而定的用户消息 token 会进入保留的会话历史,并在后续轮次中重复发送,直至另一个包将其压缩(compaction)。JSON-RPC 帧、会话通知与服务器内部记录不会增加模型上下文 token。
38
113
 
39
114
  #### KV Cache 影响
40
115
 
41
116
  仅追加;新可见内容位于可复用请求前缀之后,不会使现有 KV Cache 条目失效。
42
117
 
43
- ## 已知限制与暂缓事项
118
+ ## 已知限制与延期工作
119
+
120
+ <a id="known-limitations-and-deferred-work"></a>
121
+
122
+
123
+ 这些限制说明本插件何时需要特别的运维注意。它们是当前包约束,不是与其他服务方式的对比或任务积压。
124
+
125
+ - **协议没有逐会话关闭或提示词取消方法**——SDK 创建的 agent 会一直存活到进程关闭。
126
+ - **没有逐提示词结果**——`MessageId` 只标识 inbox 准入;拥有自动化活动区间的客户端必须自行定义并观察该区间。
127
+ - **stdout 纯净性由部署保证**——外围配置仍可能加载 stdout logger 并破坏 JSON-RPC 通道;此插件不会检查或否决同级 logger。
128
+ - **自动挂载适配器仅支持 DeepSeek**——`initialize` 可以复用任何预先注册的模型适配器,但唯一的回退行为是挂载 DeepSeek 适配器。
129
+
130
+ <a id="dev-note"></a>
131
+ ### 开发备注
132
+
133
+ <details>
134
+ <summary>维护者的工作上下文——点击展开</summary>
135
+
136
+ 本开发备注是维护者的工作上下文,明确不具权威性——已交付的行为与限制见上文各节与代码。单文件可执行运行时分发将本插件与打包的 `jsonrpc-demo` bin 配对;请让关闭/退出约定与负责 EOF 和信号退出的 app bin 保持一致。没有记录其他未解决的开放设计问题。
44
137
 
45
- - **协议没有逐会话关闭或提示词取消方法**:SDK 创建的 agent 会一直存活到进程关闭。
46
- - **没有逐提示词结果**:`MessageId` 只标识 inbox 准入;拥有自动化活动区间的客户端必须自行定义并观察该区间。
47
- - **stdout 纯净性由部署保证**:外围配置仍可能加载 stdout logger 并破坏 JSON-RPC 通道;此插件不会检查或否决同级 logger。
48
- - **自动挂载适配器仅支持 DeepSeek**:`initialize` 可以复用任何预先注册的模型适配器,但唯一的回退行为是挂载 `dsh-llm-deepseek`。
138
+ </details>
package/lib/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import Schema from "@deepseek-ai/schemastery";
2
2
  import { JsonRpcLineTransport } from "@deepseek-ai/dsh-sdk-protocol";
3
3
  import { resolve } from "node:path";
4
- import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
+ import { brandString } from "@deepseek-ai/dsh-brand";
5
+ import { admitEncodedImages } from "@deepseek-ai/dsh-attachment";
6
+ import { ReasoningEffortId, createUserMessage } from "@deepseek-ai/dsh-llm";
5
7
  import { carrierKeyOf } from "@deepseek-ai/dsh-scope";
6
- import { SessionId } from "@deepseek-ai/dsh-session";
7
8
  import * as LlmDeepSeek from "@deepseek-ai/dsh-llm-deepseek";
8
9
  //#region lib/types/server.js
9
10
  /**
@@ -12,6 +13,24 @@ import * as LlmDeepSeek from "@deepseek-ai/dsh-llm-deepseek";
12
13
  *
13
14
  * @module @deepseek-ai/dsh-sdk-jsonrpc-server/server
14
15
  */
16
+ function encodedImage(block) {
17
+ return block.type === "image" && "data" in block;
18
+ }
19
+ async function durablePromptContent(ctx, blocks) {
20
+ const images = blocks.filter(encodedImage);
21
+ if (images.length === 0) return blocks;
22
+ const attachments = ctx.get("attachments");
23
+ if (attachments === void 0) throw new Error("SDK image prompt requires an attachment store");
24
+ const refs = await admitEncodedImages(attachments, images.map((image) => ({
25
+ data: image.data,
26
+ mediaType: image.mimeType
27
+ })));
28
+ let next = 0;
29
+ return blocks.map((block) => encodedImage(block) ? {
30
+ type: "image",
31
+ attachment: refs[next++]
32
+ } : block);
33
+ }
15
34
  /** Recover the delegating parent from the service-owned scoped carrier. */
16
35
  function subagentParentOf(carrier) {
17
36
  return carrierKeyOf(carrier);
@@ -32,6 +51,7 @@ var HarnessSdkJsonRpcServer = class {
32
51
  cwd = process.cwd();
33
52
  provider = "deepseek-official";
34
53
  model = "deepseek-official";
54
+ reasoningEffort;
35
55
  maxTokens;
36
56
  llmFiber;
37
57
  sessions = /* @__PURE__ */ new Map();
@@ -39,6 +59,7 @@ var HarnessSdkJsonRpcServer = class {
39
59
  disposers = [];
40
60
  shutdownTask;
41
61
  shuttingDown = false;
62
+ initialized = false;
42
63
  constructor(ctx, transport, options = {}) {
43
64
  this.ctx = ctx;
44
65
  this.transport = transport;
@@ -82,20 +103,33 @@ var HarnessSdkJsonRpcServer = class {
82
103
  }));
83
104
  }
84
105
  /**
85
- * Configure the SDK route, mounting the DeepSeek fallback only when unowned.
106
+ * Validate and configure the SDK route, mounting the DeepSeek fallback only when unowned.
86
107
  * @param params - SDK handshake parameters.
87
108
  * @returns server identity for the handshake.
88
109
  */
89
110
  async initialize(params) {
111
+ if (params.reasoningEffort !== void 0 && (typeof params.reasoningEffort !== "string" || params.reasoningEffort.length === 0)) throw new TypeError("initialize reasoningEffort must be a non-empty string");
90
112
  if (params.maxTokens !== void 0 && (!Number.isSafeInteger(params.maxTokens) || params.maxTokens <= 0)) throw new TypeError("initialize maxTokens must be a positive safe integer");
91
- this.cwd = resolve(params.cwd);
92
- this.provider = params.provider;
93
- this.model = params.model;
94
- this.maxTokens = params.maxTokens;
95
- if (!this.hasAdapterFor(this.provider)) {
96
- if (this.provider !== "deepseek-official") throw new Error(`no adapter registered for provider "${this.provider}"`);
113
+ const cwd = resolve(params.cwd);
114
+ const provider = params.provider;
115
+ const model = params.model;
116
+ const reasoningEffort = params.reasoningEffort === void 0 ? void 0 : ReasoningEffortId(params.reasoningEffort);
117
+ if (!this.hasAdapterFor(provider)) {
118
+ if (provider !== "deepseek-official") throw new Error(`no adapter registered for provider "${provider}"`);
97
119
  this.llmFiber = await this.ctx.plugin(LlmDeepSeek, {});
98
120
  }
121
+ await this.ctx.get("llm").resolveCallConfig({
122
+ provider,
123
+ model,
124
+ ...reasoningEffort === void 0 ? {} : { reasoningEffort },
125
+ ...params.maxTokens === void 0 ? {} : { maxTokens: params.maxTokens }
126
+ });
127
+ this.cwd = cwd;
128
+ this.provider = provider;
129
+ this.model = model;
130
+ this.reasoningEffort = reasoningEffort;
131
+ this.maxTokens = params.maxTokens;
132
+ this.initialized = true;
99
133
  return { serverInfo: {
100
134
  name: "deepseek-harness-sdk-runtime",
101
135
  version: "0.0.1"
@@ -107,15 +141,21 @@ var HarnessSdkJsonRpcServer = class {
107
141
  * @returns the durable message identity.
108
142
  */
109
143
  async prompt(params) {
144
+ if (!this.initialized) throw new Error("SDK server is not initialized");
110
145
  const rec = await this.getOrCreateSession(params.sessionId);
111
- if (this.ctx.agents.get(rec.handle.agent.id) !== rec.handle.agent) throw new Error(`session agent was disposed outside the server: ${params.sessionId}`);
146
+ this.assertLiveAgent(rec, params.sessionId);
147
+ const content = await durablePromptContent(this.ctx, params.contentBlocks);
148
+ this.assertLiveAgent(rec, params.sessionId);
112
149
  const message = createUserMessage({
113
- content: params.contentBlocks,
150
+ content,
114
151
  source: { kind: "user" }
115
152
  });
116
153
  rec.handle.agent.followup(message);
117
154
  return { messageId: message.id };
118
155
  }
156
+ assertLiveAgent(rec, sessionId) {
157
+ if (this.ctx.agents.get(rec.handle.agent.id) !== rec.handle.agent) throw new Error(`session agent was disposed outside the server: ${sessionId}`);
158
+ }
119
159
  /**
120
160
  * Dispose server-owned agents, adapter, and subscriptions to quiescence.
121
161
  * The surrounding context remains running.
@@ -177,11 +217,12 @@ var HarnessSdkJsonRpcServer = class {
177
217
  }
178
218
  async createSession(sessionId) {
179
219
  const rec = { handle: await this.ctx.agents.create({
180
- sessionId: SessionId(sessionId),
220
+ sessionId: brandString(sessionId),
181
221
  meta: { cwd: this.cwd },
182
222
  agentOptions: {
183
223
  provider: this.provider,
184
224
  model: this.model,
225
+ ...this.reasoningEffort === void 0 ? {} : { reasoningEffort: this.reasoningEffort },
185
226
  ...this.maxTokens === void 0 ? {} : { maxTokens: this.maxTokens }
186
227
  }
187
228
  }) };
@@ -195,8 +236,8 @@ var HarnessSdkJsonRpcServer = class {
195
236
  //#endregion
196
237
  //#region lib/types/index.js
197
238
  /**
198
- * SDK-facing JSON-RPC plugin over stdio. An external `cordis.yml` decides
199
- * whether to load it; see the single-executable Agent Note and package README.
239
+ * SDK-facing JSON-RPC plugin over stdio. The selected dsh profile decides
240
+ * whether to load it; see the single-launch Agent Note and package README.
200
241
  * Stdout is reserved for protocol frames, so the tree must not load a stdout logger.
201
242
  * This plugin answers `shutdown`, disposes the complete root runtime, and exits 0; the app bin
202
243
  * owns EOF and signal exits. Keep named plugin exports with no default export so
@@ -1,6 +1,6 @@
1
1
  /**
2
- * SDK-facing JSON-RPC plugin over stdio. An external `cordis.yml` decides
3
- * whether to load it; see the single-executable Agent Note and package README.
2
+ * SDK-facing JSON-RPC plugin over stdio. The selected dsh profile decides
3
+ * whether to load it; see the single-launch Agent Note and package README.
4
4
  * Stdout is reserved for protocol frames, so the tree must not load a stdout logger.
5
5
  * This plugin answers `shutdown`, disposes the complete root runtime, and exits 0; the app bin
6
6
  * owns EOF and signal exits. Keep named plugin exports with no default export so
@@ -23,6 +23,7 @@ export declare class HarnessSdkJsonRpcServer {
23
23
  private cwd;
24
24
  private provider;
25
25
  private model;
26
+ private reasoningEffort;
26
27
  private maxTokens;
27
28
  private llmFiber;
28
29
  private readonly sessions;
@@ -30,9 +31,10 @@ export declare class HarnessSdkJsonRpcServer {
30
31
  private readonly disposers;
31
32
  private shutdownTask;
32
33
  private shuttingDown;
34
+ private initialized;
33
35
  constructor(ctx: Context, transport: JsonRpcTransportPeer, options?: HarnessSdkJsonRpcServerOptions);
34
36
  /**
35
- * Configure the SDK route, mounting the DeepSeek fallback only when unowned.
37
+ * Validate and configure the SDK route, mounting the DeepSeek fallback only when unowned.
36
38
  * @param params - SDK handshake parameters.
37
39
  * @returns server identity for the handshake.
38
40
  */
@@ -43,6 +45,7 @@ export declare class HarnessSdkJsonRpcServer {
43
45
  * @returns the durable message identity.
44
46
  */
45
47
  prompt(params: SessionPromptParams): Promise<SessionPromptResult>;
48
+ private assertLiveAgent;
46
49
  /**
47
50
  * Dispose server-owned agents, adapter, and subscriptions to quiescence.
48
51
  * The surrounding context remains running.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-sdk-jsonrpc-server",
3
3
  "description": "Stdio JSON-RPC server plugin for out-of-process DeepSeek Harness SDK clients",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,31 +32,35 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "@deepseek-ai/schemastery": "^3.18.1"
35
+ "@deepseek-ai/dsh-brand": "^0.1.2-alpha.2",
36
+ "@deepseek-ai/schemastery": "^3.18.2"
36
37
  },
37
38
  "peerDependencies": {
38
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
39
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
40
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
41
- "@deepseek-ai/dsh-llm-deepseek": "^0.1.1-rc.2",
42
- "@deepseek-ai/dsh-scope": "^0.1.1-rc.2",
43
- "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
44
- "@deepseek-ai/dsh-sdk-protocol": "^0.1.1-rc.2",
45
- "@deepseek-ai/dsh-subagent": "^0.1.1-rc.2",
46
- "@deepseek-ai/cordis": "^4.0.1"
39
+ "@deepseek-ai/cordis": "^4.0.2",
40
+ "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.2",
41
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
42
+ "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.2",
43
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
44
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
45
+ "@deepseek-ai/dsh-scope": "^0.1.2-alpha.2",
46
+ "@deepseek-ai/dsh-sdk-protocol": "^0.1.2-alpha.2",
47
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
48
+ "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2"
47
49
  },
48
50
  "devDependencies": {
49
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
50
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
51
- "@deepseek-ai/dsh-agent-spine-demo": "^0.1.1-rc.2",
52
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
53
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
54
- "@deepseek-ai/dsh-sdk-protocol": "^0.1.1-rc.2",
55
- "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
56
- "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.1-rc.2",
57
- "@deepseek-ai/dsh-subagent": "^0.1.1-rc.2",
58
- "@deepseek-ai/cordis": "^4.0.1",
59
- "@deepseek-ai/dsh-llm-deepseek": "^0.1.1-rc.2",
60
- "@deepseek-ai/dsh-scope": "^0.1.1-rc.2"
51
+ "@deepseek-ai/cordis": "^4.0.2",
52
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
53
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
54
+ "@deepseek-ai/dsh-agent-spine-demo": "^0.1.2-alpha.2",
55
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
56
+ "@deepseek-ai/dsh-attachment": "^0.1.2-alpha.2",
57
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
58
+ "@deepseek-ai/dsh-llm-deepseek": "^0.1.2-alpha.2",
59
+ "@deepseek-ai/dsh-scope": "^0.1.2-alpha.2",
60
+ "@deepseek-ai/dsh-sdk-protocol": "^0.1.2-alpha.2",
61
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
62
+ "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.2",
63
+ "@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2",
64
+ "@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2"
61
65
  }
62
66
  }