@khorsheed/dsh-local-agent-codex 0.2.0-rc.1 → 0.2.0

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 CHANGED
@@ -1,4 +1,29 @@
1
1
  # 变更记录
2
+ ## 0.2.0(2026-10-26)
3
+
4
+ rc.3 之后到正式版的变化:
5
+
6
+ - **每次 API 调用记自己的用量**(live 路):原来只在轮末把最后一次的用量写在最后一行,中间步骤没有 token 数据;现在每次调用结束就把它自己的用量记上。整轮合计改用上游的累计值,修正多次调用时的少算。
7
+ - **修复冷缓存时输入 token 约多算一倍**:Codex 的 `input_tokens` 已含缓存读写,计算未命中输入时现在把两者都扣掉。
8
+ - **修复子会话步骤乱序、重启后被判损坏**:live 镜像改为写入时才编号、一次只开一个步骤,先开始的先落盘(实时预览照常)。
9
+ - **修复工具发现循环在子会话里看不见调用**:`custom_tool_call` 与动态工具的结果现在如实折叠,不再显示成两段相邻的裸回复。
10
+ - 协作间的桥接调用最长可等 30 分钟(`tool_timeout_sec`),供成员等你回答问题。
11
+ - **README 重写**:改为大白话说明,完整参考移到 `docs/`。
12
+ - **版本号去掉 rc**:社区包不再发 prerelease 版本(docs/publishing.md「版本号规则」),家族各包同版 0.2.0;本节原记为 0.2.0-rc.4。
13
+ - **缓存账目补全与硬化**:`cacheReadTokens` 在上游上报该字段时恒写(0 命中也写 0,与「未上报」可区分);补上此前丢弃的 `cacheWriteTokens`(exec 的 `cache_write_input_tokens` / app-server 的 `cacheWriteInputTokens`);`turn/completed` 的镜像失败不再吞掉收尾(轮不再因折叠异常挂起)。
14
+ - **子会话日志写入 `request/header`**:codex 的流从不携带 model/effort,子会话配置事实原来无处记录。现在每轮以 provider 启动参数为权威来源写入宿主原生 `request/header`(exec 路的 spawn argv、live 路的 `turn/start` 请求参数;只在变化时记录)——Context Lens 的配置卡片由此读出真实值。
15
+ - **成员会话的模型对话框与「可用模型」短名单严格一致**:成员的模型目录原来会混入作用域配置发现的模型与历史记录(kimi 下会列出非本端点服务的配置表模型),与卡片短名单不一致。现在成员对话框的候选即短名单(按名单顺序;名单里端点已不再供应的值保留为裸行,与卡片一致),名单外只保留成员当前绑定的模型(避免既有绑定显示空白);卡片的添加对话框只列端点真实供应的模型,配置/历史附加项不再混入。
16
+
17
+ ## 0.2.0-rc.3(2026-10-22)
18
+
19
+ (版本号说明:家族的 dsh-headless 包 0.2.0-rc.2 版本号已被 npm 发布流水线占用(重复利用被 409 拒绝:「Cannot stage previously published version」——曾发布后被撤回或曾被暂存,外部无法区分,结果相同:该号不可复用);为守住家族「同一版本线」纪律(check:release-groups),本波家族整体上移 rc.3,内容即 rc.2 计划稿。)
20
+
21
+ - **BREAKING:网页登录移除,端点 key 成为唯一认证方式**。部署级凭据为插件配置 `baseUrl` / `apiKey`;每个作用域可再以 `<scopedHome>/endpoint-auth.json`(0600)按字段覆盖——「scope 文件 > profile 配置」。spawn 时注入为自定义 provider 覆盖片段加进程环境变量,`config.toml` 不被改写;key 不带 `baseUrl` 时直连 OpenAI 官方端点。**升级前必须配好端点凭据**,否则升级后读作未认证(README 带迁移说明);残留的 `auth.json` 由 provision 每次启动清扫,`/codex login` 不再存在。
22
+ - **设置卡新增端点凭据表单与「验证端点并获取模型」按钮**:卡片编辑默认 scope 的 endpoint-auth.json;验证探测 `GET <baseUrl>/v1/models`(兼容带不带 `/v1` 后缀的 baseUrl),不可达 / HTTP 状态 / 形状不符各失败形态折进判定文本。
23
+ - **「可用模型」有序短名单取代自由文本 model 字段**:与家族其他卡共用同一组件——`<scopedHome>/models.json`,首项即默认模型,端点目录弹窗勾选添加、拖拽排序。`model` 配置键作为回退静默生效(YAML、API 写入),`/codex status` 照报生效设置。
24
+ - **模型目录切换为端点供给**:目录 = 端点 `/v1/models` 实际供应的模型,与验证按钮同一次探测;原生 app-server 注册表降级为元数据补全(label / description / 推理档位 / isDefault),端点不供应的注册表项被丢弃。无端点凭据时添加弹窗带原因空载,传输故障保留上次成功清单。
25
+ - **设置卡并入「本机 Agent 委派」家族卡**:本包卡读作「本机 Codex 委派」,作为家族卡的一节呈现;家族卡随后修复为无条件注册——原先的命名空间门控让它在 prod 上永不挂载(2026-10-21)。与旧版家族核混装时回落为独立卡。
26
+ - **适配宿主 0.2.1 线**:委派链路迁移到 subagent `startActivation`(宿主 0.2.1 删除旧的 `ctx.subagents.start()`);加宽 `@deepseek-ai/dsh-*` peer 区间以覆盖宿主 0.2.1。
2
27
 
