@deepseek-ai/dsh-headless 0.1.1-rc.1 → 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/bundle/headless/README.md
5
- README.md: 3d9ca350f5f8891e60cfc57c9ca89ef57d9790d3
6
- README.zh.md: 2c7ea71025aa68db08b10d9faff8f546d12911c6
5
+ README.md: 282fa76dd720751d61ba7f44e5e35884d5f21e8f
6
+ README.zh.md: 5a2727a25e6e4840900ebc9dc70d10ff13db12dd
package/README.md CHANGED
@@ -1,20 +1,136 @@
1
- # `@deepseek-ai/dsh-headless`
1
+ ---
2
+ description: "One-shot task mode for dsh: run a single task from the command line and get the final answer printed, for users scripting or automating dsh."
3
+ kind: "package-bundle"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-headless
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- The dsh one-shot bundle. [`cordis.patch.yml`](cordis.patch.yml) rides directly over [`dsh-base`](../base/README.md): it supplies the coding persona and tool mode, disables HMR, mounts Code Mode's worker as a core execution capability, and inserts this package's `headless-runner` plugin (config `{task}`, resolved from the injected `headlessStartup` provider). It mounts no Host, HTTP server, Web runtime, or browser plugin.
10
+ ## Summary
11
+
12
+ `dsh-headless` runs one dsh task from the command line and prints the final answer, then exits — no GUI, no server, no browser. Type `dsh --profile headless "run the tests"` and the agent works through the task with the same model, tools, and safety defaults as every other surface. It is ideal for scripts, CI, and one-off jobs: the process opens no ports and leaves nothing running behind. The exit code tells you the outcome — 0 when the task completed, 1 when it aborted or errored. The main boundary: one task per invocation, with no interactive follow-up.
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
+ Run one task, get the final answer, and exit. The task is the command line itself, so the whole invocation is the smallest working example.
29
+
30
+ ### Running a one-shot task
31
+
32
+ ```sh
33
+ dsh --profile headless "run the tests"
34
+ ```
35
+
36
+ The agent works through the task, streams each non-empty provider reasoning delta to stderr under a `dsh: reasoning:` heading, then prints the final answer on stdout and exits. Consecutive reasoning deltas stay in one section, and the runner closes that section before later output when the provider supplied no trailing newline. A successful run without reasoning keeps stderr empty; a failure exits 1 and prints `dsh: <code>: <message>` to stderr. A missing or blank task is rejected before anything runs. The task text is supplied through the single `task` setting:
37
+
38
+ | Field | Default | Meaning |
39
+ |---|---|---|
40
+ | `task` | required | The task text for the single run |
41
+
42
+ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-headless) is the exhaustive source for every accepted field and its JSDoc.
43
+
44
+ ### When to use it
45
+
46
+ Use headless for scripted or automated dsh runs — CI steps, batch jobs, quick answers from a terminal. Avoid it when you need a multi-turn interactive session or a GUI; the browser surface ([dsh-web-app](../web-app/README.md)) serves that. The process stays alive only for the run, opens no listening port, and exits on its own, so it fits pipelines that wait on the process.
47
+
48
+ ### Help and task errors
49
+
50
+ `dsh --profile headless --help` prints the command's help text and exits without running anything. A missing or whitespace-only task is a usage error: nothing runs and the process exits 1.
51
+
52
+ -----
53
+
54
+ <a id="understand-the-implementation"></a>
55
+ ## Understand the implementation
56
+
57
+ <details>
58
+ <summary>Implementation internals — click to expand</summary>
59
+
60
+ The runner is a direct driver over the core API carrier: it creates one fresh Agent through the registry and folds the owned durable event interval into one process-level outcome.
61
+
62
+ ### Run flow
6
63
 
