dsh-baton-mcp 0.6.0 → 0.6.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.md +66 -28
- package/README.zh-CN.md +66 -28
- package/host/lib/client.js +40 -4
- package/host/lib/index.js +156 -6
- package/host/package.json +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/cli.mjs +16 -3
- package/src/codex-hook.mjs +191 -0
- package/src/core.mjs +43 -1
- package/src/mcp.mjs +45 -28
- package/src/register.mjs +249 -3
- package/src/setup.mjs +14 -1
package/README.md
CHANGED
|
@@ -15,11 +15,11 @@ Claude Code and Codex each hand a task to DeepSeek Harness, and its subagents bu
|
|
|
15
15
|
https://github.com/user-attachments/assets/72e2fdff-275e-48bc-8e5a-39ffb025a0a8
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
<img src="./assets/readme/features.svg" width="100%" alt="Create workspaces and sessions, continue a session, run live in the Harness UI labelled with the sender,
|
|
18
|
+
<img src="./assets/readme/features.svg" width="100%" alt="Create workspaces and sessions, continue a session, run live in the Harness UI labelled with the sender, Claude Code and Codex woken when a run ends, no extra API key and any Harness model, macOS and Windows with zero dependencies">
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
22
|
-
<img src="./assets/readme/flow.gif" width="100%" alt="Typical flow: create a workspace, send the task, wait
|
|
22
|
+
<img src="./assets/readme/flow.gif" width="100%" alt="Typical flow: create a workspace, send the task, wait without polling (Claude Code and Codex are woken when the run ends), follow up in the same session">
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
## See it run
|
|
@@ -41,7 +41,7 @@ Codex and Claude take turns in the same Harness session, and every message is la
|
|
|
41
41
|
## Quick start
|
|
42
42
|
|
|
43
43
|
<p align="center">
|
|
44
|
-
<img src="./assets/readme/install.svg" width="100%" alt="Step 1: in Harness open Plugins, Add plugin, enter dsh-baton-host, Install, Enable now; it registers dsh-baton for Claude Code and Codex. Step 2: in a new chat, ask for dsh_list_workspaces">
|
|
44
|
+
<img src="./assets/readme/install.svg" width="100%" alt="Step 1: in Harness open Plugins, Add plugin, enter dsh-baton-host, Install, Enable now; it registers dsh-baton for Claude Code and Codex. Step 2, Codex only, once: Settings, Hooks, trust dsh-baton: waiting for the Harness run. Step 3: in a new chat, ask for dsh_list_workspaces">
|
|
45
45
|
</p>
|
|
46
46
|
|
|
47
47
|
Requires DeepSeek Harness ≥ 0.2.1-rc.2 (signed in) and Claude Code or Codex. Nothing else: no Node.js, no terminal. The steps are the same on macOS and Windows.
|
|
@@ -50,7 +50,9 @@ Requires DeepSeek Harness ≥ 0.2.1-rc.2 (signed in) and Claude Code or Codex. N
|
|
|
50
50
|
|
|
51
51
|
Switched on, the plugin registers dsh-baton for Claude Code and Codex, for every folder, and Harness says so.
|
|
52
52
|
|
|
53
|
-
**2.
|
|
53
|
+
**2. Codex only, once:** Codex **Settings → Hooks** → **Trust** **dsh-baton: waiting for the Harness run**. The hook comes with plugin 0.6.0 and later (0.6.2 and later when you have no Node.js), and shows up in that list only after you restart Harness and then Codex once, after installing or updating the plugin. Node.js is not required: without one it runs on Harness's own runtime, like the server. Codex is then woken when a run ends instead of checking every 270 s. You trust it once: updates and Harness restarts do not ask again. Skip it and Codex waits with `dsh_wait` as before; the first hand-over reminds you.
|
|
54
|
+
|
|
55
|
+
**3. Check:** in a **new** Claude Code or Codex conversation, ask it to call `dsh_list_workspaces`. It lists your Harness workspaces.
|
|
54
56
|
|
|
55
57
|
That is all. If Harness says it could not register, run `npx -y dsh-baton-mcp setup` in a terminal (this needs Node.js ≥ 22.15); it does the same registration. Updating, removing and Windows details are [further down](#install-details).
|
|
56
58
|
|
|
@@ -66,9 +68,12 @@ That is all. If Harness says it could not register, run `npx -y dsh-baton-mcp se
|
|
|
66
68
|
|
|
67
69
|
**How each client waits** (nothing to configure):
|
|
68
70
|
- **Claude Code** waits for a run in the background (the `wait_command` that `dsh_send` returns) and is woken once, when the run ends.
|
|
69
|
-
- **Codex**
|
|
71
|
+
- **Codex** with the dsh-baton Stop hook trusted (from 0.6.1): after `dsh_send` it ends its turn; the hook waits for the run outside the model and, when the run ends or asks something, continues the same conversation with the result. One model request per run instead of one every 270 s.
|
|
72
|
+
- **Codex** without the hook waits inside `dsh_wait`, up to 270 s per call, just under its 300 s tool timeout. An unfinished wait returns only the state, so Codex calls again without summarising. Codex gets no `wait_command`: its terminal cannot wake it, so it would poll.
|
|
70
73
|
- Codex asks you to approve MCP tool calls: approve the `dsh-baton` tools. For unattended `codex exec` runs, add `--approve-for-me`.
|
|
71
74
|
|
|
75
|
+
**Pick a model** (from 0.6.1): just say which one, for example "hand it to DeepSeek, V4 Pro". `dsh_send` passes it as `model` (and `reasoning_effort`), matched against the models Harness lists, so a model Harness adds later works as soon as it shows up. It applies to that session only; Harness's default model stays as it was. `dsh_list_models` shows what is available.
|
|
76
|
+
|
|
72
77
|
**How a hand-over is checked** (from 0.6.0)
|
|
73
78
|
|
|
74
79
|
You don't have to take DeepSeek's word for it. Before the run, dsh-baton notes what is in the folder. When the run ends it shows exactly which files were created, changed or deleted, and runs your project's own tests itself (`npm test`, or whatever the client passes as `checks`). A second, read-only DeepSeek agent then goes through the request point by point and ends with a table: requirement, met or not, and where (file:line). Claude or GPT reads just that, sends anything missing back in one message, and opens code only where a row says "not met". Unless `cwd` names another folder, the new Harness session starts in the folder you're already working in.
|
|
@@ -79,45 +84,72 @@ You don't have to take DeepSeek's word for it. Before the run, dsh-baton notes w
|
|
|
79
84
|
|
|
80
85
|
## Token usage
|
|
81
86
|
|
|
82
|
-
**In short: hand over the big, long jobs.** A feature that spans many files, a large refactor, a whole small app: that is where it saves the most. Claude Code
|
|
87
|
+
**In short: hand over the big, long jobs.** A feature that spans many files, a large refactor, a whole small app: that is where it saves the most. Claude Code spends 51–72% less in total, DeepSeek included. Codex now saves on both test tasks (Sol 40% and Astra 81% less on the web app), and a GPT-6 Astra web-app task takes 3% of a ChatGPT Plus 5-hour window instead of 22%. On a longer job, a three-phase kanban app (Task 4), Astra spends 73% less in total and uses about 16% of the window instead of 86%.
|
|
83
88
|
|
|
84
|
-
**The honest part:**
|
|
89
|
+
**The honest part:** on the small CLI task, Codex on GPT-6.1 Sol is cheap enough that doing it alone costs a little less in total: the hand-over came out 15% more, almost all of it DeepSeek's share. The hand-over pays off when you want Claude or GPT to plan and review, a cheaper model to do the typing, and you'd rather not keep hitting the 5-hour limit.
|
|
85
90
|
|
|
86
91
|
**Who this is for:** most of us pay for Claude or ChatGPT with a plan (Plus, Pro, Max 5x…), while DeepSeek is pay-as-you-go from a balance you top up, so every hand-over is money spent outside your plan. If your plan's limits already cover how you work, you don't need this: keep it simple and keep the money. If you're like me and keep running long, messy jobs with round after round of changes, and keep hitting the limit, a little DeepSeek spend gets you a lot more out of the plan you already pay for.
|
|
87
92
|
|
|
93
|
+
**The two test tasks**, asked the way a user would (the requests are in Chinese, linked as written):
|
|
94
|
+
- **Task 1 · CLI tool:** a command-line task manager in Node, no dependencies, split into modules, with tests. [Request](docs/benchmarks/token-usage-2026-10/natural/medium.md) · [acceptance check](docs/benchmarks/token-usage-2026-10/checks/natural-medium.mjs)
|
|
95
|
+
- **Task 2 · Web app:** a task manager with a Node server and a page, REST API, stats, tests and a README. [Request](docs/benchmarks/token-usage-2026-10/natural/large.md) · [acceptance check](docs/benchmarks/token-usage-2026-10/checks/natural-large.mjs)
|
|
96
|
+
|
|
88
97
|
<p align="center">
|
|
89
|
-
<img src="./docs/benchmarks/token-usage-2026-10/charts/natural-combined.svg" width="100%" alt="Cost per task, working alone vs handed over with 0.6.
|
|
98
|
+
<img src="./docs/benchmarks/token-usage-2026-10/charts/natural-combined.svg" width="100%" alt="Cost per task, working alone vs handed over with 0.6.2, Task 1 · CLI tool and Task 2 · Web app: Claude Code (Opus 5.5) and Codex (GPT-6.1 Sol, GPT-6 Astra), each on its own scale">
|
|
90
99
|
</p>
|
|
91
100
|
|
|
92
101
|
Cost per task at list price, DeepSeek V4.1 Flash 1017 at its peak price:
|
|
93
102
|
|
|
94
103
|
| Client · model | Task | Alone | Handed over, client only | Handed over, with DeepSeek |
|
|
95
104
|
|---|---|---|---|---|
|
|
96
|
-
| Claude Code · Opus 5.5 high |
|
|
97
|
-
| Claude Code · Opus 5.5 high |
|
|
98
|
-
| Codex · GPT-6.1 Sol high |
|
|
99
|
-
| Codex · GPT-6.1 Sol high |
|
|
100
|
-
| Codex · GPT-6 Astra high |
|
|
105
|
+
| Claude Code · Opus 5.5 high | Task 1 · CLI tool | $1.07 | $0.42 (−61%) | $0.52 (−51%) |
|
|
106
|
+
| Claude Code · Opus 5.5 high | Task 2 · Web app | $2.13 | $0.47 (−78%) | $0.60 (−72%) |
|
|
107
|
+
| Codex · GPT-6.1 Sol high | Task 1 · CLI tool | $0.17 | $0.06 (−63%) | $0.20 (+15%) |
|
|
108
|
+
| Codex · GPT-6.1 Sol high | Task 2 · Web app | $0.41 | $0.06 (−84%) | $0.25 (−40%) |
|
|
109
|
+
| Codex · GPT-6 Astra high | Task 1 · CLI tool | $0.79 | $0.30 (−62%) | $0.42 (−47%) |
|
|
110
|
+
| Codex · GPT-6 Astra high | Task 2 · Web app | $2.27 | $0.30 (−87%) | $0.42 (−81%) |
|
|
111
|
+
|
|
112
|
+
<p align="center">
|
|
113
|
+
<img src="./docs/benchmarks/token-usage-2026-10/charts/natural-quota-combined.svg" width="80%" alt="Share of the ChatGPT Plus 5-hour window per task in Codex, working alone vs handed over with 0.6.2: GPT-6.1 Sol and GPT-6 Astra">
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
Share of the ChatGPT Plus 5-hour window per task: Task 1 Sol 1.9% → ≈0.6%, Task 2 Sol 4.3% → ≈0.6%, Task 1 Astra 7% → 3%, Task 2 Astra 22% → 3% (≈ estimated from cost).
|
|
117
|
+
|
|
118
|
+
**Why it got cheaper:** from 0.6.1 Codex is woken by its Stop hook instead of polling with `dsh_wait`, and 0.6.2 cuts the MCP instructions to one sentence, so Codex reads a few thousand to over ten thousand fewer new input tokens per hand-over.
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
**Task 4 · Kanban app (phased):** a kanban board in plain Node and a plain front end, given in three phases in the same conversation: ① the core (REST API, storage, drag and drop); ② labels, due dates, search syntax, archive, undo and redo, activity log, import and export; ③ version conflicts, WIP limits, bulk actions, live sync over SSE, stats. Each phase has acceptance tests written beforehand, the same for both arms, 55 in all. Requests: [phase 1](docs/benchmarks/token-usage-2026-10/xxl/seed/docs/phase1.md) · [phase 2](docs/benchmarks/token-usage-2026-10/xxl/seed/docs/phase2.md) · [phase 3](docs/benchmarks/token-usage-2026-10/xxl/seed/docs/phase3.md) · [acceptance tests](docs/benchmarks/token-usage-2026-10/xxl/seed/tests/acceptance/) · [checker](docs/benchmarks/token-usage-2026-10/xxl/check.mjs) · [how it was run](docs/benchmarks/token-usage-2026-10/xxl/README.md)
|
|
121
|
+
|
|
122
|
+
<p align="center">
|
|
123
|
+
<img src="./docs/benchmarks/token-usage-2026-10/charts/task4-kanban.svg" width="100%" alt="Task 4 · Kanban app (phased), Codex GPT-6 Astra high: $8.69 alone vs $2.36 handed over with 0.6.2 ($1.62 client, −81%); 86% of the 5-hour window vs about 16%">
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
| Codex · GPT-6 Astra high | Alone | Handed over (0.6.2) |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| Client | $8.69 | $1.62 (−81%) |
|
|
129
|
+
| DeepSeek | – | $0.74 |
|
|
130
|
+
| Total | $8.69 | $2.36 (−73%) |
|
|
131
|
+
| 5-hour window | 86% | about 16% (−81%) |
|
|
132
|
+
| Acceptance tests | 55/55, first try | 55/55, first try |
|
|
133
|
+
| Time | about 31 min | about 87 min |
|
|
134
|
+
| Lines of code | 1,876 | 9,953 |
|
|
135
|
+
|
|
136
|
+
Task 3, the same kanban app with all three phases in one request, is being measured with Opus and Sol; its results come in the next update.
|
|
103
137
|
|
|
104
138
|
<details>
|
|
105
139
|
<summary>What was tested, and how</summary>
|
|
106
140
|
|
|
107
|
-
- **
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- **The only difference** in the hand-over runs is one line added at the end: 交给 DeepSeek Harness 去做。 ("Hand it to DeepSeek Harness.")
|
|
111
|
-
- Same model and effort in both arms, three runs per cell (Astra: one run, medium only), and every run judged by the same acceptance check, which the model never sees. All 12 hand-over runs passed it the first time.
|
|
141
|
+
- **The only difference** in the hand-over runs is one line added at the end of the request: 交给 DeepSeek Harness 去做。 ("Hand it to DeepSeek Harness.")
|
|
142
|
+
- Same model and effort in both arms, and every run judged by the same acceptance check, which the model never sees.
|
|
143
|
+
- Working alone: the earlier mean of three runs per cell (Astra: one run each). Handed over with 0.6.2: one run per cell; all six passed the first time, with no rework and no `dsh_wait` call.
|
|
112
144
|
- Prices are list prices. DeepSeek is counted at its peak price, the most conservative case; the real DeepSeek bill came to about 58% of that.
|
|
113
|
-
- Every run
|
|
145
|
+
- Every run: [run log](docs/benchmarks/token-usage-2026-10/LOG.md) (section "0.6.2 hand-over rerun"); raw data in `docs/benchmarks/token-usage-2026-10/results/*-n062*.jsonl`; method and earlier builds in the [full report](docs/benchmarks/token-usage-2026-10.md).
|
|
114
146
|
|
|
115
147
|
</details>
|
|
116
148
|
|
|
117
149
|
## Tools
|
|
118
150
|
|
|
119
151
|
<p align="center">
|
|
120
|
-
<img src="./assets/readme/tools.svg" width="100%" alt="
|
|
152
|
+
<img src="./assets/readme/tools.svg" width="100%" alt="Ten tools in the tool list: list workspaces, list sessions, read session, list models, send, status, wait, answer, cancel, call. Six less used tools are reached through dsh_call: create and rename workspace, session activity, rename session, update queued, list runs">
|
|
121
153
|
</p>
|
|
122
154
|
|
|
123
155
|
<p align="center">
|
|
@@ -127,15 +159,18 @@ Share of the ChatGPT Plus 5-hour window per task: Sol medium 1.9% → 0.8%, Sol
|
|
|
127
159
|
<details>
|
|
128
160
|
<summary>Full tool reference</summary>
|
|
129
161
|
|
|
162
|
+
The server's MCP instructions are a single sentence (from 0.6.2); how to hand over, wait and answer is in each tool's own description. Codex repeats the instructions in front of every tool's description, so the catalogue it shows the model is now about 9.7k characters instead of 24.3k.
|
|
163
|
+
|
|
130
164
|
| Tool | What it does |
|
|
131
165
|
|---|---|
|
|
132
166
|
| `dsh_list_workspaces` | Workspaces in the Harness sidebar |
|
|
133
167
|
| `dsh_create_workspace` | Through `dsh_call`. Register a folder as a workspace (creates the folder if missing); returns the existing one if already registered |
|
|
134
|
-
| `dsh_list_sessions` | Sessions newest first, with title, folder and
|
|
168
|
+
| `dsh_list_sessions` | Sessions newest first, with title, folder, running state and `model` (what its next request uses); filter by folder or text. Archived sessions are left out |
|
|
169
|
+
| `dsh_list_models` | The models Harness can run (provider, id, name, reasoning efforts) and its default model |
|
|
135
170
|
| `dsh_read_session` | Recent prompts and replies of one session (with tool names); `archived: true` for an archived session |
|
|
136
|
-
| `dsh_send` | Send a message: `session_id` continues that session; otherwise a new one starts in `cwd` or `workspace_id`, or, with neither, in the folder the conversation works in (a `cwd` that is not a workspace yet is registered first; a `cwd` outside the conversation's folder is allowed and noted in `folder_note`). `checks`: test commands dsh-baton runs itself when the run ends (see How a hand-over is checked). Returns a `run_id` at once. In Harness the message is marked "Sent by Claude / ChatGPT from another chat". An archived session is refused: unarchive it in Harness, or start a new session. To a busy session, `mode` `followup` queues the message after the current turn and `steer` adds it to the running turn; left out, it does what Harness's own setting says (Settings → sending while busy: queue or steer), and `followup` when that cannot be read. `mode_source` says which applied |
|
|
137
|
-
| `dsh_status` | Run progress: state, recent text and tool calls, final reply; `waiting_on` when the turn is over but sub-agents or jobs still run; `human_interjections` when someone typed in the Harness UI mid-run; state `interrupted` for a run cut short by a Harness restart |
|
|
138
|
-
| `dsh_wait` | Wait for the run to end; a finished run carries `evidence` (files changed, check results) when the message was sent with `checks` (or until it stops to ask a person, see `dsh_answer`): up to 270 s per call for Codex, 50 s for other clients. Still going: returns `timed_out` with just the state; call it again. Clients that can wait in the background use `dsh_send`'s `wait_command` instead. `dsh_status` is a one-off look, not a way to wait |
|
|
171
|
+
| `dsh_send` | Send a message: `session_id` continues that session; otherwise a new one starts in `cwd` or `workspace_id`, or, with neither, in the folder the conversation works in (a `cwd` that is not a workspace yet is registered first; a `cwd` outside the conversation's folder is allowed and noted in `folder_note`). `checks`: test commands dsh-baton runs itself when the run ends (see How a hand-over is checked). Returns a `run_id` at once. In Harness the message is marked "Sent by Claude / ChatGPT from another chat". An archived session is refused: unarchive it in Harness, or start a new session. To a busy session, `mode` `followup` queues the message after the current turn and `steer` adds it to the running turn; left out, it does what Harness's own setting says (Settings → sending while busy: queue or steer), and `followup` when that cannot be read. `mode_source` says which applied. `model` only when the user names one ("DeepSeek-V4 Pro", "v4-pro"; `reasoning_effort` likewise): it is matched against Harness's own model list, ignoring case, spaces, hyphens and a leading DeepSeek, so a model Harness adds later (V4.1 Pro) works as soon as Harness lists it; a name that matches nothing or several fails the send, before anything is created, with the list (`provider/model` picks one). It is set on that session before the message goes in, and Harness's default model is put back as it was (Harness saves a session's pick as the default too). Left out, a continued session keeps its own model and a new one uses Harness's default. The reply says which in `model` and `model_source` |
|
|
172
|
+
| `dsh_status` | Run progress: state, recent text and tool calls, final reply; `waiting_on` when the turn is over but sub-agents or jobs still run; `human_interjections` when someone typed in the Harness UI mid-run; state `interrupted` for a run cut short by a Harness restart; `model` the run was sent with |
|
|
173
|
+
| `dsh_wait` | Codex with the Stop hook trusted does not need it. Wait for the run to end; a finished run carries `evidence` (files changed, check results) when the message was sent with `checks` (or until it stops to ask a person, see `dsh_answer`): up to 270 s per call for Codex, 50 s for other clients. Still going: returns `timed_out` with just the state; call it again. Clients that can wait in the background use `dsh_send`'s `wait_command` instead. `dsh_status` is a one-off look, not a way to wait |
|
|
139
174
|
| `dsh_answer` | Answer the questions a run stopped to ask (Harness's ask_user_question): per question, an option number or label, free text, or skip; one at a time or all at once. `dsh_wait` and `wait_command` return as soon as a run asks, with the questions (`needs_input`, `question`; the option Harness's model would pick is in `harness_suggested`, apart from the labels). The assistant answers by its own judgment only when you told it to decide, and then passes `reason` and tells you what it chose and why; otherwise it brings you the questions, starting with its own recommendation and reason. You can also answer in the Harness window: whoever answers first wins |
|
|
140
175
|
| `dsh_session_activity` | Through `dsh_call`. What a session is doing now: running turn, sub-agents (any depth), background jobs; `archived: true` for an archived session |
|
|
141
176
|
| `dsh_update_queued` | Through `dsh_call`. Change a message still waiting in the queue of a busy session, by its `run_id`: `edit` its text, `remove` it, or `steer` it into the running turn now. Once it has reached the conversation it can no longer be changed |
|
|
@@ -172,6 +207,7 @@ dsh-baton rename-workspace ~/code/new-app "New name"
|
|
|
172
207
|
| `DSH_BATON_HOME` | `~/.dsh-baton` | Socket and headless run records |
|
|
173
208
|
| `DSH_BATON_SOCKET` | `$DSH_BATON_HOME/host.sock` | Plugin socket path (must match on both sides); on Windows mapped to a per-user named pipe |
|
|
174
209
|
| `DSH_BATON_WAIT_S` | `270` for Codex, else `50` | How long one `dsh_wait` call waits; set it only if you raised the client's tool timeout, and keep it below that |
|
|
210
|
+
| `DSH_BATON_CODEX_HOOK` | `1` | `0` (set for Harness) stops the plugin adding the Codex Stop hook |
|
|
175
211
|
| `DSH_BIN` | the `dsh` inside the macOS app, else `PATH` | Harness CLI for headless mode |
|
|
176
212
|
| `DSH_HOME` | `~/.dsh` | Harness data folder (headless session reads) |
|
|
177
213
|
| `DSH_BATON_ACCOUNT` | `1` | `0` uses the API-key route in headless mode |
|
|
@@ -191,11 +227,13 @@ When the plugin is unreachable, the MCP server falls back to Harness's `dsh head
|
|
|
191
227
|
|
|
192
228
|
**What the plugin does when switched on.** It copies the MCP server to `~/.dsh-baton/app`, a folder that stays put, and writes the MCP entry into `~/.claude.json` and `~/.codex/config.toml` directly, keeping a `*.bak-dsh-baton` backup of each. It starts the server with your own Node.js ≥ 22.15 when it finds one on `PATH`; without one, with Harness's own runtime (the Harness app in Node mode, `ELECTRON_RUN_AS_NODE=1`), so nothing else has to be installed. Either way it works with only the desktop apps installed, when `claude` / `codex` are not on your `PATH`. It runs again on each Harness start: an entry that is already right, or a working one that starts another copy of dsh-baton, is left alone. In Harness, the source switches to the npm registry ("Official npm registry", 「npm 官方源」) by itself once you type the package name, and no restart is needed. To turn the registration off, set `DSH_BATON_AUTO_REGISTER=0` for Harness.
|
|
193
229
|
|
|
194
|
-
**
|
|
230
|
+
**The Codex Stop hook.** The plugin adds one `[[hooks.Stop]]` block, marked `# dsh-baton`, to `~/.codex/config.toml`; Codex shows it as **dsh-baton: waiting for the Harness run** and runs it only once you trust it (dsh-baton never trusts it for you). The block only names a small script, `~/.dsh-baton/bin/codex-stop-hook` (`codex-stop-hook.cmd` on Windows), and everything that can move, Node.js and the server, lives in that script. So the block itself never changes: updates, same-version refreshes and Harness restarts leave it as it is, and your trust stays. If the block is ever rewritten, dsh-baton drops the old trust record, and Codex asks you to trust it again. Windows uses a `.cmd` because Codex runs hooks through `PowerShell -Command`, which cannot start a quoted program path. Without a Node.js of your own, the script runs Harness's own app in Node mode (`ELECTRON_RUN_AS_NODE=1`), as the server does; the plugin rewrites it on each start, so it follows Harness updates. Not added without Codex, with `DSH_BATON_CODEX_HOOK=0`, or when Codex's dsh-baton entry names no server folder (a bare `npx` command); the first hand-over in a Codex conversation then says why. `dsh-baton setup --codex-hook` adds it by hand, `setup --remove` takes it out, `DSH_BATON_CODEX_HOOK=0` for Harness stops the plugin adding it.
|
|
231
|
+
|
|
232
|
+
**Updates need no restart.** When a newer version lands in `~/.dsh-baton/app` (a plugin update, `setup`, or a copy by hand), or the same version with changed files, conversations already open in Claude Code and Codex use it from their next `dsh_*` call: the server checks the folder before each call, starts the new version, and says once in that call's result that it was updated. A `dsh_wait` that is already running finishes on the old version first. If the new version cannot start, the old one keeps working and the result says why. A tool added in the new version shows up in Claude Code at once (the server tells the client its tool list changed); Codex keeps the tool list a conversation started with, so it does not show a new tool until the next conversation; from 0.5.9 on it can still use one at once through `dsh_call`, which the update note points to, and the tools it already has run the new code right away. This works from the version after 0.5.8 on; a server started from 0.5.8 or earlier needs one last restart (or `/mcp` reconnect) to pick it up.
|
|
195
233
|
|
|
196
234
|
**`npx -y dsh-baton-mcp setup`** does the same registration from a terminal: for a plugin that could not register, or when you come from an MCP directory. With the plugin already registered it changes nothing, then points you to the plugin if it is missing.
|
|
197
235
|
|
|
198
|
-
**Updating.** Harness has an update button only for its official plugins, so: Plugins → dsh-baton-host → **Uninstall**, then **Add plugin** → `dsh-baton-host`. For a while after a release, Harness installs the previous version instead (pnpm's `minimumReleaseAge` holds back versions published very recently, and the install dialog says so); to get the new one at once, type the version: `dsh-baton-host@0.6.
|
|
236
|
+
**Updating.** Harness has an update button only for its official plugins, so: Plugins → dsh-baton-host → **Uninstall**, then **Add plugin** → `dsh-baton-host`. For a while after a release, Harness installs the previous version instead (pnpm's `minimumReleaseAge` holds back versions published very recently, and the install dialog says so); to get the new one at once, type the version: `dsh-baton-host@0.6.2`. A pre-release must always be added by its exact version, such as `dsh-baton-host@0.6.0-next.0`: Harness resolves `@next` to a broken placeholder. Plain `dsh-baton-host` gets the latest release. Switched on again, the plugin brings the MCP server to the same version; new conversations use it. If the two are ever out of step, the first tool call that notices it says so, naming the older side and the newer side's exact version to install.
|
|
199
237
|
|
|
200
238
|
**Removing.** Harness → Plugins → dsh-baton-host → **Uninstall**, then `npx -y dsh-baton-mcp setup --remove`. The plugin cannot tell an uninstall from Harness quitting, so it leaves the MCP entries to that command, which also deletes `~/.dsh-baton/app`; run records stay in `~/.dsh-baton` until you delete it.
|
|
201
239
|
|
|
@@ -234,7 +272,7 @@ codex mcp add dsh-baton -- "$(command -v node)" "$PWD/dsh-baton/src/server.mjs"
|
|
|
234
272
|
</p>
|
|
235
273
|
|
|
236
274
|
<p align="center">
|
|
237
|
-
<img src="./assets/readme/limits.svg" width="100%" alt="Good to know: a message typed in the UI mid-run is flagged on the run as a human interjection, run records survive a Harness restart (cut-short runs show interrupted), Claude Code
|
|
275
|
+
<img src="./assets/readme/limits.svg" width="100%" alt="Good to know: a message typed in the UI mid-run is flagged on the run as a human interjection, run records survive a Harness restart (cut-short runs show interrupted), Claude Code and Codex are woken once when a run ends">
|
|
238
276
|
</p>
|
|
239
277
|
|
|
240
278
|
Any process running as your OS user can drive Harness through the socket, the same as running `dsh` directly. dsh-baton relies on Harness 0.2.1-series internal service APIs, so a major Harness update may need a follow-up release.
|
package/README.zh-CN.md
CHANGED
|
@@ -15,11 +15,11 @@ Claude Code 和 Codex 各派一个任务给 DeepSeek Harness,它的子代理
|
|
|
15
15
|
https://github.com/user-attachments/assets/ed931637-acb3-4c16-8c52-72298ea90485
|
|
16
16
|
|
|
17
17
|
<p align="center">
|
|
18
|
-
<img src="./assets/readme/zh/features.svg" width="100%" alt="
|
|
18
|
+
<img src="./assets/readme/zh/features.svg" width="100%" alt="新建工作区和对话、续接指定对话、界面实时可见并标明发送方、Claude Code 和 Codex 在运行结束时被唤醒、不用另配 key 且可选 Harness 里的任意模型、macOS 和 Windows 零依赖">
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
22
|
-
<img src="./assets/readme/zh/flow.gif" width="100%" alt="
|
|
22
|
+
<img src="./assets/readme/zh/flow.gif" width="100%" alt="典型流程:新建工作区、派发任务、不轮询地等待(Claude Code 和 Codex 在运行结束时被唤醒)、在同一个对话里续接追问">
|
|
23
23
|
</p>
|
|
24
24
|
|
|
25
25
|
## 实际演示
|
|
@@ -41,7 +41,7 @@ Codex 和 Claude 轮流往同一个 Harness 对话发消息,每条都标明是
|
|
|
41
41
|
## 快速上手
|
|
42
42
|
|
|
43
43
|
<p align="center">
|
|
44
|
-
<img src="./assets/readme/zh/install.svg" width="100%" alt="第一步:Harness → 插件 → 添加插件 → 输入 dsh-baton-host → 安装 → 立即启用,它会为 Claude Code 和 Codex 注册 dsh-baton
|
|
44
|
+
<img src="./assets/readme/zh/install.svg" width="100%" alt="第一步:Harness → 插件 → 添加插件 → 输入 dsh-baton-host → 安装 → 立即启用,它会为 Claude Code 和 Codex 注册 dsh-baton。第二步(仅 Codex,一次):设置 → 钩子 → 信任 dsh-baton: waiting for the Harness run。第三步:新开对话,让它调用 dsh_list_workspaces">
|
|
45
45
|
</p>
|
|
46
46
|
|
|
47
47
|
需要:DeepSeek Harness ≥ 0.2.1-rc.2 并已登录;装了 Claude Code 或 Codex。别的都不用装,不用 Node.js,也不用开终端。macOS 和 Windows 步骤完全相同。
|
|
@@ -50,7 +50,9 @@ Codex 和 Claude 轮流往同一个 Harness 对话发消息,每条都标明是
|
|
|
50
50
|
|
|
51
51
|
插件启用后会为 Claude Code 和 Codex 注册 dsh-baton(对所有文件夹生效),Harness 里会弹出提示。
|
|
52
52
|
|
|
53
|
-
**2.
|
|
53
|
+
**2. 仅 Codex,只需一次**:Codex **设置 → 钩子(Hooks)** → **信任** **dsh-baton: waiting for the Harness run**。钩子要插件 0.6.0 以上才有(没有 Node.js 时要 0.6.2 以上);装好或更新插件后,先重启一次 Harness,再重启一次 Codex,它才会出现在这个列表里。不需要 Node.js:没有的话它和服务器一样用 Harness 自带运行环境。之后 Codex 在任务跑完时会被自动唤醒,不用再每 270 秒查一次。只需信任一次:以后升级、热更新、重启 Harness 都不用再信任。不信任也能用,Codex 会照旧用 `dsh_wait` 等,第一次交接时会提醒你。
|
|
54
|
+
|
|
55
|
+
**3. 检查**:在 Claude Code 或 Codex 里**新开**一个对话,让它调用 `dsh_list_workspaces`,会列出你的 Harness 工作区。
|
|
54
56
|
|
|
55
57
|
就这些。如果 Harness 提示没能注册,在终端运行 `npx -y dsh-baton-mcp setup`(这需要 Node.js ≥ 22.15),它做的是同样的注册。更新、卸载和 Windows 说明在[后面](#安装细节)。
|
|
56
58
|
|
|
@@ -66,9 +68,12 @@ Codex 和 Claude 轮流往同一个 Harness 对话发消息,每条都标明是
|
|
|
66
68
|
|
|
67
69
|
**各客户端怎么等待**(不需要配置):
|
|
68
70
|
- **Claude Code** 在后台等待任务(`dsh_send` 返回的 `wait_command`),任务结束时只被唤醒一次。
|
|
69
|
-
- **Codex
|
|
71
|
+
- **Codex**(0.6.1 起)信任了 dsh-baton 的 Stop 钩子后:`dsh_send` 之后直接结束这一轮;钩子在模型之外等任务,任务结束或停下来提问时,带着结果继续同一个对话。每个任务只唤醒模型一次,而不是每 270 秒一次。
|
|
72
|
+
- **Codex** 没有钩子时在 `dsh_wait` 里等待,每次最多 270 秒,刚好低于它 300 秒的工具超时。没做完时只返回状态,Codex 会直接再调用,不会去总结进度。Codex 不会收到 `wait_command`:它的终端无法在命令结束时唤醒它,只能轮询。
|
|
70
73
|
- Codex 调用 MCP 工具时会请你批准,批准 `dsh-baton` 的工具即可;无人值守的 `codex exec` 加上 `--approve-for-me`。
|
|
71
74
|
|
|
75
|
+
**指定模型**(0.6.1 起):直接说用哪个就行,比如「交给 DeepSeek,用 V4 Pro」。`dsh_send` 会把它作为 `model`(以及 `reasoning_effort`)传过去,按 Harness 自己的模型列表匹配,所以 Harness 以后新加的模型一出现就能用。只对这个对话生效,Harness 的默认模型保持不变。`dsh_list_models` 可以看有哪些模型。
|
|
76
|
+
|
|
72
77
|
**转交之后怎么验收**(0.6.0 起)
|
|
73
78
|
|
|
74
79
|
不用光听 DeepSeek 自己说做完了。运行前,dsh-baton 先记下文件夹里有什么;运行结束后,它会列出到底新建、修改、删除了哪些文件,并亲自跑一遍项目自己的测试(`npm test`,或者客户端在 `checks` 里给的命令)。接着,另一个只读的 DeepSeek 子智能体会对照需求逐条检查,最后给一张表:哪条需求、做到没有、依据在哪个文件第几行。Claude 或 GPT 只看这些,没做到的一次性发回去改,只有表里写着「没做到」的地方才去翻代码。不用 `cwd` 指定的话,新对话就开在你当前工作的文件夹里。
|
|
@@ -79,45 +84,72 @@ Codex 和 Claude 轮流往同一个 Harness 对话发消息,每条都标明是
|
|
|
79
84
|
|
|
80
85
|
## Token 用量实测
|
|
81
86
|
|
|
82
|
-
**一句话:大活、长活交给它**。一次性做一个跨很多文件的功能、一次大重构、一整个小应用,这种时候最省:Claude Code
|
|
87
|
+
**一句话:大活、长活交给它**。一次性做一个跨很多文件的功能、一次大重构、一整个小应用,这种时候最省:Claude Code 连 DeepSeek 一起算,总花费少 51–72%。Codex 现在两个案例都省(案例 2 任务管理网页,Sol 少 40%,Astra 少 81%);GPT-6 Astra 做案例 2,ChatGPT Plus 5 小时额度从占 22% 降到 3%。更长的活,比如分三个阶段的看板应用(案例 4),Astra 总花费少 73%,额度从 86% 降到约 16%。
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
**说实话**:案例 1 这种小的命令行工具,Codex 用 GPT-6.1 Sol 自己做,总价还略便宜一点:转交反而多花 15%,多出来的几乎全是 DeepSeek 那部分。转交值得的场景是:你想让 Claude 或 GPT 负责规划和把关,让更便宜的模型去动手写,又不想老是撞上 5 小时额度上限。
|
|
85
90
|
|
|
86
91
|
**适合谁**:大多数人用 Claude 或 ChatGPT 走的是订阅套餐(Plus、Pro、Max 5x 之类),而 DeepSeek 是充值后按用量扣费,所以每次转交都是套餐之外多花的钱。如果你的套餐额度本来就够用,就不用折腾这套流程,省一点是一点。如果你像我一样,经常跑又长又杂、要反复修改的大任务,老是撞上额度上限,那花一点 DeepSeek 的钱,就能让你已经付费的套餐多干很多活。
|
|
87
92
|
|
|
93
|
+
**两个测试案例**,都按普通用户的口吻提需求(原文可以直接点开看):
|
|
94
|
+
- **案例 1 · 命令行工具**:用 Node 写一个命令行任务管理工具,零依赖,分模块,带测试。[需求原文](docs/benchmarks/token-usage-2026-10/natural/medium.md) · [验收脚本](docs/benchmarks/token-usage-2026-10/checks/natural-medium.mjs)
|
|
95
|
+
- **案例 2 · 任务管理网页**:Node 服务器加前端页面,REST 接口、统计、测试和 README。[需求原文](docs/benchmarks/token-usage-2026-10/natural/large.md) · [验收脚本](docs/benchmarks/token-usage-2026-10/checks/natural-large.mjs)
|
|
96
|
+
|
|
88
97
|
<p align="center">
|
|
89
|
-
<img src="./docs/benchmarks/token-usage-2026-10/charts/natural-combined.zh.svg" width="100%" alt="每个任务的花费,自己做与用 0.6.
|
|
98
|
+
<img src="./docs/benchmarks/token-usage-2026-10/charts/natural-combined.zh.svg" width="100%" alt="每个任务的花费,自己做与用 0.6.2 转交,案例 1 · 命令行工具和案例 2 · 任务管理网页:Claude Code(Opus 5.5)和 Codex(GPT-6.1 Sol、GPT-6 Astra),各用自己的刻度">
|
|
90
99
|
</p>
|
|
91
100
|
|
|
92
101
|
每个任务的花费,按标价计,DeepSeek V4.1 Flash 1017 按高峰价:
|
|
93
102
|
|
|
94
|
-
| 客户端 · 模型 |
|
|
103
|
+
| 客户端 · 模型 | 案例 | 自己做 | 转交,只算客户端 | 转交,含 DeepSeek |
|
|
95
104
|
|---|---|---|---|---|
|
|
96
|
-
| Claude Code · Opus 5.5 high |
|
|
97
|
-
| Claude Code · Opus 5.5 high |
|
|
98
|
-
| Codex · GPT-6.1 Sol high |
|
|
99
|
-
| Codex · GPT-6.1 Sol high |
|
|
100
|
-
| Codex · GPT-6 Astra high |
|
|
105
|
+
| Claude Code · Opus 5.5 high | 案例 1 · 命令行工具 | $1.07 | $0.42(−61%) | $0.52(−51%) |
|
|
106
|
+
| Claude Code · Opus 5.5 high | 案例 2 · 任务管理网页 | $2.13 | $0.47(−78%) | $0.60(−72%) |
|
|
107
|
+
| Codex · GPT-6.1 Sol high | 案例 1 · 命令行工具 | $0.17 | $0.06(−63%) | $0.20(+15%) |
|
|
108
|
+
| Codex · GPT-6.1 Sol high | 案例 2 · 任务管理网页 | $0.41 | $0.06(−84%) | $0.25(−40%) |
|
|
109
|
+
| Codex · GPT-6 Astra high | 案例 1 · 命令行工具 | $0.79 | $0.30(−62%) | $0.42(−47%) |
|
|
110
|
+
| Codex · GPT-6 Astra high | 案例 2 · 任务管理网页 | $2.27 | $0.30(−87%) | $0.42(−81%) |
|
|
111
|
+
|
|
112
|
+
<p align="center">
|
|
113
|
+
<img src="./docs/benchmarks/token-usage-2026-10/charts/natural-quota-combined.zh.svg" width="80%" alt="Codex 每个任务占 ChatGPT Plus 5 小时额度,自己做与用 0.6.2 转交:GPT-6.1 Sol 和 GPT-6 Astra">
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
每个任务占 ChatGPT Plus 5 小时额度:案例 1 Sol 1.9% → ≈0.6%,案例 2 Sol 4.3% → ≈0.6%,案例 1 Astra 7% → 3%,案例 2 Astra 22% → 3%(≈ 为按花费估算)。
|
|
117
|
+
|
|
118
|
+
**为什么更省了**:0.6.1 起 Codex 由 Stop 钩子唤醒,不再用 `dsh_wait` 轮询;0.6.2 把 MCP 说明缩成一句话,Codex 每次转交少读几千到一万多个新输入 token。
|
|
101
119
|
|
|
102
|
-
|
|
120
|
+
**案例 4 · 看板应用(分阶段)**:只用 Node 内置模块和原生前端的看板应用,分三个阶段在同一个对话里依次给:① 主体(REST 接口、持久化、前端拖拽);② 标签、截止日期、搜索语法、归档、撤销重做、活动日志、导入导出;③ 版本冲突检测、WIP 上限、批量操作、SSE 实时同步、统计。每个阶段都有事先写好的验收测试,两组用同一套,共 55 项。需求原文:[阶段 1](docs/benchmarks/token-usage-2026-10/xxl/seed/docs/phase1.md) · [阶段 2](docs/benchmarks/token-usage-2026-10/xxl/seed/docs/phase2.md) · [阶段 3](docs/benchmarks/token-usage-2026-10/xxl/seed/docs/phase3.md) · [验收测试](docs/benchmarks/token-usage-2026-10/xxl/seed/tests/acceptance/) · [验收脚本](docs/benchmarks/token-usage-2026-10/xxl/check.mjs) · [测试流程](docs/benchmarks/token-usage-2026-10/xxl/README.md)
|
|
121
|
+
|
|
122
|
+
<p align="center">
|
|
123
|
+
<img src="./docs/benchmarks/token-usage-2026-10/charts/task4-kanban.zh.svg" width="100%" alt="案例 4 · 看板应用(分阶段),Codex GPT-6 Astra high:自己做 $8.69,用 0.6.2 转交 $2.36(主 Agent $1.62,−81%);5 小时额度 86% 对约 16%">
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
| Codex · GPT-6 Astra high | 自己做 | 转交(0.6.2) |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| 主 Agent | $8.69 | $1.62(−81%) |
|
|
129
|
+
| DeepSeek | – | $0.74 |
|
|
130
|
+
| 合计 | $8.69 | $2.36(−73%) |
|
|
131
|
+
| 5 小时额度 | 86% | 约 16%(−81%) |
|
|
132
|
+
| 验收测试 | 55/55,一次通过 | 55/55,一次通过 |
|
|
133
|
+
| 用时 | 约 31 分钟 | 约 87 分钟 |
|
|
134
|
+
| 代码量 | 1,876 行 | 9,953 行 |
|
|
135
|
+
|
|
136
|
+
案例 3 是同一个看板应用、三个阶段一次给完的版本,Opus 和 Sol 正在测,结果下一次更新补上。
|
|
103
137
|
|
|
104
138
|
<details>
|
|
105
139
|
<summary>测了什么、怎么测的</summary>
|
|
106
140
|
|
|
107
|
-
- **两个任务,用普通用户的口吻提**(原文如下,可以直接点开看):
|
|
108
|
-
- 中号:用 Node 写一个命令行任务管理工具,零依赖,分模块,带测试。[需求原文](docs/benchmarks/token-usage-2026-10/natural/medium.md) · [验收脚本](docs/benchmarks/token-usage-2026-10/checks/natural-medium.mjs)
|
|
109
|
-
- 大号:一个任务管理网页应用,Node 服务器加前端页面,REST 接口、统计、测试和 README。[需求原文](docs/benchmarks/token-usage-2026-10/natural/large.md) · [验收脚本](docs/benchmarks/token-usage-2026-10/checks/natural-large.mjs)
|
|
110
141
|
- **转交组唯一的区别**,是在需求末尾多一句:交给 DeepSeek Harness 去做。
|
|
111
|
-
-
|
|
142
|
+
- 两组用同一个模型、同样的推理强度;每次都用同一套验收脚本检查,模型看不到这个脚本。
|
|
143
|
+
- 自己做:沿用之前每格 3 次的平均值(Astra 各 1 次)。用 0.6.2 转交:每格跑 1 次,6 次全部一次通过,没有返工,也没有调用 `dsh_wait`。
|
|
112
144
|
- 价格按标价算。DeepSeek 按高峰价,是最保守的算法;实际 DeepSeek 账单大约是这个数的 58%。
|
|
113
|
-
-
|
|
145
|
+
- 每一次运行:[运行日志](docs/benchmarks/token-usage-2026-10/LOG.md)(「0.6.2 hand-over rerun」一节);原始数据在 `docs/benchmarks/token-usage-2026-10/results/*-n062*.jsonl`;方法和更早的版本见[完整报告](docs/benchmarks/token-usage-2026-10.md)。
|
|
114
146
|
|
|
115
147
|
</details>
|
|
116
148
|
|
|
117
149
|
## 工具
|
|
118
150
|
|
|
119
151
|
<p align="center">
|
|
120
|
-
<img src="./assets/readme/zh/tools.svg" width="100%" alt="工具列表里有
|
|
152
|
+
<img src="./assets/readme/zh/tools.svg" width="100%" alt="工具列表里有 10 个:列出工作区、列出对话、读取对话、列出模型、发送、状态、等待、回答、取消、dsh_call。另外 6 个不常用的经 dsh_call 调用:新建和改名工作区、对话动态、给对话改名、修改排队消息、列出运行">
|
|
121
153
|
</p>
|
|
122
154
|
|
|
123
155
|
<p align="center">
|
|
@@ -127,15 +159,18 @@ Codex 和 Claude 轮流往同一个 Harness 对话发消息,每条都标明是
|
|
|
127
159
|
<details>
|
|
128
160
|
<summary>完整工具说明</summary>
|
|
129
161
|
|
|
162
|
+
服务器的 MCP instructions 只有一句话(0.6.2 起);怎么转交、怎么等、怎么回答问题,都写在各个工具自己的说明里。Codex 会在每个工具的说明前面重复一遍 instructions,所以它给模型看的工具目录从约 2.43 万字符降到约 0.97 万字符。
|
|
163
|
+
|
|
130
164
|
| 工具 | 作用 |
|
|
131
165
|
|---|---|
|
|
132
166
|
| `dsh_list_workspaces` | 列出 Harness 侧边栏里的工作区 |
|
|
133
167
|
| `dsh_create_workspace` | 经 `dsh_call` 调用。把一个文件夹登记为工作区(文件夹不存在会先创建);已登记的直接返回原来那个 |
|
|
134
|
-
| `dsh_list_sessions` |
|
|
168
|
+
| `dsh_list_sessions` | 列出对话(最新在前),带标题、目录、是否在跑和 `model`(下一次请求用的模型);可按目录或关键字过滤。已归档的对话不会列出 |
|
|
169
|
+
| `dsh_list_models` | 列出 Harness 能用的模型(provider、id、名称、推理强度)和它的默认模型 |
|
|
135
170
|
| `dsh_read_session` | 读某个对话最近的消息和回复(含用到的工具名);已归档的对话会带 `archived: true` |
|
|
136
|
-
| `dsh_send` | 发消息。带 `session_id` 续接那个对话;否则新开对话,建在 `cwd` 或 `workspace_id` 指定的位置,都不带时建在当前对话所在的文件夹(`cwd` 还不是工作区时会先登记;指定的 `cwd` 在当前对话文件夹之外也可以,结果里的 `folder_note` 会提醒)。`checks`:运行结束后 dsh-baton 自己跑的测试命令(见「转交之后怎么验收」)。立即返回 `run_id`。Harness 里这条消息会标注「由 Claude / ChatGPT 从另一个聊天发送」。已归档的对话会被拒绝:先在 Harness 里取消归档,或新开对话。对话正忙时,`mode` 为 `followup` 会排在当前回合之后,`steer` 会插进正在跑的回合;不传时跟随 Harness 自己的设置(设置 → 繁忙时的发送行为:排队发送或插话发送),读不到设置时按 `followup`。返回里的 `mode_source`
|
|
137
|
-
| `dsh_status` | 查看运行进度:状态、最近的文字和工具调用、最终回复;回合结束但子智能体/后台任务还在跑时给出 `waiting_on`;中途有人在 Harness 界面里发消息时给出 `human_interjections`;被 Harness 重启打断的任务状态为 `interrupted` |
|
|
138
|
-
| `dsh_wait` |
|
|
171
|
+
| `dsh_send` | 发消息。带 `session_id` 续接那个对话;否则新开对话,建在 `cwd` 或 `workspace_id` 指定的位置,都不带时建在当前对话所在的文件夹(`cwd` 还不是工作区时会先登记;指定的 `cwd` 在当前对话文件夹之外也可以,结果里的 `folder_note` 会提醒)。`checks`:运行结束后 dsh-baton 自己跑的测试命令(见「转交之后怎么验收」)。立即返回 `run_id`。Harness 里这条消息会标注「由 Claude / ChatGPT 从另一个聊天发送」。已归档的对话会被拒绝:先在 Harness 里取消归档,或新开对话。对话正忙时,`mode` 为 `followup` 会排在当前回合之后,`steer` 会插进正在跑的回合;不传时跟随 Harness 自己的设置(设置 → 繁忙时的发送行为:排队发送或插话发送),读不到设置时按 `followup`。返回里的 `mode_source` 说明用的是哪种。`model` 只在用户点名模型时传(如“DeepSeek-V4 Pro”“v4-pro”;`reasoning_effort` 同理):按 Harness 自己的模型列表匹配,不分大小写,忽略空格、连字符和开头的 DeepSeek,所以 Harness 以后上线的模型(如 V4.1 Pro)一出现在列表里就能用;匹配不到或匹配到多个时,发送直接失败、什么都不创建,并列出可用模型(用 `provider/model` 指定其一)。模型在消息发出前设到该对话上,Harness 的默认模型会恢复原样(Harness 会把对话里选的模型同时存成默认)。不传时,续接的对话沿用它自己的模型,新对话用 Harness 默认模型。返回值的 `model` 和 `model_source` 写明用的是哪个 |
|
|
172
|
+
| `dsh_status` | 查看运行进度:状态、最近的文字和工具调用、最终回复;回合结束但子智能体/后台任务还在跑时给出 `waiting_on`;中途有人在 Harness 界面里发消息时给出 `human_interjections`;被 Harness 重启打断的任务状态为 `interrupted`;以及这次运行发送时用的 `model` |
|
|
173
|
+
| `dsh_wait` | 信任了 Stop 钩子的 Codex 用不到它。等运行结束;发消息时带了 `checks` 的,结束后会附上 `evidence`(改了哪些文件、测试结果)(或者等到它停下来问人,见 `dsh_answer`):Codex 每次最多等 270 秒,其他客户端 50 秒。没做完会带 `timed_out`,只返回状态,再调用一次即可。能在后台等待的客户端改用 `dsh_send` 返回的 `wait_command`。`dsh_status` 只用来看一眼,不用来等待 |
|
|
139
174
|
| `dsh_answer` | 回答运行中途提出的问题(Harness 的 ask_user_question):每道题可以填选项编号或选项文字、自由填写,或者跳过;可以逐题回答,也可以一次答完。运行一提问,`dsh_wait` 和 `wait_command` 就会立刻返回,并带上题目(`needs_input`、`question`;Harness 那边模型推荐的选项单独放在 `harness_suggested` 里,不再写在选项文字中)。只有你说过让它自己决定时,助手才会按自己的判断回答,这时它会带上 `reason`,并告诉你选了什么、为什么;否则它会把题目转给你,开头先写它自己的建议和理由。你也可以直接在 Harness 窗口里回答,谁先答就算谁的 |
|
|
140
175
|
| `dsh_session_activity` | 经 `dsh_call` 调用。对话此刻在做什么:正在跑的回合、子智能体(任意层级)、后台任务;已归档的对话会带 `archived: true` |
|
|
141
176
|
| `dsh_update_queued` | 经 `dsh_call` 调用。按 `run_id` 修改还在忙碌对话队列里等待的消息:`edit` 改文字,`remove` 撤回,`steer` 立刻插进正在跑的回合。消息一旦进入对话就改不了了 |
|
|
@@ -174,6 +209,7 @@ dsh-baton rename-workspace ~/code/new-app 新名字
|
|
|
174
209
|
| `DSH_BATON_HOME` | `~/.dsh-baton` | 套接字和 headless 运行记录的位置 |
|
|
175
210
|
| `DSH_BATON_SOCKET` | `$DSH_BATON_HOME/host.sock` | 插件套接字路径(插件和 MCP 两边要一致);Windows 上映射为按用户区分的命名管道,见 [WINDOWS.md](WINDOWS.md) |
|
|
176
211
|
| `DSH_BATON_WAIT_S` | Codex 为 `270`,其他为 `50` | 一次 `dsh_wait` 等多久;只有调大了客户端的工具超时才需要设,并且要小于那个超时 |
|
|
212
|
+
| `DSH_BATON_CODEX_HOOK` | `1` | 给 Harness 设为 `0`,插件就不再添加 Codex 的 Stop 钩子 |
|
|
177
213
|
| `DSH_BIN` | macOS 应用内的 `dsh`,否则查 `PATH` | headless 模式用的 Harness 命令行 |
|
|
178
214
|
| `DSH_HOME` | `~/.dsh` | Harness 数据目录(headless 模式读取对话用) |
|
|
179
215
|
| `DSH_BATON_ACCOUNT` | `1` | headless 模式下设为 `0` 则改用 API key 路线 |
|
|
@@ -193,11 +229,13 @@ dsh-baton rename-workspace ~/code/new-app 新名字
|
|
|
193
229
|
|
|
194
230
|
**插件启用时做了什么**。它把 MCP 服务器复制到 `~/.dsh-baton/app` 这个固定的文件夹,再直接把 MCP 注册写进 `~/.claude.json` 和 `~/.codex/config.toml`,各留一份 `*.bak-dsh-baton` 备份。电脑上有 Node.js ≥ 22.15(在 `PATH` 里能找到)就用它启动服务器;没有的话,就用 Harness 自带的运行环境(Harness 应用本身以 Node 模式运行,`ELECTRON_RUN_AS_NODE=1`),不用另外安装任何东西。两种情况下,只装了桌面应用、`claude` / `codex` 不在 `PATH` 里也能用。每次 Harness 启动都会再检查一遍:已经正确的注册、或者指向另一份可用 dsh-baton 的注册,都不会改动。在 Harness 里输入包名后,安装源会自动切到「npm 官方源」(英文界面是 "Official npm registry"),不用另外选择,也不用重启。不想让插件自动注册,给 Harness 设置 `DSH_BATON_AUTO_REGISTER=0`。
|
|
195
231
|
|
|
196
|
-
|
|
232
|
+
**Codex 的 Stop 钩子**。插件会在 `~/.codex/config.toml` 里加一段带 `# dsh-baton` 标记的 `[[hooks.Stop]]`,Codex 里显示为 **dsh-baton: waiting for the Harness run**,你信任之后才会运行(dsh-baton 不会替你信任)。这一段只写了一个小脚本的路径 `~/.dsh-baton/bin/codex-stop-hook`(Windows 上是 `codex-stop-hook.cmd`),Node.js 和服务器这些会变的路径都放在脚本里。所以这一段本身永远不变:升级、同版本刷新、重启 Harness 都不会动它,信任一直有效。万一这一段被改写,dsh-baton 会清掉旧的信任记录,Codex 会请你重新信任。Windows 上用 `.cmd`,是因为 Codex 通过 `PowerShell -Command` 运行钩子,启动不了带引号的程序路径。没有自己的 Node.js 时,脚本和服务器一样以 Node 模式(`ELECTRON_RUN_AS_NODE=1`)运行 Harness 程序本身;插件每次启动都会重写它,所以 Harness 更新后路径也会跟上。只有没装 Codex、设了 `DSH_BATON_CODEX_HOOK=0`、或 Codex 里的 dsh-baton 条目没指向服务器目录(比如裸的 `npx` 命令)时才不加;这时 Codex 对话里第一次交接会说明原因。手动添加用 `dsh-baton setup --codex-hook`,`setup --remove` 会删掉它,给 Harness 设置 `DSH_BATON_CODEX_HOOK=0` 则插件不再添加。
|
|
233
|
+
|
|
234
|
+
**更新不用重启**。新版本装进 `~/.dsh-baton/app` 后(插件更新、`setup` 或手动复制),或者同一版本但文件有改动时,Claude Code 和 Codex 里已经开着的对话,下一次调用 `dsh_*` 工具时就会用上:服务器每次调用前检查这个文件夹,启动新版本,并在这次调用的结果里提示一次已经更新。正在进行的 `dsh_wait` 会先在旧版本里跑完。新版本启动失败时,旧版本继续工作,结果里会说明原因。新版本新增的工具,Claude Code 里马上就能用(服务器会通知客户端工具列表变了);Codex 一直用对话开始时的工具列表,新工具要到下一个新对话才会显示;从 0.5.9 起,旧对话里也能通过 `dsh_call` 马上用上新功能(更新提示里会写明),已有的工具也会立刻用上新代码。这个功能从 0.5.8 之后的版本开始有效;由 0.5.8 或更早版本启动的服务器,还需要最后重启一次(或在 `/mcp` 里重新连接)。
|
|
197
235
|
|
|
198
236
|
**`npx -y dsh-baton-mcp setup`** 在终端里做同样的注册:用于插件没能注册的情况,或者从 MCP 目录过来的用户。插件已经注册过时它什么都不改;插件没装的话,它会提示去添加。
|
|
199
237
|
|
|
200
|
-
**更新**。Harness 只给官方插件提供"更新"按钮,所以要:插件 → dsh-baton-host → **卸载**,再**添加插件** → `dsh-baton-host`。新版本刚发布的一段时间里,Harness 会装上一个版本(pnpm 的 `minimumReleaseAge` 会暂缓刚发布的版本,安装弹窗里会说明);想马上用新版,输入带版本号的名字:`dsh-baton-host@0.6.
|
|
238
|
+
**更新**。Harness 只给官方插件提供"更新"按钮,所以要:插件 → dsh-baton-host → **卸载**,再**添加插件** → `dsh-baton-host`。新版本刚发布的一段时间里,Harness 会装上一个版本(pnpm 的 `minimumReleaseAge` 会暂缓刚发布的版本,安装弹窗里会说明);想马上用新版,输入带版本号的名字:`dsh-baton-host@0.6.2`。预发布版必须写确切版本号添加,比如 `dsh-baton-host@0.6.0-next.0`:Harness 会把 `@next` 解析成一个坏掉的占位包。只输入 `dsh-baton-host` 装的是最新正式版。重新启用后,插件会把 MCP 服务器也更新到同一版本,新开的对话就会用上。万一两边版本对不上,第一次发现不一致的工具调用会提示,并写明哪一边旧、另一边的确切版本号。
|
|
201
239
|
|
|
202
240
|
**卸载**。Harness → 插件 → dsh-baton-host → **卸载**,再运行 `npx -y dsh-baton-mcp setup --remove`。插件分不清"被卸载"和"Harness 退出",所以 MCP 注册交给这条命令删,它也会删掉 `~/.dsh-baton/app`;运行记录留在 `~/.dsh-baton`,不需要的话自己删掉。
|
|
203
241
|
|
|
@@ -236,7 +274,7 @@ codex mcp add dsh-baton -- "$(command -v node)" "$PWD/dsh-baton/src/server.mjs"
|
|
|
236
274
|
</p>
|
|
237
275
|
|
|
238
276
|
<p align="center">
|
|
239
|
-
<img src="./assets/readme/zh/limits.svg" width="100%" alt="使用须知:任务进行中在界面里发的话会作为人工插话记到任务上;任务记录在 Harness 重启后还在,被打断的标为 interrupted;Claude Code
|
|
277
|
+
<img src="./assets/readme/zh/limits.svg" width="100%" alt="使用须知:任务进行中在界面里发的话会作为人工插话记到任务上;任务记录在 Harness 重启后还在,被打断的标为 interrupted;Claude Code 和 Codex 都只在运行结束时被唤醒一次">
|
|
240
278
|
</p>
|
|
241
279
|
|
|
242
280
|
任何能以你的身份运行程序的进程都能通过这个套接字驱动 Harness,这和直接运行 `dsh` 的权限一样。本项目依赖 Harness 0.2.1 系列的内部服务接口,Harness 大版本更新后可能需要跟进。
|
package/host/lib/client.js
CHANGED
|
@@ -54,6 +54,8 @@ window.__ModuleLoader__.load({
|
|
|
54
54
|
notInstalled: '未安装',
|
|
55
55
|
notRegistered: '未注册',
|
|
56
56
|
and: '和',
|
|
57
|
+
hookTitle: 'Codex 还差一步',
|
|
58
|
+
hookText: '打开 Codex → Settings → Hooks,信任 Stop 钩子“dsh-baton: waiting for the Harness run”。信任后,Codex 等 DeepSeek 时不再消耗额度。',
|
|
57
59
|
}
|
|
58
60
|
const en = {
|
|
59
61
|
sentFrom: 'Sent by {name} from another chat',
|
|
@@ -74,6 +76,8 @@ window.__ModuleLoader__.load({
|
|
|
74
76
|
notInstalled: 'not installed',
|
|
75
77
|
notRegistered: 'not registered',
|
|
76
78
|
and: 'and',
|
|
79
|
+
hookTitle: 'One more step for Codex',
|
|
80
|
+
hookText: 'Open Codex → Settings → Hooks and trust the Stop hook “dsh-baton: waiting for the Harness run”. Once trusted, Codex no longer uses your quota while it waits for DeepSeek.',
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
// --- Plugins page ------------------------------------------------------------
|
|
@@ -94,7 +98,12 @@ window.__ModuleLoader__.load({
|
|
|
94
98
|
if (res.ok) next = (await res.json()).registration
|
|
95
99
|
} catch { /* not served yet */ }
|
|
96
100
|
if (stopped) return
|
|
97
|
-
if (next && next.state !== 'pending')
|
|
101
|
+
if (next && next.state !== 'pending') {
|
|
102
|
+
setReg(next)
|
|
103
|
+
// Waiting for the user to trust the Codex hook: look again, so the note goes once they do.
|
|
104
|
+
if (untrusted(next)) setTimeout(poll, 4000)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
98
107
|
if (++tries >= 30) return setReg({ state: 'unknown' })
|
|
99
108
|
setTimeout(poll, 700)
|
|
100
109
|
}
|
|
@@ -104,6 +113,30 @@ window.__ModuleLoader__.load({
|
|
|
104
113
|
return reg
|
|
105
114
|
}
|
|
106
115
|
|
|
116
|
+
const untrusted = (reg) => Boolean(reg?.codexHook?.installed && !reg.codexHook.trusted)
|
|
117
|
+
|
|
118
|
+
// Codex runs dsh-baton's Stop hook only once the user trusts it in Codex; until then
|
|
119
|
+
// Codex spends model requests while it waits. Shown until they do, in Harness's accent.
|
|
120
|
+
function TrustNote({ reg, t }) {
|
|
121
|
+
if (!untrusted(reg)) return null
|
|
122
|
+
const accent = 'var(--dsw-alias-state-business-primary)'
|
|
123
|
+
return h('div', {
|
|
124
|
+
role: 'note', 'data-dsh-baton-trust': '',
|
|
125
|
+
style: {
|
|
126
|
+
display: 'flex', gap: 10, alignItems: 'flex-start', marginTop: 8, padding: '10px 12px', borderRadius: 8,
|
|
127
|
+
background: `color-mix(in srgb, ${accent} 7%, transparent)`,
|
|
128
|
+
border: `1px solid color-mix(in srgb, ${accent} 22%, transparent)`,
|
|
129
|
+
color: 'var(--dsw-alias-label-secondary)', fontSize: 13, lineHeight: '20px', textAlign: 'start',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': true, style: { flex: 'none', marginTop: 2, color: accent } },
|
|
133
|
+
h('path', { d: 'M8 1.75 2.75 3.9v3.6c0 3.1 2.2 5.6 5.25 6.75 3.05-1.15 5.25-3.65 5.25-6.75V3.9z', stroke: 'currentColor', strokeWidth: 1.3, strokeLinejoin: 'round' }),
|
|
134
|
+
h('path', { d: 'm5.6 8.1 1.7 1.7 3.2-3.4', stroke: 'currentColor', strokeWidth: 1.3, strokeLinecap: 'round', strokeLinejoin: 'round' })),
|
|
135
|
+
h('div', null,
|
|
136
|
+
h('div', { style: { color: 'var(--dsw-alias-label-primary)', fontWeight: 600 } }, t('hookTitle')),
|
|
137
|
+
h('div', null, t('hookText'))))
|
|
138
|
+
}
|
|
139
|
+
|
|
107
140
|
// What to tell the user: [title or null, text]. null while the host is still working.
|
|
108
141
|
function describe(reg, t) {
|
|
109
142
|
if (!reg || reg.state === 'pending') return [null, t('pending')]
|
|
@@ -122,16 +155,18 @@ window.__ModuleLoader__.load({
|
|
|
122
155
|
// Shown once, right after "Enable now".
|
|
123
156
|
function wrapActivation(t) {
|
|
124
157
|
return function DshBatonActivation({ onDismiss }) {
|
|
125
|
-
const
|
|
158
|
+
const reg = useRegistration()
|
|
159
|
+
const [title, text] = describe(reg, t)
|
|
126
160
|
if (ui?.Modal && ui?.Button) {
|
|
127
161
|
return h(ui.Modal, {
|
|
128
162
|
open: true, title: title ?? t('readyTitle'), closeLabel: t('ok'), onClose: onDismiss,
|
|
129
163
|
footer: h(ui.Button, { variant: 'primary', 'data-modal-autofocus': true, onClick: onDismiss }, t('ok')),
|
|
130
|
-
}, h('p', null, text))
|
|
164
|
+
}, h('p', null, text), h(TrustNote, { reg, t }))
|
|
131
165
|
}
|
|
132
166
|
return h('div', { role: 'status', style: { margin: '12px 0', padding: 12, borderRadius: 8, border: '1px solid var(--dsw-alias-border-l3)', color: 'var(--dsw-alias-label-primary)' } },
|
|
133
167
|
title ? h('strong', null, title) : null,
|
|
134
168
|
h('p', { style: { margin: '6px 0' } }, text),
|
|
169
|
+
h(TrustNote, { reg, t }),
|
|
135
170
|
h('button', { type: 'button', onClick: onDismiss }, t('ok')))
|
|
136
171
|
}
|
|
137
172
|
}
|
|
@@ -147,7 +182,8 @@ window.__ModuleLoader__.load({
|
|
|
147
182
|
return h('div', { style: { display: 'flex', flexDirection: 'column', gap: 4, color: 'var(--dsw-alias-label-secondary)' } },
|
|
148
183
|
h('strong', { style: { color: 'var(--dsw-alias-label-primary)' } }, t('status')),
|
|
149
184
|
rows.length ? h('span', null, rows.join(' · ')) : null,
|
|
150
|
-
h('span', null, text)
|
|
185
|
+
h('span', null, text),
|
|
186
|
+
h(TrustNote, { reg, t }))
|
|
151
187
|
}
|
|
152
188
|
}
|
|
153
189
|
|