3
28
  ## 0.2.0-rc.1(2026-09-30)
4
29
 
package/README.en.md CHANGED
@@ -2,144 +2,45 @@
2
2
 
3
3
  English | [中文](README.md)
4
4
 
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.
5
+ Hand coding tasks to the Codex on your machine: watch it work live, get the result back in your session, and keep the conversation going.
6
6
 
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.
7
+ Some jobs you simply want Codex to do, and that used to mean running it in a terminal and carrying the result back yourself. With this plugin you say "give this to Codex" in a session, and the agent hands the task to Codex on your machine. Its reasoning, tool calls, and file changes show up live in a sub-session, and the result comes back with its real usage and time. Codex runs in its own separate folder; your existing `~/.codex` config, sign-in, and sessions are never touched.
8
8
 
9
- <img src="https://raw.githubusercontent.com/Khorsheed/dsh-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">
9
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/local-agent-codex.png" width="560" alt="The expanded Codex section: Codex signed in with its version, endpoint, API key, "Verify endpoint & list models" and the available-models list">
10
10
 
11
- ## Features
11
+ ## What it does
12
12
 
13
- - **Per-session delegation grant** — the `subagent_codex` tool is this package's `./tool` composition entry, granted to the sessions of an agent preset that names it (e.g. dev mode); no per-preset setup.
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.
19
- - **Custom endpoint** — route Codex's LLM requests through your own router via a scoped `config.toml` provider.
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-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">
13
+ - **Hand it off in one sentence**: tell the agent which part goes to Codex; it writes up the brief, and the result comes back to your session.
14
+ - **See the work as it happens**: reasoning, commands, and changed files appear live in the sub-session, and you can stop it any time.
15
+ - **Keep talking, even after a restart**: have the agent follow up, or open the sub-session and message Codex yourself; it's the same Codex thread, and it picks up again after DSH restarts.
16
+ - **Paste a key and go**: enter the endpoint and API key on the settings card (an official platform key or a relay key), press "Save credential", then "Verify endpoint & list models" to check it connects.
17
+ - **Pick your models**: under "Available models", tick and drag to reorder; the top one is the default. You can also switch model and reasoning effort inside a sub-session.
18
+ - **Resident mode is faster**: turn on "Resident mode (live)" and the Codex process stays up, streaming output into the sub-session; stopping doesn't kill it, and after a crash it resumes the same thread.
23
19
 
24
20
  ## Install
25
21
 
26
- Prerequisites: a working dsh profile and the Codex CLI (`codex`) on `PATH` — the plugin neither installs it nor logs in for you.
22
+ First install the Codex CLI yourself (`codex` runs in your terminal); the plugin doesn't install it. Then in DSH, open Plugins → Add plugin, enter this package name, and press Install:
27
23
 
28
- ```sh
29
- dsh plugin --profile web add @khorsheed/dsh-local-agent
30
- dsh plugin --profile web add @khorsheed/dsh-local-agent-codex
31
24
  ```
32
-
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.
34
-
35
- Uninstall:
36
-
37
- ```sh
38
- dsh plugin --profile web remove @khorsheed/dsh-local-agent-codex
25
+ @khorsheed/dsh-bundle-local-agent
39
26
  ```
40
27
 
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.
42
-
43
- ## Config
44
-
45
- Optional, in the profile patch layer:
46
-
47
- ```yaml
48
- - id: local-agent-codex
49
- config:
50
- sandbox: workspace-write # codex exec policy: read-only | workspace-write | danger-full-access
51
- model: gpt-5.2 # optional: every delegation round starts the CLI with it; absent passes no model flag at all (below)
52
- live: false # live driver: one resident codex app-server process per member, one turn per round (runtime-level graceful interrupt, push-mode mirroring); off — or a channel that cannot come up — means the one-shot exec path
53
- liveIdleMs: 1800000 # idle lifetime of a resident runtime before reclaim (default 30 min)
54
- ```
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
-
58
- ### Default model (`model`)
59
-
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.
28
+ Codex comes with the 「多Agent协作」 (multi-agent) suite; all the local agents share one core, so on its own it won't work. If you mostly write code, the Dev Suite `@khorsheed/dsh-dev-suite` includes it too. Restart DSH, then on the Plugins page open the 「本机 Agent 委派」 (Local Agent Delegation) card, expand the Codex section, and fill in the endpoint and API key.
61
29
 
