@ckanner/dsh-plugin-flywheel 0.27.3 → 0.28.1

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/PROTOCOL.md CHANGED
@@ -1,6 +1,6 @@
1
- # Go ↔ DSH worker 协议 v1
1
+ # Go ↔ DSH / Codex Pro worker 协议 v1
2
2
 
3
- 此文件是 `internal/drivers/dsh.go` 与本目录 worker 的互操作契约。Go 负责状态机、验收、费用上限及最终进程组清扫;worker 负责一个独立的官方 DSH SDK 进程。两者之间只有本地 stdio,不开放 HTTP 端口。
3
+ 此文件记录两种独立 worker 协议。前半部分对应 `internal/drivers/dsh.go` 与官方 DSH SDK worker;[Codex Pro 部分](#codex-pro-worker协议)对应 `internal/drivers/codex_pro.go` 和插件 machine API worker。Go 负责状态机、验收、费用上限及最终进程判断;worker 仅执行一次有边界的调用。两者之间只有本地 stdio,不开放 HTTP 端口。
4
4
 
5
5
  ## 启动与输入
6
6
 
@@ -89,6 +89,58 @@ v1 每次调用使用全新 session。Go 重试时重新提交冻结任务输入
89
89
 
90
90
  Go 的 `DSHArgs.Doctor` 使用这个入口;为保持 v1 必填 `prompt` 的 admission schema,它会用固定的探针说明替代调用者文本,worker 不将其交给 `session/prompt`。doctor 仍必须具备匹配的 ready、可靠 result、正常 SDK shutdown 与物理退出,不能因为没有推理而放松清理或权限检查。
91
91
 
92
+ ## Codex Pro worker协议
93
+
94
+ 公开入口为 `@ckanner/dsh-plugin-flywheel/codex-pro-worker`,可执行入口为 `flywheel-codex-pro-worker`。`FLYWHEEL_CODEX_PRO_WORKER` 允许显式覆盖;npm 启动器解析同版本依赖,不依靠碰巧可用的全局旧 worker。worker 实际导入已发布的 `dsh-subagent-codex-pro@0.8.0` 的 `./machine` API,严格检查包名、版本和机器协议 1。Node 最低 22.18.0,仅支持 macOS/Linux。
95
+
96
+ ### 请求与父进程连接
97
+
98
+ ```json
99
+ {"type":"start","protocol":1,"requestId":"call-123","cwd":"/absolute/worktree","model":"gpt-6.1-sol","reasoningEffort":"high","permissionMode":"workspace-write","prompt":"执行冻结工作单元","timeoutMs":1800000}
100
+ ```
101
+
102
+ 只接受这些字段;不接受 provider、resume、threadId、额外写目录、任意 CLI 参数或危险权限模式。requestId 非空且最多 256 字符;cwd 为绝对路径,Go 先规范化成物理目录。model/reasoningEffort/prompt 必须是非空文本,permissionMode 仅 read-only/workspace-write。timeoutMs 显式值必须为 1–2,147,483,647 的安全整数,省略使用上游 600,000 毫秒;worker 不接受通过 0 关闭期限。Go 传递有效 wall deadline 和 high 默认 thinking;worker 将 reasoningEffort 映射到 machine 的 effort,固定 threadMode=ephemeral、command=`FLYWHEEL_CODEX_BIN` 或 `codex`,不恢复旧线程。
103
+
104
+ **这里与 DSH worker 的 EOF 语义不同:Go 写入一行后保持 stdin 开放。** stdin 是父进程的存活连接;未完成前 EOF、读取错误或第二条非空输入触发 AbortSignal,并等待插件有界清理。不能用 `echo … | worker` 的关闭 stdin 行为冒充正常调用。运行请求首行等待上限 10 秒;请求最多 1 MiB,machine 原始输出上限 1 MiB,序列化 progress 累计最多 1 MiB,完整终态帧最多 8 MiB。超限明确失败,不能裁掉回执后判成功。
105
+
106
+ ### 进度与完整结果
107
+
108
+ 零或多个进度帧:
109
+
110
+ ```json
111
+ {"protocol":1,"type":"progress","requestId":"call-123","text":"插件报告的活动"}
112
+ ```
113
+
114
+ 最后恰好一个 result,字段结构如下,`receipt` 为插件实际完整对象:
115
+
116
+ ```json
117
+ {"protocol":1,"type":"result","requestId":"call-123","adapter":{"package":"dsh-subagent-codex-pro","version":"0.8.0","protocol":1},"receipt":null,"error":{"code":"INVALID_REQUEST","message":"启动请求未通过校验"}}
118
+ ```
119
+
120
+ 启动前不能获得 machine receipt 时为 null。运行已开始则即使失败也保留完整 receipt;`error` 非 null、重复/丢失 result、尾部垃圾、请求身份或 adapter 版本不符都不能成功。本通道没有 DSH `ready` 帧,不能据此虚构运行前完整沙箱握手或实际 effort 回显。
121
+
122
+ Go 同时校验外层 worker 物理终态和插件 receipt:请求及 run/thread/turn 身份、模型、cwd、approval=never、实际沙箱模式、完整 sandboxPolicy、stopReason、cleanup 与执行事实。workspaceWrite 的显式 writableRoots 必须处于工作区内,networkAccess 必须明确 false;Codex 标准临时目录标记保留,不宣称只能写 cwd。插件 0.8.0 只在 turn 前检查模式等基本配置;完整策略是返回后的交付资格检查,**不能阻止自定义 Codex 配置已经允许的访问**。
123
+
124
+ effort 的证据只可为 turn/start 请求接受,保留 `effectiveEffort:null`。usage 使用 `run` 增量,不能把 threadTotal 当成本次消耗;只有 complete 才记入可知预算,partial/unknown 原样存入 receipt 并增加未知调用计数。cached input/reasoning output 是总 input/output 的子项,不重复累加。订阅 costUSD 保持 null、costStatus 保持 unknown。receipt 没有实际 provider 时保持未知,不根据 channel 发明 provider。
125
+
126
+ ### 取消与进程终态
127
+
128
+ worker 捕获 SIGINT/SIGTERM/SIGHUP,先取消并等待插件返回清理结果,再分别退出 130/143/129;父连接断开为失败。Go 为 Codex Pro worker 保留至少 10 秒终止宽限,全局 kill switch 不提前缩短该子进程的宽限。插件 guardian 拥有 app-server 进程组;worker 意外死亡由插件自己的连接与 guardian 负责终止其拥有的进程。Go 未收到完整回执时不能把内部清理记为已确认,主动脱离拥有进程组的外部程序不属于此保证。
129
+
130
+ 已启动 worker 后缺少可信 `cleanup.confirmed` 会记录 `CleanupError`,即使外层进程已退出。引擎和建议模块保存诊断后停止本次运行,不执行传输重试、渠道降级、Plan 冷启动或下一任务。完整回执中确认清理的普通服务错误仍使用原重试策略。恢复前先确认该次调用遗留的进程已停止;等待固定秒数不构成清理确认。
131
+
132
+ 正常完成后 app-server 仍可能需要 TERM/KILL 收尾。只有 completed、cleanup.confirmed、无取消/超时/截断/失败事实且外层 worker 正常退出时,才允许 receipt 中正常 terminationRequested 导致的信号或未知 Codex exit code。不能将 supervisor exit code 冒充模型进程的物理退出码,也不能把文本答案当终态证明。
133
+
134
+ ### 无推理诊断
135
+
136
+ `flywheel-codex-pro-worker --doctor` 不读取 stdin,不调用 runMachine,不启动 app-server,仅导入并核验公开接口。成功返回单个 JSON:
137
+
138
+ ```json
139
+ {"protocol":1,"type":"doctor","package":"dsh-subagent-codex-pro","version":"0.8.0","machineProtocol":1,"available":true,"inferenceTested":false}
140
+ ```
141
+
142
+ Go doctor 同时检查 Node 与 Codex 命令。包导入成功不证明账号、模型、完整策略或真实推理能力,`--probe` 也不会对 Codex Pro 发模型任务。
143
+
92
144
  ## 桌面控制响应(独立于 worker JSONL)
93
145
 
94
146
  桌面控制层调用 Go 的 `control` 命令,只接收一个 JSON 文档,不把它与上述 worker 流混用。原 `start/status/stop` 字段不变,新增两种 action:
@@ -108,6 +160,33 @@ Go 的 `DSHArgs.Doctor` 使用这个入口;为保持 v1 必填 `prompt` 的 ad
108
160
 
109
161
  config/doctor 的 Go CLI 将 SIGTERM/SIGINT 转为 context 取消,等待 watchdog 收尾后才退出。控制调用取消或超过窗口时,host 先发 SIGTERM,诊断动作再留 20 秒让 Go 完成分阶段清理,仍未退出才 SIGKILL;取消始终报告控制失败,即使迟到结果成功也不覆盖它。start/status/stop 保持原控制进程的 500ms 收尾,后台引擎停止仍走独立 stop 协议。
110
162
 
163
+ ### DSH 命令失败与是否派发
164
+
165
+ `/flywheel` 的 DSH command 结果仍使用 `kind:"success"` 或 `kind:"error"`。成功结果的 `text` 是上述 Go control JSON,包括具有合法诊断报告的 doctor 失败。新的 host 将所有 command 失败包装为以下独立 JSON,放在 `kind:"error"` 的 `text` 中;这不是 Go stdout envelope,也不改变 worker JSONL 或模型工具的异常接口:
166
+
167
+ ```json
168
+ {"protocol":1,"type":"flywheel-control-error","action":"start","dispatch":"not-dispatched","message":"taskDir must be an existing directory"}
169
+ ```
170
+
171
+ | 字段 | 语义 |
172
+ |---|---|
173
+ | `protocol`、`type` | 固定为 `1` 和 `flywheel-control-error`,用于区分命令失败与引擎结果 |
174
+ | `action` | 可读取的请求 action 字符串;解析失败或没有字符串 action 时为 `null`;界面必须与自己的在途请求匹配 |
175
+ | `dispatch` | `not-dispatched` 表示宿主证明本次请求没有启动 Go 控制进程;否则为 `unknown` |
176
+ | `message` | 给用户诊断的错误文本,本身不证明进程是否启动,也不能作为另一个 envelope 递归解析 |
177
+
178
+ 只有参数/目录等派发前校验失败、派发前取消,以及进程创建失败且没有成功启动的证据,才能返回 `not-dispatched`。进入控制进程后出现的非零退出、超时、取消、坏 JSON 或任务身份不符都不能取得这项证明,即使错误文本包含相同 JSON。host 对普通错误同样增加外层 envelope,只把原始错误放入 `message`,避免进程返回的文字冒充宿主确认。
179
+
180
+ 界面只有在版本、类型、action、dispatch 和 message 都符合契约时才使用宿主证明。未知协议、旧 host 的纯文本错误、DSH 传输失败或丢失响应均保持 `unknown`。启动/停止的 `unknown` 结果必须先成功查询原任务状态,再决定后续变更;不得自动重试。已确认未派发的拒绝会显示原因,用户可以修正输入或环境后重新提交。一次未派发的状态查询也不能解除更早启动/停止留下的未确认结果。
181
+
182
+ host 在调用 Go 前把任务和仓库目录规范化为真实路径,并校验引擎返回的任务身份。界面不能把用户输入的符号链接、`..`、尾部斜杠或 Mac `/var` 与 `/private/var` 的文本差异当作引擎身份错误;响应接纳依赖 host 的路径校验和当前请求关联。
183
+
184
+ ### 浏览器查看状态的边界
185
+
186
+ 浏览器端遵循[外部 plain-JS 插件实践](https://github.com/deepseek-ai/deepseek-harness/blob/b830ec950faba0a46dda564be3e2d854afed94f1/packages/preset/agent-preset/skills/cordis-plugin-development/references/practices.md),不运行时导入 Harness Client 包。插件自有的 store handle 只通过公开的 `spec`/`create()` 结构交给槽位注册;每个实例提供 `actions`、`getSnapshot()`、`subscribe()` 和 `clearPersisted()`,由宿主负责会话作用域、实例缓存与 hooks 绑定。当前没有持久化配置,`clearPersisted()` 不删除任何任务文件。
187
+
188
+ 异步控制层另有插件自有 observable,快照在未变化时保持同一身份,经 `inject.hooks` 交给宿主绑定;React 组件不自行订阅,不读取 ctx 或原始服务对象。路径草稿与控制快照只是查看缓存,不成为任务账本,也不写入新的 DSH 会话事件。模型可见的命令和工具结果仍由宿主原有记录机制处理。此查看契约不改变 Go control 协议、worker JSONL 或后台任务的停止语义。
189
+
111
190
  ## npm 安装报告与桌面入口
112
191
 
113
192
  以下是 Node 安装器的独立契约,不与 worker JSONL、Go control envelope 或任务 `state.json` 混用。入口为 `npx -y @ckanner/flywheel@latest install` 或已安装包的 `flywheel setup`;二者共用参数与行为。普通 npm 包安装没有修改用户目录的 postinstall,只有显式 install/setup 执行 skills、安装记录和 DSH profile 配置。
@@ -124,7 +203,8 @@ config/doctor 的 Go CLI 将 SIGTERM/SIGINT 转为 context 取消,等待 watch
124
203
  | `steps` | 顺序记录 `global-cli`、`skills`、`desktop-launcher`、`dsh-plugin`、`preflight`;失败时只含已开始的步骤 |
125
204
  | `ready` | 本地 doctor 是否无阻断项;诊断未完成时为 `false` |
126
205
  | `authentication` | 固定 `unknown`,没有自动登录或真实模型请求 |
127
- | `prefix`、`paths` | 得到后才记录;paths 包含 CLI、原生二进制、worker 与生成的 launcher 的绝对路径 |
206
+ | `prefix`、`paths` | 得到后才记录;paths 包含 CLI、原生二进制、DSH worker、codexProWorker 与生成的 launcher 的绝对路径 |
207
+ | `adapter` | 实际导入通过后记录 Codex Pro 包名、精确 0.8.0、protocol 1 与 inferenceTested=false;不代表账号已就绪 |
128
208
  | `skills` | 安装成功时的数量及准确目录;每项结果保存在 skills 步骤的 `summary` |
129
209
  | `diagnostic` | 合法的完整本地 doctor 报告,包括失败检查项;不执行 `--probe` |
130
210
  | `error`、`cancelledBy` | 失败原因、可选取消信号;不是成功结果的一部分 |
@@ -139,7 +219,7 @@ config/doctor 的 Go CLI 将 SIGTERM/SIGINT 转为 context 取消,等待 watch
139
219
 
140
220
  ### GUI 安装记录
141
221
 
142
- 安装器生成 `~/.config/flywheel/installation.json`(0600)与 `npm-launcher`(0755)。记录包含 `protocol:1`、`owner:"@ckanner/flywheel"`、`version`、`prefix`、`cli`、`worker`、`node` 和 `launcher:{path,sha256}`。它只描述软件入口,不保存账号凭证,不构成第二份任务状态。
222
+ 安装器生成 `~/.config/flywheel/installation.json`(0600)与 `npm-launcher`(0755)。记录包含 `protocol:1`、`owner:"@ckanner/flywheel"`、`version`、`prefix`、`cli`、`worker`、`codexProWorker`、`adapter`、`node` 和 `launcher:{path,sha256}`。它只描述软件入口,不保存账号凭证,不构成第二份任务状态。旧记录仍按原字段读取,升级 setup 生成新字段;用户修改的 launcher 继续受保护。
143
223
 
144
224
  launcher 使用绝对 Node 与 CLI 路径,设置安装时可见的绝对工具 PATH 目录,并在用户没有显式设置 `FLYWHEEL_DSH_BIN` 时使用已核对的桌面命令。npm CLI 启动器解析版本一致的原生包和 worker,缺失或版本不匹配时拒绝继续,不隐式下载另一份二进制。临时 npx 缓存与 `node_modules/.bin` 不被记录成持久工具 PATH。
145
225
 
package/README.en.md ADDED
@@ -0,0 +1,349 @@
1
+ # Flywheel for DSH
2
+
3
+ [简体中文](README.md) · [Project overview](https://github.com/ckanner/flywheel/blob/main/README.en.md) · [Getting started](https://github.com/ckanner/flywheel/blob/main/docs/en/getting-started.md)
4
+
5
+ This package connects the DSH desktop to Flywheel's deterministic Go engine and
6
+ provides its DSH SDK and Codex Pro workers. The engine owns task state, budgets,
7
+ verification, Git delivery, and process lifecycles. The desktop is a control and
8
+ observation interface; it does not need a parent model session to keep a task alive.
9
+
10
+ This guide describes the **Flywheel 0.28.1** interfaces. Source availability does
11
+ not mean that npm `latest` already contains them. Check the registry version and
12
+ the [release guide](https://github.com/ckanner/flywheel/blob/main/docs/en/releasing.md) before installing. The desktop
13
+ compatibility baseline is **DSH Mac 0.2.1-rc.5**, not a claim that every DSH build
14
+ or provider adapter is compatible.
15
+
16
+ **Flywheel 0.28.1 introduces the native conversation view.** Version `0.28.0`
17
+ uses the composer popup. Upgrade the CLI and desktop plugin to the same new
18
+ version; merging source does not update an installed plugin.
19
+
20
+ ## Components
21
+
22
+ | Component | Responsibility |
23
+ |---|---|
24
+ | Flywheel conversation view | A native view alongside Chat and Trajectory for task progress, intervention, configuration, checks, and explicit start/refresh/stop |
25
+ | `/flywheel` command | Invoke the Go control interface from an explicit desktop command |
26
+ | `flywheel_control` tool | Expose the same control operations to the host model |
27
+ | `flywheel-dsh-worker` | Run one native model session through an independent DSH SDK profile |
28
+ | `flywheel-codex-pro-worker` | Call the public machine API of the pinned `dsh-subagent-codex-pro@0.8.0` package |
29
+
30
+ The plugin has no separate task database and does not write `state.json`.
31
+ Kimi CLI, Codex Pro, and the explicit legacy Codex CLI path have separate Go
32
+ drivers. A DSH LLM provider and an external agent tool are different interfaces.
33
+
34
+ The view answers which stage is running, what needs attention, which models are
35
+ configured, and how to stop or recover the task. Requirement discussion, contract
36
+ preparation, and ordinary conversation stay in DSH's chat and skills. Flywheel
37
+ does not replace the host's composer, model selector, trajectory, or right sidebar.
38
+
39
+ ## Install the complete application
40
+
41
+ You need **Node.js 22.18.0 or later**, npm, and the existing DSH Mac desktop with
42
+ its desktop-installed `dsh` command. Verify the desktop version and the command
43
+ you are actually running. Do not guess an executable path inside an application
44
+ bundle.
45
+
46
+ ```sh
47
+ npm view @ckanner/flywheel version
48
+ npx -y @ckanner/flywheel@latest install
49
+ ```
50
+
51
+ The explicit installer installs the resolved Flywheel version, the matching Go
52
+ platform binary and workers, and ten embedded skills. It creates a desktop
53
+ launcher, registers the same plugin version with the desktop profile, and checks
54
+ the installed versions. It uses the desktop's plugin management interface:
55
+
56
+ ```text
57
+ dsh plugin --profile desktop add --save-exact --ignore-scripts @ckanner/dsh-plugin-flywheel@<resolved-version>
58
+ ```
59
+
60
+ The standalone npm DSH CLI cannot manage the reserved `desktop` profile. If the
61
+ desktop command is not on PATH, pass `--dsh-bin /absolute/path/to/dsh` to the
62
+ Flywheel installer. Reload or restart as instructed by the host.
63
+
64
+ Use `--prefix "$HOME/.local"` if you need a writable installation prefix, and add
65
+ its `bin` directory to your shell PATH. `--no-dsh` explicitly skips desktop
66
+ registration. Go CLI packages are provided for macOS and Linux, on arm64 and
67
+ x64; Linux package availability does not establish support for the Mac desktop
68
+ or its sandbox. See the [installation guide](https://github.com/ckanner/flywheel/blob/main/docs/en/getting-started.md)
69
+ for the full options and supported workflow.
70
+
71
+ Software installation and model readiness are separate. With `--json`,
72
+ `ok: true` means installation succeeded; `ready: false` can still report missing
73
+ Kimi or Codex executables or other local prerequisites. Authentication and actual
74
+ inference remain unverified. Flywheel does not install or update DSH, sign in to
75
+ model services, copy credentials, or provide subscription access.
76
+
77
+ Ordinary `npm install --global @ckanner/flywheel` only installs package files.
78
+ It has no postinstall hook that changes your profile or skills. To finish setup
79
+ for an existing persistent installation, run:
80
+
81
+ ```sh
82
+ flywheel setup --no-global
83
+ ```
84
+
85
+ Installing `@ckanner/dsh-plugin-flywheel` alone gives you this plugin and its
86
+ workers, not the complete Flywheel application.
87
+
88
+ ## Prepare the SDK profile and selected models
89
+
90
+ The native DSH worker uses `sdk` by default. A provider installed or authorized
91
+ in the desktop profile is not automatically present in the SDK profile.
92
+ `FLYWHEEL_DSH_PROFILE` can select another SDK profile, but cannot select
93
+ `desktop`. Keep `DSH_HOME` consistent with the configuration you intend to use.
94
+
95
+ The native DeepSeek provider is `deepseek-official`, with `deepseek-flash` in
96
+ the default configuration. At the checked rc.5 baseline its effort values are
97
+ `off`, `low`, `high`, and `max`; there is no `medium`. Other providers have their
98
+ own accepted values. For native GPT routes, install and authorize the selected
99
+ ChatGPT adapter in the SDK profile. One checked compatibility example is:
100
+
101
+ ```sh
102
+ dsh plugin --profile sdk add dsh-plugin-chatgpt@0.1.17
103
+ ```
104
+
105
+ This example does not migrate desktop credentials or prove that your account
106
+ can use every model in Flywheel's configuration. Confirm the exact model IDs
107
+ and effort values against the selected provider. The
108
+ [configuration guide](https://github.com/ckanner/flywheel/blob/main/docs/en/configuration.md) explains profiles,
109
+ provider IDs, account channels, and existing-configuration migration.
110
+
111
+ The default `hard` route uses `harness: "codex-pro"`. Its worker directly imports
112
+ `dsh-subagent-codex-pro/machine`; the package runs Codex app-server using your
113
+ existing Codex installation and login. It does not ask a DSH parent model to
114
+ delegate again. `subagent_codex_pro` is not an LLM provider ID, and this route does
115
+ not silently fall back to `codex exec`.
116
+
117
+ | Setting | Meaning |
118
+ |---|---|
119
+ | `FLYWHEEL_DSH_BIN` | Explicit DSH executable; otherwise use the configured launcher environment or PATH |
120
+ | `FLYWHEEL_DSH_PROFILE` | Independent SDK profile, default `sdk` |
121
+ | `DSH_HOME` | Optional DSH configuration root |
122
+ | `FLYWHEEL_DSH_WORKER` | Explicit DSH worker override; the npm launcher normally resolves the matching dependency |
123
+ | `FLYWHEEL_CODEX_PRO_WORKER` | Explicit Codex Pro worker override; normally resolved by the npm launcher |
124
+ | `FLYWHEEL_CODEX_BIN` | Codex executable used by the plugin, default `codex` |
125
+ | `CODEX_HOME` | Optional existing Codex configuration and login directory |
126
+ | `FLYWHEEL_KIMI_BIN` | Kimi executable for routes that use its CLI, default `kimi` |
127
+
128
+ The selected executables and configuration must be visible to the process that
129
+ starts the engine. A shell export does not update an already running desktop.
130
+
131
+ ## Check the installation without inference
132
+
133
+ Run these from the task's integration worktree, after preparing and reviewing
134
+ its contracts. Replace the task name with your own:
135
+
136
+ ```sh
137
+ flywheel config inspect --cwd "$PWD" --task-dir "$PWD/docs/my-task" --json
138
+ flywheel doctor --cwd "$PWD" --task-dir "$PWD/docs/my-task" --json
139
+ flywheel doctor --cwd "$PWD" --task-dir "$PWD/docs/my-task" --probe --json
140
+ ```
141
+
142
+ Omit `--task-dir` to inspect the general configuration. Ordinary doctor checks
143
+ local configuration and commands. `--probe` additionally initializes the DSH
144
+ SDK, checks its working directory and permission enforcement, and closes the
145
+ session. Provider initialization may query a model catalog or create SDK
146
+ session/cache files, but it does not send an inference prompt or run task
147
+ acceptance commands. It does not write Flywheel state or create worktrees.
148
+
149
+ For Codex Pro, doctor imports the pinned machine API and checks local prerequisites;
150
+ it does not start an inference task. The worker's narrower package check is:
151
+
152
+ ```sh
153
+ flywheel-codex-pro-worker --doctor
154
+ ```
155
+
156
+ Its successful response reports `available: true` and `inferenceTested: false`.
157
+ Neither check proves account authorization, quota, model availability, or an
158
+ actual inference result. Keep `warn`, `unknown`, and `skipped` distinct from
159
+ `pass`. A blocking diagnostic returns exit code 1 with the report intact.
160
+
161
+ ## Use the desktop controls
162
+
163
+ Open a DSH session and choose **Flywheel** in the native view bar alongside
164
+ Chat and Trajectory. If the entry is not directly visible, check that bar's
165
+ More menu. Select the absolute task worktree path as the repository directory
166
+ and the task directory inside that same worktree. A blank repository field
167
+ uses the current session's starting directory. Prepare and review the contracts
168
+ first; the view does not turn an empty directory into a complete task plan.
169
+
170
+ The panel, `/flywheel`, and `flywheel_control` share these Go operations:
171
+
172
+ ```sh
173
+ flywheel control start --cwd "$PWD" --task-dir "$PWD/docs/my-task"
174
+ flywheel control status --cwd "$PWD" --task-dir "$PWD/docs/my-task"
175
+ flywheel control stop --cwd "$PWD" --task-dir "$PWD/docs/my-task"
176
+ flywheel control config --cwd "$PWD"
177
+ flywheel control doctor --cwd "$PWD" --task-dir "$PWD/docs/my-task" --probe
178
+ ```
179
+
180
+ Starting a real task calls the selected models. `start` launches a background
181
+ engine; closing the panel or desktop is not a stop request. Refresh status to
182
+ observe it, and use `stop` to request shutdown. `ok: true` describes a successful
183
+ control operation; task completion is recorded in `state.status`.
184
+
185
+ Opening the view starts no task or probe. It reads on explicit actions, without
186
+ automatic polling or task discovery. To observe a task started through another
187
+ entry point, select its task directory and refresh. Effective configuration comes
188
+ from the engine's selected file; installing the plugin or changing DSH's chat
189
+ model does not migrate that configuration.
190
+
191
+ ### View, request, and task lifetimes
192
+
193
+ Task and repository drafts and fetched snapshots are retained separately for
194
+ each DSH session across Chat/Trajectory/Flywheel view switches while the host
195
+ retains that session binding. They are not a task database or durable storage
196
+ across binding release, plugin reload, or desktop restart. Only one control
197
+ request is pending per session. Start/stop locks the directory fields while
198
+ waiting. A read-only request allows path edits, but stays pending until its reply
199
+ settles and rejects late results for the previous selection. Hiding a view does
200
+ not make a pending operation complete.
201
+
202
+ Task state is the last successful snapshot with its refresh time, not a live
203
+ monitor. Failed reads retain valid prior content and show the failure. A lost,
204
+ timed-out, or disconnected start/stop response means the outcome is **unconfirmed**;
205
+ it does not prove that nothing started or that a task stopped. Refresh the same
206
+ task first and inspect its log when needed before deciding how to recover.
207
+ Until a successful status refresh reconciles that task, the view keeps the
208
+ uncertainty visible and disables further starts and stops. It never automatically
209
+ retries a start or stop.
210
+
211
+ A missing directory or a control process that cannot be created is recoverable
212
+ after correcting the input or environment only when the host supplies structured
213
+ evidence that the request was not dispatched. An ordinary error, connection loss,
214
+ or error-message text cannot establish that fact or dismiss an unconfirmed outcome.
215
+ The `/flywheel` command's error `text` uses the JSON discriminator
216
+ `type:"flywheel-control-error"`; its fields and compatibility behavior are defined
217
+ in the [command failure protocol](PROTOCOL.md#dsh-命令失败与是否派发) (Chinese).
218
+
219
+ Switching views, closing the session page, removing the plugin, or exiting DSH
220
+ does not stop the independent Go task. Request an explicit stop and await engine
221
+ confirmation. DSH's trajectory retains its own parent-session command and tool
222
+ events. Complete SDK, Kimi, and Codex Pro worker histories are not automatically
223
+ merged into that trajectory or presented as native DSH background tasks.
224
+
225
+ ### Host UI contract
226
+
227
+ The client uses `ctx.slots.inject()` and a distinct `id: flywheel` in the
228
+ `conversation.view` list. The host owns session scope and view navigation. Chat,
229
+ Trajectory, the composer, and the right sidebar keep their existing entries;
230
+ Flywheel neither shadows them nor draws a separate floating sidebar or composer
231
+ popup. Long paths, model details, and errors wrap or scroll inside the view.
232
+
233
+ As an external plain-JS plugin, the browser module imports only shared React at
234
+ runtime. It does not import `ui-primitives`, `client-store`, or another Harness
235
+ Client package. The plugin owns its buttons, inputs, viewing-state store handle,
236
+ and observable; the host manages registered session instances and binds their
237
+ read hooks. Locale comes through the injected service. Controls follow the
238
+ checked host markup and interactions, consume semantic color aliases, inherit
239
+ the host font, and mount compiled CSS Modules through the React view.
240
+
241
+ The baseline is the exact source snapshot `b830ec950faba0a46dda564be3e2d854afed94f1`,
242
+ which declares version `0.2.1-rc.5`: [external plugin practices](https://github.com/deepseek-ai/deepseek-harness/blob/b830ec950faba0a46dda564be3e2d854afed94f1/packages/preset/agent-preset/skills/cordis-plugin-development/references/practices.md),
243
+ [UI guidance](https://github.com/deepseek-ai/deepseek-harness/blob/b830ec950faba0a46dda564be3e2d854afed94f1/.agents/skills/dsh-client-ui-ux/SKILL.md),
244
+ [layout](https://github.com/deepseek-ai/deepseek-harness/blob/b830ec950faba0a46dda564be3e2d854afed94f1/packages/client/ui-layout/README.md),
245
+ [conversation slots](https://github.com/deepseek-ai/deepseek-harness/blob/b830ec950faba0a46dda564be3e2d854afed94f1/packages/client/ui-conversation/src/client/contract/slots.ts),
246
+ and [slot lifecycle](https://github.com/deepseek-ai/deepseek-harness/blob/b830ec950faba0a46dda564be3e2d854afed94f1/docs/subsystems/slots.md).
247
+ The view sits within Conversation's existing header and content geometry; it
248
+ does not change global window-chrome clearance. Source-contract inspection does
249
+ not establish a live Mac desktop pass.
250
+
251
+ Offline UI checks execute the built plugin's own controls, store handle, and
252
+ observable with React and jsdom. Component fixtures adapt slot bindings and
253
+ Remote; a separate check uses the published rc.5 renderer, slots, and Cordis to
254
+ exercise registration, session generations, declaration collapse/restoration,
255
+ and disposal. These checks do not establish real browser geometry or Mac
256
+ window behavior.
257
+
258
+ Use one task integration worktree for preparation, planning, milestone
259
+ verification, and the eventual PR. The engine manages packet worktrees for
260
+ execution, L1, and review. A successful packet is merged into the branch of the
261
+ checkout from which the engine was started. The engine does not push, open
262
+ GitHub PRs, merge remote PRs, or publish packages. See the
263
+ [worktree and PR workflow](https://github.com/ckanner/flywheel/blob/main/docs/en/workflow.md).
264
+
265
+ ## Permissions and lifecycle boundaries
266
+
267
+ Desktop integration and worker policy have different scopes. The worker's
268
+ temporary overlay applies only to its independent **SDK process**; it does not
269
+ edit the desktop profile or remove the desktop's plugins, skills, tools, or UI.
270
+ Conversely, desktop installation and sign-in do not prove that the SDK profile
271
+ loads and authorizes the same capabilities. Verify each selected harness through
272
+ its own configuration and supported authorization flow.
273
+
274
+ The DSH worker requests `read-only` or `workspace-write`, fixes approvals to
275
+ `never`, and verifies the effective SDK policy before sending the task prompt.
276
+ In the checked Mac source, the DSH sandbox uses its Seatbelt runner and filesystem
277
+ fence. Workspace writes include DSH's permitted temporary area; this is not a
278
+ network-isolation guarantee. Missing or incomplete enforcement is a failure,
279
+ not a reason to switch to unrestricted access.
280
+
281
+ The SDK overlay disables worker-owned delegation, workflow/goal drivers, model
282
+ titles, and automatic model compaction so the Go owner can bound work and
283
+ account for model calls. It also currently disables the whole `tool-web` package,
284
+ including both `web_search` and `web_fetch`. This is a conservative policy,
285
+ not a claim that every DSH web operation requires a model: the official
286
+ [web tool](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/web/tool-web/README.md)
287
+ configures search and fetch independently, while the
288
+ [DeepSeek search backend](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/web/web-search-deepseek/README.md)
289
+ does use an auxiliary model request. This UI fix keeps worker policy unchanged.
290
+ Restoring retrieval or [automatic compaction](https://github.com/deepseek-ai/deepseek-harness/blob/fcc0ad462172522aaf5c93a7b2e01f6b9f0b0159/packages/compaction/compaction-basic/README.md)
291
+ requires a separate check of accounting, budgets, cancellation, and directory
292
+ ownership; the view's location does not establish those guarantees.
293
+
294
+ Codex Pro uses fresh ephemeral calls and checks the model, directory, approval
295
+ policy, and sandbox mode before execution. In version 0.8.0, the full
296
+ `sandboxPolicy` reaches Flywheel only in the returned receipt. Flywheel rejects
297
+ delivery when that policy permits unexpected writable roots or network access,
298
+ but **this check happens after execution and cannot undo access already
299
+ permitted by custom Codex configuration**. It is not a complete pre-execution
300
+ policy guard. Kimi CLI print mode likewise does not provide an OS sandbox that
301
+ this adapter can promise.
302
+
303
+ A worktree separates Git working files and index state; it is not a security
304
+ sandbox. The current engine can fall back to `in-place` if packet worktree
305
+ creation fails. If the task requires strict workspace separation, stop and
306
+ resolve that condition before continuing. Restarting alone does not convert
307
+ saved `in-place` state into a new isolated worktree.
308
+
309
+ Go owns worker shutdown; Codex Pro also owns a separate guardian/app-server
310
+ lifecycle. Text output or the outer worker exiting does not prove that every
311
+ owned process has stopped. When cleanup cannot be confirmed, Flywheel preserves
312
+ the workspace and audit evidence and stops automatic retries, fallback, cold
313
+ restarts, and later work in that run. Confirm the owned processes have ended
314
+ before recovery. A successful stop operation is not proof that an arbitrary
315
+ escaped process has been cleaned up.
316
+
317
+ Complete receipts preserve configuration, usage, and termination evidence.
318
+ Accepted effort is not proof of the server's effective reasoning effort.
319
+ Unreported usage and dollar cost remain unknown; subscriptions are not recorded
320
+ as free calls. The current DSH bridge does not support session resume or image
321
+ and attachment inputs. Its protocol and implementation details are documented
322
+ in [PROTOCOL.md](PROTOCOL.md) (Chinese).
323
+
324
+ ## Upgrade and maintain the desktop entry
325
+
326
+ The installer records an absolute Node/CLI launcher under
327
+ `~/.config/flywheel/`, including its content hash and the checked tool paths.
328
+ An explicit plugin `flywheelBinary` setting takes precedence. Without an
329
+ explicit setting, the plugin uses a valid installation record; only a missing
330
+ record permits legacy PATH lookup. A damaged record or changed launcher fails
331
+ visibly instead of selecting a different engine.
332
+
333
+ After upgrading Flywheel, moving the npm prefix, changing Node, or adding a
334
+ required CLI, run `flywheel setup --no-global` from a terminal where those tools
335
+ are available. Retain your chosen prefix and DSH executable options. Reload the
336
+ desktop as needed and run its checks again. Custom configuration and modified
337
+ skills are preserved and reported as conflicts; save your changes and resolve
338
+ the conflict rather than deleting their ownership records.
339
+
340
+ Existing Flywheel configuration is not automatically migrated. To adopt the new
341
+ plugin path from 0.27.x, change the effective hard-route harness from `codex` to
342
+ `codex-pro`, including any referenced profile, and inspect the expanded result.
343
+ The full upgrade and removal procedure is in
344
+ [Getting started](https://github.com/ckanner/flywheel/blob/main/docs/en/getting-started.md).
345
+
346
+ For source changes and offline verification, read the
347
+ [contribution guide](https://github.com/ckanner/flywheel/blob/main/docs/contributing.md). The repository currently uses
348
+ `UNLICENSED`; an npm release or a future public repository does not by itself
349
+ establish an open-source license.