7
- After the Loader settles, the runner reads the shared [`ctx.agentDefaultModel`](../../core/agent-default-model/README.md), creates one fresh persisted Agent through `ctx.agents`, submits the task as an ordinary user message, and waits for quiescence. It flushes the Session before folding the owned durable event interval, writes the last non-empty assistant text to stdout, and requests exit through the launcher-provided `ctx.appExit` host hook ([`dsh-cmdline`](../../boot/cmdline/README.md)) (final `turn/end` completed → 0, otherwise 1). A terminal `error` reason also writes its code and message to stderr; successful runs keep stderr empty. The process opens no listening port. The task text is this app's command line: the ordinary `headless-startup` provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the positional argument of `dsh --profile headless "task"`, prints the app's `--help`, and provides `headlessStartup`; the runner injects that service and reads its task from lazy config. A missing or whitespace-only task is rejected before the runner activates.
64
+ The runner awaits the complete application (`ctx.get('loader')?.await()`) so the composed tools and adapters are not half-mounted, reads the shared [`agentDefaultModel`](../../core/agent-default-model/README.md) selection, creates one fresh persisted Agent with that provider and model, and submits the task as an ordinary user message. It streams that Agent's non-empty reasoning deltas to stderr, waits for quiescence, then flushes the Session and folds the owned interval (`firstSeq` onward) into the last non-empty `assistant/message` text and final `turn/end` reason. It writes the final text to stdout and requests exit.
8
65
 
66
+ ### Patch surface over base
67
+
68
+ The patch rides over `dsh-base`: it inherits the projection cache, sets the coding persona on the base `system-prompt` row, keeps the same temporary process-wide PTC mode opt-in (`DSH_TOOLS_MODE`) as the Web surface, disables the shared HMR row, inserts PTC mode's worker as a core execution capability, and mounts the startup provider and the runner. The cache checkpoints each persisted one-shot session for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. The startup provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the positional argument, prints the app's `--help`, and provides `headlessStartup`; the runner injects that service and reads its task from lazy config.
69
+
70
+ ### Exit mapping
71
+
72
+ A completed final `turn/end` exits 0; any other outcome — aborted, error, or no turn in the owned interval — exits 1. An `error` reason also writes `dsh: <code>: <message>` to stderr. A direct driver failure (for example, Agent creation) writes `dsh: <message>` to stderr and exits 1.
73
+
74
+ ### Source map
75
+
76
+ | File | Role |
77
+ |---|---|
78
+ | [`src/index.ts`](src/index.ts) | The `headless-runner` plugin: run flow, output contract, exit mapping |
79
+ | [`src/startup.ts`](src/startup.ts) | The `headless-startup` provider: task positional and `--help` |
80
+ | [`cordis.patch.yml`](cordis.patch.yml) | The one-shot patch over `dsh-base` |
81
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion: no runtime invariant; the observable contract is process-level |
82
+ | [`tests/headless.spec.ts`](tests/headless.spec.ts) | Run flow, aggregation, flush, and exit mapping |
83
+ | [`tests/startup.spec.ts`](tests/startup.spec.ts) | Command-line parsing over a real Loader tree |
84
+
85
+ ### Invariant ownership
86
+
87
+ The invariant companion registers an empty installer because the runner's observable contract (final text on stdout, exit code by turn-end reason) is process-level and owned by the launcher e2e; the plugin registers nothing and holds no mutable relation to audit inside the tree.
88
+
89
+ </details>
90
+
91
+ -----
92
+
93
+ <a id="further-exploration"></a>
94
+ ## Further Exploration
95
+
96
+ Read these pages when you want to go deeper into the shared core, the sibling GUI, or the command-line handoff.
97
+
98
+ - [Bundle package map](../README.md) — the surfaces built on the same core.
99
+ - [dsh-base](../base/README.md) — the shared core headless runs on.
100
+ - [dsh-web-app](../web-app/README.md) — the interactive browser sibling for multi-turn work.
101
+ - [dsh-cmdline](../../boot/cmdline/README.md) — how the launcher hands the command line to the app.
102
+ - [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-headless) — every accepted config field and its source declaration.
103
+
104
+ -----
105
+
106
+ <a id="model-experience"></a>
9
107
  ## Model Experience