62
- **Set = every delegation round starts the CLI with it.** A fresh round and a resume round are treated alike:
30
+ Handing work to Codex happens in a mode that grants delegation, such as "开发模式" (dev mode): start a new session, pick that mode, and tell the agent "give this to Codex". Not seeing Codex delegation in other modes is expected.
63
31
 
64
- | Drive | How it is passed |
65
- |---|---|
66
- | one-shot (default) | `codex exec -m <model> …` — `-m` is an option of `codex exec` itself, so it precedes the `resume` subcommand (`codex exec resume` declares no `-m` of its own) |
67
- | resident (`live: true`) | `codex app-server -c model="<model>" --stdio` — the app-server has no `-m`, so the model rides the process-level `-c` override the CLI documents for this |
68
-
69
- The scoped `config.toml` is never rewritten: `-m` overrules it per round and the file stays exactly as you edited it.
70
-
71
- The settings card’s "Default model" writes the provider setting for subsequent rounds. Its shared picker displays the scoped model directory, discovery source and completeness, plus an explicit model-ID input when needed. Clearing the selection follows the effective configuration/default chain. Saving does not interrupt an active round and requires no reload. If the shared picker is unavailable, the card retains its text-input fallback. Per-member model and effort changes use the durable controls described below.
72
-
73
- The rich directory read now preserves native display labels, hidden candidates and reasoning options, traverses pagination, and uses the core cache and refresh subscription. Its context follows the member's scope and cwd. Failed refreshes retain the last successful snapshot as stale; configuration and history suggestions retain their source labels. The shared model picker and member effort controls are connected. Busy selections apply at the next complete turn boundary, including tool continuations; core owns current/pending state, cancellation and retry, while frozen evaluation members reject changes. A native catalog candidate is not proof of account access.
74
-
75
- **A delegation's own model outranks this key.** An orchestrator may name the model for ONE delegation through the facade's `DelegationCallOptions.model` (the fixed order: session override > delegation record > this key > scoped config > CLI built-in). The first round's request is recorded and every resume round re-requests it — `resume` takes no model of its own. In live mode such a round is no longer refused: the model becomes the member's **start model**, bound at the app-server spawn; a resident runtime bound to a different model is retired first (the same codex thread resumes via thread/resume) so the round respawns onto the asked-for model.
76
-
77
- **Member-level switching (the composer model picker).** A member session can switch its model per session: an in-memory session-level override, the highest-priority layer, deliberately lost on a host restart. A switch is refused while a round is in flight; when idle and the member has a resident runtime bound to a different model, the switch retires that runtime — the next round respawns onto the new model while the CLI session (rollout) itself carries over. On the one-shot (exec) drive there is no resident process, so the override simply decides the next round's `-m`.
78
-
79
- **This is not an evaluation gap.** A run freezes its condition at setup: change the key mid-run and the next round's model read-back sees declared ≠ observed and fails the run as misattributed (frozen decision 5). "A new run follows the new value, a running one is never switched underneath you" is the design, not an oversight.
80
-
81
- ## Custom endpoint
82
-
83
- Route Codex's LLM requests through your own endpoint by adding a custom provider to the scoped `$DSH_HOME/local-agent/codex/config.toml` (built-in providers can't be overridden, `OPENAI_BASE_URL` is ignored, and provisioning never overwrites an existing config):
84
-
85
- ```toml
86
- [model_providers.dsh-router]
87
- name = "dsh-router"
88
- base_url = "https://your-router.example/v1"
89
- model_provider = "dsh-router"
90
- ```
32
+ ## Known limitations
91
33
 
