@deepseek-ai/dsh-llm 0.2.1-alpha.1 → 0.2.1-alpha.3

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
@@ -21,11 +21,11 @@
21
21
  en: c3ecb5a1421f27db
22
22
  zh: 4bbd3c0e513845e1
23
23
  /deepseek-ai-dsh-llm/use-this-package/minimal-composition:
24
- en: 7550d716b90fcb09
25
- zh: 77891a44ee623c7a
24
+ en: 9d481d19dc52e404
25
+ zh: aa09cf4462e93ba6
26
26
  /deepseek-ai-dsh-llm/use-this-package/what-you-can-do:
27
- en: b45d7a414141734e
28
- zh: a641c8ea3827cee6
27
+ en: dc61eb86585f6e0d
28
+ zh: 9c0d6b240dfd2cda
29
29
  /deepseek-ai-dsh-llm/use-this-package/failures-and-recovery:
30
30
  en: 311d8c92447b7094
31
31
  zh: 68cf5cb1c3853cc1
@@ -39,8 +39,8 @@
39
39
  en: ae1618577f4e8364
40
40
  zh: a6e8ab1563c08dfb
41
41
  /deepseek-ai-dsh-llm/understand-the-implementation/main-flow:
42
- en: da12553c289e1705
43
- zh: 28827a81cd63ff84
42
+ en: c95d4947745e39bb
43
+ zh: ec37b969f27629e2
44
44
  /deepseek-ai-dsh-llm/understand-the-implementation/invariants:
45
45
  en: 0163a050239248e5
46
46
  zh: 885c95bedcbe2ff4
package/README.md CHANGED
@@ -60,11 +60,30 @@ After a successful mount, `ctx.llm.listProviders()` reports the registered route
60
60
 
61
61
  `GenerateOptions.messages` accepts durable `Message` values and request-only `RequestUserInput` values. Request-only inputs carry user-role content with no `id` or `source`; Session writes and Agent delivery still require durable messages. Callers keep auxiliary inputs unchanged until the stream settles. A caller that records its exact request, such as session-title generation, must use durable messages.
62
62
 
63
+ `prepareCall(config, signal, configure?)` accepts concrete `LlmCallConfig` values. The optional synchronous, pure `ConfigureCall` function receives detached, deeply frozen `LlmCallControls` and model metadata from the captured adapter generation. Callers can compose ordinary functions to choose concrete controls; later writes replace earlier ones. Preparation retains the captured route, applies defaults to omitted controls, validates the result, and returns a frozen configuration for recording and dispatch. Callback failures or cancellation reject before dispatch. Preparation requires a registered adapter.
64
+
65
+ Configuration functions select controls before adapter defaults are materialized. For example, combine least-effort selection with an explicit output cap, then pass `configure(maxTokens)` as the third argument:
66
+
67
+ ```ts
68
+ import type { ConfigureCall } from '@deepseek-ai/dsh-llm'
69
+
70
+ const leastReasoning: ConfigureCall = (controls, model) => {
71
+ const first = model.reasoning?.efforts[0]
72
+ return first === undefined ? controls : { ...controls, reasoningEffort: first.id }
73
+ }
74
+
75
+ const outputLimit = (maxTokens: number): ConfigureCall =>
76
+ controls => ({ ...controls, maxTokens })
77
+
78
+ const configure = (maxTokens: number): ConfigureCall =>
79
+ (controls, model) => outputLimit(maxTokens)(leastReasoning(controls, model), model)
80
+ ```
81
+
63
82
  ### What you can do
64
83
 
65
84
  - **Stream one model call** — `ctx.llm.stream(options)` yields raw chunks (token-level deltas) for any registered provider and model; consumers assemble them with `BlockAssembler`.
66
85
  - **Register provider adapters** — an adapter owns one or more provider routes, and its registration captures that route's retry policy; registering the same route twice fails with `DUPLICATE_ADAPTER`.
67
- - **Expose and activate providers through configuration** — adapters declare configurable-provider routes plus a settings namespace, so configuration surfaces can activate dormant providers and edit connection facts without a restart. `LlmConfigurableProvider.error` reports a configuration diagnostic for repair; unaffected models can remain serviceable.
86
+ - **Expose and activate providers through configuration** — adapters declare configurable-provider routes plus a settings namespace, so configuration surfaces can activate dormant providers and edit connection facts without a restart. `LlmConfigurableProvider.error` reports a configuration diagnostic for repair; unaffected models can remain serviceable. An entry can also carry `defaultBaseURL`, the endpoint the adapter uses when the profile sets none, and `nativeAuth`, the environment variables and OAuth sign-in that the adapter's authentication reads when the profile names no credential reference; `baseURLEnv` lists environment variables used to configure the endpoint. Configuration UIs read these fields from the directory without calling the adapter.
68
87
  - **Discover and resolve models** — List the models an adapter advertises, interrogate an endpoint for the models it serves (candidates carry optional `inputModalities`; absence means unknown), and resolve one exact model's context window, output default, reasoning efforts, input modalities, and system prompt update mode: `LlmResolvedModelInfo.systemPromptUpdate` is `'in-history'` when the model reads the latest `system` message at any position as the effective system prompt and absent when only a leading system message is read; `normalizeModelInfo` rejects any other value with `INVALID_MODEL_INFO`.
69
88
  - **Validate call config** — an explicit or configured reasoning effort is checked against the exact model before any provider I/O, and an adapter-configured output cap is materialized when the request omits one.
70
89
  - **Read an embedded Assistant stream without expanding it** — `assistantStreamFirstTokenTime` (first token), `assistantStreamHasVisibleContent` (any visible content), and `assistantStreamHasVisibleText` (any visible text) answer their questions from the compact records with early exit; `lastAssistantStreamChunk` scans backward to the last raw chunk of one type, `assistantStreamChunks` and `joinAssistantStreamText` scan the whole stream, and `assembleAssistantStream` feeds a `BlockAssembler` one joined delta per run with the same blocks, usage, and replay state as the per-member expansion. `runFirstTokenTime` and `runFirstVisibleTime` do the early-exit scan for one packed run, and `isTokenDelta`, `isVisibleChunk`, and `chunkHasVisibleText` define the token and visibility rules for a single chunk. `expandAssistantStream` remains the validating path for records read at a durable boundary; it is not memoized, because a retained expansion costs roughly ten times the compact stream for as long as the event lives.
@@ -105,7 +124,7 @@ The service is built on one separation: **the logical contract is provider-neutr
105
124
 
106
125
  ### Main flow
107
126
 
108
- A request is validated against its exact model's capability — context window, output default, reasoning efforts, input modalities, and `systemPromptUpdate` mode — and any adapter-configured defaults are materialized. The runtime preserves freezing for already-frozen input; hand-built callers own input immutability. `prepareCall()` binds those facts, detached context, and retry policy to the exact adapter generation that performs terminal dispatch, so HMR or dynamic settings cannot combine one generation's image capability with another generation's endpoint. An image-capable adapter projects durable references into route-specific request versions; `resolveImageAttachmentAccess()` separately maps an attachment provider's optional host object into the current tool execution world without changing the request image or its `variantId`. A text-only route receives deterministic per-image placeholders, including tool-role result images, without rewriting append-only session history. Durable `FileBlock` references never reach any adapter: request assembly replaces each one, including tool-role result occurrences, with deterministic handle text naming the file and its saved read-only path, resolved through the mounted attachment and filesystem providers. `ctx.llm.fileRequestText(ref)` exposes that exact synchronous projection to request measurement. An image occurrence derived with `offloaded: true` reaches every route as placeholder text through `projectOffloadedImages()`. An image-capable route whose retained occurrences exceed its `LlmImageRequestBudget` at their exact bytes fails with `IMAGE_OFFLOAD_REQUIRED` naming the additional oldest occurrences (`requiredImageOffload()`), never with an unlogged projection; `dsh-compaction-image-offload` logs the selected occurrences in one `image/offload` event and retries. Adapters that charge visual tokens declare per-route `imageRequestPricing`, which `ctx.llm.imageRequestPricing(provider, model)` resolves synchronously for the token meter. Dispatch goes through the `llm/stream` waterfall, then chunks return as token-level deltas and every adapter outcome reaches the consumer as one terminal `finish` chunk.
127
+ A request is validated against its exact model's capability — context window, output default, reasoning efforts, input modalities, and `systemPromptUpdate` mode — and any adapter-configured defaults are materialized. The runtime preserves freezing for already-frozen input; hand-built callers own input immutability. `prepareCall()` binds those facts, detached context, and retry policy to the exact adapter generation that performs terminal dispatch, so HMR or dynamic settings cannot combine one generation's image capability with another generation's endpoint. An image-capable adapter projects durable references into route-specific request versions; `resolveImageAttachmentAccess()` separately maps an attachment provider's optional host object into the current tool execution world without changing the request image or its `variantId`. A text-only route receives deterministic per-image placeholders, including tool-role result images, without rewriting append-only session history. Durable `FileBlock` references never reach any adapter: request assembly replaces each one, including tool-role result occurrences, with deterministic handle text naming the file and its saved read-only path, resolved through the mounted attachment and filesystem providers. `ctx.llm.fileRequestText(ref)` exposes that exact synchronous projection to request measurement. An image occurrence derived with `offloaded: true` reaches every route as placeholder text through `projectOffloadedImages()`. An image-capable route whose retained occurrences exceed its `LlmImageRequestBudget` at their exact bytes fails with `IMAGE_OFFLOAD_REQUIRED` naming the additional oldest occurrences (`requiredImageOffload()`), never with an unlogged projection; `dsh-compaction-image-offload` logs the selected occurrences in one `image/offload` event and retries. Adapters that charge visual tokens declare per-route `imageRequestPricing`, which `ctx.llm.imageRequestPricing(provider, model)` resolves synchronously for the token meter. Dispatch goes through the `llm/stream` waterfall, then chunks return as token-level deltas and every adapter outcome reaches the consumer as one terminal `finish` chunk. `imageRequestTarget(provider, model, image)` exposes the same request-image dimensions and byte target to screenshot consumers; adapters without image resizing preserve the normalized source.
109
128
 
110
129
  File detection reads current content, including tool-role result content, on every request without caching message identities or freeze state. The [archived file-scan decision](../../../.agents/notes/archived/simplification/2026-09-07-file-content-scan.md) records the measured traversal cost.
111
130
 
