@khorsheed/dsh-local-agent-codex 0.1.0-rc.6 → 0.1.0-rc.7

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/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # 变更记录
2
+
3
+ ## 0.1.0-rc.7(2026-09-27)
4
+
5
+ 家族同版联动:随宿主 0.1.7-rc.2 基线发布波重发,全量构建+测试在 rc.2 基线通过。README 截图整理(委派视图一张从未存在的引用删除,全部图片引用可解析)。无功能变更。
6
+
7
+ ## 0.1.0-rc.6(2026-09-26)
8
+
9
+ 首个公开发布。
package/README.en.md CHANGED
@@ -1,30 +1,36 @@
1
- # `@khorsheed/dsh-local-agent-codex`
1
+ # @khorsheed/dsh-local-agent-codex
2
2
 
3
3
  English | [中文](README.md)
4
4
 
5
- **Live output migration.** Live runs always consume incremental output. The old `liveMirrorGranularity: event | token` key is accepted for existing profiles but ignored; changing it never changes a running process. Exec remains available for evaluation. Final provider items remain authoritative, including tool history and usage.
5
+ Hand coding tasks to the Codex CLI installed on your own machine — its own login, its own session records, and your `~/.codex` never gets touched.
6
6
 
7
- Delegate coding tasks from any dsh agent preset to your locally installed Codex CLI. Delegations run under a plugin-scoped home, so your personal `~/.codex` — config, credentials, sessions — is never touched.
7
+ dsh agents already get work done, but some tasks you simply want to give to your local Codex CLI: its account, its models, the toolchain you know. This plugin wires Codex in as a dsh local subagent — any agent preset can delegate with one call through the `subagent_codex` tool; delegations run inside a plugin-scoped home (`$DSH_HOME/local-agent/codex`), so your personal config, credentials and sessions are never touched; the process details (reasoning, tool calls, file changes) mirror live into the child session, and the final answer comes back to the parent.
8
+
9
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-web-basic/main/docs/screenshots/08-local-agent.png" width="640" alt="the Local Agent · Codex card on the Settings → plugin configuration page, its header status dot showing the auth state at a glance">
8
10
 
9
11
  ## Features
10
12
 
11
13
  - **Delegate from any preset** — the `subagent_codex` tool mounts at the profile root; no per-preset setup.
12
- - **Scoped-home isolation** — all Codex state lives in `$DSH_HOME/local-agent/codex`, separate from your `~/.codex`.
13
- - **In-session login** — device-code `/codex login`, with auth status and sign-out under Settings → 本地 Agent.
14
- - **Resume a thread** — pass `resume="<childSessionId>"` to continue the same codex thread in the same dsh child session.
14
+ - **Scoped-home isolation** — all Codex state (config, credentials, rollout session records) lives in `$DSH_HOME/local-agent/codex`, separate from your `~/.codex`.
15
+ - **In-session login** — `/codex login` runs `codex login` on a pseudo-terminal and the authorization page opens in your browser by itself; when the page hands you a code, paste it back with `/codex code <code>`. The settings card shows auth status and offers sign-out.
16
+ - **Resident mode (live)** — one resident `codex app-server` process per member, one turn per round: output streams into the child session in real time, cancel does not kill the process, and a crash resumes the same thread; off — or a channel that cannot come up — means the one-shot `codex exec` path, hot-switched without a reload.
17
+ - **Settings card** — the Codex card on the Plugins detail page (on 0.1.5, Settings → Plugins → 插件配置): sign-in, a default model (blank = follow the CLI default), and the resident switch; a save applies on the next round.
18
+ - **Resume a thread** — pass `resume="<childSessionId>"` to continue the same codex thread in the same dsh child session, even across host restarts.
15
19
  - **Custom endpoint** — route Codex's LLM requests through your own router via a scoped `config.toml` provider.
16
20
  - **Read-back and per-cell working directory** — every settled round reads the model, CLI version and usage back out of its own rollout into the delegation record; the file is located by thread id, cwd and time window, so concurrent runs each read their own round. Orchestrators pass a `cwd` per cell, and a resume in a different directory is rejected.
21
+
22
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-web-basic/main/docs/screenshots/local-agent-codex-card.png" width="640" alt="the expanded Codex settings card: auth status with sign-in and sign-out, the default model, and the resident-mode switch">
23
+
17
24
  ## Install
18
25
 
19
- Prerequisites: a dsh profile and the Codex CLI (`codex`) on `PATH` — the plugin neither installs it nor logs in for you.
26
+ Prerequisites: a working dsh profile and the Codex CLI (`codex`) on `PATH` — the plugin neither installs it nor logs in for you.
20
27
 
21
28
  ```sh
22
29
  dsh plugin --profile web add @khorsheed/dsh-local-agent
23
30
  dsh plugin --profile web add @khorsheed/dsh-local-agent-codex
24
- # restart the profile, then run /codex login once from a session
25
31
  ```
26
32
 
27
- Both packages must be named explicitly — `dsh plugin add` reconciles only direct dependencies.
33
+ Both packages must be named explicitly — `dsh plugin add` reconciles only direct dependencies, so the family core is never mounted by a transitive dependency. Restart the web instance to activate, then run `/codex login` once from any session to authorize.
28
34
 
29
35
  Uninstall:
30
36
 
@@ -32,7 +38,7 @@ Uninstall:
32
38
  dsh plugin --profile web remove @khorsheed/dsh-local-agent-codex
33
39
  ```
34
40
 
35
- The scoped home (`$DSH_HOME/local-agent/codex`) is kept so a reinstall needs no fresh login; delete it to remove every trace.
41
+ The scoped home (`$DSH_HOME/local-agent/codex`) is kept on purpose, so a reinstall needs no fresh login; delete it to remove every trace.
36
42
 
37
43
  ## Config
38
44
 
@@ -47,6 +53,8 @@ Optional, in the profile patch layer:
47
53
  liveIdleMs: 1800000 # idle lifetime of a resident runtime before reclaim (default 30 min)
48
54
  ```
49
55
 
56
+ **Legacy key migration.** Live runs always consume incremental output: the old `liveMirrorGranularity: event | token` key is accepted for existing profiles but ignored, and changing it never changes a running process. Exec remains available for evaluation; final provider items remain authoritative, including tool history and usage.
57
+
50
58
  ### Default model (`model`)
51
59
 
52
60
  **Absent = today's behavior.** Without this key the plugin adds no model flag to the argv at all: the scoped `config.toml`'s top-level `model` decides which model runs, and codex's own default decides when that is missing too.
@@ -89,13 +97,14 @@ The last line selects the provider for delegations; keep the rest of the file in
89
97
 
90
98
  ## Compatibility
91
99
 
92
- - npm release line (`@deepseek-ai/dsh@0.1.5-rc.1`): ✅ public API compatible. Live generation uses the local-agent transient Remote and public Conversation nodes; suffix checkpoints provide recovery, and native final messages retain transcript and usage semantics. Browser P95 acceptance is tracked separately in the room coordinator proposal. Older hosts stay on the previous release line.
100
+ - npm release line (`@deepseek-ai/dsh@0.1.5-rc.1`): ✅ public API compatible. Live generation uses the local-agent transient Remote and public Conversation nodes; incremental durable checkpoints provide recovery, and native final messages retain their transcript and usage semantics. End-to-end latency acceptance is tracked by the room coordinator proposal. minHost 0.1.5-rc.1 — older hosts stay on the previous release line.
93
101
  - source line (deepseek-harness master): ✅ (verifiedHost: 0.1.5-rc.1)
94
102
 
95
103
  ## Known Limitations
96
104
 
97
- - **Login needs one browser step** — device-code only; no API-key path.
98
- - **`codex exec` is non-interactive** — actions the sandbox policy would approve are denied, not prompted; see the `sandbox` config.
105
+ - **Login needs one browser authorization** — `/codex login` opens the authorization page itself, and when the CLI asks for a code you paste it back with `/codex code <code>`; there is no API-key path.
106
+ - **`codex exec` is non-interactive** — actions the sandbox policy would approve are denied, not prompted; in resident mode approval-shaped requests are likewise auto-declined unattended (cancel/decline). See the `sandbox` config.
107
+ - **Named scopes are exec-only** — the resident app-server binds the default scoped home, so a scoped delegation never enters live mode.
99
108
  - **Headless caveat** — `/codex` commands need a Web session; headless can still delegate via a composition mounting the tool row.
100
109
 
101
110
  ## How it works
@@ -105,11 +114,11 @@ The last line selects the provider for delegations; keep the rest of the file in
105
114
 
106
115
  **Named scopes.** `/codex login --scope <name>` opens a second scoped home at `<homesRoot>/codex@<name>`: the directory is provisioned with the same `config.toml` that pins credential storage to a file rather than the macOS keychain, so that scope's login lands in its own `auth.json`. A scoped delegation runs `codex exec` there, writes its rollout there and reads it back from there — exec-only, because the resident app-server is bound to the default scoped home.
107
116
 