92
- The last line selects the provider for delegations; keep the rest of the file intact.
93
-
94
- ⚠️ **Credential exposure**: the scoped `auth.json` token is sent to whatever endpoint serves the request — only use endpoints you control or trust. Each delegation logs the effective endpoint at info level.
95
-
96
- **Evaluation snapshot (`effectiveSettings`).** The harness declares a live-read snapshot of its fairness-relevant settings for the evaluation condition hash: drive (exec/live), the sandbox policy (plugin config), the reasoning effort (the scoped config's top-level `model_reasoning_effort`), whether a custom endpoint is pinned (read from the scoped config's provider, hostname only), the configured model (the plugin config's `model` key first — it overrules the file on every argv — then the scoped config's top-level `model`; absent when neither names one — never guessed), and the CLI version (`codex --version`, cached against the executable's path + mtime; absent when the CLI cannot be asked). `/codex status` and the `LocalAgentStatus` Remote attach the same snapshot.
34
+ - Only an endpoint plus API key is supported; signing in with a ChatGPT subscription is not.
35
+ - Nobody is there to approve Codex's requests: actions that would need your OK are refused outright rather than asking you.
36
+ - The `/codex` commands only work in web sessions.
97
37
 
98
38
  ## Compatibility
99
39
 
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.
101
- - source line (deepseek-harness master): ✅ (verifiedHost: 0.1.5-rc.1)
102
-
103
- ## Known Limitations
104
-
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.
108
- - **Headless caveat** — `/codex` commands need a Web session; headless can still delegate via a composition mounting the tool row.
109
-
110
- ## How it works
111
-
112
- <details>
113
- <summary>Internals (click to expand)</summary>
114
-
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.
116
-
117
- **Bundle composition.** The patch registers the `codex` harness (scoped `CODEX_HOME`, PTY login, rollout-file session records) — exactly one provider row; since the family fold the `subagent_codex` tool is this package's `./tool` composition entry, granted per session by an agent preset that names it (the dev preset's `tool-subagent-codex-local` row), no longer mounted at the profile root. The family keeps the official model-facing name `subagent_codex`: the official preset row of that name already ships `disabled: true`, and the patch disables the profile-root row 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.
118
-
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.
120
-
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.
122
-
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.
124
-
125
- **Tool-call accounting.** Every settled round also reports how many tool calls it made, on the `settled` run-progress event (`toolCalls: { count, byName }`). The count comes only from the `item.completed` branches the stream fold ALREADY walks — no new parse path: `command_execution`, `web_search_call`, `file_change`, and `function_call` each count once, and `function_call_output` is a result, not a second call. `byName` keys are **codex's own item types** (`command_execution`), not the display names the mirrored cards carry (`Bash`) — the accounting records what the CLI calls the thing. One accounting per round, never cumulative; a round that called nothing carries no field at all (absent is not zero). Measured: one "list the directory and count the files" round reads back `{count: 1, byName: {command_execution: 1}}`, and a two-command round reads back `{count: 2}`.
126
-
127
- Both the locate and the scan are calibrated for concurrency. codex writes `turn_context` when the TURN STARTS, so a round that then produces enough events pushes it past the end of any tail-only scan — which read back null in a real evaluation run (the 38 KB smoke round read fine, the 100–500 KB cells did not), so a tail miss is followed by a bounded full-file read. The locate takes the round's **cwd** alongside the thread id: concurrent delegations put several cells' files inside one time window, and `session_meta.payload.cwd` is what tells them apart. A window holding candidates but none in this round's directory reports nothing rather than a neighbour's round — except when the window holds exactly one candidate, where the mismatch is path skew rather than ambiguity. An orchestrator may also pass a per-round working directory through the facade's `DelegationCallOptions.cwd` (recorded as the record's `cwd`); when a resume round resolves to a different directory than the recorded first round, it fails loud before any process spawns — a CLI session continues in the directory its first round ran in.
128
-
129
- **Container delegation.** An orchestrator may run the round inside an **already-acquired container** through the facade's `DelegationCallOptions.exec` (`{ container, workdir, env? }`): the argv becomes `docker exec -w <workdir> [-e NAME…] <container> codex exec …` and everything else (stream parse, settle, rollout readback, recording) is byte-for-byte unchanged. `env` must name the in-container `CODEX_HOME`, and it should be the read-write bind mount of the host scoped home — the rollout readback reads the host copy. A container round always takes the exec one-shot (the resident app-server is a host process) and declares no member bridge (a host unix socket cannot reach into the container). Measured: one "answer 2+2" round in an `eval-env:pinned` unit settles `completed` with output `4`, and `observedModel` reads back as `gpt-5.6-sol` from the rollout the container wrote into the host scoped home.
130
-
131
- **Resume.** The family tool (this package's `./tool` composition entry, sharing the family core's `./tool-factory` implementation) adds an optional `resume` parameter to the official `description`/`prompt` subset. A fresh delegation's result self-describes its handle (`追问请带 resume="<childSessionId>"`); passing it back continues the same codex thread (`codex exec --json resume <thread_id>`) in the same dsh child session, with per-round accounting. The handle is read only from the `resume` parameter and resolved by the `localAgent` registry only for the recording parent session and provider — a forged handle is rejected before any CLI process starts.
132
-
133
- **Event-stream mirror.** The subagent session mirrors the `codex exec --json` stream in order: `reasoning` → reasoning blocks, `agent_message` → reply text, `command_execution`/`web_search_call`/`function_call_output` → tool lines (`[工具 Bash] <command> → output`), `file_change` → an `ApplyPatch` card (kind and path per change), `function_call` (e.g. the multi-agent `wait`) → a card under its own name with the `function_call_output` merged in; the final `agent_message` is the run output and the round's usage rides the last mirrored assistant message. Resumed rounds append their own turn without duplication. Aborting settles the tool result immediately while keeping the events and usage already received.
134
-
135
- **Model experience.** Each delegation is a fresh one-shot `codex exec` in the delegating session's workspace. The parent submits only the task text and sees only the final answer or the exact error — Codex commentary, tool activity, and workspace diffs never cross into the parent session, and child tokens never enter the parent's context.
136
-
137
- **Live driver (`live: true`).** Replaces the per-round spawn: the member's first delegation brings up one resident `codex app-server --stdio` process (same scoped home, same `-c` member-bridge declaration), creates a persisted thread (`thread/start` with `ephemeral: false`), and every later round is a `turn/start` to that living runtime; `item/completed` events fold into the child session as they arrive (same `CodexTranscriptLine` fold and append core as exec, the last line held back until `turn/completed` to carry usage), and `cancel` lands as `turn/interrupt` — the process survives and the thread stays continuable. Approval-shaped server→client requests are auto-answered unattended (cancel/decline, matching exec behavior). Runtimes are reclaimed after an idle timeout (the app-server wire has no shutdown method: stdin EOF → SIGTERM ladder); after a crash the next round re-spawns and `thread/resume`s the on-disk thread; a handshake failure trips a cooldown breaker and falls back to exec per round.
138
-
139
- **Accounting.** The provider opens `turn/start` at spawn and closes `turn/end` at settle — including on `error`/`aborted` — so `subagentTiming` duration equals real CLI runtime; the final assistant message carries token usage parsed from the event stream. Codex's `input_tokens` includes cache hits, so the uncached bucket is `input_tokens − cached_input_tokens`, `cached_input_tokens` maps to cache read, and there is no cache-write concept — the `tokenUsage` projection never double-counts cache hits. Resumed rounds repeat this under their own turn number. **Usage fallback for non-completed terminal states (aborted/error).** An interrupted or failed round never sees `turn.completed`, so its event stream carries no usage — yet codex has already written the round's real token spend to the rollout file under the scoped home. The exec mirror then recovers the LAST `token_count` of THIS run's rollout file and attaches it as the round's usage: the file is located by thread id (the `session_meta` head id), falling back to the spawn-time window when the stream was truncated before `thread.started`; the caliber is byte-identical to `turn.completed` (`input − cached` and the rest, via the shared `usageFromCodex`). A hard kill that wrote no `token_count` at all still leaves the round without usage — the fallback never guesses.
140
-
141
- </details>
40
+ Needs DSH 0.1.5-rc.1 or later; verified on 0.2.1-rc.1.
142
41
 