10
108
 
11
- None, as the runner submits the task as an ordinary user message; prompts and tools belong to the base and headless bundle rows.
109
+ None, as the runner submits the task as an ordinary user message and the composed base and headless rows own the prompts and tools.
12
110
 
13
111
  #### KV Cache effect
14
112
 
15
- None; the runner adds nothing to the request prefix.
113
+ The runner adds nothing to the request prefix; it only drives one user message through the composed tree.
16
114
 
17
115
  ## Known Limitations and Deferred Work
18
116
 
19
- - **One submitted task only** — the runner has no interactive follow-up surface; it waits through any work the Agent completes before returning to idle and prints the last non-empty assistant message in that interval.
20
- - **`ctx.appExit` is launcher-owned** — booting the headless profile outside the `dsh` launcher fails loud at activation until the host provides the exit request.
117
+ <a id="known-limitations-and-deferred-work"></a>
118
+
119
+
120
+ These limits tell you when headless does not fit and what it needs from the `dsh` launcher. They are current package constraints, not a general CLI comparison or a task backlog.
121
+
122
+ - **One task per run** — after the task is answered the process exits; there is no interactive follow-up, so split multi-step work into separate runs.
123
+ - **Runs through the `dsh` launcher** — starting the headless profile another way fails at startup, because only the launcher can request the process exit.
124
+ - **No pre-token heartbeat** — stderr stays silent until the provider emits a non-empty reasoning delta; a delayed first token exposes no earlier progress signal.
125
+ - **Reasoning enters stderr logs** — redirection and supervisors may retain substantially more and potentially sensitive model output; route stderr to a controlled sink when needed.
126
+ - **Only reasoning and the final answer are printed** — a run without an assistant message prints an empty stdout line and exits 1; intermediate tool output is not printed.
127
+
128
+ <a id="dev-note"></a>
129
+ ### Dev Note
130
+
131
+ <details>
132
+ <summary>Working context for maintainers — click to expand</summary>
133
+
134
+ None.
135
+
136
+ </details>
package/README.zh.md CHANGED
@@ -1,20 +1,136 @@
1
- # `@deepseek-ai/dsh-headless`
1
+ ---
2
+ description: "dsh 的一次性任务模式:从命令行运行单个任务并打印最终答案,供用户脚本化或自动化 dsh。"
3
+ kind: "package-bundle"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-headless
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- dsh 一次性任务组合包。[`cordis.patch.yml`](cordis.patch.yml) 直接叠加在 [`dsh-base`](../base/README.zh.md) 之上:提供编码 persona 和工具模式、禁用 HMR(热模块替换)、将 Code Mode 的 worker 作为核心执行能力挂载,并插入本包的 `headless-runner` 插件(配置为 `{task}`,从注入的 `headlessStartup` 提供方解析)。它不挂载任何 Host、HTTP server、Web runtime 或浏览器插件。
10
+ ## 概述
11
+
12
+ `dsh-headless` 从命令行运行一个 dsh 任务并打印最终答案,然后退出——没有 GUI、没有服务器、没有浏览器。输入 `dsh --profile headless "run the tests"`,agent(智能体)会以与其他表层相同的模型、工具与安全默认值完成该任务。它非常适合脚本、CI 与一次性任务:进程不打开任何端口,也不会留下任何后台运行的东西。退出码告诉你结果——任务完成时为 0,中止或出错时为 1。主要边界:每次调用只运行一个任务,没有交互式后续。
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
+ 运行一个任务,获得最终答案,然后退出。任务就是命令行本身,因此整条命令就是最小的可运行示例。
29
+
30
+ ### 运行一次性任务
31
+
32
+ ```sh
33
+ dsh --profile headless "run the tests"
34
+ ```
35
+
36
+ agent(智能体)会完成该任务,把提供方的每个非空推理增量流式写入 stderr 的 `dsh: reasoning:` 段,然后把最终答案写入 stdout 并退出。连续推理增量保持在同一段中;提供方未给尾换行时,runner 会在后续输出前结束该段。没有推理内容的成功运行保持 stderr 为空;失败时退出码为 1,并以 `dsh: <code>: <message>` 向 stderr 写入错误。缺失或空白任务会在任何内容运行前被拒绝。任务文本通过唯一的 `task` 设置提供:
37
+
38
+ | 字段 | 默认值 | 含义 |
39
+ |---|---|---|
40
+ | `task` | 必填 | 单次运行的任务文本 |
41
+
42
+ 生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-headless)是每个受支持字段及其 JSDoc 的穷尽式真源。
43
+
44
+ ### 何时使用
45
+
46
+ 在脚本化或自动化的 dsh 运行中使用 headless——CI 步骤、批处理任务、从终端快速获取答案。当需要多轮交互会话或 GUI 时请避免它;浏览器表层([dsh-web-app](../web-app/README.zh.md))负责这类场景。进程只为本次运行而存活,不打开监听端口,并且自行退出,因此适合等待进程结束的流水线。
47
+
48
+ ### 帮助与任务错误
49
+
50
+ `dsh --profile headless --help` 打印该命令的帮助文本并直接退出,不运行任何内容。缺失或只有空白的任务属于用法错误:什么都不运行,进程退出 1。
51
+
52
+ -----
53
+
54
+ <a id="understand-the-implementation"></a>
55
+ ## 理解实现
56
+
57
+ <details>
58
+ <summary>实现细节——点击展开</summary>
59
+
60
+ runner 是核心 API 载体之上的直接驱动器:它通过注册表创建一个全新的 Agent(智能体),并把所属的持久化事件区间折叠成一个进程级结果。
61
+
62
+ ### 运行流程
6
63
 