package/README.zh.md CHANGED
@@ -60,11 +60,30 @@ for await (const chunk of ctx.llm.stream({
60
60
 
61
61
  `GenerateOptions.messages` 接受持久 `Message` 值和仅供请求使用的 `RequestUserInput` 值。仅供请求使用的输入包含 user-role 内容,不含 `id` 或 `source`;Session 写入和 Agent 投递仍然要求持久消息。调用方必须在流结束前保持辅助输入不变。会记录完整请求的调用方(例如会话标题生成)必须使用持久消息。
62
62
 
63
+ `prepareCall(config, signal, configure?)` 接受具体的 `LlmCallConfig` 值。可选的同步纯函数 `ConfigureCall` 接收已分离且深度冻结的 `LlmCallControls`,以及捕获的适配器代次所提供的模型元数据。调用方可组合普通函数来选择具体控制项;后执行的写入替换先前的值。准备操作保留捕获的路由,为省略的控制项应用默认值,校验结果,再返回用于记录与分发的冻结配置。回调失败或取消会在分发前拒绝。准备操作要求注册适配器。
64
+
65
+ 配置函数在适配器默认值物化之前选择控制项。例如,将最低强度选择与显式输出上限组合,再将 `configure(maxTokens)` 作为第三个参数传入:
66
+
67
+ ```ts
68
+ import type { ConfigureCall } from '@deepseek-ai/dsh-llm'
69
+
70
+ const leastReasoning: ConfigureCall = (controls, model) => {
71
+ const first = model.reasoning?.efforts[0]
72
+ return first === undefined ? controls : { ...controls, reasoningEffort: first.id }
73
+ }
74
+
75
+ const outputLimit = (maxTokens: number): ConfigureCall =>
76
+ controls => ({ ...controls, maxTokens })
77
+
78
+ const configure = (maxTokens: number): ConfigureCall =>
79
+ (controls, model) => outputLimit(maxTokens)(leastReasoning(controls, model), model)
80
+ ```
81
+
63
82
  ### 你可以做什么
64
83
 
65
84
  - **流式发起一次模型调用**——`ctx.llm.stream(options)` 为任何已注册提供方与模型产出原始分片(token 级增量);消费方用 `BlockAssembler` 组装。
66
85
  - **注册提供方适配器**——一个适配器拥有一个或多个提供方路由,其注册会捕获该路由的重试策略;重复注册同一路由会以 `DUPLICATE_ADAPTER` 失败。
67
- - **通过配置暴露并激活提供方**——适配器声明可配置提供方路由与 settings namespace,配置界面因此可以激活休眠提供方并编辑连接信息,无需重启。`LlmConfigurableProvider.error` 报告供修复的配置诊断;未受影响的模型仍可提供服务。
86
+ - **通过配置暴露并激活提供方**——适配器声明可配置提供方路由与 settings namespace,配置界面因此可以激活休眠提供方并编辑连接信息,无需重启。`LlmConfigurableProvider.error` 报告供修复的配置诊断;未受影响的模型仍可提供服务。条目还可携带 `defaultBaseURL`,即配置未设置端点时适配器使用的端点;以及 `nativeAuth`,即配置未指定凭据引用时适配器认证读取的环境变量与 OAuth 登录;`baseURLEnv` 列出配置端点所用的环境变量。配置界面直接从目录读取这些字段,无需调用适配器。
68
87
  - **发现与解析模型**——列出适配器公布的模型、询问端点它提供哪些模型(候选携带可选的 `inputModalities`;缺省表示未知),并解析某个精确模型的上下文窗口、输出默认值、推理(reasoning)强度、输入模态与系统提示词更新模式:当模型把任意位置最新的 `system` 消息读作有效系统提示词时,`LlmResolvedModelInfo.systemPromptUpdate` 为 `'in-history'`;只读取开头 system 消息时该字段缺失;`normalizeModelInfo` 以 `INVALID_MODEL_INFO` 拒绝任何其他值。
69
88
  - **校验调用配置**——显式或配置的推理强度会在任何提供方 I/O 之前对照精确模型校验;请求省略输出上限时,会填入适配器配置的输出上限。
70
89
  - **不展开即读取内嵌 Assistant 流**——`assistantStreamFirstTokenTime`(首 token)、`assistantStreamHasVisibleContent`(任一可见内容)与 `assistantStreamHasVisibleText`(任一可见文本)通过可提前退出的扫描直接从紧凑记录得出结果;`lastAssistantStreamChunk` 反向扫描到某一类型的最后一个原始 chunk,`assistantStreamChunks` 与 `joinAssistantStreamText` 扫描整个流,`assembleAssistantStream` 向 `BlockAssembler` 每个 run 喂一段拼接 delta,blocks/usage/replayState 与逐成员展开相同。`runFirstTokenTime` 与 `runFirstVisibleTime` 对单个打包 run 做提前退出扫描,`isTokenDelta`、`isVisibleChunk` 与 `chunkHasVisibleText` 定义单个 chunk 的 token 与可见性规则。`expandAssistantStream` 仍是持久边界读取记录的校验路径;它不被记忆化,因为保留的展开在事件生命周期内约花费紧凑流的十倍内存。
@@ -105,7 +124,7 @@ for await (const chunk of ctx.llm.stream({
105
124
 
106
125
  ### 主流程
107
126
 
108
- 请求会对照其精确模型的能力校验,包括上下文窗口、输出默认值、推理强度、输入模态与 `systemPromptUpdate` 模式,并填入任何适配器配置的默认值。运行时保留已冻结输入的冻结状态;手动构建请求的调用方负责保证输入不可变。`prepareCall()` 把这些事实、分离的上下文与重试策略绑定到执行最终分发的精确适配器代次,因此 HMR(热模块替换)或动态设置无法把一个代次的图片能力与另一代次的端点混用。支持图片的适配器把持久引用投影为路由专用请求版本;`resolveImageAttachmentAccess()` 会单独把附件提供方的可选宿主对象映射进当前工具执行世界,而不改变请求图片或其 `variantId`。纯文本路由接收确定性的逐图片占位符,包括 tool-role 结果图片,而不会改写仅追加会话历史。持久 `FileBlock` 引用永远不会到达任何适配器:请求组装把每个引用(包括 tool-role 结果中的出现)替换为确定性句柄文本,指出文件与其只读保存路径,路径经由挂载的附件与文件系统提供方解析。`ctx.llm.fileRequestText(ref)` 向请求计量公开相同的同步投影。派生后带有 `offloaded: true` 的图片出现位置,经 `projectOffloadedImages()` 以占位文本到达每条路由。支持图片的路由在保留的出现位置按精确字节超过其 `LlmImageRequestBudget` 时,以 `IMAGE_OFFLOAD_REQUIRED` 失败并说明还需省略多少最老的出现位置(`requiredImageOffload()`),绝不发送未记录的投影;`dsh-compaction-image-offload` 用一条 `image/offload` 事件记录所选位置并重试。对视觉 token 收费的适配器声明按路由的 `imageRequestPricing`,`ctx.llm.imageRequestPricing(provider, model)` 为 token meter 同步解析它。分发经过 `llm/stream` waterfall(瀑布式事件),随后分片以 token 级增量返回,每个适配器结果都以唯一一个终止 `finish` 分片到达消费方。
127
+ 请求会对照其精确模型的能力校验,包括上下文窗口、输出默认值、推理强度、输入模态与 `systemPromptUpdate` 模式,并填入任何适配器配置的默认值。运行时保留已冻结输入的冻结状态;手动构建请求的调用方负责保证输入不可变。`prepareCall()` 把这些事实、分离的上下文与重试策略绑定到执行最终分发的精确适配器代次,因此 HMR(热模块替换)或动态设置无法把一个代次的图片能力与另一代次的端点混用。支持图片的适配器把持久引用投影为路由专用请求版本;`resolveImageAttachmentAccess()` 会单独把附件提供方的可选宿主对象映射进当前工具执行世界,而不改变请求图片或其 `variantId`。纯文本路由接收确定性的逐图片占位符,包括 tool-role 结果图片,而不会改写仅追加会话历史。持久 `FileBlock` 引用永远不会到达任何适配器:请求组装把每个引用(包括 tool-role 结果中的出现)替换为确定性句柄文本,指出文件与其只读保存路径,路径经由挂载的附件与文件系统提供方解析。`ctx.llm.fileRequestText(ref)` 向请求计量公开相同的同步投影。派生后带有 `offloaded: true` 的图片出现位置,经 `projectOffloadedImages()` 以占位文本到达每条路由。支持图片的路由在保留的出现位置按精确字节超过其 `LlmImageRequestBudget` 时,以 `IMAGE_OFFLOAD_REQUIRED` 失败并说明还需省略多少最老的出现位置(`requiredImageOffload()`),绝不发送未记录的投影;`dsh-compaction-image-offload` 用一条 `image/offload` 事件记录所选位置并重试。对视觉 token 收费的适配器声明按路由的 `imageRequestPricing`,`ctx.llm.imageRequestPricing(provider, model)` 为 token meter 同步解析它。分发经过 `llm/stream` waterfall(瀑布式事件),随后分片以 token 级增量返回,每个适配器结果都以唯一一个终止 `finish` 分片到达消费方。 `imageRequestTarget(provider, model, image)` 向截图调用方提供与模型请求一致的图片尺寸和字节目标;不缩放图片的适配器保留归一化后的原图。
109
128
 
110
129
  文件检测在每次请求时读取当前内容,包括 tool-role 结果内容,不缓存消息身份或冻结状态。[已归档的文件扫描决策](../../../.agents/notes/archived/simplification/2026-09-07-file-content-scan.md)记录了实测遍历成本。
111
130
 
package/lib/index.js CHANGED
@@ -1696,6 +1696,21 @@ var LlmAdapter = class {
1696
1696
  */
1697
1697
  imageRequestPricing(_provider, _model) {}
1698
1698
  /**
1699
+ * Resolve the image dimensions and byte target this adapter sends to the model.
1700
+ * The default preserves normalized images; adapters that resize must override it.
1701
+ * @param _provider - registered provider route.
1702
+ * @param _model - exact model id.
1703
+ * @param image - normalized image whose request projection is being resolved.
1704
+ * @returns the same target used by request serialization, before provider-internal processing.
1705
+ */
1706
+ imageRequestTarget(_provider, _model, image) {
1707
+ return {
1708
+ width: image.width,
1709
+ height: image.height,
1710
+ maxBytes: image.bytes
1711
+ };
1712
+ }
1713
+ /**
1699
1714
  * List models this adapter can currently advertise for one owned provider.
1700
1715
  * Core routing accepts unlisted model ids; catalog-driven entry points such
1701
1716
  * as the GUI may require membership. Adapters used there must advertise
@@ -1918,10 +1933,7 @@ let LlmRuntime = (() => {
1918
1933
  if (entry.provider.length === 0 || entry.displayName.length === 0 || entry.settingsNs.length === 0) throw new LlmError("configurable providers need a non-empty provider, displayName, and settingsNs", "INVALID_DIRECTORY");
1919
1934
  if (entry.settingsPath.some((segment) => segment.length === 0)) throw new LlmError(`configurable provider "${entry.provider}" has an empty settingsPath segment`, "INVALID_DIRECTORY");
1920
1935
  if (this.directory.has(entry.provider) && !own.has(entry.provider) || detached.some((seen) => seen.provider === entry.provider)) throw new LlmError(`configurable provider "${entry.provider}" is already declared`, "DUPLICATE_DIRECTORY");
1921
- detached.push({
1922
- ...entry,
1923
- settingsPath: [...entry.settingsPath]
1924
- });
1936
+ detached.push(detachDirectoryEntry(entry));
1925
1937
  }
1926
1938
  for (const entry of held) this.directory.delete(entry.provider);
1927
1939
  for (const entry of detached) this.directory.set(entry.provider, entry);
@@ -1950,10 +1962,7 @@ let LlmRuntime = (() => {
1950
1962
  * @returns detached directory entries in declaration order.
1951
1963
  */
1952
1964
  listConfigurableProviders() {
1953
- return [...this.directory.values()].map((entry) => ({
1954
- ...entry,
1955
- settingsPath: [...entry.settingsPath]
1956
- }));
1965
+ return [...this.directory.values()].map(detachDirectoryEntry);
1957
1966
  }
1958
1967
  /**
1959
1968
  * Offer to interrogate provider endpoints on behalf of the settings
@@ -2045,6 +2054,16 @@ let LlmRuntime = (() => {
2045
2054
  return this.adapters.get(provider)?.adapter.imageRequestPricing(provider, model);
2046
2055
  }
2047
2056
  /**
2057
+ * Resolve the image projection for an exact registered model route.
2058
+ * @param provider - registered provider route.
2059
+ * @param model - exact model id.
2060
+ * @param image - normalized image reference.
2061
+ * @returns dimensions and byte target used by the adapter's request serializer.
2062
+ */
2063
+ imageRequestTarget(provider, model, image) {
2064
+ return this.registration(provider).adapter.imageRequestTarget(provider, model, image);
2065
+ }
2066
+ /**
2048
2067
  * Resolve the exact text one durable file occurrence contributes to every
2049
2068
  * provider request in the current execution environment.
2050
2069
  * @param ref - durable verbatim file reference from model history.
@@ -2190,20 +2209,33 @@ let LlmRuntime = (() => {
2190
2209
  * Resolve one call under its current adapter registration. The returned
2191
2210
  * one-shot handle keeps that registration across header logging and dispatch,
2192
2211
  * so HMR cannot combine one adapter's capability result with another adapter.
2193
- * @param config - provider/model route and optional request controls.
2212
+ * An optional synchronous callback selects concrete controls using captured
2213
+ * model metadata. Defaults and validation apply to its result. Callback
2214
+ * failures and cancellation reject preparation before dispatch.
2215
+ * @param config - provider/model route and optional concrete request controls.
2194
2216
  * @param signal - optional cancellation for adapter-owned capability lookup.
2217
+ * @param configure - pure control selection, called once before defaults and validation.
2195
2218
  * @returns a prepared config and its registration-bound stream entry point.
2196
2219
  */
2197
- async prepareCall(config, signal) {
2198
- const registration = this.registration(config.provider);
2199
- const adapterCall = await registration.adapter.prepareCall(config.provider, config.model, signal);
2200
- const modelInfo = this.normalizeModelInfo(registration, config.model, adapterCall.model);
2201
- const resolved = this.resolveCallWithInfo(config, modelInfo);
2220
+ async prepareCall(config, signal, configure) {
2221
+ signal?.throwIfAborted();
2222
+ const { provider, model, ...controls } = config;
2223
+ const registration = this.registration(provider);
2224
+ const adapterCall = await registration.adapter.prepareCall(provider, model, signal);
2225
+ signal?.throwIfAborted();
2226
+ const modelInfo = this.normalizeModelInfo(registration, model, adapterCall.model);
2227
+ const proposed = configure === void 0 ? config : {
2228
+ ...configure(deepFreeze(structuredClone(controls)), deepFreeze(modelInfo)),
2229
+ provider,
2230
+ model
2231
+ };
2232
+ signal?.throwIfAborted();
2233
+ const resolved = this.resolveCallWithInfo(proposed, modelInfo);
2202
2234
  const resolvedConfig = deepFreeze(structuredClone(resolved.config));
2203
2235
  const context = resolved.context === void 0 ? void 0 : deepFreeze(structuredClone(resolved.context));
2204
2236
  const adapterDefaults = deepFreeze({
2205
- ...config.reasoningEffort === void 0 && resolvedConfig.reasoningEffort !== void 0 ? { reasoningEffort: true } : {},
2206
- ...config.maxTokens === void 0 && resolvedConfig.maxTokens !== void 0 ? { maxTokens: true } : {}
2237
+ ...proposed.reasoningEffort === void 0 && resolvedConfig.reasoningEffort !== void 0 ? { reasoningEffort: true } : {},
2238
+ ...proposed.maxTokens === void 0 && resolvedConfig.maxTokens !== void 0 ? { maxTokens: true } : {}
2207
2239
  });
2208
2240
  let dispatched = false;
2209
2241
  return Object.freeze({
@@ -2380,5 +2412,18 @@ function adapterFailureChunk(error, signal) {
2380
2412
  }
2381
2413
  };
2382
2414
  }
2415
+ /** Copy a directory entry, including its arrays, so neither side can mutate the other's copy. */
2416
+ function detachDirectoryEntry(entry) {
2417
+ const { nativeAuth } = entry;
2418
+ return {
2419
+ ...entry,
2420
+ settingsPath: [...entry.settingsPath],
2421
+ ...entry.baseURLEnv === void 0 ? {} : { baseURLEnv: [...entry.baseURLEnv] },
2422
+ ...nativeAuth === void 0 ? {} : { nativeAuth: {
2423
+ ...nativeAuth,
2424
+ env: [...nativeAuth.env]
2425
+ } }
2426
+ };
2427
+ }
2383
2428
  //#endregion
2384
2429
  export { ACCOUNT_QUOTA_EXCEEDED_CODE, APP_IDENTITY, AssistantStreamAccumulator, BlockAssembler, CONTEXT_SUMMARY_MAX_CHARS, CONTEXT_WINDOW_EXCEEDED_CODE, EMPTY_RESPONSE_CODE, HarnessError, IMAGE_OFFLOAD_REQUIRED_CODE, INVALID_CREDENTIAL_CODE, LlmAdapter, LlmAttemptId, LlmError, LlmRuntime, LlmRuntime as default, MessageId, ProviderRequestId, QUOTA_EXCEEDED_CODE, ReasoningEffortId, RetryPolicySchema, ToolCallId, assembleAssistantStream, assertUsableApiKey, assistantStreamChunks, assistantStreamFirstTokenTime, assistantStreamHasVisibleContent, assistantStreamHasVisibleText, attributionHeaders, boundContextSummary, callConfigEquals, chunkHasVisibleText, contentHasFile, contentHasImage, createAssistantMessage, createDeveloperMessage, createMessage, createSystemMessage, createToolResultMessage, createUserMessage, errorChain, expandAssistantStream, fileHandleText, freezeMessage, isAgentLoopRequest, isContextWindowExceededError, isHarnessError, isQuotaExceededError, isTokenDelta, isVisibleChunk, joinAssistantStreamText, lastAssistantStreamChunk, markAgentLoopRequest, normalizeApiKey, offloadedImageText, projectFilesToText, projectImagesForTextModel, projectOffloadedImages, projectToolUpdates, requestImageHandleText, requiredImageOffload, resolveImageAttachmentAccess, resolveRetryPolicy, runFirstTokenTime, runFirstVisibleTime, textOnlyImageText, userAgent };
@@ -1,70 +1,96 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
+ import { createTypertRemoteInfo } from '@deepseek-ai/dsh-typert-protocol/remote-info'
5
+
4
6
  let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema$value
5
7
  const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema$value ??= z.string())
6
8
  let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema$value
7
9
  const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema$value ??= z.object({
8
- 'provider': z.string().optional(),
9
- 'baseURL': z.string().optional(),
10
- 'api': z.string().optional(),
11
- 'apiKey': z.string().optional(),
10
+ 'provider': z.string().optional().describe('Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter\'s own registry is the better answer, and it costs no network call.'),
11
+ 'baseURL': z.string().optional().describe('Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one.'),
12
+ 'api': z.string().optional().describe('Wire protocol the endpoint speaks, when the draft names one.'),
13
+ 'apiKey': z.string().optional().describe('Credential for this interrogation alone; the harness never stores it.'),
12
14
  }))
13
15
  let _deepseek_ai_dsh_llm_llm_discoverModels_result$schema$value
14
16
  const _deepseek_ai_dsh_llm_llm_discoverModels_result$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_result$schema$value ??= z.array(z.object({
15
- 'id': z.string(),
16
- 'name': z.string().optional(),
17
- 'contextWindow': z.number().optional(),
18
- 'maxTokens': z.number().optional(),
19
- 'inputModalities': z.array(z.union([z.literal("text"), z.literal("image")])).optional(),
17
+ 'id': z.string().describe('Model id the endpoint accepts.'),
18
+ 'name': z.string().optional().describe('Human-readable name when the endpoint supplies one.'),
19
+ 'contextWindow': z.number().optional().describe('Maximum combined request and response context, when disclosed.'),
20
+ 'maxTokens': z.number().optional().describe('Maximum output tokens, when disclosed.'),
21
+ 'inputModalities': z.array(z.union([z.literal("text"), z.literal("image")])).optional().describe('Accepted input types when disclosed by the catalog or endpoint; absent means unknown.'),
20
22
  })))
21
23
  let _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema$value
22
24
  const _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema = () => (_deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema$value ??= z.array(z.object({
23
- 'provider': z.string(),
24
- 'displayName': z.string(),
25
- 'settingsNs': z.string(),
26
- 'settingsPath': z.array(z.string()),
27
- 'declared': z.boolean().optional(),
28
- 'error': z.string().optional(),
25
+ 'provider': z.string().describe('Provider route key this entry activates when configured.'),
26
+ 'displayName': z.string().describe('Human-readable provider name for configuration surfaces.'),
27
+ 'settingsNs': z.string().describe('User-settings namespace whose section configures this provider.'),
28
+ 'settingsPath': z.array(z.string()).describe('Path from that namespace\'s section root to this provider\'s profile object; empty when the whole section is the profile.'),
29
+ 'declared': z.boolean().optional().describe('Whether the owning adapter knows this route only because configuration declared it — a gateway or self-hosted server it ships nothing about. Absent means the adapter draws no such distinction; false means it does and this route is one of its own. Only the adapter can answer: a stored profile is how a user-added route AND a corrected shipped one both look from outside.'),
30
+ 'defaultBaseURL': z.string().optional().describe('Route endpoint the adapter uses when the profile sets no `baseURL`; the adapter may still send an individual model to that model\'s own endpoint. Absent when the route has no fixed endpoint.'),
31
+ 'baseURLEnv': z.array(z.string()).optional().describe('Environment variables used to construct a provider endpoint; absent when none are reported.'),
32
+ 'nativeAuth': z.object({
33
+ 'env': z.array(z.string()).describe('Environment variables the route\'s authentication reads, in lookup order; empty when it reads none.'),
34
+ 'oauth': z.boolean().describe('Whether a stored OAuth sign-in can authenticate the route.'),
35
+ }).optional().describe('Authentication the adapter performs when the profile names no credential reference; absent when it reports none.'),
36
+ 'error': z.string().optional().describe('Configuration diagnostic for repair; unaffected models may remain serviceable.'),
29
37
  })))
30
38
  let _deepseek_ai_dsh_llm_llm_listProviders_result$schema$value
31
39
  const _deepseek_ai_dsh_llm_llm_listProviders_result$schema = () => (_deepseek_ai_dsh_llm_llm_listProviders_result$schema$value ??= z.array(z.object({
32
- 'id': z.string(),
33
- 'name': z.string(),
40
+ 'id': z.string().describe('Provider route key used by {@link GenerateOptions.provider}.'),
41
+ 'name': z.string().describe('Human-readable provider name for selectors and diagnostics.'),
34
42
  })))
43
+ let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema$value
44
+ const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema$value ??= z.string())
45
+ let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema$value
46
+ const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema$value ??= z.strictObject({
47
+ 'provider': z.string().optional().describe('Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter\'s own registry is the better answer, and it costs no network call.'),
48
+ 'baseURL': z.string().optional().describe('Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one.'),
49
+ 'api': z.string().optional().describe('Wire protocol the endpoint speaks, when the draft names one.'),
50
+ 'apiKey': z.string().optional().describe('Credential for this interrogation alone; the harness never stores it.'),
51
+ }))
35
52
 
36
- export const TYPERT = {
53
+ const $remote = createTypertRemoteInfo({
37
54
  package: '@deepseek-ai/dsh-llm',
38
- face: 'host',
39
- schemas: [
40
- ],
41
- invocations: [
42
- {
43
- id: '@deepseek-ai/dsh-llm#llm/discoverModels',
55
+ methods: {
56
+ 'llm/discoverModels': {
44
57
  service: 'llm',
45
- namespace: 'llm',
46
- method: 'discoverModels',
58
+ description: 'Remote adapter for one draft provider interrogation.',
47
59
  implementation: 'remoteDiscoverModels',
48
60
  invocation: { kind: 'direct' },
49
61
  parameters: [
50
62
  {
51
63
  name: 'settingsNs',
52
- wire: 'settingsNs',
64
+ description: 'namespace whose registered discovery serves this draft.',
53
65
  source: 'json',
54
66
  codec: {
55
67
  mode: 'strict',
56
68
  typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:settingsNs',
57
69
  create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema,
70
+ jsonSchema: {"type":"string"},
71
+ },
72
+ strictCodec: {
73
+ mode: 'strict',
74
+ typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:settingsNs',
75
+ create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema,
76
+ jsonSchema: {"type":"string"},
58
77
  },
59
78
  },
60
79
  {
61
80
  name: 'request',
62
- wire: 'request',
81
+ description: 'endpoint, protocol, and one-shot credential to use.',
63
82
  source: 'json',
64
83
  codec: {
65
84
  mode: 'strict',
66
85
  typeSymbol: '@deepseek-ai/dsh-llm/types#LlmModelDiscoveryRequest',
67
86
  create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema,
87
+ jsonSchema: {"type":"object","properties":{"provider":{"type":"string","description":"Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter's own registry is the better answer, and it costs no network call."},"baseURL":{"type":"string","description":"Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one."},"api":{"type":"string","description":"Wire protocol the endpoint speaks, when the draft names one."},"apiKey":{"type":"string","description":"Credential for this interrogation alone; the harness never stores it."}},"required":[],"additionalProperties":true},
88
+ },
89
+ strictCodec: {
90
+ mode: 'strict',
91
+ typeSymbol: '@deepseek-ai/dsh-llm/types#LlmModelDiscoveryRequest',
92
+ create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema,
93
+ jsonSchema: {"type":"object","properties":{"provider":{"type":"string","description":"Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter's own registry is the better answer, and it costs no network call."},"baseURL":{"type":"string","description":"Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one."},"api":{"type":"string","description":"Wire protocol the endpoint speaks, when the draft names one."},"apiKey":{"type":"string","description":"Credential for this interrogation alone; the harness never stores it."}},"required":[],"additionalProperties":false},
68
94
  },
69
95
  },
70
96
  ],
@@ -73,14 +99,14 @@ export const TYPERT = {
73
99
  mode: 'strict',
74
100
  typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:result',
75
101
  create: _deepseek_ai_dsh_llm_llm_discoverModels_result$schema,
102
+ description: 'advertised models in endpoint order.',
103
+ jsonSchema: {"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Model id the endpoint accepts."},"name":{"type":"string","description":"Human-readable name when the endpoint supplies one."},"contextWindow":{"type":"number","description":"Maximum combined request and response context, when disclosed."},"maxTokens":{"type":"number","description":"Maximum output tokens, when disclosed."},"inputModalities":{"type":"array","items":{"type":"string","enum":["text","image"]},"description":"Accepted input types when disclosed by the catalog or endpoint; absent means unknown."}},"required":["id"],"additionalProperties":true}},
76
104
  },
77
- sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":631,"column":9},
105
+ sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":643,"column":9},
78
106
  },
79
- {
80
- id: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders',
107
+ 'llm/listConfigurableProviders': {
81
108
  service: 'llm',
82
- namespace: 'llm',
83
- method: 'listConfigurableProviders',
109
+ description: 'List every declared configurable provider, registered or dormant.',
84
110
  invocation: { kind: 'direct' },
85
111
  parameters: [
86
112
  ],
@@ -88,14 +114,14 @@ export const TYPERT = {
88
114
  mode: 'strict',
89
115
  typeSymbol: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders:result',
90
116
  create: _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema,
117
+ description: 'detached directory entries in declaration order.',
118
+ jsonSchema: {"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","description":"Provider route key this entry activates when configured."},"displayName":{"type":"string","description":"Human-readable provider name for configuration surfaces."},"settingsNs":{"type":"string","description":"User-settings namespace whose section configures this provider."},"settingsPath":{"type":"array","items":{"type":"string"},"description":"Path from that namespace's section root to this provider's profile object; empty when the whole section is the profile."},"declared":{"type":"boolean","description":"Whether the owning adapter knows this route only because configuration declared it — a gateway or self-hosted server it ships nothing about. Absent means the adapter draws no such distinction; false means it does and this route is one of its own. Only the adapter can answer: a stored profile is how a user-added route AND a corrected shipped one both look from outside."},"defaultBaseURL":{"type":"string","description":"Route endpoint the adapter uses when the profile sets no `baseURL`; the adapter may still send an individual model to that model's own endpoint. Absent when the route has no fixed endpoint."},"baseURLEnv":{"type":"array","items":{"type":"string"},"description":"Environment variables used to construct a provider endpoint; absent when none are reported."},"nativeAuth":{"type":"object","properties":{"env":{"type":"array","items":{"type":"string"},"description":"Environment variables the route's authentication reads, in lookup order; empty when it reads none."},"oauth":{"type":"boolean","description":"Whether a stored OAuth sign-in can authenticate the route."}},"required":["env","oauth"],"additionalProperties":true,"description":"Authentication the adapter performs when the profile names no credential reference; absent when it reports none."},"error":{"type":"string","description":"Configuration diagnostic for repair; unaffected models may remain serviceable."}},"required":["provider","displayName","settingsNs","settingsPath"],"additionalProperties":true}},
91
119
  },
92
- sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":543,"column":3},
120
+ sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":555,"column":3},
93
121
  },
94
- {
95
- id: '@deepseek-ai/dsh-llm#llm/listProviders',
122
+ 'llm/listProviders': {
96
123
  service: 'llm',
97
- namespace: 'llm',
98
- method: 'listProviders',
124
+ description: 'Describe provider routes with a registered adapter.',
99
125
  invocation: { kind: 'direct' },
100
126
  parameters: [
101
127
  ],
@@ -103,10 +129,20 @@ export const TYPERT = {
103
129
  mode: 'strict',
104
130
  typeSymbol: '@deepseek-ai/dsh-llm#llm/listProviders:result',
105
131
  create: _deepseek_ai_dsh_llm_llm_listProviders_result$schema,
132
+ description: 'detached provider metadata in registration order.',
133
+ jsonSchema: {"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Provider route key used by {@link GenerateOptions.provider}."},"name":{"type":"string","description":"Human-readable provider name for selectors and diagnostics."}},"required":["id","name"],"additionalProperties":true}},
106
134
  },
107
- sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":471,"column":3},
135
+ sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":483,"column":3},
108
136
  },
137
+ },
138
+ }, z)
139
+
140
+ export const TYPERT = {
141
+ package: '@deepseek-ai/dsh-llm',
142
+ face: 'host',
143
+ schemas: [
109
144
  ],
145
+ get invocations() { return $remote.descriptors },
110
146
  model: {
111
147
  "services": [
112
148
  {
@@ -180,6 +216,13 @@ export const TYPERT = {
180
216
  "summary": "Resolve provider-side request-image pricing for one exact route, or `undefined` when the provider is unregistered or declares none.",
181
217
  "jsDoc": "/**\n * Resolve provider-side request-image pricing for one exact route, or\n * `undefined` when the provider is unregistered or declares none. Unknown\n * providers degrade to `undefined` rather than throwing because callers\n * price durable history whose route may no longer be mounted.\n * @param provider - provider route named by a request header.\n * @param model - exact model id named by the same header.\n * @returns the owning adapter's image pricing for the route, when declared.\n */"
182
218
  },
219
+ {
220
+ "kind": "method",
221
+ "name": "imageRequestTarget",
222
+ "signature": "imageRequestTarget(provider: string, model: string, image: ImageAttachmentRef): ImageRequestTarget",
223
+ "summary": "Resolve the image projection for an exact registered model route.",
224
+ "jsDoc": "/**\n * Resolve the image projection for an exact registered model route.\n * @param provider - registered provider route.\n * @param model - exact model id.\n * @param image - normalized image reference.\n * @returns dimensions and byte target used by the adapter's request serializer.\n */"
225
+ },
183
226
  {
184
227
  "kind": "method",
185
228
  "name": "fileRequestText",
@@ -211,9 +254,9 @@ export const TYPERT = {
211
254
  {
212
255
  "kind": "method",
213
256
  "name": "prepareCall",
214
- "signature": "async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>",
257
+ "signature": "async prepareCall( config: LlmCallConfig, signal?: AbortSignal, configure?: ConfigureCall, ): Promise<PreparedLlmCall>",
215
258
  "summary": "Resolve one call under its current adapter registration.",
216
- "jsDoc": "/**\n * Resolve one call under its current adapter registration. The returned\n * one-shot handle keeps that registration across header logging and dispatch,\n * so HMR cannot combine one adapter's capability result with another adapter.\n * @param config - provider/model route and optional request controls.\n * @param signal - optional cancellation for adapter-owned capability lookup.\n * @returns a prepared config and its registration-bound stream entry point.\n */"
259
+ "jsDoc": "/**\n * Resolve one call under its current adapter registration. The returned\n * one-shot handle keeps that registration across header logging and dispatch,\n * so HMR cannot combine one adapter's capability result with another adapter.\n * An optional synchronous callback selects concrete controls using captured\n * model metadata. Defaults and validation apply to its result. Callback\n * failures and cancellation reject preparation before dispatch.\n * @param config - provider/model route and optional concrete request controls.\n * @param signal - optional cancellation for adapter-owned capability lookup.\n * @param configure - pure control selection, called once before defaults and validation.\n * @returns a prepared config and its registration-bound stream entry point.\n */"
217
260
  },
218
261
  {
219
262
  "kind": "method",
@@ -264,6 +307,10 @@ export const TYPERT = {
264
307
  "name": "CompactionId",
265
308
  "declaration": "export type CompactionId = Branded<'CompactionId'>;"
266
309
  },
310
+ {
311
+ "name": "ConfigureCall",
312
+ "declaration": "export type ConfigureCall = (controls: Readonly<LlmCallControls>, model: Readonly<LlmResolvedModelInfo>) => LlmCallControls;"
313
+ },
267
314
  {
268
315
  "name": "ContentBlock",
269
316
  "declaration": "export type ContentBlock = ContentBlockMap[ContentBlockType];"
@@ -332,21 +379,29 @@ export const TYPERT = {
332
379
  "name": "ImageMediaType",
333
380
  "declaration": "export type ImageMediaType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';"
334
381
  },
382
+ {
383
+ "name": "ImageRequestTarget",
384
+ "declaration": "export interface ImageRequestTarget {\n width: number;\n height: number;\n maxBytes: number;\n}"
385
+ },
335
386
  {
336
387
  "name": "LlmAdapter",
337
- "declaration": "export abstract class LlmAdapter {\n providerInfo(provider: string): LlmProviderInfo;\n providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;\n imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;\n listModels(_provider: string): Promise<readonly LlmModelInfo[]>;\n resolveModel(provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;\n prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;\n abstract stream(options: GenerateOptions): AsyncIterable<StreamChunk>;\n}"
388
+ "declaration": "export abstract class LlmAdapter {\n providerInfo(provider: string): LlmProviderInfo;\n providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;\n imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;\n imageRequestTarget(_provider: string, _model: string, image: ImageAttachmentRef): ImageRequestTarget;\n listModels(_provider: string): Promise<readonly LlmModelInfo[]>;\n resolveModel(provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;\n prepareCall(provider: string, model: string, signal?: AbortSignal): Promise<PreparedAdapterCall>;\n abstract stream(options: GenerateOptions): AsyncIterable<StreamChunk>;\n}"
338
389
  },
339
390
  {
340
391
  "name": "LlmCallConfig",
341
- "declaration": "export interface LlmCallConfig {\n provider: string;\n model: string;\n reasoningEffort?: ReasoningEffortId;\n temperature?: number;\n maxTokens?: number;\n stop?: string[];\n}"
392
+ "declaration": "export interface LlmCallConfig extends LlmCallControls {\n provider: string;\n model: string;\n}"
342
393
  },
343
394
  {
344
395
  "name": "LlmCallConfigAdapterDefaults",
345
396
  "declaration": "export interface LlmCallConfigAdapterDefaults {\n reasoningEffort?: true;\n maxTokens?: true;\n}"
346
397
  },
398
+ {
399
+ "name": "LlmCallControls",
400
+ "declaration": "export interface LlmCallControls {\n reasoningEffort?: ReasoningEffortId;\n temperature?: number;\n maxTokens?: number;\n stop?: string[];\n}"
401
+ },
347
402
  {
348
403
  "name": "LlmConfigurableProvider",
349
- "declaration": "export interface LlmConfigurableProvider {\n provider: string;\n displayName: string;\n settingsNs: string;\n settingsPath: readonly string[];\n declared?: boolean;\n error?: string;\n}"
404
+ "declaration": "export interface LlmConfigurableProvider {\n provider: string;\n displayName: string;\n settingsNs: string;\n settingsPath: readonly string[];\n declared?: boolean;\n defaultBaseURL?: string;\n baseURLEnv?: readonly string[];\n nativeAuth?: LlmNativeAuth;\n error?: string;\n}"
350
405
  },
351
406
  {
352
407
  "name": "LlmDiscoveredModel",
@@ -380,6 +435,10 @@ export const TYPERT = {
380
435
  "name": "LlmModelReasoningInfo",
381
436
  "declaration": "export interface LlmModelReasoningInfo {\n efforts: readonly LlmReasoningEffortInfo[];\n defaultEffort?: ReasoningEffortId;\n}"
382
437
  },
438
+ {
439
+ "name": "LlmNativeAuth",
440
+ "declaration": "export interface LlmNativeAuth {\n env: readonly string[];\n oauth: boolean;\n}"
441
+ },
383
442
  {
384
443
  "name": "LlmProviderInfo",
385
444
  "declaration": "export interface LlmProviderInfo {\n id: string;\n name: string;\n}"
@@ -414,7 +473,7 @@ export const TYPERT = {
414
473
  },
415
474
  {
416
475
  "name": "MessageSourceMap",
417
- "declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n 'compact-checkpoint': CompactionCheckpointSource;\n 'user-question-reply': { kind: 'user-question-reply'; callId: ToolCallId; outcome: 'answered'; };\n goal: GoalMessageSource;\n schedule: { kind: 'schedule'; } & ContextFormed;\n 'session-reference': SessionReferenceSource;\n}"
476
+ "declaration": "export interface MessageSourceMap {\n user: { kind: 'user'; };\n model: ModelMessageSource;\n tool: ToolMessageSource;\n 'system-prompt': SystemPromptMessageSource;\n 'model-selection': { kind: 'model-selection'; } & ContextFormed;\n 'runtime-context': { kind: 'runtime-context'; } & ContextFormed;\n 'working-directory': { kind: 'working-directory'; };\n 'user-approval': { kind: 'user-approval'; } & ContextFormed;\n 'ptc-mode': { kind: 'ptc-mode'; };\n 'tool-registry': { kind: 'tool-registry'; };\n 'user-rpc': { kind: 'user'; rpcId: SessionRequestId; clientTimeZone?: string; principal?: Principal; };\n 'agent-message': AgentMessageSource;\n 'subagent-settled': SubagentSettledMessageSource;\n 'skill-invocation': SkillInvocationSource;\n 'compact-checkpoint': CompactionCheckpointSource;\n 'cordis-host-runner': { kind: 'cordis-host-runner'; };\n 'team-message': TeamMessageSource;\n 'user-question-reply': { kind: 'user-question-reply'; callId: ToolCallId; outcome: 'answered'; };\n goal: GoalMessageSource;\n 'plan-mode': { kind: 'plan-mode'; } & ContextFormed;\n schedule: { kind: 'schedule'; } & ContextFormed;\n 'session-reference': SessionReferenceSource;\n webhook: { readonly kind: 'webhook'; readonly provider: string; readonly source: WebhookSourceId; readonly deliveryId: WebhookDeliveryId; readonly ruleId: WebhookRuleId; readonly form: 'notice'; readonly summary: string; };\n}"
418
477
  },
419
478
  {
420
479
  "name": "ModelMessageSource",
@@ -440,6 +499,14 @@ export const TYPERT = {
440
499
  "name": "PreparedLlmCall",
441
500
  "declaration": "export interface PreparedLlmCall {\n readonly config: LlmCallConfig;\n readonly retryPolicy: ResolvedRetryPolicy;\n readonly context?: LlmModelContext;\n readonly inputModalities?: readonly ModelModality[];\n readonly systemPromptUpdate?: SystemPromptUpdate;\n readonly toolUpdate?: ToolUpdate;\n readonly adapterDefaults: LlmCallConfigAdapterDefaults;\n stream(options: GenerateOptions): AsyncIterable<StreamChunk>;\n}"
442
501
  },
502
+ {
503
+ "name": "Principal",
504
+ "declaration": "export type Principal = PrincipalMap[keyof PrincipalMap];"
505
+ },
506
+ {
507
+ "name": "PrincipalMap",
508
+ "declaration": "export interface PrincipalMap {\n owner: { readonly kind: 'owner'; };\n}"
509
+ },
443
510
  {
444
511
  "name": "ProviderRequestId",
445
512
  "declaration": "export type ProviderRequestId = Branded<'ProviderRequestId'>;"
@@ -520,6 +587,18 @@ export const TYPERT = {
520
587
  "name": "SystemPromptUpdate",
521
588
  "declaration": "export type SystemPromptUpdate = 'in-history';"
522
589
  },
590
+ {
591
+ "name": "TeamId",
592
+ "declaration": "export type TeamId = Branded<'TeamId'>;"
593
+ },
594
+ {
595
+ "name": "TeamMessageId",
596
+ "declaration": "export type TeamMessageId = Branded<'TeamMessageId'>;"
597
+ },
598
+ {
599
+ "name": "TeamMessageSource",
600
+ "declaration": "export interface TeamMessageSource {\n readonly kind: 'team-message';\n readonly teamId: TeamId;\n readonly messageId: TeamMessageId;\n readonly senderId: SessionId;\n readonly senderName: string;\n}"
601
+ },
523
602
  {
524
603
  "name": "TextBlock",
525
604
  "declaration": "export interface TextBlock {\n type: 'text';\n text: string;\n}"
@@ -567,6 +646,18 @@ export const TYPERT = {
567
646
  {
568
647
  "name": "UserMessage",
569
648
  "declaration": "export interface UserMessage extends MessageBase {\n readonly role: 'user';\n}"
649
+ },
650
+ {
651
+ "name": "WebhookDeliveryId",
652
+ "declaration": "export type WebhookDeliveryId = Branded<'WebhookDeliveryId'>;"
653
+ },
654
+ {
655
+ "name": "WebhookRuleId",
656
+ "declaration": "export type WebhookRuleId = Branded<'WebhookRuleId'>;"
657
+ },
658
+ {
659
+ "name": "WebhookSourceId",
660
+ "declaration": "export type WebhookSourceId = Branded<'WebhookSourceId'>;"
570
661
  }
571
662
  ]
572
663
  }
@@ -1,7 +1,8 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
2
2
  import type {
3
3
  RemoteResult,
4
- TypertRemoteContribution,
4
+ TypertRemoteInfo,
5
+ TypertRemoteMethod,
5
6
  } from '@deepseek-ai/dsh-typert-protocol'
6
7
  import type { LlmConfigurableProvider, LlmDiscoveredModel, LlmModelDiscoveryRequest, LlmProviderInfo } from '@deepseek-ai/dsh-llm/types'
7
8
 
@@ -21,6 +22,10 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
21
22
  }
22
23
  }
23
24
 
24
- export declare const TYPERT_REMOTE: TypertRemoteContribution
25
+ export declare const TYPERT_REMOTE: TypertRemoteInfo<{
26
+ readonly 'llm/discoverModels': TypertRemoteMethod<{ readonly 'settingsNs': string; readonly 'request': LlmModelDiscoveryRequest }, LlmDiscoveredModel[]>
27
+ readonly 'llm/listConfigurableProviders': TypertRemoteMethod<{ }, LlmConfigurableProvider[]>
28
+ readonly 'llm/listProviders': TypertRemoteMethod<{ }, LlmProviderInfo[]>
29
+ }>
25
30
  export default TYPERT_REMOTE
26
31
  //# sourceMappingURL=typert.remote-client.d.ts.map
@@ -1,67 +1,96 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
+ import { createTypertRemoteInfo } from '@deepseek-ai/dsh-typert-protocol/remote-info'
5
+
4
6
  let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema$value
5
7
  const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema$value ??= z.string())
6
8
  let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema$value
7
9
  const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema$value ??= z.object({
8
- 'provider': z.string().optional(),
9
- 'baseURL': z.string().optional(),
10
- 'api': z.string().optional(),
11
- 'apiKey': z.string().optional(),
10
+ 'provider': z.string().optional().describe('Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter\'s own registry is the better answer, and it costs no network call.'),
11
+ 'baseURL': z.string().optional().describe('Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one.'),
12
+ 'api': z.string().optional().describe('Wire protocol the endpoint speaks, when the draft names one.'),
13
+ 'apiKey': z.string().optional().describe('Credential for this interrogation alone; the harness never stores it.'),
12
14
  }))
13
15
  let _deepseek_ai_dsh_llm_llm_discoverModels_result$schema$value
14
16
  const _deepseek_ai_dsh_llm_llm_discoverModels_result$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_result$schema$value ??= z.array(z.object({
15
- 'id': z.string(),
16
- 'name': z.string().optional(),
17
- 'contextWindow': z.number().optional(),
18
- 'maxTokens': z.number().optional(),
19
- 'inputModalities': z.array(z.union([z.literal("text"), z.literal("image")])).optional(),
17
+ 'id': z.string().describe('Model id the endpoint accepts.'),
18
+ 'name': z.string().optional().describe('Human-readable name when the endpoint supplies one.'),
19
+ 'contextWindow': z.number().optional().describe('Maximum combined request and response context, when disclosed.'),
20
+ 'maxTokens': z.number().optional().describe('Maximum output tokens, when disclosed.'),
21
+ 'inputModalities': z.array(z.union([z.literal("text"), z.literal("image")])).optional().describe('Accepted input types when disclosed by the catalog or endpoint; absent means unknown.'),
20
22
  })))
21
23
  let _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema$value
22
24
  const _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema = () => (_deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema$value ??= z.array(z.object({
23
- 'provider': z.string(),
24
- 'displayName': z.string(),
25
- 'settingsNs': z.string(),
26
- 'settingsPath': z.array(z.string()),
27
- 'declared': z.boolean().optional(),
28
- 'error': z.string().optional(),
25
+ 'provider': z.string().describe('Provider route key this entry activates when configured.'),
26
+ 'displayName': z.string().describe('Human-readable provider name for configuration surfaces.'),
27
+ 'settingsNs': z.string().describe('User-settings namespace whose section configures this provider.'),
28
+ 'settingsPath': z.array(z.string()).describe('Path from that namespace\'s section root to this provider\'s profile object; empty when the whole section is the profile.'),
29
+ 'declared': z.boolean().optional().describe('Whether the owning adapter knows this route only because configuration declared it — a gateway or self-hosted server it ships nothing about. Absent means the adapter draws no such distinction; false means it does and this route is one of its own. Only the adapter can answer: a stored profile is how a user-added route AND a corrected shipped one both look from outside.'),
30
+ 'defaultBaseURL': z.string().optional().describe('Route endpoint the adapter uses when the profile sets no `baseURL`; the adapter may still send an individual model to that model\'s own endpoint. Absent when the route has no fixed endpoint.'),
31
+ 'baseURLEnv': z.array(z.string()).optional().describe('Environment variables used to construct a provider endpoint; absent when none are reported.'),
32
+ 'nativeAuth': z.object({
33
+ 'env': z.array(z.string()).describe('Environment variables the route\'s authentication reads, in lookup order; empty when it reads none.'),
34
+ 'oauth': z.boolean().describe('Whether a stored OAuth sign-in can authenticate the route.'),
35
+ }).optional().describe('Authentication the adapter performs when the profile names no credential reference; absent when it reports none.'),
36
+ 'error': z.string().optional().describe('Configuration diagnostic for repair; unaffected models may remain serviceable.'),
29
37
  })))
30
38
  let _deepseek_ai_dsh_llm_llm_listProviders_result$schema$value
31
39
  const _deepseek_ai_dsh_llm_llm_listProviders_result$schema = () => (_deepseek_ai_dsh_llm_llm_listProviders_result$schema$value ??= z.array(z.object({
32
- 'id': z.string(),
33
- 'name': z.string(),
40
+ 'id': z.string().describe('Provider route key used by {@link GenerateOptions.provider}.'),
41
+ 'name': z.string().describe('Human-readable provider name for selectors and diagnostics.'),
34
42
  })))
43
+ let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema$value
44
+ const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema$value ??= z.string())
45
+ let _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema$value
46
+ const _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema = () => (_deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema$value ??= z.strictObject({
47
+ 'provider': z.string().optional().describe('Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter\'s own registry is the better answer, and it costs no network call.'),
48
+ 'baseURL': z.string().optional().describe('Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one.'),
49
+ 'api': z.string().optional().describe('Wire protocol the endpoint speaks, when the draft names one.'),
50
+ 'apiKey': z.string().optional().describe('Credential for this interrogation alone; the harness never stores it.'),
51
+ }))
35
52
 
36
- export const TYPERT_REMOTE = {
53
+ export const TYPERT_REMOTE = createTypertRemoteInfo({
37
54
  package: '@deepseek-ai/dsh-llm',
38
- descriptors: [
39
- {
40
- id: '@deepseek-ai/dsh-llm#llm/discoverModels',
55
+ methods: {
56
+ 'llm/discoverModels': {
41
57
  service: 'llm',
42
- namespace: 'llm',
43
- method: 'discoverModels',
58
+ description: 'Remote adapter for one draft provider interrogation.',
44
59
  implementation: 'remoteDiscoverModels',
45
60
  invocation: { kind: 'direct' },
46
61
  parameters: [
47
62
  {
48
63
  name: 'settingsNs',
49
- wire: 'settingsNs',
64
+ description: 'namespace whose registered discovery serves this draft.',
50
65
  source: 'json',
51
66
  codec: {
52
67
  mode: 'strict',
53
68
  typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:settingsNs',
54
69
  create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0$schema,
70
+ jsonSchema: {"type":"string"},
71
+ },
72
+ strictCodec: {
73
+ mode: 'strict',
74
+ typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:settingsNs',
75
+ create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_0_strict$schema,
76
+ jsonSchema: {"type":"string"},
55
77
  },
56
78
  },
57
79
  {
58
80
  name: 'request',
59
- wire: 'request',
81
+ description: 'endpoint, protocol, and one-shot credential to use.',
60
82
  source: 'json',
61
83
  codec: {
62
84
  mode: 'strict',
63
85
  typeSymbol: '@deepseek-ai/dsh-llm/types#LlmModelDiscoveryRequest',
64
86
  create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1$schema,
87
+ jsonSchema: {"type":"object","properties":{"provider":{"type":"string","description":"Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter's own registry is the better answer, and it costs no network call."},"baseURL":{"type":"string","description":"Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one."},"api":{"type":"string","description":"Wire protocol the endpoint speaks, when the draft names one."},"apiKey":{"type":"string","description":"Credential for this interrogation alone; the harness never stores it."}},"required":[],"additionalProperties":true},
88
+ },
89
+ strictCodec: {
90
+ mode: 'strict',
91
+ typeSymbol: '@deepseek-ai/dsh-llm/types#LlmModelDiscoveryRequest',
92
+ create: _deepseek_ai_dsh_llm_llm_discoverModels_parameter_1_strict$schema,
93
+ jsonSchema: {"type":"object","properties":{"provider":{"type":"string","description":"Route the draft is editing, when it edits an existing one. A route whose adapter already knows its models answers from that knowledge instead of asking the endpoint — the adapter's own registry is the better answer, and it costs no network call."},"baseURL":{"type":"string","description":"Endpoint to interrogate. Optional because a route the adapter already describes needs none; a route it does not must supply one."},"api":{"type":"string","description":"Wire protocol the endpoint speaks, when the draft names one."},"apiKey":{"type":"string","description":"Credential for this interrogation alone; the harness never stores it."}},"required":[],"additionalProperties":false},
65
94
  },
66
95
  },
67
96
  ],
@@ -70,14 +99,14 @@ export const TYPERT_REMOTE = {
70
99
  mode: 'strict',
71
100
  typeSymbol: '@deepseek-ai/dsh-llm#llm/discoverModels:result',
72
101
  create: _deepseek_ai_dsh_llm_llm_discoverModels_result$schema,
102
+ description: 'advertised models in endpoint order.',
103
+ jsonSchema: {"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Model id the endpoint accepts."},"name":{"type":"string","description":"Human-readable name when the endpoint supplies one."},"contextWindow":{"type":"number","description":"Maximum combined request and response context, when disclosed."},"maxTokens":{"type":"number","description":"Maximum output tokens, when disclosed."},"inputModalities":{"type":"array","items":{"type":"string","enum":["text","image"]},"description":"Accepted input types when disclosed by the catalog or endpoint; absent means unknown."}},"required":["id"],"additionalProperties":true}},
73
104
  },
74
- sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":631,"column":9},
105
+ sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":643,"column":9},
75
106
  },
76
- {
77
- id: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders',
107
+ 'llm/listConfigurableProviders': {
78
108
  service: 'llm',
79
- namespace: 'llm',
80
- method: 'listConfigurableProviders',
109
+ description: 'List every declared configurable provider, registered or dormant.',
81
110
  invocation: { kind: 'direct' },
82
111
  parameters: [
83
112
  ],
@@ -85,14 +114,14 @@ export const TYPERT_REMOTE = {
85
114
  mode: 'strict',
86
115
  typeSymbol: '@deepseek-ai/dsh-llm#llm/listConfigurableProviders:result',
87
116
  create: _deepseek_ai_dsh_llm_llm_listConfigurableProviders_result$schema,
117
+ description: 'detached directory entries in declaration order.',
118
+ jsonSchema: {"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","description":"Provider route key this entry activates when configured."},"displayName":{"type":"string","description":"Human-readable provider name for configuration surfaces."},"settingsNs":{"type":"string","description":"User-settings namespace whose section configures this provider."},"settingsPath":{"type":"array","items":{"type":"string"},"description":"Path from that namespace's section root to this provider's profile object; empty when the whole section is the profile."},"declared":{"type":"boolean","description":"Whether the owning adapter knows this route only because configuration declared it — a gateway or self-hosted server it ships nothing about. Absent means the adapter draws no such distinction; false means it does and this route is one of its own. Only the adapter can answer: a stored profile is how a user-added route AND a corrected shipped one both look from outside."},"defaultBaseURL":{"type":"string","description":"Route endpoint the adapter uses when the profile sets no `baseURL`; the adapter may still send an individual model to that model's own endpoint. Absent when the route has no fixed endpoint."},"baseURLEnv":{"type":"array","items":{"type":"string"},"description":"Environment variables used to construct a provider endpoint; absent when none are reported."},"nativeAuth":{"type":"object","properties":{"env":{"type":"array","items":{"type":"string"},"description":"Environment variables the route's authentication reads, in lookup order; empty when it reads none."},"oauth":{"type":"boolean","description":"Whether a stored OAuth sign-in can authenticate the route."}},"required":["env","oauth"],"additionalProperties":true,"description":"Authentication the adapter performs when the profile names no credential reference; absent when it reports none."},"error":{"type":"string","description":"Configuration diagnostic for repair; unaffected models may remain serviceable."}},"required":["provider","displayName","settingsNs","settingsPath"],"additionalProperties":true}},
88
119
  },
89
- sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":543,"column":3},
120
+ sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":555,"column":3},
90
121
  },
91
- {
92
- id: '@deepseek-ai/dsh-llm#llm/listProviders',
122
+ 'llm/listProviders': {
93
123
  service: 'llm',
94
- namespace: 'llm',
95
- method: 'listProviders',
124
+ description: 'Describe provider routes with a registered adapter.',
96
125
  invocation: { kind: 'direct' },
97
126
  parameters: [
98
127
  ],
@@ -100,10 +129,12 @@ export const TYPERT_REMOTE = {
100
129
  mode: 'strict',
101
130
  typeSymbol: '@deepseek-ai/dsh-llm#llm/listProviders:result',
102
131
  create: _deepseek_ai_dsh_llm_llm_listProviders_result$schema,
132
+ description: 'detached provider metadata in registration order.',
133
+ jsonSchema: {"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Provider route key used by {@link GenerateOptions.provider}."},"name":{"type":"string","description":"Human-readable provider name for selectors and diagnostics."}},"required":["id","name"],"additionalProperties":true}},
103
134
  },
104
- sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":471,"column":3},
135
+ sourceLocation: {"file":"packages/llm/llm/src/index.ts","line":483,"column":3},
105
136
  },
106
- ],
107
- }
137
+ },
138
+ }, z)
108
139
 