143
- ## Development
42
+ ## For developers
144
43
 
145
- Part of the [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo (`packages/local-agent-codex`). Issues and contributions welcome there.
44
+ - Config keys (sandbox, default model, resident mode), where the endpoint credential is stored, custom endpoints, read-back, and internals are in the [full reference](https://github.com/Khorsheed/dsh-plugins/blob/main/packages/local-agent-codex/docs/reference.en.md).
45
+ - To install it alone, name the core too: `dsh plugin --profile web add @khorsheed/dsh-local-agent @khorsheed/dsh-local-agent-codex`; the delegation tool is granted by a mode and won't appear unless a mode names it.
46
+ - Part of [dsh-plugins](https://github.com/Khorsheed/dsh-plugins); open issues and contributions there. Changes: [CHANGELOG.md](CHANGELOG.md).
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: db346c4441e59ca802de149ec93b253a250e9cdd
6
- packages/local-agent-codex/README.md: 10d69741bf5034c13705231065546981d5596aaf
5
+ packages/local-agent-codex/README.en.md: 86803cc98209985a1bce01c21dde37ff24c58b20
6
+ packages/local-agent-codex/README.md: baa5511e5dba84d3f1e3abe05d57680202718cfb
package/README.md CHANGED
@@ -2,144 +2,45 @@
2
2
 
3
3
  [English](README.en.md) | 中文
4
4
 
5
- 把编码任务甩给你本机装好的 Codex CLI 跑——它自己的登录、自己的会话记录,你的 `~/.codex` 一根手指都不碰。
5
+ 把编码任务交给你电脑上的 Codex:过程实时看得见,跑完结果回到当前会话,还能接着聊。
6
6
 
7
- dsh 的 agent 已经能干活,但有些任务你就是想交给本机的 Codex CLI:它的账号、它的模型、你熟悉的工具链。这个插件把 Codex 接成 dsh 的本地子代理——任意 agent preset 都能经 `subagent_codex` 工具一句话委派;委派跑在插件隔离的作用域目录(`$DSH_HOME/local-agent/codex`)里,你的个人 config、凭据、会话完全不被触碰;过程细节(推理、工具调用、文件改动)实时镜像在子会话里,最终回答回到父会话。
7
+ 有些活你就是想让 Codex 来干,以前得自己开终端跑、再把结果搬回来。装上之后,你在会话里说一句「这个交给 Codex」,agent 就把任务交给本机的 Codex;它的推理、工具调用和改了哪些文件实时出现在子会话里,跑完把结果带回来,附上真实的用量和耗时。Codex 在自己单独的目录里运行,你电脑上原来的 `~/.codex` 配置、登录和会话一点不碰。
8
8
 
9
- <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/08-local-agent.png" width="640" alt="设置 → 插件配置页里的 Local Agent · Codex 卡片,卡头状态点一眼可见授权状态">
9
+ <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/local-agent-codex.png" width="560" alt="展开的 Codex 一节:已登录的 Codex 和版本号,端点地址、API Key、「验证端点并获取模型」和可用模型名单">
10
10
 
11
- ## 特性
11
+ ## 能做什么
12
12
 
13
- - **按会话授予的委派**——`subagent_codex` 工具是本包的 `./tool` 组合入口,由点名它的 agent 预设(如开发模式)授予该预设的会话,无需逐 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 线程,宿主重启后也续得上。
19
- - **自定义端点**——经作用域 `config.toml` 的自定义 provider 把 Codex 的 LLM 请求路由到你自己的路由端点。
20
- - **回读与独立工作目录**——每轮从本轮 rollout 回读实际模型、CLI 版本与用量写进委派记录;定位按 threadId + cwd + 时间窗,并发跑也读的是自己那一轮。编排器可用 `cwd` 选项给每格独立目录,resume 换目录即拒绝。
21
-
22
- <img src="https://raw.githubusercontent.com/Khorsheed/dsh-basic/main/docs/screenshots/local-agent-codex-card.png" width="640" alt="展开的 Codex 设置卡:认证状态与登录/退出、默认模型、常驻模式开关">
13
+ - **一句话交出去**:告诉 agent 哪段活给 Codex,它会自己写好要求交过去,结果回到当前会话。
14
+ - **过程看得见**:推理、执行的命令和改动的文件实时出现在子会话里,可以随时停下。
15
+ - **接着聊,重启也续得上**:让 agent 追问,或者打开子会话直接发消息,继续的是同一个 Codex 线程;DSH 重启之后也接得上。
16
+ - **填好 key 就能用**:在设置卡片里填端点地址和 API Key(官方平台的 key 或中转的 key 都行),点「保存凭据」,再点「验证端点并获取模型」确认能连上。
17
+ - **自己挑模型**:在「可用模型」里勾选、拖动排序,排第一的就是默认模型;子会话里也能临时换模型和推理强度。
18
+ - **常驻模式更快**:打开「常驻模式(live)」,Codex 进程常驻,输出实时流进子会话;停下不杀进程,崩溃后自动续上原来的线程。
23
19
 
24
20
  ## 安装
25
21
 
26
- 前置依赖:一个可运行的 dsh profile 和 `PATH` 上的 Codex CLI(`codex`)——插件不安装它、也不替你登录。
22
+ 先在电脑上装好 Codex CLI(命令行里能运行 `codex`),插件不替你装。然后在 DSH 的「插件 → 添加插件」里输入下面的包名,点安装:
27
23
 
28
- ```sh
29
- dsh plugin --profile web add @khorsheed/dsh-local-agent
30
- dsh plugin --profile web add @khorsheed/dsh-local-agent-codex
31
24
  ```
32
-
33
- 两个包必须同时点名——`dsh plugin add` 只调和**直接**依赖,家族 core 不会被传递依赖自动挂载。重启 web 实例后生效,然后在任意会话里跑一次 `/codex login` 完成授权。
34
-
35
- 卸载:
36
-
37
- ```sh
38
- dsh plugin --profile web remove @khorsheed/dsh-local-agent-codex
25
+ @khorsheed/dsh-bundle-local-agent
39
26
  ```
40
27
 
41
- 作用域目录(`$DSH_HOME/local-agent/codex`)会刻意保留,重装无需重新登录;删除它即可清除全部痕迹。
42
-
43
- ## 配置
44
-
45
- 可选,写在 profile patch 层:
46
-
47
- ```yaml
48
- - id: local-agent-codex
49
- config:
50
- sandbox: workspace-write # codex exec 策略:read-only | workspace-write | danger-full-access
51
- model: gpt-5.2 # 可选:每轮委派以它起 CLI;不写就一个模型参数都不传(见下)
52
- live: false # 常驻驱动:每成员常驻一个 codex app-server 进程,按轮发 turn(运行时级优雅中断、事件推送镜像);关闭或通道不可用即回一次性 exec
53
- liveIdleMs: 1800000 # 常驻 runtime 空闲回收时限(默认 30 分钟)
54
- ```
55
-
56
- **旧键迁移。** live 轮次统一消费增量输出:旧 `liveMirrorGranularity: event | token` 配置继续兼容读取,但不再影响行为,也不会改变运行中的进程。评测继续保留 exec 驱动;最终内容仍以 provider 完成项为准,包括工具记录和用量。
57
-
58
- ### 默认模型(`model`)
59
-
60
- **不写 = 今天的表现。**没有这个键时,本插件在 argv 上一个模型参数都不加:跑哪个模型由作用域 `config.toml` 的顶层 `model` 决定,它也没有时由 codex 自己的默认决定。
28
+ Codex 会跟着「多Agent协作」套件一起装好(各家本机 agent 共用同一个底座,单装用不起来);如果你主要写代码,直接装开发套件 `@khorsheed/dsh-dev-suite` 也包含它。装完重启 DSH,到「插件」页的「本机 Agent 委派」卡片里,展开 Codex 一节填好端点和 API Key。
61
29
 
62
- **写了 = 每轮委派以它起 CLI。**新起一轮与续接(resume)同等对待:
63
-
64
- | 驱动 | 传法 |
65
- |---|---|
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` 覆盖 |
68
-
69
- 作用域 `config.toml` 不会被改写——`-m` 每轮覆盖它,文件仍是你编辑的样子。
70
-
71
- 设置卡「默认模型」修改后续轮次使用的 provider 配置。共享选择器展示当前作用域的模型目录、发现来源和完整性,并保留按需填写模型 ID 的入口。清空选择后跟随有效配置及默认值链。保存不会打断当前轮次,也无需重载;共享选择器不可用时,卡片保留文本输入兜底。成员级模型和推理强度修改使用下文的持久控制面。
72
-
73
- 丰富目录读面现已保留原生显示名、隐藏候选和推理选项,遍历分页,并接入 core 缓存与刷新订阅。查询上下文跟随成员的 scope 和 cwd。刷新失败保留最后成功快照并标为过期;配置项和历史建议保留来源标识。共用模型菜单和成员 effort 控制已接入。运行中选择排到下一完整轮次(含工具续跑);core 统一持有当前/待生效配置、撤销与重试,冻结评测成员禁止变更。原生目录候选不等于账号权限证明。
74
-
75
- **委派级的模型优先。**编排器可以经门面 `DelegationCallOptions.model` 给**某一次委派**点名模型,它排在这个键之前(固定顺序:会话覆盖 > 委派记录 > 本键 > 作用域 config > CLI 内置)。首轮请求的值记进委派记录,resume 轮照它重发——resume 不接受 model 参数。常驻模式下带模型的轮次不再被拒绝:它成为该成员的**起始模型**,在 app-server 起进程时绑定;若该成员的常驻 runtime 绑定的是另一个模型,先回收重生(同一 codex 线程经 thread/resume 续上),再以所点模型起新一轮。
76
-
77
- **成员级切换(作曲器模型选择器)。**成员会话里可以按会话切换模型:一个内存中的会话级覆盖,优先级最高,宿主重启即失。切换在有轮次进行中时被拒绝;空闲时若成员有常驻 runtime 且绑定模型不同,切换即回收该 runtime——下一轮以新模型重生,CLI 会话(rollout)本身延续。一次性(exec)驱动下没有常驻进程,覆盖直接决定下一轮的 `-m`。
78
-
79
- **这不是评测的缺口。**评测 run 的条件在建立时冻结:run 跑到一半改这个键,下一轮的模型回读会发现声明模型 ≠ 实测模型,run 直接判为 misattributed 而失败(冻结决策 5)。换句话说「切了新 run 照新走、进行中的 run 不被悄悄换掉」是设计出来的,不是漏掉的。
80
-
81
- ## 自定义端点
82
-
83
- 把 Codex 的 LLM 请求路由到你自己的端点:在作用域 `$DSH_HOME/local-agent/codex/config.toml` 里**新增自定义 provider**(内置 provider 不可覆盖、`OPENAI_BASE_URL` 不生效、预置逻辑从不覆盖已存在的 config):
84
-
85
- ```toml
86
- [model_providers.dsh-router]
87
- name = "dsh-router"
88
- base_url = "https://your-router.example/v1"
89
- model_provider = "dsh-router"
90
- ```
91
-
92
- 最后一行为委派选择该 provider;文件其余内容必须保留。
93
-
94
- ⚠️ **凭据暴露**:作用域 `auth.json` token 会发送给处理请求的端点——只使用你控制或信任的端点。每次委派在 info 级别记录生效的端点。
95
-
96
- **评测快照(effectiveSettings)。** 本 harness 向注册表声明一份实时读取的公平性设置快照,供评测条件哈希使用:drive(exec/live)、sandbox 策略(插件配置)、推理强度(读作用域 config 的顶层 `model_reasoning_effort`)、端点是否固定(读作用域 config 的自定义 provider,只报主机名)、已配置模型(先看插件配置的 `model` 键——它每轮覆盖文件;没有才读作用域 config 的顶层 `model`;都没有就不给字段)、CLI 版本(`codex --version`,按可执行文件路径+mtime 缓存;探测不到即字段缺位)。`/codex status` 与 `LocalAgentStatus` Remote 附带同一份快照。
97
-
98
- ## Compatibility
99
-
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)
30
+ 把活交给 Codex 要在带委派的模式里进行,比如「开发模式」:开一个新会话、选这个模式,对 agent 说「把这个交给 Codex」。在其它模式里看不到 Codex 委派是正常的。
102
31
 
103
32
  ## 已知限制
104
33
 
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 仍可通过挂载工具行的组合进行委派。
109
-
110
- ## 实现原理
111
-
112
- <details>
113
- <summary>内部结构(点击展开)</summary>
114
-
115
- **命名 scope。** `/codex login --scope <名>` 会在 `<homesRoot>/codex@<名>` 里另开一份作用域目录:目录建立时自动跑同一份 `config.toml` provision(把凭据存储钉在文件而非 macOS keychain),因此该 scope 的登录落在它自己的 `auth.json` 里。带 scope 的委派用它跑 `codex exec`、把 rollout 写进它、从它回读;只走 exec——常驻 app-server 绑的是缺省作用域目录。
116
-
117
- **bundle 组成。** patch 注册 `codex` harness(`CODEX_HOME` 作用域目录、PTY 登录、rollout 文件会话记录)——恰好一行 provider 行;`subagent_codex` 工具自家族折叠起是本包的 `./tool` 组合入口,由点名它的 agent 预设按会话授予(dev 预设的 `tool-subagent-codex-local` 行),不再挂 profile 根。家族沿用官方模型名 `subagent_codex`:官方 preset 的同名行本就 `disabled: true`,patch 对 profile 根的同名行再禁一次,社区安装永远不会注册出两个 `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`。
118
-
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`,之后重新登录即可换账号。
34
+ - 只支持端点和 API Key,不支持用 ChatGPT 订阅登录。
35
+ - Codex 没人在旁边批准:本来要你点头的操作会被直接拒掉,不会弹出来问你。
36
+ - `/codex` 这组命令只能在网页会话里用。
120
37
 
121
- **会话记录。** `/codex sessions` 列出作用域目录的 `sessions/YYYY/MM/DD/rollout-*.jsonl` 文件——仅本插件委派产生的会话,绝不含你的私人会话。会话头部的记录入口(家族 core 的客户端提供)把同一份列表收窄到 `workDir` 与当前会话 cwd 一致的记录。
122
-
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`)。取不到即缺位,绝不猜测。
124
-
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}`。
126
-
127
- 定位与扫描两处都按并发校准过。`turn_context` 是 codex 在**回合开始**时写的,所以一轮里之后产生的事件一多就会把它挤出文件尾——只扫尾部会读回 null(一次真实评测跑就是这么丢的:38 KB 的 smoke 轮读得到,100–500 KB 的正式格读不到),因此尾部扫不到就再做一次有界的整文件读。定位则在 threadId 之外把**本轮 cwd** 也算进去:并发委派会让多个格的文件落进同一个时间窗,`session_meta.payload.cwd` 才是区分它们的字段;窗口里有候选却没有一个对得上本轮目录时,宁可什么都不报也不报邻居那一轮(窗口里只有一个候选除外——那是路径写法差异,不是歧义)。编排器还可以经门面 `DelegationCallOptions.cwd` 给本轮指定工作目录(记录进 `cwd` 字段);resume 轮解析出的目录若与首轮记录不一致,进程启动前即 fail loud——CLI 会话延续的是首轮所在目录的上下文。
128
-
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`。
130
-
131
- **续聊(resume)。** 家族工具(本包的 `./tool` 组合入口,实现共享自家族 core 的 `./tool-factory`)在官方 `description`/`prompt` 子集上增加可选 `resume` 参数。首次委派的结果文本自述句柄(`追问请带 resume="<childSessionId>"`);后续轮次传回它即在**同一个** dsh 子会话里继续**同一个** codex 线程(`codex exec --json resume <thread_id>`),按轮记账。句柄只从 `resume` 参数读取,localAgent registry 仅对记录该委派的同一 parent 会话与 provider 解析——伪造句柄在任何 CLI 进程启动前就被拒绝。
132
-
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,并保留已收到的事件与用量。
134
-
135
- **模型体验。** 每次委派都是委派 Session 工作区内一个全新的一次性 `codex exec` 进程。父级只提交任务文本、只看到最终回答或精确错误——Codex 的评论、工具活动、工作区 diff 不会跨进父级会话,子会话 token 也永不进入父级上下文。
136
-
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。
138
-
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` 都没写出的极端情况仍保持无用量,不猜测。
38
+ ## Compatibility
140
39
 
141
- </details>
40
+ 需要 DSH 0.1.5-rc.1 或更新版本,已在 0.2.1-rc.1 上验证。
142
41
 
143
- ## 开发
42
+ ## 给开发者
144
43
 
145
- 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins) monorepo(`packages/local-agent-codex`)。问题与贡献请移步该仓库。
44
+ - 配置项(沙箱、默认模型、常驻模式)、端点凭据的存放、自定义端点、回读与内部结构,都在[完整参考](https://github.com/Khorsheed/dsh-plugins/blob/main/packages/local-agent-codex/docs/reference.md)里。
45
+ - 单独安装要连同底座一起点名:`dsh plugin --profile web add @khorsheed/dsh-local-agent @khorsheed/dsh-local-agent-codex`;委派工具由模式授予,没有模式点名时不会出现。
46
+ - 隶属 [dsh-plugins](https://github.com/Khorsheed/dsh-plugins),问题与贡献请到该仓库;变更见 [CHANGELOG.md](CHANGELOG.md)。