7
- Loader 结算后,runner 读取共享的 [`ctx.agentDefaultModel`](../../core/agent-default-model/README.zh.md),通过 `ctx.agents` 创建一个全新的持久化 Agent(智能体),将任务作为普通用户消息提交,并等待完全停稳。它对 Session 执行 flush 后再汇总自身持有的持久化事件区间,将最后一条非空 assistant 文本写入 stdout,再经启动器提供的 `ctx.appExit` 宿主钩子([`dsh-cmdline`](../../boot/cmdline/README.zh.md))请求退出(最终 `turn/end` 完成 → 0,否则为 1)。最终结束原因为 `error` 时,还会将 code 与 message 写入 stderr;成功运行时 stderr 保持为空。进程不会打开监听端口。任务文本就是这个应用的命令行:普通 `headless-startup` 提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取 `dsh --profile headless "task"` 的位置参数、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 注入该服务,再从惰性配置中读取任务。缺失或只有空白的任务会在 runner 激活前被拒绝。
64
+ runner 等待整个应用结算(`ctx.get('loader')?.await()`),确保已组合的工具与适配器不会半挂载,读取共享的 [`agentDefaultModel`](../../core/agent-default-model/README.zh.md) 选择,用该 provider 与模型创建一个全新的持久化 Agent(智能体),并把任务作为普通用户消息提交。它把该 Agent 的非空推理增量流式写入 stderr、等待完全停稳,然后 flush Session,并把所属区间(从 `firstSeq` 起)折叠为最后一条非空 `assistant/message` 文本与最终 `turn/end` 原因。最后,它把最终文本写入 stdout 并请求退出。
8
65
 