108
- **Bundle composition.** The patch registers the `codex` harness (scoped `CODEX_HOME`, device-code login, rollout-file session records) and mounts `subagent_codex` at the profile root; the `codex-local` one-shot provider spawns `codex exec` under that home. The settings section ships with the family core's `./client` half; the core itself comes from `@khorsheed/dsh-local-agent`, declared as a dependency.
117
+ **Bundle composition.** The patch registers the `codex` harness (scoped `CODEX_HOME`, PTY login, rollout-file session records) and mounts the `subagent_codex` tool at the profile root — the official preset row of that name already ships `disabled: true`, and the patch disables it once more, so a community install can never register two `subagent_codex` tools; a user who deliberately re-enables the official row in their own preset layer gets a loud duplicate-name conflict and must choose one, by design. The `codex-local` provider spawns `codex exec` under that home (the one-shot drive when live is off). The browser half ships with this package's `./client`: the Codex settings card composes the family core's shared `ProviderAuthBlock`, landing on the Plugins page's per-package detail view (`plugins.bundle.config`, keyed by package name) for a standalone install, on the bundle detail view's row-level configure entry (`plugins.row.config`) when installed as a family-bundle member, and — on 0.1.5 — on the Settings → Plugins → 插件配置 tab (`settings.plugin.item`, keyed by the settings namespace); card writes ride the bound settingsScope and hot-apply without a reload. The core itself comes from `@khorsheed/dsh-local-agent`, declared as a dependency.
109
118
 
110
- **Login and credentials.** `/codex login` shows the device-code URL in-session and polls in the background; credentials land in the scoped home on authorization. First start writes a minimal `config.toml` pinning `cli_auth_credentials_store = "file"` — Codex's default `auto` would resolve to the macOS keychain, leaking credentials outside the scoped home and defeating this package's `auth.json` presence check; an existing config is left untouched. `/codex logout` deletes the scoped `auth.json`, so a later login authorizes a fresh account.
119
+ **Login and credentials.** `/codex login` runs `codex login` on a pseudo-terminal through the host's subprocess seam — the CLI starts its own localhost callback server and opens the browser itself, the first-class flow; the command reply carries the authorization URL captured from the output as a fallback link, and when the CLI asks for a code the user pastes it with `/codex code <code>` (written to the child's stdin). Completion is decided by a credential watch on the scoped home: `auth.json` present with a stamp newer than the watch start — a leftover credential is never mistaken for a fresh login. A browserless remote terminal can still run `codex login --device-auth` by hand with `CODEX_HOME` pointed at the scoped home, landing in the same `auth.json`. First start writes a minimal `config.toml` pinning `cli_auth_credentials_store = "file"` — Codex's default `auto` would resolve to the macOS keychain, leaking credentials outside the scoped home and defeating this package's `auth.json` presence check; an existing config is left untouched. `/codex logout` deletes the scoped `auth.json`, so a later login authorizes a fresh account.
111
120
 
112
- **Session records.** `/codex sessions` lists the scoped home's `sessions/YYYY/MM/DD/rollout-*.jsonl` files — sessions this plugin's delegations created, never your personal ones. The settings section narrows the list to records whose `workDir` matches the session cwd.
121
+ **Session records.** `/codex sessions` lists the scoped home's `sessions/YYYY/MM/DD/rollout-*.jsonl` files — sessions this plugin's delegations created, never your personal ones. The records entry in the session header (provided by the family core's client) narrows the same list to records whose `workDir` matches the session cwd.
113
122
 
114
123
  **Read-back and the cwd override.** After every settled round the provider reads three facts back out of that round's own rollout file, reports them through the `settled` run-progress event, and merges them into `delegations.jsonl`: the model (`turn_context.payload.model` — codex 0.144.0's exec stream carries no model at all, so the rollout is the only authority; filtered to this round's time window, so a resumed thread's earlier models are never misread), the CLI version (`session_meta.payload.cli_version` — written by the codex build that actually served the round, a stronger answer than probing the executable afterwards), and the token usage a non-completed round never streamed (the last `token_count`). Absence is recorded, never guessed.
115
124
 
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # last confirmed-consistent state. Both languages carry equal authority; after
3
3
  # editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/local-agent-codex/README.en.md
5
- packages/local-agent-codex/README.en.md: 9e97dee795a54e41d13515d895365a7347751c2f
6
- packages/local-agent-codex/README.md: a75ad178e28915254eb7ab2693a52ef7451cccfc
5
+ packages/local-agent-codex/README.en.md: 9fc4710953666e5aced6b17cdc8b95ef072deaff
6
+ packages/local-agent-codex/README.md: b39c0e06ca0a4550c90783b0e3929772dcb61756
package/README.md CHANGED
@@ -1,78 +1,86 @@
1
- # `@khorsheed/dsh-local-agent-codex`
1
+ # @khorsheed/dsh-local-agent-codex
2
2
 
3
3
  [English](README.en.md) | 中文
4
4
 
5
- **实时输出迁移。** live 轮次统一消费增量输出。旧 `liveMirrorGranularity: event | token` 配置继续兼容读取,但不再影响行为,也不会改变运行中的进程。评测继续保留 exec。最终内容仍以 provider 完成项为准,包括工具记录和用量。
5
+ 把编码任务甩给你本机装好的 Codex CLI 跑——它自己的登录、自己的会话记录,你的 `~/.codex` 一根手指都不碰。
6
6
 
7
- 把编码任务从任意 dsh agent preset 委派给你本地安装的 Codex CLI。委派在插件隔离的作用域目录下运行,你个人的 `~/.codex`——config、凭据、会话——完全不被触碰。
7
+ dsh 的 agent 已经能干活,但有些任务你就是想交给本机的 Codex CLI:它的账号、它的模型、你熟悉的工具链。这个插件把 Codex 接成 dsh 的本地子代理——任意 agent preset 都能经 `subagent_codex` 工具一句话委派;委派跑在插件隔离的作用域目录(`$DSH_HOME/local-agent/codex`)里,你的个人 config、凭据、会话完全不被触碰;过程细节(推理、工具调用、文件改动)实时镜像在子会话里,最终回答回到父会话。
8
+
9
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-web-basic/main/docs/screenshots/08-local-agent.png" width="640" alt="设置 → 插件配置页里的 Local Agent · Codex 卡片,卡头状态点一眼可见授权状态">
8
10
 
9
11
  ## 特性
10
12
 
11
- - **任意 preset 皆可委派**——`subagent_codex` 工具挂在 profile 根,无需逐 preset 配置。
12
- - **作用域目录隔离**——Codex 的全部状态留在 `$DSH_HOME/local-agent/codex`,与你的 `~/.codex` 互不干扰。
13
- - **会话内登录**——device-code `/codex login`,设置 → 本地 Agent 显示认证状态并提供退出登录按钮。
14
- - **线程续聊**——传入 `resume="<childSessionId>"` 在同一个 dsh 子会话里继续同一个 codex 线程。
13
+ - **任意 preset 皆可委派**——`subagent_codex` 工具挂在 profile 根,无需逐 preset 配置。
14
+ - **作用域目录隔离**——Codex 的全部状态(config、凭据、rollout 会话记录)留在 `$DSH_HOME/local-agent/codex`,与你的 `~/.codex` 互不干扰。
15
+ - **会话内登录**——`/codex login` 在伪终端里跑 `codex login`,授权页自动在浏览器打开;页面给出 code 时用 `/codex code <code>` 粘贴回来。设置卡片显示认证状态并提供退出登录。
16
+ - **常驻模式(live)**——每成员常驻一个 `codex app-server` 进程,按轮发 turn:输出实时流入子会话、取消不杀进程、崩溃后自动续上原线程;关闭或通道起不来即回一次性 `codex exec`,热切换无需重载。
17
+ - **设置卡片**——Plugins 详情页(0.1.5 为 设置 → 插件 → 插件配置)里的 Codex 卡:登录授权、默认模型(留空 = 跟随 CLI 默认)、常驻开关,保存即于下一轮生效。
18
+ - **线程续聊**——传入 `resume="<childSessionId>"` 在同一个 dsh 子会话里继续同一个 codex 线程,宿主重启后也续得上。
15
19
  - **自定义端点**——经作用域 `config.toml` 的自定义 provider 把 Codex 的 LLM 请求路由到你自己的路由端点。
16
- - **回读与独立工作目录**——每轮从本轮 rollout 回读实际模型、CLI 版本与用量写进委派记录;定位按 threadId + cwd + 时间窗,并发跑也读的是自己那一轮。编排器可用 `cwd` 选项给每格独立目录,resume 换目录即拒绝。
20
+ - **回读与独立工作目录**——每轮从本轮 rollout 回读实际模型、CLI 版本与用量写进委派记录;定位按 threadId + cwd + 时间窗,并发跑也读的是自己那一轮。编排器可用 `cwd` 选项给每格独立目录,resume 换目录即拒绝。
21
+
22
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-web-basic/main/docs/screenshots/local-agent-codex-card.png" width="640" alt="展开的 Codex 设置卡:认证状态与登录/退出、默认模型、常驻模式开关">
23
+
17
24
  ## 安装
18
25
 
19
- 前置依赖:一个可运行的 dsh profile 和 `PATH` 上的 Codex CLI(`codex`)——插件不安装它、也不替你登录。
26
+ 前置依赖:一个可运行的 dsh profile 和 `PATH` 上的 Codex CLI(`codex`)——插件不安装它、也不替你登录。
20
27
 
21
28
  ```sh
22
29
  dsh plugin --profile web add @khorsheed/dsh-local-agent
23
30
  dsh plugin --profile web add @khorsheed/dsh-local-agent-codex
24
- # restart the profile, then run /codex login once from a session
25
31
  ```
26
32
 
27
- 两个包必须同时点名——`dsh plugin add` 只调和**直接**依赖。
33
+ 两个包必须同时点名——`dsh plugin add` 只调和**直接**依赖,家族 core 不会被传递依赖自动挂载。重启 web 实例后生效,然后在任意会话里跑一次 `/codex login` 完成授权。
28
34
 