109
140
  export default TYPERT_REMOTE
@@ -5,22 +5,35 @@
5
5
  * changed snapshots instead of allowing silent per-call drift.
6
6
  * @module dsh-llm/call-config
7
7
  */
8
- import type { GenerateOptions } from './types.ts';
8
+ import type { GenerateOptions, LlmResolvedModelInfo } from './types.ts';
9
9
  import type { ReasoningEffortId } from './brand.ts';
10
+ /** Concrete generation settings; omitted controls use the selected route's defaults. */
11
+ export interface LlmCallControls {
12
+ reasoningEffort?: ReasoningEffortId;
13
+ temperature?: number;
14
+ maxTokens?: number;
15
+ stop?: string[];
16
+ }
10
17
  /**
11
18
  * Provider, model, reasoning effort, and sampling scalars of one conversation's
12
19
  * requests. Every field maps 1:1 onto the same-named `GenerateOptions` field;
13
20
  * the loop builds requests from the logged header rather than accepting these
14
21
  * per call.
15
22
  */
16
- export interface LlmCallConfig {
23
+ export interface LlmCallConfig extends LlmCallControls {
17
24
  provider: string;
18
25
  model: string;
19
- reasoningEffort?: ReasoningEffortId;
20
- temperature?: number;
21
- maxTokens?: number;
22
- stop?: string[];
23
26
  }
27
+ /**
28
+ * Synchronous, pure configuration of one call before defaults and validation.
29
+ * Compose functions in the desired order; later writes replace earlier ones.
30
+ * The returned controls cannot change the captured route. Errors reject
31
+ * preparation before dispatch; only the resolved configuration is recordable.
32
+ * @param controls - detached, deeply frozen proposed controls, without defaults.
33
+ * @param model - detached, deeply frozen metadata from the captured adapter generation.
34
+ * @returns concrete controls; omitted fields receive the route's defaults.
35
+ */
36
+ export type ConfigureCall = (controls: Readonly<LlmCallControls>, model: Readonly<LlmResolvedModelInfo>) => LlmCallControls;
24
37
  /**
25
38
  * Effective config fields supplied by exact-model adapter resolution rather
26
39
  * than by the caller's request proposal.
@@ -10,9 +10,9 @@ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
10
10
  import type { GenerateOptions, LlmConfigurableProvider, LlmDiscoveredModel, LlmFailure, LlmImageRequestPricing, LlmModelContext, LlmModelDiscoveryRequest, LlmModelInfo, LlmResolvedModelInfo, LlmProviderInfo, ModelModality, StreamChunk, SystemPromptUpdate, ToolUpdate } from './types.ts';
11
11
  import type { ResolvedRetryPolicy } from './retry-policy.ts';
12
12
  import type { ProviderRequestId } from './brand.ts';
13
- import type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
13
+ import type { ConfigureCall, LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
14
14
  import { HarnessError } from './error.ts';
15
- import type { FileAttachmentRef } from '@deepseek-ai/dsh-attachment';
15
+ import type { FileAttachmentRef, ImageAttachmentRef, ImageRequestTarget } from '@deepseek-ai/dsh-attachment';
16
16
  export * from './attribution.ts';
17
17
  export * from './brand.ts';
18
18
  export * from './error.ts';
@@ -24,7 +24,7 @@ export * from './message.ts';
24
24
  export * from './retry-policy.ts';
25
25
  export { BlockAssembler } from './assembler.ts';
26
26
  export { callConfigEquals, isAgentLoopRequest, markAgentLoopRequest } from './call-config.ts';
27
- export type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
27
+ export type { ConfigureCall, LlmCallConfig, LlmCallConfigAdapterDefaults, LlmCallControls } from './call-config.ts';
28
28
  declare module '@deepseek-ai/cordis' {
29
29
  interface Context {
30
30
  llm: LlmRuntime;
@@ -150,6 +150,15 @@ export declare abstract class LlmAdapter {
150
150
  * @returns route-owned image pricing, or `undefined` when the route declares none.
151
151
  */
152
152
  imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
153
+ /**
154
+ * Resolve the image dimensions and byte target this adapter sends to the model.
155
+ * The default preserves normalized images; adapters that resize must override it.
156
+ * @param _provider - registered provider route.
157
+ * @param _model - exact model id.
158
+ * @param image - normalized image whose request projection is being resolved.
159
+ * @returns the same target used by request serialization, before provider-internal processing.
160
+ */
161
+ imageRequestTarget(_provider: string, _model: string, image: ImageAttachmentRef): ImageRequestTarget;
153
162
  /**
154
163
  * List models this adapter can currently advertise for one owned provider.
155
164
  * Core routing accepts unlisted model ids; catalog-driven entry points such
@@ -331,6 +340,14 @@ export declare class LlmRuntime extends TypertRemoteService {
331
340
  * @returns the owning adapter's image pricing for the route, when declared.
332
341
  */
333
342
  imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined;
343
+ /**
344
+ * Resolve the image projection for an exact registered model route.
345
+ * @param provider - registered provider route.
346
+ * @param model - exact model id.
347
+ * @param image - normalized image reference.
348
+ * @returns dimensions and byte target used by the adapter's request serializer.
349
+ */
350
+ imageRequestTarget(provider: string, model: string, image: ImageAttachmentRef): ImageRequestTarget;
334
351
  /**
335
352
  * Resolve the exact text one durable file occurrence contributes to every
336
353
  * provider request in the current execution environment.
@@ -379,11 +396,15 @@ export declare class LlmRuntime extends TypertRemoteService {
379
396
  * Resolve one call under its current adapter registration. The returned
380
397
  * one-shot handle keeps that registration across header logging and dispatch,
381
398
  * so HMR cannot combine one adapter's capability result with another adapter.
382
- * @param config - provider/model route and optional request controls.
399
+ * An optional synchronous callback selects concrete controls using captured
400
+ * model metadata. Defaults and validation apply to its result. Callback
401
+ * failures and cancellation reject preparation before dispatch.
402
+ * @param config - provider/model route and optional concrete request controls.
383
403
  * @param signal - optional cancellation for adapter-owned capability lookup.
404
+ * @param configure - pure control selection, called once before defaults and validation.
384
405
  * @returns a prepared config and its registration-bound stream entry point.
385
406
  */
386
- prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>;
407
+ prepareCall(config: LlmCallConfig, signal?: AbortSignal, configure?: ConfigureCall): Promise<PreparedLlmCall>;
387
408
  private registration;
388
409
  /** Remove replay state whose historical route is owned by another adapter. */
389
410
  private forAdapter;
@@ -167,6 +167,17 @@ export class LlmAdapter {
167
167
  imageRequestPricing(_provider, _model) {
168
168
  return undefined;
169
169
  }
170
+ /**
171
+ * Resolve the image dimensions and byte target this adapter sends to the model.
172
+ * The default preserves normalized images; adapters that resize must override it.
173
+ * @param _provider - registered provider route.
174
+ * @param _model - exact model id.
175
+ * @param image - normalized image whose request projection is being resolved.
176
+ * @returns the same target used by request serialization, before provider-internal processing.
177
+ */
178
+ imageRequestTarget(_provider, _model, image) {
179
+ return { width: image.width, height: image.height, maxBytes: image.bytes };
180
+ }
170
181
  /**
171
182
  * List models this adapter can currently advertise for one owned provider.
172
183
  * Core routing accepts unlisted model ids; catalog-driven entry points such
@@ -384,7 +395,7 @@ let LlmRuntime = (() => {
384
395
  || detached.some(seen => seen.provider === entry.provider)) {
385
396
  throw new LlmError(`configurable provider "${entry.provider}" is already declared`, 'DUPLICATE_DIRECTORY');
386
397
  }
387
- detached.push({ ...entry, settingsPath: [...entry.settingsPath] });
398
+ detached.push(detachDirectoryEntry(entry));
388
399
  }
389
400
  for (const entry of held)
390
401
  this.directory.delete(entry.provider);
@@ -420,7 +431,7 @@ let LlmRuntime = (() => {
420
431
  * @returns detached directory entries in declaration order.
421
432
  */
422
433
  listConfigurableProviders() {
423
- return [...this.directory.values()].map(entry => ({ ...entry, settingsPath: [...entry.settingsPath] }));
434
+ return [...this.directory.values()].map(detachDirectoryEntry);
424
435
  }
425
436
  /**
426
437
  * Offer to interrogate provider endpoints on behalf of the settings
@@ -525,6 +536,16 @@ let LlmRuntime = (() => {
525
536
  imageRequestPricing(provider, model) {
526
537
  return this.adapters.get(provider)?.adapter.imageRequestPricing(provider, model);
527
538
  }
539
+ /**
540
+ * Resolve the image projection for an exact registered model route.
541
+ * @param provider - registered provider route.
542
+ * @param model - exact model id.
543
+ * @param image - normalized image reference.
544
+ * @returns dimensions and byte target used by the adapter's request serializer.
545
+ */
546
+ imageRequestTarget(provider, model, image) {
547
+ return this.registration(provider).adapter.imageRequestTarget(provider, model, image);
548
+ }
528
549
  /**
529
550
  * Resolve the exact text one durable file occurrence contributes to every
530
551
  * provider request in the current execution environment.
@@ -716,24 +737,35 @@ let LlmRuntime = (() => {
716
737
  * Resolve one call under its current adapter registration. The returned
717
738
  * one-shot handle keeps that registration across header logging and dispatch,
718
739
  * so HMR cannot combine one adapter's capability result with another adapter.
719
- * @param config - provider/model route and optional request controls.
740
+ * An optional synchronous callback selects concrete controls using captured
741
+ * model metadata. Defaults and validation apply to its result. Callback
742
+ * failures and cancellation reject preparation before dispatch.
743
+ * @param config - provider/model route and optional concrete request controls.
720
744
  * @param signal - optional cancellation for adapter-owned capability lookup.
745
+ * @param configure - pure control selection, called once before defaults and validation.
721
746
  * @returns a prepared config and its registration-bound stream entry point.
722
747
  */
723
- async prepareCall(config, signal) {
724
- const registration = this.registration(config.provider);
725
- const adapterCall = await registration.adapter.prepareCall(config.provider, config.model, signal);
726
- const modelInfo = this.normalizeModelInfo(registration, config.model, adapterCall.model);
727
- const resolved = this.resolveCallWithInfo(config, modelInfo);
748
+ async prepareCall(config, signal, configure) {
749
+ signal?.throwIfAborted();
750
+ const { provider, model, ...controls } = config;
751
+ const registration = this.registration(provider);
752
+ const adapterCall = await registration.adapter.prepareCall(provider, model, signal);
753
+ signal?.throwIfAborted();
754
+ const modelInfo = this.normalizeModelInfo(registration, model, adapterCall.model);
755
+ const proposed = configure === undefined
756
+ ? config
757
+ : { ...configure(deepFreeze(structuredClone(controls)), deepFreeze(modelInfo)), provider, model };
758
+ signal?.throwIfAborted();
759
+ const resolved = this.resolveCallWithInfo(proposed, modelInfo);
728
760
  const resolvedConfig = deepFreeze(structuredClone(resolved.config));
729
761
  const context = resolved.context === undefined
730
762
  ? undefined
731
763
  : deepFreeze(structuredClone(resolved.context));
732
764
  const adapterDefaults = deepFreeze({
733
- ...config.reasoningEffort === undefined && resolvedConfig.reasoningEffort !== undefined
765
+ ...proposed.reasoningEffort === undefined && resolvedConfig.reasoningEffort !== undefined
734
766
  ? { reasoningEffort: true }
735
767
  : {},
736
- ...config.maxTokens === undefined && resolvedConfig.maxTokens !== undefined
768
+ ...proposed.maxTokens === undefined && resolvedConfig.maxTokens !== undefined
737
769
  ? { maxTokens: true }
738
770
  : {},
739
771
  });
@@ -936,5 +968,15 @@ function adapterFailureChunk(error, signal) {
936
968
  : { kind: 'error', failure },
937
969
  };
938
970
  }
971
+ /** Copy a directory entry, including its arrays, so neither side can mutate the other's copy. */
972
+ function detachDirectoryEntry(entry) {
973
+ const { nativeAuth } = entry;
974
+ return {
975
+ ...entry,
976
+ settingsPath: [...entry.settingsPath],
977
+ ...entry.baseURLEnv === undefined ? {} : { baseURLEnv: [...entry.baseURLEnv] },
978
+ ...(nativeAuth === undefined ? {} : { nativeAuth: { ...nativeAuth, env: [...nativeAuth.env] } }),
979
+ };
980
+ }
939
981
  export default LlmRuntime;
940
982
  //# sourceMappingURL=index.js.map
@@ -242,9 +242,26 @@ export interface LlmConfigurableProvider {
242
242
  * from outside.
243
243
  */
244
244
  declared?: boolean;
245
+ /**
246
+ * Route endpoint the adapter uses when the profile sets no `baseURL`; the
247
+ * adapter may still send an individual model to that model's own endpoint.
248
+ * Absent when the route has no fixed endpoint.
249
+ */
250
+ defaultBaseURL?: string;
251
+ /** Environment variables used to construct a provider endpoint; absent when none are reported. */
252
+ baseURLEnv?: readonly string[];
253
+ /** Authentication the adapter performs when the profile names no credential reference; absent when it reports none. */
254
+ nativeAuth?: LlmNativeAuth;
245
255
  /** Configuration diagnostic for repair; unaffected models may remain serviceable. */
246
256
  error?: string;
247
257
  }
258
+ /** Authentication a provider route performs without a credential reference in its profile. */
259
+ export interface LlmNativeAuth {
260
+ /** Environment variables the route's authentication reads, in lookup order; empty when it reads none. */
261
+ env: readonly string[];
262
+ /** Whether a stored OAuth sign-in can authenticate the route. */
263
+ oauth: boolean;
264
+ }
248
265
  /**
249
266
  * One interrogation of a provider endpoint that configuration has not stored
250
267
  * yet. Configuration surfaces send the draft a user is still editing, so the
@@ -348,7 +365,7 @@ export interface LlmReasoningEffortInfo {
348
365
  }
349
366
  /** Selectable reasoning efforts for one exact provider/model route. */
350
367
  export interface LlmModelReasoningInfo {
351
- /** Supported efforts in adapter-preferred display order. */
368
+ /** Supported efforts from least to greatest selectable reasoning effort, not predicted token use or latency. */
352
369
  efforts: readonly LlmReasoningEffortInfo[];
353
370
  /**
354
371
  * Adapter-configured default materialized into requests when callers omit
@@ -524,8 +541,8 @@ export interface GenerateOptions {
524
541
  sessionId?: Branded<'SessionId'>;
525
542
  /**
526
543
  * Provider-neutral classification for an auxiliary model call. Adapters may
527
- * map the purpose to model-hidden transport metadata or purpose-specific
528
- * generation policy. Ordinary conversation requests leave it unset.
544
+ * map the purpose to model-hidden transport metadata. Ordinary conversation
545
+ * requests leave it unset.
529
546
  */
530
547
  purpose?: 'compaction' | 'session-title';
531
548
  }
package/locale/en.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "meta": {
3
+ "title": "llm"
4
+ }
5
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "模型调用服务",
4
+ "description": "为不同模型提供统一的调用接口。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-llm",
3
3
  "description": "Provider-neutral LLM service interface for the DeepSeek Harness",
4
- "version": "0.2.1-alpha.1",
4
+ "version": "0.2.1-alpha.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -43,9 +43,11 @@
43
43
  "default": "./lib/typert.remote-client.js"
44
44
  },
45
45
  "./src/*": "./src/*",
46
- "./package.json": "./package.json"
46
+ "./package.json": "./package.json",
47
+ "./locale/*.json": "./locale/*.json"
47
48
  },
48
49
  "files": [
50
+ "locale/*.json",
49
51
  "lib/index.js",
50
52
  "lib/types/**/*.js",
51
53
  "lib/types/**/*.d.ts",
@@ -56,19 +58,19 @@
56
58
  ],
57
59
  "license": "MIT",
58
60
  "peerDependencies": {
59
- "@deepseek-ai/cordis": "~4.0.5-alpha.1"
61
+ "@deepseek-ai/cordis": "~4.0.5-alpha.2"
60
62
  },
61
63
  "dependencies": {
62
- "zod": "^4.4.3",
63
- "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.1",
64
- "@deepseek-ai/schemastery": "~3.18.5-alpha.1",
65
- "@deepseek-ai/dsh-timeout": "0.2.1-alpha.1",
66
- "@deepseek-ai/dsh-brand": "0.2.1-alpha.1",
67
- "@deepseek-ai/dsh-util-values": "0.2.1-alpha.1",
68
- "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.1"
64
+ "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.3",
65
+ "@deepseek-ai/schemastery": "~3.18.5-alpha.2",
66
+ "zod": "4.4.3",
67
+ "@deepseek-ai/dsh-timeout": "0.2.1-alpha.3",
68
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.3",
69
+ "@deepseek-ai/dsh-util-values": "0.2.1-alpha.3",
70
+ "@deepseek-ai/dsh-typert-protocol": "0.2.1-alpha.3"
69
71
  },
70
72
  "devDependencies": {
71
- "@deepseek-ai/dsh-attachment": "0.2.1-alpha.1",
72
- "@deepseek-ai/cordis": "~4.0.5-alpha.1"
73
+ "@deepseek-ai/dsh-attachment": "0.2.1-alpha.3",
74
+ "@deepseek-ai/cordis": "~4.0.5-alpha.2"
73
75
  }
74
76
  }