66
+ ### 叠加在 base 之上的 patch 表层
67
+
68
+ patch 叠加在 `dsh-base` 之上:继承投影缓存,在基础 `system-prompt` 行上设置编码 persona,保留与 Web 表层相同的临时进程级 PTC mode 开关(`DSH_TOOLS_MODE`),禁用共享的 HMR 行,把 PTC mode 的 worker 作为核心执行能力插入,并挂载启动提供方与 runner。缓存为每个已持久化的一次性会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 行。启动提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取位置参数、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 注入该服务,再从惰性配置中读取任务。
69
+
70
+ ### 退出映射
71
+
72
+ 最终 `turn/end` 完成时退出码为 0;任何其他结果——aborted、error,或所属区间内没有轮次——退出码为 1。结束原因为 `error` 时还会向 stderr 写入 `dsh: <code>: <message>`。直接驱动器失败(例如 Agent 创建失败)向 stderr 写入 `dsh: <message>` 并退出 1。
73
+
74
+ ### 源码地图
75
+
76
+ | 文件 | 职责 |
77
+ |---|---|
78
+ | [`src/index.ts`](src/index.ts) | `headless-runner` 插件:运行流程、输出约定、退出映射 |
79
+ | [`src/startup.ts`](src/startup.ts) | `headless-startup` 提供方:任务位置参数与 `--help` |
80
+ | [`cordis.patch.yml`](cordis.patch.yml) | 叠加在 `dsh-base` 之上的一次性 patch |
81
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件:无运行时不变式;可观察约定是进程级的 |
82
+ | [`tests/headless.spec.ts`](tests/headless.spec.ts) | 运行流程、汇总、flush 与退出映射 |
83
+ | [`tests/startup.spec.ts`](tests/startup.spec.ts) | 在真实 Loader 树上的命令行解析 |
84
+
85
+ ### 不变式归属
86
+
87
+ 不变式伴生插件注册一个空安装器,因为 runner 的可观察约定(stdout 的最终文本、按轮次结束原因决定的退出码)是进程级的、由启动器 e2e 负责;插件不注册任何内容,树内也没有任何可变关系可审计。
88
+
89
+ </details>
90
+
91
+ -----
92
+
93
+ <a id="further-exploration"></a>
94
+ ## 进一步探索
95
+
96
+ 当你想深入了解共享核心、兄弟 GUI 或命令行交接时,阅读以下页面。
97
+
98
+ - [组合包包映射](../README.zh.md)——基于同一核心构建的表层。
99
+ - [dsh-base](../base/README.zh.md)——headless 运行其上的共享核心。
100
+ - [dsh-web-app](../web-app/README.zh.md)——用于多轮工作的交互式浏览器兄弟表层。
101
+ - [dsh-cmdline](../../boot/cmdline/README.zh.md)——启动器如何把命令行交给应用。
102
+ - [生成配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-headless)——每个受支持配置字段及其源声明。
103
+
104
+ -----
105
+
106
+ <a id="model-experience"></a>
9
107
  ## 模型体验
10
108
 
11
- 无影响,因为 runner 把任务作为普通用户消息提交;提示词与工具由 base 和 headless 组合包中的相应条目提供。
109
+ 无,因为 runner 把任务作为普通用户消息提交,提示词与工具由组合出的 base 与 headless 行提供。
12
110
 
13
111
  #### KV Cache 影响
14
112
 
15
- 无;runner 不向请求前缀添加任何内容。
113
+ runner 不向请求前缀添加任何内容;它只是把一条用户消息驱动经过组合出的配置树。
114
+
115
+ ## 已知限制与延期工作
116
+
117
+ <a id="known-limitations-and-deferred-work"></a>
118
+
119
+
120
+ 这些限制告诉你 headless 何时不适用、它需要 `dsh` 启动器提供什么。它们是当前包约束,不是通用的 CLI 对比或任务积压。
121
+
122
+ - **每次运行一个任务**——任务得到回答后进程即退出;没有交互式后续,因此多步工作请拆成多次运行。
123
+ - **通过 `dsh` 启动器运行**——以其他方式启动 headless profile 会在启动时失败,因为只有启动器能请求进程退出。
124
+ - **首个 token 前没有心跳**——提供方发出第一个非空推理增量前,stderr 保持静默;延迟首个 token 的提供方不会更早给出进度信号。
125
+ - **推理进入 stderr 日志**——重定向与监督进程可能保留更多且可能敏感的模型输出;需要时应把 stderr 路由到受控位置。
126
+ - **只打印推理和最终答案**——没有 assistant 消息的运行向 stdout 打印空行并以 1 退出;中间工具输出不会打印。
127
+
128
+ <a id="dev-note"></a>
129
+ ### 开发备注
130
+
131
+ <details>
132
+ <summary>维护者的工作上下文——点击展开</summary>
16
133
 