29
- 卸载:
35
+ 卸载:
30
36
 
31
37
  ```sh
32
38
  dsh plugin --profile web remove @khorsheed/dsh-local-agent-codex
33
39
  ```
34
40
 
35
- 作用域目录(`$DSH_HOME/local-agent/codex`)会刻意保留,重装无需重新登录;删除它即可清除全部痕迹。
41
+ 作用域目录(`$DSH_HOME/local-agent/codex`)会刻意保留,重装无需重新登录;删除它即可清除全部痕迹。
36
42
 
37
43
  ## 配置
38
44
 
39
- 可选,写在 profile patch 层:
45
+ 可选,写在 profile patch 层:
40
46
 
41
47
  ```yaml
42
48
  - id: local-agent-codex
43
49
  config:
44
50
  sandbox: workspace-write # codex exec 策略:read-only | workspace-write | danger-full-access
45
51
  model: gpt-5.2 # 可选:每轮委派以它起 CLI;不写就一个模型参数都不传(见下)
46
- live: false # 长驻驱动:每成员常驻一个 codex app-server 进程,按轮发 turn(runtime 级优雅中断、事件推送镜像);关闭或通道不可用即回一次性 exec
47
- liveIdleMs: 1800000 # 长驻 runtime 空闲回收时限(默认 30 分钟)
52
+ live: false # 常驻驱动:每成员常驻一个 codex app-server 进程,按轮发 turn(运行时级优雅中断、事件推送镜像);关闭或通道不可用即回一次性 exec
53
+ liveIdleMs: 1800000 # 常驻 runtime 空闲回收时限(默认 30 分钟)
48
54
  ```
49
55
 
50
- ### 默认模型(`model`)
56
+ **旧键迁移。** live 轮次统一消费增量输出:旧 `liveMirrorGranularity: event | token` 配置继续兼容读取,但不再影响行为,也不会改变运行中的进程。评测继续保留 exec 驱动;最终内容仍以 provider 完成项为准,包括工具记录和用量。
57
+
58
+ ### 默认模型(`model`)
51
59
 
52
- **不写 = 今天的表现。**没有这个键时,本插件在 argv 上一个模型参数都不加:跑哪个模型由作用域 `config.toml` 的顶层 `model` 决定,它也没有时由 codex 自己的默认决定。
60
+ **不写 = 今天的表现。**没有这个键时,本插件在 argv 上一个模型参数都不加:跑哪个模型由作用域 `config.toml` 的顶层 `model` 决定,它也没有时由 codex 自己的默认决定。
53
61
 
54
- **写了 = 每轮委派以它起 CLI。**新起一轮与续接(resume)同等对待:
62
+ **写了 = 每轮委派以它起 CLI。**新起一轮与续接(resume)同等对待:
55
63
 
56
64
  | 驱动 | 传法 |
57
65
  |---|---|
58
- | 一次性(默认) | `codex exec -m <model> …`;`-m` 是 `codex exec` 自己的选项,因此排在 `resume` 子命令之前(`codex exec resume` 不认自己的 `-m`) |
59
- | 常驻(`live: true`) | `codex app-server -c model="<model>" --stdio`;app-server 没有 `-m`,用 CLI 为此提供的进程级 `-c` 覆盖 |
66
+ | 一次性(默认) | `codex exec -m <model> …`;`-m` 是 `codex exec` 自己的选项,因此排在 `resume` 子命令之前(`codex exec resume` 不认自己的 `-m`) |
67
+ | 常驻(`live: true`) | `codex app-server -c model="<model>" --stdio`;app-server 没有 `-m`,用 CLI 为此提供的进程级 `-c` 覆盖 |
60
68
 
61
- 作用域 `config.toml` 不会被改写——`-m` 每轮覆盖它,文件仍是你编辑的样子。
69
+ 作用域 `config.toml` 不会被改写——`-m` 每轮覆盖它,文件仍是你编辑的样子。
62
70
 
63
- 设置卡「默认模型」修改后续轮次使用的 provider 配置。共享选择器展示当前作用域的模型目录、发现来源和完整性,并保留按需填写模型 ID 的入口。清空选择后跟随有效配置及默认值链。保存不会打断当前轮次,也无需重载;共享选择器不可用时,卡片保留文本输入兜底。成员级模型和推理强度修改使用下文的持久控制面。
71
+ 设置卡「默认模型」修改后续轮次使用的 provider 配置。共享选择器展示当前作用域的模型目录、发现来源和完整性,并保留按需填写模型 ID 的入口。清空选择后跟随有效配置及默认值链。保存不会打断当前轮次,也无需重载;共享选择器不可用时,卡片保留文本输入兜底。成员级模型和推理强度修改使用下文的持久控制面。
64
72
 
65
- 丰富目录读面现已保留原生显示名、隐藏候选和推理选项,遍历分页,并接入 core 缓存与刷新订阅。查询上下文跟随成员的 scope 和 cwd。刷新失败保留最后成功快照并标为过期;配置项和历史建议保留来源标识。共用模型菜单和成员 effort 控制已接入。运行中选择排到下一完整轮次(含工具续跑);core 统一持有当前/待生效配置、撤销与重试,冻结评测成员禁止变更。原生目录候选不等于账号权限证明。
73
+ 丰富目录读面现已保留原生显示名、隐藏候选和推理选项,遍历分页,并接入 core 缓存与刷新订阅。查询上下文跟随成员的 scope 和 cwd。刷新失败保留最后成功快照并标为过期;配置项和历史建议保留来源标识。共用模型菜单和成员 effort 控制已接入。运行中选择排到下一完整轮次(含工具续跑);core 统一持有当前/待生效配置、撤销与重试,冻结评测成员禁止变更。原生目录候选不等于账号权限证明。
66
74
 
67
- **委派级的模型优先。**编排器可以经门面 `DelegationCallOptions.model` 给**某一次委派**点名模型,它排在这个键之前(固定顺序:会话覆盖 > 委派记录 > 本键 > 作用域 config > CLI 内置)。首轮请求的值记进委派记录,resume 轮照它重发——resume 不接受 model 参数。常驻模式下带模型的轮次不再被拒绝:它成为该成员的**起始模型**,在 app-server 起进程时绑定;若该成员的常驻 runtime 绑定的是另一个模型,先回收重生(同一 codex 线程经 thread/resume 续上),再以所点模型起新一轮。
75
+ **委派级的模型优先。**编排器可以经门面 `DelegationCallOptions.model` 给**某一次委派**点名模型,它排在这个键之前(固定顺序:会话覆盖 > 委派记录 > 本键 > 作用域 config > CLI 内置)。首轮请求的值记进委派记录,resume 轮照它重发——resume 不接受 model 参数。常驻模式下带模型的轮次不再被拒绝:它成为该成员的**起始模型**,在 app-server 起进程时绑定;若该成员的常驻 runtime 绑定的是另一个模型,先回收重生(同一 codex 线程经 thread/resume 续上),再以所点模型起新一轮。
68
76
 
69
- **成员级切换(作曲器模型选择器)。**成员会话里可以按会话切换模型:一个内存中的会话级覆盖,优先级最高,宿主重启即失。切换在有轮次进行中时被拒绝;空闲时若成员有常驻 runtime 且绑定模型不同,切换即回收该 runtime——下一轮以新模型重生,CLI 会话(rollout)本身延续。一次性(exec)驱动下没有常驻进程,覆盖直接决定下一轮的 `-m`。
77
+ **成员级切换(作曲器模型选择器)。**成员会话里可以按会话切换模型:一个内存中的会话级覆盖,优先级最高,宿主重启即失。切换在有轮次进行中时被拒绝;空闲时若成员有常驻 runtime 且绑定模型不同,切换即回收该 runtime——下一轮以新模型重生,CLI 会话(rollout)本身延续。一次性(exec)驱动下没有常驻进程,覆盖直接决定下一轮的 `-m`。
70
78
 
71
- **这不是评测的缺口。**评测 run 的条件在建立时冻结:run 跑到一半改这个键,下一轮的模型回读会发现声明模型 ≠ 实测模型,run 直接判为 misattributed 而失败(冻结决策 5)。换句话说「切了新 run 照新走、进行中的 run 不被悄悄换掉」是设计出来的,不是漏掉的。
79
+ **这不是评测的缺口。**评测 run 的条件在建立时冻结:run 跑到一半改这个键,下一轮的模型回读会发现声明模型 ≠ 实测模型,run 直接判为 misattributed 而失败(冻结决策 5)。换句话说「切了新 run 照新走、进行中的 run 不被悄悄换掉」是设计出来的,不是漏掉的。
72
80
 
73
81
  ## 自定义端点
74
82
 
75
- 把 Codex 的 LLM 请求路由到你自己的端点:在作用域 `$DSH_HOME/local-agent/codex/config.toml` 里**新增自定义 provider**(内置 provider 不可覆盖、`OPENAI_BASE_URL` 不生效、预置逻辑从不覆盖已存在的 config):
83
+ 把 Codex 的 LLM 请求路由到你自己的端点:在作用域 `$DSH_HOME/local-agent/codex/config.toml` 里**新增自定义 provider**(内置 provider 不可覆盖、`OPENAI_BASE_URL` 不生效、预置逻辑从不覆盖已存在的 config):
76
84
 
77
85
  ```toml
78
86
  [model_providers.dsh-router]
@@ -81,56 +89,57 @@ base_url = "https://your-router.example/v1"
81
89
  model_provider = "dsh-router"
82
90
  ```