17
- ## 已知限制与暂缓事项
134
+ 无。
18
135
 
19
- - **只提交一个任务**:runner 没有用于交互式后续输入的 surface;它会等待 Agent 在返回 idle 前完成的所有工作,并打印该区间内最后一条非空 assistant 消息。
20
- - **`ctx.appExit` 由启动器持有**:在 `dsh` 启动器之外启动 headless profile 会在激活时明确报错,直到宿主提供该退出请求。
136
+ </details>
package/cordis.patch.yml CHANGED
@@ -9,18 +9,13 @@
9
9
  persona: >-
10
10
  You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.
11
11
 
12
- # The shared module-reload HMR row stays off; the launcher's watch-only
13
- # fallback still keeps the user patch layers live until the run exits.
14
- - id: hmr
15
- disabled: true
16
-
17
12
  - id: tools
18
13
  config:
19
- # Keep the same temporary process-wide Code Mode opt-in as the Web surface.
14
+ # Keep the same temporary process-wide PTC mode opt-in as the Web surface.
20
15
  mode: !!js process.env.DSH_TOOLS_MODE
21
16
 
22
17
  - insert:
23
- # Code Mode is a core execution capability, not a Web component.
18
+ # PTC mode is a core execution capability, not a Web component.
24
19
  - id: code-runtime
25
20
  name: '@deepseek-ai/dsh-code-runtime-worker-thread'
26
21
 
package/lib/index.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import z from "@deepseek-ai/schemastery";
3
+ import { brandString } from "@deepseek-ai/dsh-brand";
3
4
  import { installModelSelection } from "@deepseek-ai/dsh-agent";
4
5
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
5
- import { SessionId } from "@deepseek-ai/dsh-session";
6
+ import { assertNever } from "@deepseek-ai/dsh-util-values";
6
7
  //#region lib/types/index.js
7
8
  /**
8
9
  * @deepseek-ai/dsh-headless — one-shot direct Agent driver. The bundle patch
9
10
  * rides over dsh-base without Host, HTTP, or browser plugins; this runner
10
11
  * creates one Agent through the core registry, drives the task to quiescence,
11
- * flushes its Session, prints the final assistant text, and exits.
12
+ * streams provider reasoning to stderr, flushes its Session, prints the final
13
+ * assistant text to stdout, and exits.
12
14
  *
13
15
  * @module @deepseek-ai/dsh-headless
14
16
  */
@@ -49,6 +51,64 @@ function summarize(events, firstSeq) {
49
51
  reason
50
52
  };
51
53
  }
54
+ /**
55
+ * Project provider-reported reasoning from one owned run to stderr as it is
56
+ * appended, while keeping final outcome derivation on the durable log.
57
+ * @param ctx - plugin context carrying the Session event feed.
58
+ * @param agent - the exact Agent whose reasoning belongs to this invocation.
59
+ * @param stderr - progress output sink.
60
+ * @returns a disposer that also terminates an unterminated reasoning line.
61
+ */
62
+ function streamReasoning(ctx, agent, stderr) {
63
+ let started = false;
64
+ let open = false;
65
+ let endsWithNewline = true;
66
+ const close = () => {
67
+ if (!open) return;
68
+ if (!endsWithNewline) stderr.write("\n");
69
+ open = false;
70
+ endsWithNewline = true;
71
+ };
72
+ const dispose = ctx.on("session/event", (session, event) => {
73
+ if (session !== agent.session) return;
74
+ if (event.type === "turn/start") {
75
+ close();
76
+ started = true;
77
+ return;
78
+ }
79
+ if (!started || event.type !== "assistant/chunk") return;
80
+ const chunk = event.data.chunk;
81
+ switch (chunk.type) {
82
+ case "reasoning-delta":
83
+ if (chunk.text === "") return;
84
+ if (!open) {
85
+ stderr.write("dsh: reasoning:\n");
86
+ open = true;
87
+ }
88
+ stderr.write(chunk.text);
89
+ endsWithNewline = chunk.text.endsWith("\n");
90
+ return;
91
+ case "block-start":
92
+ if (chunk.blockType !== "reasoning") close();
93
+ return;
94
+ case "block-end":
95
+ if (chunk.block.type !== "reasoning") close();
96
+ return;
97
+ case "usage": return;
98
+ case "text-delta":
99
+ case "tool-call-delta":
100
+ case "finish":
101
+ close();
102
+ return;
103
+ /* v8 ignore next -- closed-union exhaustiveness guard */
104
+ default: return assertNever(chunk, "headless reasoning stream");
105
+ }
106
+ });
107
+ return () => {
108
+ dispose();
109
+ close();
110
+ };
111
+ }
52
112
  /** Report an unexpected direct-driver failure and request a failing exit. */