83
91
 
84
- 最后一行为委派选择该 provider;文件其余内容必须保留。
92
+ 最后一行为委派选择该 provider;文件其余内容必须保留。
85
93
 
86
- ⚠️ **凭据暴露**:作用域 `auth.json` token 会发送给处理请求的端点——只使用你控制或信任的端点。每次委派在 info 级别记录生效的端点。
94
+ ⚠️ **凭据暴露**:作用域 `auth.json` token 会发送给处理请求的端点——只使用你控制或信任的端点。每次委派在 info 级别记录生效的端点。
87
95
 
88
- **评测快照(effectiveSettings)。** 本 harness 向注册表声明一份实时读取的公平性设置快照,供评测条件哈希使用:drive(exec/live)、sandbox 策略(插件配置)、推理强度(读作用域 config 的顶层 `model_reasoning_effort`)、端点是否固定(读作用域 config 的自定义 provider,只报主机名)、已配置模型(先看插件配置的 `model` 键——它每轮覆盖文件;没有才读作用域 config 的顶层 `model`;都没有就不给字段)、CLI 版本(`codex --version`,按可执行文件路径+mtime 缓存;探测不到即字段缺位)。`/codex status` 与 `LocalAgentStatus` Remote 附带同一份快照。
96
+ **评测快照(effectiveSettings)。** 本 harness 向注册表声明一份实时读取的公平性设置快照,供评测条件哈希使用:drive(exec/live)、sandbox 策略(插件配置)、推理强度(读作用域 config 的顶层 `model_reasoning_effort`)、端点是否固定(读作用域 config 的自定义 provider,只报主机名)、已配置模型(先看插件配置的 `model` 键——它每轮覆盖文件;没有才读作用域 config 的顶层 `model`;都没有就不给字段)、CLI 版本(`codex --version`,按可执行文件路径+mtime 缓存;探测不到即字段缺位)。`/codex status` 与 `LocalAgentStatus` Remote 附带同一份快照。
89
97
 
90
98
  ## Compatibility
91
99
 
92
- - npm 发布线(`@deepseek-ai/dsh@0.1.5-rc.1`):✅ 公开 API 兼容。生成中的内容走 local-agent 瞬时 Remote 与公开 Conversation 节点;后缀检查点负责恢复,最终原生消息保留转写与用量语义。浏览器 P95 另在 room 协调者提案中验收。更旧宿主留在前一发布线。
93
- - 源码线(deepseek-harness master):✅(verifiedHost: 0.1.5-rc.1)
100
+ - npm 发布线(`@deepseek-ai/dsh@0.1.5-rc.1`):✅ 公开 API 兼容。生成中的内容走 local-agent 瞬时 Remote 与公开 Conversation 节点;增量耐久检查点负责恢复,最终原生消息保留转写与用量语义。端到端延迟验收由 room 协调者提案跟踪。minHost 0.1.5-rc.1,更旧宿主请停留在旧发布线。
101
+ - 源码线(deepseek-harness master):✅(verifiedHost: 0.1.5-rc.1)
94
102
 
95
103
  ## 已知限制
96
104
 
97
- - **登录需要一次浏览器授权**——仅 device-code,没有 API key 路径。
98
- - **`codex exec` 非交互运行**——沙箱策略本需审批的动作会被拒绝而非弹提示;见 `sandbox` 配置。
99
- - **headless 注意**——`/codex` 命令需要 Web 会话;headless 仍可通过挂载工具行的组合进行委派。
105
+ - **登录需要一次浏览器授权**——`/codex login` 自动打开授权页,CLI 追问 code 时以 `/codex code <code>` 粘贴回来;没有 API key 路径。
106
+ - **`codex exec` 非交互运行**——沙箱策略本需审批的动作会被拒绝而非弹提示;常驻模式下审批类请求同样按无人值守策略自动谢绝(cancel/decline)。见 `sandbox` 配置。
107
+ - **命名 scope 只走一次性 exec**——常驻 app-server 绑定缺省作用域目录,带 scope 的委派不进 live。
108
+ - **headless 注意**——`/codex` 命令需要 Web 会话;headless 仍可通过挂载工具行的组合进行委派。
100
109
 
101
110
  ## 实现原理
102
111
 
103
112
  <details>
104
- <summary>内部结构(点击展开)</summary>
113
+ <summary>内部结构(点击展开)</summary>
105
114
 
106
- **命名 scope。** `/codex login --scope <名>` 会在 `<homesRoot>/codex@<名>` 里另开一份作用域目录:目录建立时自动跑同一份 `config.toml` provision(把凭据存储钉在文件而非 macOS keychain),因此该 scope 的登录落在它自己的 `auth.json` 里。带 scope 的委派用它跑 `codex exec`、把 rollout 写进它、从它回读;只走 exec——常驻 app-server 绑的是缺省作用域目录。
115
+ **命名 scope。** `/codex login --scope <名>` 会在 `<homesRoot>/codex@<名>` 里另开一份作用域目录:目录建立时自动跑同一份 `config.toml` provision(把凭据存储钉在文件而非 macOS keychain),因此该 scope 的登录落在它自己的 `auth.json` 里。带 scope 的委派用它跑 `codex exec`、把 rollout 写进它、从它回读;只走 exec——常驻 app-server 绑的是缺省作用域目录。
107
116
 
108
- **bundle 组成。** patch 注册 `codex` harness(`CODEX_HOME` 作用域目录、device-code 登录、rollout 文件会话记录),并把 `subagent_codex` 工具挂到 profile 根;`codex-local` 一次性 provider 在该作用域目录下 spawn `codex exec`。设置分区随家族 core 的 `./client` 半提供;core 本身来自声明为依赖的 `@khorsheed/dsh-local-agent`。
117
+ **bundle 组成。** patch 注册 `codex` harness(`CODEX_HOME` 作用域目录、PTY 登录、rollout 文件会话记录),并把 `subagent_codex` 工具挂到 profile 根——官方 preset 的同名行本就 `disabled: true`,patch 再禁一次,社区安装永远不会注册出两个 `subagent_codex`;刻意在自有 preset 层重新启用官方行的用户会得到响亮的重名冲突,按设计必须二选一。`codex-local` provider 在该作用域目录下 spawn `codex exec`(live 未启用时的一次性驱动)。浏览器半部分随本包 `./client` 提供:Codex 设置卡组合家族 core 的共享 `ProviderAuthBlock`,独立安装落在 Plugins 页的包详情视图(`plugins.bundle.config`,按包名派发),作为家族 bundle 成员安装落在 bundle 详情页的行级配置入口(`plugins.row.config`),0.1.5 落在 设置 → 插件 → 插件配置(`settings.plugin.item`,按设置命名空间派发);卡片写入经绑定的 settingsScope 热生效,不用重载。core 本身来自声明为依赖的 `@khorsheed/dsh-local-agent`。
109
118
 
110
- **登录与凭据。** `/codex login` 在会话中显示 device-code URL 并在后台轮询;用户授权后凭据写入作用域目录。首次启动写入一份最小 `config.toml`,固定 `cli_auth_credentials_store = "file"`——Codex 默认的 `auto` 会解析到 macOS keychain,把凭据泄漏到作用域目录之外并使本包的 `auth.json` 存在性检查失效;已存在的 config 保持不动。`/codex logout` 删除作用域 `auth.json`,之后重新登录即可换账号。
119
+ **登录与凭据。** `/codex login` 经宿主 subprocess seam 在伪终端里跑 `codex login`——CLI 自己起 localhost 回调服务并打开浏览器,这是第一类流程;命令回复附带从输出里抓取的授权 URL 作兜底链接,CLI 追问 code 时用户以 `/codex code <code>` 粘贴(写入子进程 stdin)。完成由监听作用域目录的凭证 watch 判定:`auth.json` 出现且时间戳新于 watch 起点——残留的旧凭证不会被误认成新登录。无浏览器的远程终端可以手动以 `CODEX_HOME` 指向作用域目录跑 `codex login --device-auth`,凭据落在同一份 `auth.json`。首次启动写入一份最小 `config.toml`,固定 `cli_auth_credentials_store = "file"`——Codex 默认的 `auto` 会解析到 macOS keychain,把凭据泄漏到作用域目录之外并使本包的 `auth.json` 存在性检查失效;已存在的 config 保持不动。`/codex logout` 删除作用域 `auth.json`,之后重新登录即可换账号。
111
120
 
112
- **会话记录。** `/codex sessions` 列出作用域目录的 `sessions/YYYY/MM/DD/rollout-*.jsonl` 文件——仅本插件委派产生的会话,绝不含你的私人会话。设置分区把列表收窄到 `workDir` 与当前会话 cwd 一致的记录。
121
+ **会话记录。** `/codex sessions` 列出作用域目录的 `sessions/YYYY/MM/DD/rollout-*.jsonl` 文件——仅本插件委派产生的会话,绝不含你的私人会话。会话头部的记录入口(家族 core 的客户端提供)把同一份列表收窄到 `workDir` 与当前会话 cwd 一致的记录。
113
122
 