53
113
  function fail(io, error) {
54
114
  io.stderr.write(`dsh: ${error instanceof Error ? error.message : String(error)}\n`);
@@ -68,7 +128,7 @@ async function run(ctx, task, io) {
68
128
  if (agents === void 0 || defaultModel === void 0 || sessions === void 0) return;
69
129
  const selection = defaultModel.currentSelection();
70
130
  const { agent } = await agents.create({
71
- sessionId: SessionId(`session-${randomUUID()}`),
131
+ sessionId: brandString(`session-${randomUUID()}`),
72
132
  meta: { cwd: process.cwd() },
73
133
  agentOptions: {
74
134
  provider: selection.provider,
@@ -83,14 +143,19 @@ async function run(ctx, task, io) {
83
143
  });
84
144
  await agent.whenIdle();
85
145
  const firstSeq = agent.session.seq;
86
- agent.followup(createUserMessage({
87
- content: [{
88
- type: "text",
89
- text: task
90
- }],
91
- source: { kind: "user" }
92
- }));
93
- await agent.whenIdle();
146
+ const stopReasoning = streamReasoning(ctx, agent, io.stderr);
147
+ try {
148
+ agent.followup(createUserMessage({
149
+ content: [{
150
+ type: "text",
151
+ text: task
152
+ }],
153
+ source: { kind: "user" }
154
+ }));
155
+ await agent.whenIdle();
156
+ } finally {
157
+ stopReasoning();
158
+ }
94
159
  await sessions.flush(agent.session);
95
160
  const outcome = summarize(agent.session.events, firstSeq);
96
161
  io.stdout.write(outcome.text + "\n");
package/lib/invariant.js CHANGED
@@ -9,10 +9,10 @@ const name = "headless-invariant";
9
9
  /** Service required before the companion can register. */
10
10
  const inject = ["invariants"];
11
11
  /**
12
- * No runtime invariant: the runner is a one-shot driver over the API carrier
13
- * whose observable contract (final text on stdout, exit code by turn-end
14
- * reason) is process-level and owned by the launcher e2e; it registers
15
- * nothing and holds no mutable relation to audit inside the tree.
12
+ * No runtime invariant: the runner's observable contract (provider reasoning
13
+ * on stderr, final text on stdout, exit code by turn-end reason) is
14
+ * process-level and owned by the launcher e2e; it registers nothing and holds
15
+ * no mutable relation to audit inside the tree.
16
16
  */
17
17
  const install = () => {};
18
18
  /**
package/lib/startup.js CHANGED
@@ -18,7 +18,7 @@ const HEADLESS_STARTUP_SERVICE = "headlessStartup";
18
18
  * @returns a fresh program, so one process can parse more than once (tests).
19
19
  */
20
20
  function headlessCommand() {
21
- return new Command().name("dsh --profile headless").description("Answer one task, print the final assistant message, and exit.").helpOption("-h, --help", "show this help").argument("[task...]", "the task text; multiple words are joined by spaces").addHelpText("after", `
21
+ return new Command().name("dsh --profile headless").description("Answer one task, stream reasoning to stderr, print the final assistant message, and exit.").helpOption("-h, --help", "show this help").argument("[task...]", "the task text; multiple words are joined by spaces").addHelpText("after", `
22
22
  Examples:
23
23
  dsh --profile headless "run the tests" answer one task and exit
24
24
  `);
@@ -2,7 +2,8 @@
2
2
  * @deepseek-ai/dsh-headless — one-shot direct Agent driver. The bundle patch
3
3
  * rides over dsh-base without Host, HTTP, or browser plugins; this runner
4
4
  * creates one Agent through the core registry, drives the task to quiescence,
5
- * flushes its Session, prints the final assistant text, and exits.
5
+ * streams provider reasoning to stderr, flushes its Session, prints the final
6
+ * assistant text to stdout, and exits.
6
7
  *
7
8
  * @module @deepseek-ai/dsh-headless
8
9
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-headless",
3
3
  "description": "The dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layer",
4
- "version": "0.1.1-rc.1",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -45,26 +45,28 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "commander": "^15.0.0",
48
- "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.1-rc.1",
49
- "@deepseek-ai/dsh-cmdline": "^0.1.1-rc.1",
50
- "@deepseek-ai/schemastery": "^3.18.1"
48
+ "@deepseek-ai/dsh-brand": "^0.1.2-alpha.2",
49
+ "@deepseek-ai/dsh-cmdline": "^0.1.2-alpha.2",
50
+ "@deepseek-ai/dsh-code-runtime-worker-thread": "^0.1.2-alpha.2",
51
+ "@deepseek-ai/dsh-util-values": "^0.1.2-alpha.2",
52
+ "@deepseek-ai/schemastery": "^3.18.2"
51
53
  },
52
54
  "peerDependencies": {
53
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
54
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.1",
55
- "@deepseek-ai/dsh-agent-default-model": "^0.1.1-rc.1",
56
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.1",
57
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.1",
58
- "@deepseek-ai/dsh-session": "^0.1.1-rc.1",
59
- "@deepseek-ai/cordis": "^4.0.1"
55
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
56
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.2",
57
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
58
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
59
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
60
+ "@deepseek-ai/cordis": "^4.0.2",
61
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2"
60
62
  },
61
63
  "devDependencies": {
62
- "@deepseek-ai/cordis-plugin-loader": "^1.0.2",
63
- "@deepseek-ai/dsh-agent-default-model": "^0.1.1-rc.1",
64
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.1",
65
- "@deepseek-ai/dsh-llm": "^0.1.1-rc.1",
66
- "@deepseek-ai/dsh-session": "^0.1.1-rc.1",
67
- "@deepseek-ai/cordis": "^4.0.1",
68
- "@deepseek-ai/dsh-agent": "^0.1.1-rc.1"
64
+ "@deepseek-ai/cordis": "^4.0.2",
65
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
66
+ "@deepseek-ai/dsh-agent-default-model": "^0.1.2-alpha.2",
67
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
68
+ "@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
69
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
70
+ "@deepseek-ai/dsh-agent": "^0.1.2-alpha.2"
69
71
  }
70
72
  }