114
- **回读与 cwd 覆盖。** 每轮 settle 后,provider 从本轮自己的 rollout 文件里回读三件事,随 `settled` 进度事件上报并合并进 `delegations.jsonl`:模型(`turn_context.payload.model`——codex 0.144.0 的 exec 流事件根本不带 model,rollout 是唯一权威;按本轮时间窗过滤,resume 线程里先前轮次的模型不会被误读)、CLI 版本(`session_meta.payload.cli_version`——服务本轮的那个 codex build 自己写下的,比事后探测可执行文件更准)、以及非正常结束时流里缺失的 token 用量(最后一条 `token_count`)。取不到即缺位,绝不猜测。
123
+ **回读与 cwd 覆盖。** 每轮 settle 后,provider 从本轮自己的 rollout 文件里回读三件事,随 `settled` 进度事件上报并合并进 `delegations.jsonl`:模型(`turn_context.payload.model`——codex 0.144.0 的 exec 流事件根本不带 model,rollout 是唯一权威;按本轮时间窗过滤,resume 线程里先前轮次的模型不会被误读)、CLI 版本(`session_meta.payload.cli_version`——服务本轮的那个 codex build 自己写下的,比事后探测可执行文件更准)、以及非正常结束时流里缺失的 token 用量(最后一条 `token_count`)。取不到即缺位,绝不猜测。
115
124
 
116
- **工具调用计数。** 每轮 settle 时,provider 顺带数出本轮的工具调用,随 `settled` 进度事件上报(`toolCalls: { count, byName }`)。计数只来自流解析**已经走过**的 `item.completed` 分支,不新增任何解析路径:`command_execution`、`web_search_call`、`file_change`、`function_call` 各记一次,`function_call_output` 是结果、不计。`byName` 的键是 **codex 自己的 item 类型**(`command_execution`),不是镜像卡片上的显示名(`Bash`)——记的是 CLI 怎么叫这件事。本轮一份,绝不累计;一次都没调用就整个字段缺位(缺席 ≠ 0)。实测:一轮「列目录并统计文件数」回读 `{count: 1, byName: {command_execution: 1}}`,两条命令的一轮回读 `{count: 2}`。
125
+ **工具调用计数。** 每轮 settle 时,provider 顺带数出本轮的工具调用,随 `settled` 进度事件上报(`toolCalls: { count, byName }`)。计数只来自流解析**已经走过**的 `item.completed` 分支,不新增任何解析路径:`command_execution`、`web_search_call`、`file_change`、`function_call` 各记一次,`function_call_output` 是结果、不计。`byName` 的键是 **codex 自己的 item 类型**(`command_execution`),不是镜像卡片上的显示名(`Bash`)——记的是 CLI 怎么叫这件事。本轮一份,绝不累计;一次都没调用就整个字段缺位(缺席 ≠ 0)。实测:一轮「列目录并统计文件数」回读 `{count: 1, byName: {command_execution: 1}}`,两条命令的一轮回读 `{count: 2}`。
117
126
 
118
- 定位与扫描两处都按并发校准过。`turn_context` 是 codex 在**回合开始**时写的,所以一轮里之后产生的事件一多就会把它挤出文件尾——只扫尾部会读回 null(一次真实评测跑就是这么丢的:38 KB 的 smoke 轮读得到,100–500 KB 的正式格读不到),因此尾部扫不到就再做一次有界的整文件读。定位则在 threadId 之外把**本轮 cwd** 也算进去:并发委派会让多个格的文件落进同一个时间窗,`session_meta.payload.cwd` 才是区分它们的字段;窗口里有候选却没有一个对得上本轮目录时,宁可什么都不报也不报邻居那一轮(窗口里只有一个候选除外——那是路径写法差异,不是歧义)。编排器还可以经门面 `DelegationCallOptions.cwd` 给本轮指定工作目录(记录进 `cwd` 字段);resume 轮解析出的目录若与首轮记录不一致,进程启动前即 fail loud——CLI 会话延续的是首轮所在目录的上下文。
127
+ 定位与扫描两处都按并发校准过。`turn_context` 是 codex 在**回合开始**时写的,所以一轮里之后产生的事件一多就会把它挤出文件尾——只扫尾部会读回 null(一次真实评测跑就是这么丢的:38 KB 的 smoke 轮读得到,100–500 KB 的正式格读不到),因此尾部扫不到就再做一次有界的整文件读。定位则在 threadId 之外把**本轮 cwd** 也算进去:并发委派会让多个格的文件落进同一个时间窗,`session_meta.payload.cwd` 才是区分它们的字段;窗口里有候选却没有一个对得上本轮目录时,宁可什么都不报也不报邻居那一轮(窗口里只有一个候选除外——那是路径写法差异,不是歧义)。编排器还可以经门面 `DelegationCallOptions.cwd` 给本轮指定工作目录(记录进 `cwd` 字段);resume 轮解析出的目录若与首轮记录不一致,进程启动前即 fail loud——CLI 会话延续的是首轮所在目录的上下文。
119
128
 
120
- **容器内委派。** 编排器可以经门面 `DelegationCallOptions.exec`(`{ container, workdir, env? }`)让本轮跑在一个**已取得的容器**里:argv 变成 `docker exec -w <workdir> [-e NAME…] <container> codex exec …`,其余(流解析、settle、rollout 回读、记录)逐字节不变。`env` 必须给出容器内的 `CODEX_HOME`,且它应当是宿主作用域目录的 rw bind 挂载点——rollout 回读读的是宿主那份文件。容器轮固定走 exec 一次性驱动(长驻 app-server 是宿主进程),且不声明成员桥(宿主 unix socket 进不去容器)。实测:`eval-env:pinned` 单元里一次「回答 2+2」settle 为 `completed`,输出 `4`,`observedModel` 从容器写进宿主作用域目录的 rollout 里回读为 `gpt-5.6-sol`。
129
+ **容器内委派。** 编排器可以经门面 `DelegationCallOptions.exec`(`{ container, workdir, env? }`)让本轮跑在一个**已取得的容器**里:argv 变成 `docker exec -w <workdir> [-e NAME…] <container> codex exec …`,其余(流解析、settle、rollout 回读、记录)逐字节不变。`env` 必须给出容器内的 `CODEX_HOME`,且它应当是宿主作用域目录的 rw bind 挂载点——rollout 回读读的是宿主那份文件。容器轮固定走 exec 一次性驱动(长驻 app-server 是宿主进程),且不声明成员桥(宿主 unix socket 进不去容器)。实测:`eval-env:pinned` 单元里一次「回答 2+2」settle 为 `completed`,输出 `4`,`observedModel` 从容器写进宿主作用域目录的 rollout 里回读为 `gpt-5.6-sol`。
121
130
 
122
- **续聊(resume)。** 家族工具(`@khorsheed/dsh-local-agent-tool-subagent`)在官方 `description`/`prompt` 子集上增加可选 `resume` 参数。首次委派的结果文本自述句柄(`追问请带 resume="<childSessionId>"`);后续轮次传回它即在**同一个** dsh 子会话里继续**同一个** codex 线程(`codex exec --json resume <thread_id>`),按轮记账。句柄只从 `resume` 参数读取,localAgent registry 仅对记录该委派的同一 parent 会话与 provider 解析——伪造句柄在任何 CLI 进程启动前就被拒绝。
131
+ **续聊(resume)。** 家族工具(`@khorsheed/dsh-local-agent-tool-subagent`)在官方 `description`/`prompt` 子集上增加可选 `resume` 参数。首次委派的结果文本自述句柄(`追问请带 resume="<childSessionId>"`);后续轮次传回它即在**同一个** dsh 子会话里继续**同一个** codex 线程(`codex exec --json resume <thread_id>`),按轮记账。句柄只从 `resume` 参数读取,localAgent registry 仅对记录该委派的同一 parent 会话与 provider 解析——伪造句柄在任何 CLI 进程启动前就被拒绝。
123
132
 
124
- **事件流镜像。** 子代理会话按顺序镜像 `codex exec --json` 事件流:`reasoning` → 推理块、`agent_message` → 回复文本、`command_execution`/`web_search_call`/`function_call_output` → 工具行(`[工具 Bash] <command> → output`),`file_change` → `ApplyPatch` 卡(逐 change 列出 kind 与路径),`function_call`(如 multi-agent 的 `wait`)→ 同名工具卡、结果由 `function_call_output` 并入;最终 `agent_message` 作为运行输出,当轮用量挂在最后一条镜像的 assistant 消息上。续聊轮各自追加自己的 turn,不会重复。中止会让工具结果立即 settle,并保留已收到的事件与用量。
133
+ **事件流镜像。** 子代理会话按顺序镜像 `codex exec --json` 事件流:`reasoning` → 推理块、`agent_message` → 回复文本、`command_execution`/`web_search_call`/`function_call_output` → 工具行(`[工具 Bash] <command> → output`),`file_change` → `ApplyPatch` 卡(逐 change 列出 kind 与路径),`function_call`(如 multi-agent 的 `wait`)→ 同名工具卡、结果由 `function_call_output` 并入;最终 `agent_message` 作为运行输出,当轮用量挂在最后一条镜像的 assistant 消息上。续聊轮各自追加自己的 turn,不会重复。中止会让工具结果立即 settle,并保留已收到的事件与用量。
125
134
 
126
- **模型体验。** 每次委派都是委派 Session 工作区内一个全新的一次性 `codex exec` 进程。父级只提交任务文本、只看到最终回答或精确错误——Codex 的评论、工具活动、工作区 diff 不会跨进父级会话,子会话 token 也永不进入父级上下文。
135
+ **模型体验。** 每次委派都是委派 Session 工作区内一个全新的一次性 `codex exec` 进程。父级只提交任务文本、只看到最终回答或精确错误——Codex 的评论、工具活动、工作区 diff 不会跨进父级会话,子会话 token 也永不进入父级上下文。
127
136
 
128
- **长驻驱动(`live: true`)。** 替代每轮 spawn:成员首轮委派拉起一个常驻 `codex app-server --stdio` 进程(同 scoped home、同 `-c` 成员桥声明),创建持久线程(`thread/start`,`ephemeral: false`),之后每轮 = 向活着的 runtime 发 `turn/start`;`item/completed` 事件即时折进子会话(与 exec 共享同一 `CodexTranscriptLine` 折叠与 append 核,最后一行留置到 `turn/completed` 以挂用量),`cancel` 落地为 `turn/interrupt`——进程不死、线程可续。审批类 server→client 请求按无人值守策略自动应答(cancel/decline,与 exec 行为一致)。runtime 空闲超时回收(app-server 无 shutdown 方法:stdin EOF → SIGTERM 阶梯),崩溃后下一轮自动重连并 `thread/resume` 盘上线程;握手失败进冷却熔断,逐轮回退 exec。
137
+ **长驻驱动(`live: true`)。** 替代每轮 spawn:成员首轮委派拉起一个常驻 `codex app-server --stdio` 进程(同 scoped home、同 `-c` 成员桥声明),创建持久线程(`thread/start`,`ephemeral: false`),之后每轮 = 向活着的 runtime 发 `turn/start`;`item/completed` 事件即时折进子会话(与 exec 共享同一 `CodexTranscriptLine` 折叠与 append 核,最后一行留置到 `turn/completed` 以挂用量),`cancel` 落地为 `turn/interrupt`——进程不死、线程可续。审批类 server→client 请求按无人值守策略自动应答(cancel/decline,与 exec 行为一致)。runtime 空闲超时回收(app-server 无 shutdown 方法:stdin EOF → SIGTERM 阶梯),崩溃后下一轮自动重连并 `thread/resume` 盘上线程;握手失败进冷却熔断,逐轮回退 exec。
129
138
 
130
- **委派记账。** provider 在 spawn 时开 `turn/start`、settle 时关 `turn/end`——失败或取消(`error`/`aborted`)同样关闭——`subagentTiming` 的时长等于实际 CLI 运行时长;最终 assistant 消息携带从事件流解析的 token 用量。codex 的 `input_tokens` 含缓存命中,所以未缓存桶取 `input_tokens − cached_input_tokens`、`cached_input_tokens` 映射缓存读取、没有缓存写入概念——`tokenUsage` 不会双重计数缓存命中。每个续聊轮在各自递增的轮次号下重复这套记账。**非 completed 终态(aborted/error)的用量回落。** 被中止/失败的轮次永远收不到 `turn.completed`,事件流里没有用量——但 codex 已把本轮真实 token 消耗写进了 scoped home 的 rollout 文件。此时 exec 镜像改读**本次 run 的 rollout 文件末条 `token_count`** 挂用量:文件按线程 id(`session_meta` 头)定位,流在 `thread.started` 之前就被截断时按 spawn 时间窗回落;口径与 `turn.completed` 完全一致(`input − cached` 等桶,共享 `usageFromCodex`)。硬杀到连 `token_count` 都没写出的极端情况仍保持无用量,不猜测。
139
+ **委派记账。** provider 在 spawn 时开 `turn/start`、settle 时关 `turn/end`——失败或取消(`error`/`aborted`)同样关闭——`subagentTiming` 的时长等于实际 CLI 运行时长;最终 assistant 消息携带从事件流解析的 token 用量。codex 的 `input_tokens` 含缓存命中,所以未缓存桶取 `input_tokens − cached_input_tokens`、`cached_input_tokens` 映射缓存读取、没有缓存写入概念——`tokenUsage` 不会双重计数缓存命中。每个续聊轮在各自递增的轮次号下重复这套记账。**非 completed 终态(aborted/error)的用量回落。** 被中止/失败的轮次永远收不到 `turn.completed`,事件流里没有用量——但 codex 已把本轮真实 token 消耗写进了 scoped home 的 rollout 文件。此时 exec 镜像改读**本次 run 的 rollout 文件末条 `token_count`** 挂用量:文件按线程 id(`session_meta` 头)定位,流在 `thread.started` 之前就被截断时按 spawn 时间窗回落;口径与 `turn.completed` 完全一致(`input − cached` 等桶,共享 `usageFromCodex`)。硬杀到连 `token_count` 都没写出的极端情况仍保持无用量,不猜测。
131
140
 
132
141
  </details>
133
142
 
134
143
  ## 开发
135
144
 
136
- 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo(`packages/local-agent-codex`)。问题与贡献请移步该仓库。
145
+ 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo(`packages/local-agent-codex`)。问题与贡献请移步该仓库。
package/lib/client.js CHANGED
@@ -233,7 +233,7 @@ window.__ModuleLoader__.load({
233
233
  }
234
234
  //#endregion
235
235
  //#region \0dsh-css:../local-agent/src/client/ProviderAuthBlock.module.css.mjs
236
- const css$2 = ".On5p7W_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:4px;display:flex}.On5p7W_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.On5p7W_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.On5p7W_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.On5p7W_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:10px;padding:12px 14px;display:flex}.On5p7W_rowPending{opacity:.6}.On5p7W_rowHead{align-items:center;gap:10px;display:flex}.On5p7W_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.On5p7W_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.On5p7W_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.On5p7W_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.On5p7W_dotOn{background:var(--dsw-alias-state-success-primary)}.On5p7W_dotOff{background:var(--dsw-alias-state-error-primary)}.On5p7W_dotPending{background:var(--dsw-alias-fill-l3)}.On5p7W_status{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;font-size:12px;line-height:18px}.On5p7W_rowActions{align-items:center;gap:4px;display:inline-flex}.On5p7W_loginButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_loginButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.On5p7W_loginButton:disabled{color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l2);cursor:not-allowed}.On5p7W_logoutButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-state-error-primary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_logoutButton:hover{border-color:var(--dsw-alias-state-error-primary)}.On5p7W_rosterError{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;margin:8px 0 0;font-size:12px;line-height:18px;display:flex}.On5p7W_retryButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:12px;justify-content:center;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_retryButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.On5p7W_loginPrompt{align-items:flex-start;gap:10px;display:flex}.On5p7W_promptText{min-width:0;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;flex:1;font-size:12px;line-height:18px}.On5p7W_codeInput{border:1px solid var(--dsw-alias-line-normal,currentColor);min-width:0;color:inherit;background:0 0;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;line-height:18px}.On5p7W_openPage{color:var(--dsw-alias-accent-link);flex:none;font-size:12px;line-height:18px;text-decoration:none}.On5p7W_openPage:hover{text-decoration:underline}";
236
+ const css$2 = ".n6PEuW_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:4px;display:flex}.n6PEuW_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.n6PEuW_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.n6PEuW_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.n6PEuW_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:10px;padding:12px 14px;display:flex}.n6PEuW_rowPending{opacity:.6}.n6PEuW_rowHead{align-items:center;gap:10px;display:flex}.n6PEuW_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.n6PEuW_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.n6PEuW_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.n6PEuW_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.n6PEuW_dotOn{background:var(--dsw-alias-state-success-primary)}.n6PEuW_dotOff{background:var(--dsw-alias-state-error-primary)}.n6PEuW_dotPending{background:var(--dsw-alias-fill-l3)}.n6PEuW_status{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;font-size:12px;line-height:18px}.n6PEuW_rowActions{align-items:center;gap:4px;display:inline-flex}.n6PEuW_loginButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.n6PEuW_loginButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.n6PEuW_loginButton:disabled{color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l2);cursor:not-allowed}.n6PEuW_logoutButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-state-error-primary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.n6PEuW_logoutButton:hover{border-color:var(--dsw-alias-state-error-primary)}.n6PEuW_rosterError{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;margin:8px 0 0;font-size:12px;line-height:18px;display:flex}.n6PEuW_retryButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:12px;justify-content:center;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.n6PEuW_retryButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.n6PEuW_loginPrompt{align-items:flex-start;gap:10px;display:flex}.n6PEuW_promptText{min-width:0;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;flex:1;font-size:12px;line-height:18px}.n6PEuW_codeInput{border:1px solid var(--dsw-alias-line-normal,currentColor);min-width:0;color:inherit;background:0 0;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;line-height:18px}.n6PEuW_openPage{color:var(--dsw-alias-accent-link);flex:none;font-size:12px;line-height:18px;text-decoration:none}.n6PEuW_openPage:hover{text-decoration:underline}";
237
237
  const tagId$2 = "@khorsheed/dsh-local-agent-codex/ProviderAuthBlock.module.css";
238
238
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
239
239
  const tag = document.createElement("style");
@@ -243,30 +243,30 @@ window.__ModuleLoader__.load({
243
243
  document.head.appendChild(tag);
244
244
  }
245
245
  var ProviderAuthBlock_module_css_default = {
246
- "rowHead": "On5p7W_rowHead",
247
- "rosterError": "On5p7W_rosterError",
248
- "openPage": "On5p7W_openPage",
249
- "rowTag": "On5p7W_rowTag",
250
- "rowName": "On5p7W_rowName",
251
- "promptText": "On5p7W_promptText",
252
- "logoutButton": "On5p7W_logoutButton",
253
- "loginPrompt": "On5p7W_loginPrompt",
254
- "loginButton": "On5p7W_loginButton",
255
- "section": "On5p7W_section",
256
- "dotPending": "On5p7W_dotPending",
257
- "rows": "On5p7W_rows",
258
- "rowIdentity": "On5p7W_rowIdentity",
259
- "title": "On5p7W_title",
260
- "credentialDot": "On5p7W_credentialDot",
261
- "codeInput": "On5p7W_codeInput",
262
- "intro": "On5p7W_intro",
263
- "rowPending": "On5p7W_rowPending",
264
- "rowCard": "On5p7W_rowCard",
265
- "dotOn": "On5p7W_dotOn",
266
- "retryButton": "On5p7W_retryButton",
267
- "rowActions": "On5p7W_rowActions",
268
- "status": "On5p7W_status",
269
- "dotOff": "On5p7W_dotOff"
246
+ "openPage": "n6PEuW_openPage",
247
+ "rowTag": "n6PEuW_rowTag",
248
+ "dotPending": "n6PEuW_dotPending",
249
+ "rowActions": "n6PEuW_rowActions",
250
+ "dotOn": "n6PEuW_dotOn",
251
+ "rowName": "n6PEuW_rowName",
252
+ "section": "n6PEuW_section",
253
+ "promptText": "n6PEuW_promptText",
254
+ "rosterError": "n6PEuW_rosterError",
255
+ "retryButton": "n6PEuW_retryButton",
256
+ "intro": "n6PEuW_intro",
257
+ "rowHead": "n6PEuW_rowHead",
258
+ "dotOff": "n6PEuW_dotOff",
259
+ "loginButton": "n6PEuW_loginButton",
260
+ "status": "n6PEuW_status",
261
+ "logoutButton": "n6PEuW_logoutButton",
262
+ "loginPrompt": "n6PEuW_loginPrompt",
263
+ "rows": "n6PEuW_rows",
264
+ "codeInput": "n6PEuW_codeInput",
265
+ "credentialDot": "n6PEuW_credentialDot",
266
+ "rowPending": "n6PEuW_rowPending",
267
+ "rowIdentity": "n6PEuW_rowIdentity",
268
+ "title": "n6PEuW_title",
269
+ "rowCard": "n6PEuW_rowCard"
270
270
  };
271
271
  //#endregion
272
272
  //#region ../local-agent/src/client/ProviderAuthBlock.tsx
@@ -529,7 +529,7 @@ window.__ModuleLoader__.load({
529
529
  }
530
530
  //#endregion
531
531
  //#region \0dsh-css:../local-agent/src/client/AuthStatusDot.module.css.mjs
532
- const css$1 = ".OFjNUG_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.OFjNUG_dotOn{background:var(--dsw-alias-state-success-primary)}.OFjNUG_dotOff{background:var(--dsw-alias-state-error-primary)}.OFjNUG_dotPending{background:var(--dsw-alias-fill-l3)}";
532
+ const css$1 = ".TFxvHa_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.TFxvHa_dotOn{background:var(--dsw-alias-state-success-primary)}.TFxvHa_dotOff{background:var(--dsw-alias-state-error-primary)}.TFxvHa_dotPending{background:var(--dsw-alias-fill-l3)}";
533
533
  const tagId$1 = "@khorsheed/dsh-local-agent-codex/AuthStatusDot.module.css";
534
534
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
535
535
  const tag = document.createElement("style");
@@ -539,10 +539,10 @@ window.__ModuleLoader__.load({
539
539
  document.head.appendChild(tag);
540
540
  }
541
541
  var AuthStatusDot_module_css_default = {
542
- "dotOff": "OFjNUG_dotOff",
543
- "dotPending": "OFjNUG_dotPending",
544
- "credentialDot": "OFjNUG_credentialDot",
545
- "dotOn": "OFjNUG_dotOn"
542
+ "dotPending": "TFxvHa_dotPending",
543
+ "dotOff": "TFxvHa_dotOff",
544
+ "dotOn": "TFxvHa_dotOn",
545
+ "credentialDot": "TFxvHa_credentialDot"
546
546
  };
547
547
  //#endregion
548
548
  //#region ../local-agent/src/client/AuthStatusDot.tsx
@@ -558,7 +558,7 @@ window.__ModuleLoader__.load({
558
558
  }
559
559
  //#endregion
560
560
  //#region \0dsh-css:src/client/SettingsCard.module.css.mjs
561
- const css = ".oWITGW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.oWITGW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.oWITGW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.oWITGW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.oWITGW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.oWITGW_nameRow{align-items:center;gap:6px;display:flex}.oWITGW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.oWITGW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.oWITGW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.oWITGW_chevronOpen{transform:rotate(180deg)}.oWITGW_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;margin:0 16px;padding:12px 0 8px;display:flex}.oWITGW_block{flex-direction:column;gap:10px;display:flex}.oWITGW_blockTitle{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;margin:0;font-size:13px;font-weight:500;line-height:1.4;display:flex}.oWITGW_info{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:help;background:0 0;border:0;border-radius:50%;padding:0 2px;font-size:12px;line-height:1}.oWITGW_info:hover,.oWITGW_info:focus-visible{color:var(--dsw-alias-label-primary)}.oWITGW_row{align-items:center;gap:12px;display:flex}.oWITGW_rowLabel{color:var(--dsw-alias-label-primary);align-items:center;gap:4px;font-size:13px;line-height:1.4;display:inline-flex}.oWITGW_switch{border:1px solid color-mix(in srgb, currentColor 30%, transparent);background:color-mix(in srgb, currentColor 12%, transparent);cursor:pointer;width:2.25rem;height:1.25rem;color:var(--dsw-alias-label-primary);border-radius:999px;flex:none;align-self:center;padding:.125rem;transition:background .12s}.oWITGW_switch:disabled{opacity:.5;cursor:default}.oWITGW_switchOn{background:color-mix(in srgb, var(--dsh-accent,#4f8cff) 70%, transparent)}.oWITGW_knob{background:var(--dsh-surface,#fff);border-radius:999px;width:.9375rem;height:.9375rem;transition:transform .12s;display:block}.oWITGW_switchOn .oWITGW_knob{transform:translate(.9375rem)}.oWITGW_saved{color:var(--dsw-alias-state-success-primary);font-size:12px}.oWITGW_errorText{color:var(--dsw-alias-state-error-primary);font-size:12px}.oWITGW_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.oWITGW_modelInput{min-width:0;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:5px 8px;font-size:13px;line-height:1.4}.oWITGW_modelInput::placeholder{color:var(--dsw-alias-label-caption);opacity:1}.oWITGW_modelInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_modelInput:disabled{opacity:.5}.oWITGW_modelField{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;flex:1;align-items:center;min-width:0;display:flex;position:relative}.oWITGW_modelField:focus-within{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_modelField .oWITGW_modelInput{background:0 0;border:0;border-radius:0}.oWITGW_modelField .oWITGW_modelInput:focus-visible{outline:none}.oWITGW_modelMenuButton{width:24px;height:24px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;margin-right:4px;display:flex}.oWITGW_modelMenuButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.oWITGW_modelMenuButton:disabled{opacity:.5;cursor:default}.oWITGW_modelMenuChevron{transition:transform .16s}.oWITGW_modelMenuChevronOpen{transform:rotate(180deg)}.oWITGW_modelMenu{z-index:10;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l2));background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,#fff)));min-width:160px;max-height:240px;backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px));box-shadow:var(--dsw-elevation-prominent,var(--dsw-shadow-lv3,0 0 1px 0 #0003, 0 0 4px 0 #00000005, 0 12px 32px 0 #00000014));border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;top:calc(100% + 6px);left:0;right:0;overflow-y:auto}.oWITGW_modelMenuUp{top:auto;bottom:calc(100% + 6px)}.oWITGW_modelItem,.oWITGW_modelItemCurrent,.oWITGW_modelItemDefault{color:var(--dsw-alias-label-primary);text-align:left;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:6px 10px;font-size:12px;line-height:18px;display:flex;overflow:hidden}.oWITGW_modelItem:hover,.oWITGW_modelItemCurrent:hover,.oWITGW_modelItemDefault:hover{background:var(--dsw-alias-interactive-bg-hover)}.oWITGW_modelItemCurrent{font-weight:560}.oWITGW_modelItemLabel{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.oWITGW_modelItemCheck{text-align:center;width:14px;color:var(--dsw-alias-label-secondary);flex:none}.oWITGW_modelItemDefault{color:var(--dsw-alias-label-secondary);box-shadow:0 1px 0 var(--dsw-alias-border-l2);border-radius:8px 8px 0 0;margin-bottom:2px;padding-bottom:7px}.oWITGW_modelSave{appearance:none;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;padding:5px 10px;font-size:13px;line-height:1.4}.oWITGW_modelSave:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}.oWITGW_modelSave:disabled{opacity:.5;cursor:default}";
561
+ const css = ".sXV0EG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.sXV0EG_card:hover{border-color:var(--dsw-alias-label-dimmed)}.sXV0EG_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.sXV0EG_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.sXV0EG_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.sXV0EG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.sXV0EG_nameRow{align-items:center;gap:6px;display:flex}.sXV0EG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.sXV0EG_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.sXV0EG_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.sXV0EG_chevronOpen{transform:rotate(180deg)}.sXV0EG_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;margin:0 16px;padding:12px 0 8px;display:flex}.sXV0EG_block{flex-direction:column;gap:10px;display:flex}.sXV0EG_blockTitle{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;margin:0;font-size:13px;font-weight:500;line-height:1.4;display:flex}.sXV0EG_info{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:help;background:0 0;border:0;border-radius:50%;padding:0 2px;font-size:12px;line-height:1}.sXV0EG_info:hover,.sXV0EG_info:focus-visible{color:var(--dsw-alias-label-primary)}.sXV0EG_row{align-items:center;gap:12px;display:flex}.sXV0EG_rowLabel{color:var(--dsw-alias-label-primary);align-items:center;gap:4px;font-size:13px;line-height:1.4;display:inline-flex}.sXV0EG_switch{border:1px solid color-mix(in srgb, currentColor 30%, transparent);background:color-mix(in srgb, currentColor 12%, transparent);cursor:pointer;width:2.25rem;height:1.25rem;color:var(--dsw-alias-label-primary);border-radius:999px;flex:none;align-self:center;padding:.125rem;transition:background .12s}.sXV0EG_switch:disabled{opacity:.5;cursor:default}.sXV0EG_switchOn{background:color-mix(in srgb, var(--dsh-accent,#4f8cff) 70%, transparent)}.sXV0EG_knob{background:var(--dsh-surface,#fff);border-radius:999px;width:.9375rem;height:.9375rem;transition:transform .12s;display:block}.sXV0EG_switchOn .sXV0EG_knob{transform:translate(.9375rem)}.sXV0EG_saved{color:var(--dsw-alias-state-success-primary);font-size:12px}.sXV0EG_errorText{color:var(--dsw-alias-state-error-primary);font-size:12px}.sXV0EG_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.sXV0EG_modelInput{min-width:0;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:5px 8px;font-size:13px;line-height:1.4}.sXV0EG_modelInput::placeholder{color:var(--dsw-alias-label-caption);opacity:1}.sXV0EG_modelInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.sXV0EG_modelInput:disabled{opacity:.5}.sXV0EG_modelField{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;flex:1;align-items:center;min-width:0;display:flex;position:relative}.sXV0EG_modelField:focus-within{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.sXV0EG_modelField .sXV0EG_modelInput{background:0 0;border:0;border-radius:0}.sXV0EG_modelField .sXV0EG_modelInput:focus-visible{outline:none}.sXV0EG_modelMenuButton{width:24px;height:24px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;margin-right:4px;display:flex}.sXV0EG_modelMenuButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.sXV0EG_modelMenuButton:disabled{opacity:.5;cursor:default}.sXV0EG_modelMenuChevron{transition:transform .16s}.sXV0EG_modelMenuChevronOpen{transform:rotate(180deg)}.sXV0EG_modelMenu{z-index:10;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l2));background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,#fff)));min-width:160px;max-height:240px;backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px));box-shadow:var(--dsw-elevation-prominent,var(--dsw-shadow-lv3,0 0 1px 0 #0003, 0 0 4px 0 #00000005, 0 12px 32px 0 #00000014));border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;top:calc(100% + 6px);left:0;right:0;overflow-y:auto}.sXV0EG_modelMenuUp{top:auto;bottom:calc(100% + 6px)}.sXV0EG_modelItem,.sXV0EG_modelItemCurrent,.sXV0EG_modelItemDefault{color:var(--dsw-alias-label-primary);text-align:left;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:6px 10px;font-size:12px;line-height:18px;display:flex;overflow:hidden}.sXV0EG_modelItem:hover,.sXV0EG_modelItemCurrent:hover,.sXV0EG_modelItemDefault:hover{background:var(--dsw-alias-interactive-bg-hover)}.sXV0EG_modelItemCurrent{font-weight:560}.sXV0EG_modelItemLabel{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.sXV0EG_modelItemCheck{text-align:center;width:14px;color:var(--dsw-alias-label-secondary);flex:none}.sXV0EG_modelItemDefault{color:var(--dsw-alias-label-secondary);box-shadow:0 1px 0 var(--dsw-alias-border-l2);border-radius:8px 8px 0 0;margin-bottom:2px;padding-bottom:7px}.sXV0EG_modelSave{appearance:none;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;padding:5px 10px;font-size:13px;line-height:1.4}.sXV0EG_modelSave:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}.sXV0EG_modelSave:disabled{opacity:.5;cursor:default}";
562
562
  const tagId = "@khorsheed/dsh-local-agent-codex/SettingsCard.module.css";
563
563
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
564
564
  const tag = document.createElement("style");
@@ -568,40 +568,40 @@ window.__ModuleLoader__.load({
568
568
  document.head.appendChild(tag);
569
569
  }
570
570
  var SettingsCard_module_css_default = {
571
- "modelSave": "oWITGW_modelSave",
572
- "card": "oWITGW_card",
573
- "switch": "oWITGW_switch",
574
- "hint": "oWITGW_hint",
575
- "modelMenuChevronOpen": "oWITGW_modelMenuChevronOpen",
576
- "knob": "oWITGW_knob",
577
- "modelMenuChevron": "oWITGW_modelMenuChevron",
578
- "saved": "oWITGW_saved",
579
- "modelItem": "oWITGW_modelItem",
580
- "description": "oWITGW_description",
581
- "blockTitle": "oWITGW_blockTitle",
582
- "name": "oWITGW_name",
583
- "modelInput": "oWITGW_modelInput",
584
- "cardOpen": "oWITGW_cardOpen",
585
- "row": "oWITGW_row",
586
- "chevronOpen": "oWITGW_chevronOpen",
587
- "info": "oWITGW_info",
588
- "modelItemCurrent": "oWITGW_modelItemCurrent",
589
- "rowLabel": "oWITGW_rowLabel",
590
- "header": "oWITGW_header",
591
- "headText": "oWITGW_headText",
592
- "modelItemDefault": "oWITGW_modelItemDefault",
593
- "modelItemLabel": "oWITGW_modelItemLabel",
594
- "block": "oWITGW_block",
595
- "modelField": "oWITGW_modelField",
596
- "modelMenuUp": "oWITGW_modelMenuUp",
597
- "nameRow": "oWITGW_nameRow",
598
- "body": "oWITGW_body",
599
- "switchOn": "oWITGW_switchOn",
600
- "chevron": "oWITGW_chevron",
601
- "errorText": "oWITGW_errorText",
602
- "modelMenu": "oWITGW_modelMenu",
603
- "modelItemCheck": "oWITGW_modelItemCheck",
604
- "modelMenuButton": "oWITGW_modelMenuButton"
571
+ "switch": "sXV0EG_switch",
572
+ "saved": "sXV0EG_saved",
573
+ "card": "sXV0EG_card",
574
+ "cardOpen": "sXV0EG_cardOpen",
575
+ "hint": "sXV0EG_hint",
576
+ "modelMenuChevronOpen": "sXV0EG_modelMenuChevronOpen",
577
+ "rowLabel": "sXV0EG_rowLabel",
578
+ "modelField": "sXV0EG_modelField",
579
+ "modelMenu": "sXV0EG_modelMenu",
580
+ "nameRow": "sXV0EG_nameRow",
581
+ "modelInput": "sXV0EG_modelInput",
582
+ "row": "sXV0EG_row",
583
+ "switchOn": "sXV0EG_switchOn",
584
+ "modelItemDefault": "sXV0EG_modelItemDefault",
585
+ "modelSave": "sXV0EG_modelSave",
586
+ "header": "sXV0EG_header",
587
+ "modelItemCheck": "sXV0EG_modelItemCheck",
588
+ "modelItemLabel": "sXV0EG_modelItemLabel",
589
+ "modelMenuButton": "sXV0EG_modelMenuButton",
590
+ "modelMenuUp": "sXV0EG_modelMenuUp",
591
+ "modelItem": "sXV0EG_modelItem",
592
+ "modelItemCurrent": "sXV0EG_modelItemCurrent",
593
+ "description": "sXV0EG_description",
594
+ "body": "sXV0EG_body",
595
+ "knob": "sXV0EG_knob",
596
+ "headText": "sXV0EG_headText",
597
+ "blockTitle": "sXV0EG_blockTitle",
598
+ "chevron": "sXV0EG_chevron",
599
+ "modelMenuChevron": "sXV0EG_modelMenuChevron",
600
+ "chevronOpen": "sXV0EG_chevronOpen",
601
+ "info": "sXV0EG_info",
602
+ "errorText": "sXV0EG_errorText",
603
+ "name": "sXV0EG_name",
604
+ "block": "sXV0EG_block"
605
605
  };
606
606
  //#endregion
607
607
  //#region src/client/SettingsCard.tsx
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "local-agent",
9
9
  "codex"
10
10
  ],
11
- "version": "0.1.0-rc.6",
11
+ "version": "0.1.0-rc.7",
12
12
  "type": "module",
13
13
  "main": "lib/index.js",
14
14
  "types": "lib/types/index.d.ts",
@@ -60,12 +60,13 @@
60
60
  "files": [
61
61
  "lib",
62
62
  "cordis.patch.yml",
63
- "locale/*.json"
63
+ "locale/*.json",
64
+ "CHANGELOG.md"
64
65
  ],
65
66
  "license": "MIT",
66
67
  "dependencies": {
67
- "@khorsheed/dsh-local-agent": "^0.1.0-rc.6",
68
- "@khorsheed/dsh-local-agent-tool-subagent": "^0.1.0-rc.6"
68
+ "@khorsheed/dsh-local-agent": "^0.1.0-rc.7",
69
+ "@khorsheed/dsh-local-agent-tool-subagent": "^0.1.0-rc.7"
69
70
  },
70
71
  "peerDependencies": {
71
72
  "@deepseek-ai/cordis": "^4.0.1",