@ckanner/flywheel 0.32.1 → 0.33.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 +10 -1
- package/README.md +7 -0
- package/README.zh-CN.md +5 -0
- package/THIRD_PARTY_NOTICES.md +42 -0
- package/bin/runtime.js +25 -31
- package/cordis.patch.yml +5 -0
- package/dist/bridge.mjs +421 -0
- package/dist/claude-code-pro-bridge.mjs +82 -0
- package/dist/claude-code-pro-worker.mjs +5 -0
- package/dist/client.js +4078 -0
- package/dist/codex-pro-bridge.mjs +75 -0
- package/dist/codex-pro-worker.mjs +5 -0
- package/dist/console-read.mjs +51 -0
- package/dist/control.mjs +218 -0
- package/dist/index.mjs +185 -0
- package/dist/installation.mjs +56 -0
- package/dist/machine-worker.mjs +241 -0
- package/dist/policy.mjs +91 -0
- package/dist/prewarm.mjs +136 -0
- package/dist/protocol.mjs +260 -0
- package/dist/task-association.mjs +127 -0
- package/dist/worker.mjs +52 -0
- package/dist/workspaces.mjs +42 -0
- package/docs/README.md +1 -1
- package/docs/README.zh-CN.md +1 -1
- package/docs/design.md +7 -4
- package/docs/en/configuration.md +1 -1
- package/docs/en/contributing.md +5 -6
- package/docs/en/flow-console.md +1 -1
- package/docs/en/getting-started.md +9 -2
- package/docs/en/releasing.md +132 -174
- package/docs/validation/2026-11-13-release-0.32.1.md +83 -0
- package/docs/validation/2026-11-14-single-package.md +60 -0
- package/docs/validation/README.md +2 -0
- package/docs/zh-CN/configuration.md +1 -1
- package/docs/zh-CN/contributing.md +4 -5
- package/docs/zh-CN/flow-console.md +1 -1
- package/docs/zh-CN/getting-started.md +7 -3
- package/docs/zh-CN/releasing.md +97 -81
- package/native/darwin-amd64/flywheel +0 -0
- package/native/darwin-arm64/flywheel +0 -0
- package/native/linux-amd64/flywheel +0 -0
- package/native/linux-arm64/flywheel +0 -0
- package/native/manifest.json +22 -0
- package/node_modules/dsh-subagent-codex-pro/CHANGELOG.md +403 -0
- package/node_modules/dsh-subagent-codex-pro/LICENSE +21 -0
- package/node_modules/dsh-subagent-codex-pro/README.md +557 -0
- package/node_modules/dsh-subagent-codex-pro/cordis.patch.yml +30 -0
- package/node_modules/dsh-subagent-codex-pro/docs/README.zh.md +355 -0
- package/node_modules/dsh-subagent-codex-pro/docs/machine-lifecycle-validation.md +65 -0
- package/node_modules/dsh-subagent-codex-pro/docs/machine-lifecycle.md +144 -0
- package/node_modules/dsh-subagent-codex-pro/docs/machine-lifecycle.zh.md +98 -0
- package/node_modules/dsh-subagent-codex-pro/docs/machine.md +194 -0
- package/node_modules/dsh-subagent-codex-pro/docs/orchestration.zh.md +86 -0
- package/node_modules/dsh-subagent-codex-pro/lib/codex-exec.js +68 -0
- package/node_modules/dsh-subagent-codex-pro/lib/codex-exec.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/dsh-types.js +15 -0
- package/node_modules/dsh-subagent-codex-pro/lib/dsh-types.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/index.js +830 -0
- package/node_modules/dsh-subagent-codex-pro/lib/index.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/jobs.js +35 -0
- package/node_modules/dsh-subagent-codex-pro/lib/jobs.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/jsonrpc.js +229 -0
- package/node_modules/dsh-subagent-codex-pro/lib/jsonrpc.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-cli.js +101 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-cli.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-local.js +122 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-local.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-recovery.js +167 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-recovery.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-supervision.js +141 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-supervision.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-supervisor.js +128 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine-supervisor.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine.js +104 -0
- package/node_modules/dsh-subagent-codex-pro/lib/machine.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/process-guardian.js +156 -0
- package/node_modules/dsh-subagent-codex-pro/lib/process-guardian.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/run.js +842 -0
- package/node_modules/dsh-subagent-codex-pro/lib/run.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/structured.js +329 -0
- package/node_modules/dsh-subagent-codex-pro/lib/structured.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/tool.js +295 -0
- package/node_modules/dsh-subagent-codex-pro/lib/tool.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/codex-exec.d.ts +88 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/codex-exec.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/dsh-types.d.ts +179 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/dsh-types.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/index.d.ts +266 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/index.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/jobs.d.ts +34 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/jobs.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/jsonrpc.d.ts +113 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/jsonrpc.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-cli.d.ts +3 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-cli.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-local.d.ts +4 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-local.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-recovery.d.ts +53 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-recovery.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-supervision.d.ts +8 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-supervision.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-supervisor.d.ts +2 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine-supervisor.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine.d.ts +70 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/machine.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/process-guardian.d.ts +22 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/process-guardian.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/run.d.ts +386 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/run.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/structured.d.ts +35 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/structured.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/tool.d.ts +107 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/tool.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/usage.d.ts +131 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/usage.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/wire.d.ts +321 -0
- package/node_modules/dsh-subagent-codex-pro/lib/types/wire.d.ts.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/usage.js +356 -0
- package/node_modules/dsh-subagent-codex-pro/lib/usage.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/lib/wire.js +986 -0
- package/node_modules/dsh-subagent-codex-pro/lib/wire.js.map +1 -0
- package/node_modules/dsh-subagent-codex-pro/package.json +76 -0
- package/npm/install.js +59 -6
- package/package.json +74 -12
- package/plugins/dsh/LICENSE +21 -0
- package/plugins/dsh/PROTOCOL.md +607 -0
- package/plugins/dsh/README.en.md +4 -0
- package/plugins/dsh/README.md +342 -0
- package/plugins/dsh/README.zh-CN.md +279 -0
- package/plugins/dsh/THIRD_PARTY_NOTICES.md +42 -0
- package/plugins/dsh/vendor/README.md +17 -0
- package/plugins/dsh/vendor/codex-pro.json +16 -0
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
# dsh-subagent-codex-pro
|
|
2
|
+
|
|
3
|
+
English | [中文](docs/README.zh.md)
|
|
4
|
+
|
|
5
|
+
A Codex delegation tool that can carry **a model and a reasoning effort per
|
|
6
|
+
call** — the two things `@deepseek-ai/dsh-subagent-codex` cannot express.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
tools.subagent_codex({
|
|
10
|
+
description: "Probe output PROBE_OK",
|
|
11
|
+
prompt: "Output exactly one line of text: PROBE_OK",
|
|
12
|
+
model: "gpt-6-astra",
|
|
13
|
+
reasoning_effort: "xhigh",
|
|
14
|
+
})
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 0.9.0 source candidate: durable lifecycle and recovery
|
|
18
|
+
|
|
19
|
+
The public machine API now exposes awaitable typed checkpoints, a private
|
|
20
|
+
per-invocation journal, and read-only recovery. An independent machine supervisor
|
|
21
|
+
preserves the original terminal receipt after caller death. The source candidate
|
|
22
|
+
is **not an npm release**. See the [lifecycle contract](docs/machine-lifecycle.md)
|
|
23
|
+
and [中文说明](docs/machine-lifecycle.zh.md); existing DSH tool behavior is unchanged.
|
|
24
|
+
|
|
25
|
+
## 0.8.0 integration and migration
|
|
26
|
+
|
|
27
|
+
The plugin now exposes a versioned [machine API and CLI](docs/machine.md) for
|
|
28
|
+
orchestrators, alongside its DSH tool/provider. Both use the same supervised
|
|
29
|
+
Codex app-server runner. See the [Flywheel, preparation and PR guide](docs/orchestration.zh.md).
|
|
30
|
+
|
|
31
|
+
The default permission is now **workspace-write + never**. Existing profiles
|
|
32
|
+
that explicitly configured full access keep that explicit choice; profiles that
|
|
33
|
+
omitted it receive the new bounded default. Per-call overrides can narrow the
|
|
34
|
+
configured policy, not silently expand it. Required effective configuration
|
|
35
|
+
fields must be present and match before a task is sent. DSH provider/maxTokens
|
|
36
|
+
overrides that this Codex backend cannot honor are rejected.
|
|
37
|
+
|
|
38
|
+
The complete machine receipt includes configuration, effort-request evidence,
|
|
39
|
+
usage completeness, actual process facts and cleanup. DSH Host results retain
|
|
40
|
+
the official narrow shape. Full details never require parsing the diagnostic log.
|
|
41
|
+
|
|
42
|
+
## The gap this fills
|
|
43
|
+
|
|
44
|
+
`subagent_codex`'s schema exposes only `description` and `prompt`, so a caller
|
|
45
|
+
cannot name a model or a thinking level. In a recorded session a model asked to
|
|
46
|
+
do exactly that tried the obvious thing and was refused:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
ToolCallError: child model selection is disabled for this tool instance
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The chain, from the harness source:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
tool schema exposes model / reasoning_effort
|
|
56
|
+
← only when modelSelectionEnabled
|
|
57
|
+
modelSelectionEnabled ← modelSelectionSettings: true on the tool row
|
|
58
|
+
modelSelectionSettings: true ← requires a provider declaring `agentOptions`
|
|
59
|
+
@deepseek-ai/dsh-subagent-codex ← declares NO_START_CAPABILITIES (no agentOptions)
|
|
60
|
+
→ enabling it throws while the preset mounts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The Codex protocol supports the choices this package exposes: `model` and the
|
|
64
|
+
unattended permission policy on `thread/start`, model/cwd/permission overrides on
|
|
65
|
+
`thread/resume` (0.165+), and `effort` per turn on `turn/start`. The official
|
|
66
|
+
provider never sends them. Reported upstream:
|
|
67
|
+
<https://github.com/deepseek-ai/deepseek-harness/discussions/9115>
|
|
68
|
+
|
|
69
|
+
There is a second, separate defect: enabling `modelSelectionSettings: true` on a
|
|
70
|
+
row whose provider is external (`codex-pro`) makes that row **silently**
|
|
71
|
+
register no tool at all — no error, no log, no failed-preset badge. A control row
|
|
72
|
+
identical except for the flag registers normally. Also in that report.
|
|
73
|
+
|
|
74
|
+
## How this works instead
|
|
75
|
+
|
|
76
|
+
Two layers, both under this package's control.
|
|
77
|
+
|
|
78
|
+
### Execution: the same protocol the official provider speaks
|
|
79
|
+
|
|
80
|
+
`src/wire.ts` and `src/run.ts` are a port of the official app-server adapter —
|
|
81
|
+
the initialize handshake, the ephemeral thread, thread/turn association
|
|
82
|
+
including notifications that arrive before `turn/start` returns, unattended
|
|
83
|
+
approval decisions, and terminal-answer selection by phase — with these
|
|
84
|
+
additions:
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
// thread/start
|
|
88
|
+
{ cwd, ephemeral, ...(model ? { model } : {}), ...THREAD_PERMISSION_PARAMS[mode] }
|
|
89
|
+
// thread/resume (0.165+)
|
|
90
|
+
{ threadId, cwd, ...(model ? { model } : {}), ...THREAD_PERMISSION_PARAMS[mode] }
|
|
91
|
+
// turn/start
|
|
92
|
+
{ threadId, input: [...], effort, ...(outputSchema ? { outputSchema } : {}) }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Neither literal appears anywhere in the official adapter's 871 lines. A thinking
|
|
96
|
+
level is a `turn/start` field, not a `thread/start` field in 0.165/0.166; the
|
|
97
|
+
thread-time default is left to Codex. Two
|
|
98
|
+
mechanical differences from the source: the JSON-RPC transport is this package's
|
|
99
|
+
own (`src/jsonrpc.ts`), and the app-server is started as
|
|
100
|
+
`<command> app-server --stdio` from `PATH` rather than from `@openai/codex`'s
|
|
101
|
+
manifest. `src/codex-exec.ts`, the earlier `codex exec` path, is retained and
|
|
102
|
+
tested but unused.
|
|
103
|
+
|
|
104
|
+
The executable is the `command` setting (default `codex`), resolved through the
|
|
105
|
+
Host's `PATH` — and the desktop app captures that environment when it launches.
|
|
106
|
+
A Node version switch, or any change to the login shell, can leave the global
|
|
107
|
+
install out of it. When that happens, set `command` to an absolute path: the
|
|
108
|
+
official `@deepseek-ai/dsh-subagent-codex` bundle ships a pinned executable
|
|
109
|
+
inside the profile (`node_modules/@openai/codex-darwin-arm64/vendor/<arch>/bin/codex`
|
|
110
|
+
on Apple silicon), so Codex Pro and the official provider can share one CLI
|
|
111
|
+
without depending on `PATH`.
|
|
112
|
+
|
|
113
|
+
### Tool registration: this plugin's own context
|
|
114
|
+
|
|
115
|
+
The plugin registers the delegation tool **itself**, on `agent/created`, into
|
|
116
|
+
that Agent's own context — the path `dsh-plugin-lcu` uses for its tools:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
ctx.on('agent/created', ({ agent }) => { if (!presetAllows(agent)) return; serve(agent) })
|
|
120
|
+
ctx.on('agent-preset/selected', (sessionId, preset) => { … })
|
|
121
|
+
// serve(): agent.ctx.tools.register(createCodexTool({ … }))
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
That keeps the harness's preset-row machinery out of the path entirely. A preset
|
|
125
|
+
needs no rows of its own: `presets` names the committed Agent presets that get
|
|
126
|
+
the tool — `daily` and `heavy` here.
|
|
127
|
+
|
|
128
|
+
Because the tool name is fixed, the official `subagent_codex` must not also
|
|
129
|
+
serve those presets. As of DSH 0.2.1 the official
|
|
130
|
+
`@deepseek-ai/dsh-subagent-codex` bundle ships `tool-subagent-codex` as a
|
|
131
|
+
**global Host tool** (visible to every preset), so a profile cannot switch it
|
|
132
|
+
off by disabling a per-preset row. This plugin registers its tool into the
|
|
133
|
+
Agent's own scope on `agent/created`, and a scoped registration shadows the
|
|
134
|
+
global one for its Agents — the official README documents the same precedence.
|
|
135
|
+
Two registrations in *one* scope still collide: the later one throws and its
|
|
136
|
+
tool silently never appears, which is why a profile must not also enable a
|
|
137
|
+
same-name `tool-subagent` row beside this provider.
|
|
138
|
+
|
|
139
|
+
**Whatever the prompt names is what runs.** `model` and `reasoning_effort` are in
|
|
140
|
+
the schema, and an explicit choice in the call wins; when a delegation names
|
|
141
|
+
neither, the choice stays with Codex's own configuration (`~/.codex/config.toml`)
|
|
142
|
+
— the plugin is configured without defaults so that nothing silently overrides
|
|
143
|
+
either. `modelSelection: false` hides the two fields entirely.
|
|
144
|
+
|
|
145
|
+
The Host's `subagent-model-selection` setting is deliberately **not** consulted.
|
|
146
|
+
It authorizes a child **LLM route** chosen from the DSH catalog and billed as
|
|
147
|
+
tokens, while a Codex delegation names a **Codex model id** under the Codex CLI's
|
|
148
|
+
own subscription: different resources, different namespaces. That setting can
|
|
149
|
+
neither authorize nor constrain this choice, and reading it would only couple the
|
|
150
|
+
tool to a setting that has nothing to say about it.
|
|
151
|
+
|
|
152
|
+
## Install
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
# as a bundle in a DSH profile
|
|
156
|
+
dsh plugin --profile desktop add dsh-subagent-codex-pro
|
|
157
|
+
# or from a checkout, for development
|
|
158
|
+
dsh plugin --profile desktop add link:/path/to/dsh-subagent-codex-pro
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Verify
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
npm ci --ignore-scripts
|
|
165
|
+
npm run typecheck
|
|
166
|
+
npm test # offline suite; live cases skip
|
|
167
|
+
CODEX_SUBAGENT_LIVE=1 npm test # extra real turn through the live Codex
|
|
168
|
+
npm run build
|
|
169
|
+
npm run test:package # install and run the actual tarball
|
|
170
|
+
npm run test:coverage # Node's built-in coverage report
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The plugin logs every decision to `~/.dsh/codex-pro.log`:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
apply: provider "codex-pro" registered with agentOptions: true; presets=["daily","heavy"]
|
|
177
|
+
decide … composed=standard header=standard -> skip
|
|
178
|
+
serve …: subagent_codex registered (modelSelection=true, background=true, via=app-server)
|
|
179
|
+
run: model=gpt-6-astra effort=xhigh cwd=… prompt=179B via=app-server
|
|
180
|
+
run: published 3f2a…
|
|
181
|
+
run done: text=8B thread=… images=0 usage=complete(312 tokens) cleanup=confirmed config=true
|
|
182
|
+
run config: requested={"cwd":"…","approvalPolicy":"never","sandbox":"read-only"} effective={"cwd":"…","approvalPolicy":"never","sandbox":"read-only"}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Historical 0.7.0 observation: 30 s wall clock, the model's `PROBE_OK` returned as
|
|
186
|
+
the tool result. The live app-server test opens a thread and puts `effort` on the
|
|
187
|
+
turn; it does **not** claim a thread-level thinking level, which 0.165/0.166 do
|
|
188
|
+
not accept.
|
|
189
|
+
|
|
190
|
+
## Verification layers
|
|
191
|
+
|
|
192
|
+
The 0.8.0 verification matrix includes real fake-process tests and installed-tarball smoke tests on Ubuntu/macOS and Node 22/24. The live observations below are from the installed Codex **0.165.0**; they cover the explicit permission modes used by the cases, not every plugin default:
|
|
193
|
+
|
|
194
|
+
1. **Offline / hermetic** — the default `npm test` suite. It drives the real
|
|
195
|
+
protocol classes and a real child process through a scripted fake app-server:
|
|
196
|
+
startup hangs, turn hangs, caller cancel, early exit, resuming, usage replay,
|
|
197
|
+
duplicates, read-only permissions, structured verification, process-group
|
|
198
|
+
descendants, and idempotent teardown. No model is called and no Codex account
|
|
199
|
+
is touched.
|
|
200
|
+
2. **Real Codex app-server** — the live app-server cases were run against the
|
|
201
|
+
installed Codex **0.165.0** with `CODEX_SUBAGENT_LIVE=1`:
|
|
202
|
+
`4/4` passed. They covered model + effort, explicit permission modes, a persistent
|
|
203
|
+
thread resumed with explicit model/cwd/read-only overrides (including usage
|
|
204
|
+
replay and effective-config verification), and `output_schema` decoding. A
|
|
205
|
+
separate real check also exercised the new `workspace-write` default and
|
|
206
|
+
`approve-for-me`. These live cases are part of the 0.8.0 release validation;
|
|
207
|
+
the DSH desktop smoke remains separate. The
|
|
208
|
+
separate `codex exec` live case also passed (`20/20` in that file, one real
|
|
209
|
+
CLI call). The official **0.166.0** TypeScript schemas were reviewed for
|
|
210
|
+
`ThreadResumeParams`, `ThreadResumeResponse`, `ThreadTokenUsage`, and
|
|
211
|
+
`token_usage_replay.rs`, but no 0.166 runtime was executed. The plugin calls
|
|
212
|
+
the same `codex app-server --stdio` path.
|
|
213
|
+
3. **Real DSH desktop end-to-end** — still not run. The implementation keeps
|
|
214
|
+
the existing DSH provider/tool/job contracts; a desktop smoke test remains
|
|
215
|
+
the one unexecuted layer. Neither CI nor the package smoke changes a user
|
|
216
|
+
login or calls a model.
|
|
217
|
+
|
|
218
|
+
## Making the answer machine-readable
|
|
219
|
+
|
|
220
|
+
`output_schema` hands Codex a JSON Schema its final answer must conform to, so a later step can read the
|
|
221
|
+
answer instead of parsing prose and hoping:
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
tools.subagent_codex({
|
|
225
|
+
description: "Smoke verdict",
|
|
226
|
+
prompt: "Assess whether both endpoints returned 200.",
|
|
227
|
+
output_schema: {
|
|
228
|
+
type: "object",
|
|
229
|
+
properties: { verdict: { type: "string", enum: ["pass", "fail"] }, count: { type: "number" } },
|
|
230
|
+
required: ["verdict", "count"],
|
|
231
|
+
additionalProperties: false,
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The schema must be object-rooted — the same rule the harness applies to a schema it passes a provider — because
|
|
237
|
+
Codex constrains a whole answer and a bare scalar has nowhere to put the shape. The app-server **enforces** it:
|
|
238
|
+
a measured run came back as `{"verdict":"pass","reason":"…","count":2}`, with no extra keys and every required
|
|
239
|
+
one present. The answer arrives as **text**, and that text is JSON.
|
|
240
|
+
|
|
241
|
+
It is sent on the turn, not the thread: the same field on `thread/start` is accepted and silently ignored — the
|
|
242
|
+
answer came back as prose. Measured both ways.
|
|
243
|
+
|
|
244
|
+
A provider the harness drives (`thread-subagent`) can also be given a schema by its row, and this plugin
|
|
245
|
+
declares that capability and verifies the answer before reporting `structured`. Verification is now
|
|
246
|
+
**fail-closed**: invalid JSON, a non-object answer, a required/type/range violation, or a schema keyword this
|
|
247
|
+
package cannot verify makes the run report an error/diagnostic instead of quietly omitting `structured`. The
|
|
248
|
+
validator covers the object-rooted JSON Schema subset this tool accepts and rejects unsupported keywords rather
|
|
249
|
+
than pretending they were checked.
|
|
250
|
+
|
|
251
|
+
For a call carrying `output_schema`, the answer stays exactly the JSON text Codex returned. The persistent
|
|
252
|
+
thread marker is **not** appended to machine-readable output; the thread id is reported separately and in the
|
|
253
|
+
diagnostic log. Plain human-text answers keep the marker that makes a thread continuable.
|
|
254
|
+
|
|
255
|
+
## Showing Codex a picture
|
|
256
|
+
|
|
257
|
+
`images` hands Codex pictures alongside the task, so it receives them as pictures instead of being told to go
|
|
258
|
+
and open a file:
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
tools.subagent_codex({
|
|
262
|
+
description: "Match this design",
|
|
263
|
+
prompt: "Rebuild this screen's layout in the component below.",
|
|
264
|
+
images: ["/tmp/design.png"],
|
|
265
|
+
})
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Each path must be absolute, must exist, and must be a file; anything else fails the call before anything is
|
|
269
|
+
spawned. That check is not decoration: **the app-server silently ignores a `localImage` path that does not
|
|
270
|
+
exist** — measured, with the turn completing normally and no error item — so an unchecked typo would produce a
|
|
271
|
+
delegation that quietly lacks the picture the task was about.
|
|
272
|
+
|
|
273
|
+
The pictures are sent as the app-server's `localImage` variant, which is the one form it accepts without the
|
|
274
|
+
bytes travelling: its `image` variant wants an `ImageReference` this plugin cannot construct, and both
|
|
275
|
+
`{type:'image', data, mimeType}` and a `data:` URL are rejected outright. A host path needs no encoding.
|
|
276
|
+
|
|
277
|
+
Naming images does not disturb the thread: they belong to the turn, not to the thread, so a resumed thread can
|
|
278
|
+
be shown a new picture.
|
|
279
|
+
|
|
280
|
+
## Choosing the directory
|
|
281
|
+
|
|
282
|
+
A delegation runs in the session's working directory by default. `cwd` names another one:
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
tools.subagent_codex({
|
|
286
|
+
description: "Post-deploy UI smoke",
|
|
287
|
+
prompt: "…",
|
|
288
|
+
cwd: "/tmp/mesh-smoke/20261010-0800",
|
|
289
|
+
})
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The path must be absolute, must exist, and must be a directory; anything else is refused with a message naming
|
|
293
|
+
the problem rather than a run that quietly happens somewhere unexpected. It is a working directory, not a
|
|
294
|
+
sandbox boundary — what a run may touch is the permission mode's decision.
|
|
295
|
+
|
|
296
|
+
**On 0.165, `thread/resume` accepts overrides, and this plugin actually sends them.** A resumed delegation
|
|
297
|
+
sends the requested `cwd`, `model` (when one is chosen), and permission parameters, then compares the
|
|
298
|
+
`thread/resume` response's effective `cwd`, `model`, `approvalPolicy`, `approvalsReviewer`, and `sandbox`
|
|
299
|
+
with what was requested. A response that ignored an override is an error, not a silent fallback to the
|
|
300
|
+
thread's history. This replaces the 0.164-era behaviour where `cwd` beside a `thread_id` did not relocate the
|
|
301
|
+
thread: that is no longer the protocol this package targets.
|
|
302
|
+
|
|
303
|
+
For a **remembered** thread (`threadMode: session`, not an explicit `thread_id`), a resume that cannot apply
|
|
304
|
+
the current configuration is treated as incompatible reuse: the plugin logs it and starts a fresh thread,
|
|
305
|
+
under the same call deadline. For an **explicit** `thread_id`, the requested thread is the whole request, so an
|
|
306
|
+
incompatible or refused resume is reported; it is never silently replaced by a different thread.
|
|
307
|
+
|
|
308
|
+
`reasoning_effort` remains a per-turn `turn/start` setting and still applies when the thread is resumed. The
|
|
309
|
+
thread's historical default thinking level is visible in the resume response but is not restated by this
|
|
310
|
+
package.
|
|
311
|
+
|
|
312
|
+
## Images
|
|
313
|
+
|
|
314
|
+
Codex can look at pictures and can take screenshots; this tool can now hand those pictures back.
|
|
315
|
+
|
|
316
|
+
**What arrives.** The app-server reports an image two ways, and both are captured. `imageView` names a **file on
|
|
317
|
+
this host** — the bytes are never inlined, because the host that asked for the work is expected to read the file
|
|
318
|
+
itself, which is exactly what this plugin does. A computer-use screenshot instead arrives as an **image content
|
|
319
|
+
block** inside an MCP tool result, carrying its bytes.
|
|
320
|
+
|
|
321
|
+
**What is delivered.** The plugin reads the file (or decodes the block), hands the bytes to the harness's
|
|
322
|
+
attachment store, and returns image blocks alongside the text. The model then sees an image rather than a
|
|
323
|
+
description of one.
|
|
324
|
+
|
|
325
|
+
**What the model's route decides, and why the rule is deliberately loose.** An image is refused only when the
|
|
326
|
+
route **declares** input modalities and `image` is not among them — the harness's own rule, so that an
|
|
327
|
+
undeclared route is left to the harness rather than second-guessed here. `deepseek-flash`, the default, declares
|
|
328
|
+
`['text', 'image']`, so screenshots do arrive in an ordinary session.
|
|
329
|
+
|
|
330
|
+
An earlier guard failed closed whenever the route could not be resolved, which silently turned every image into
|
|
331
|
+
a text note in a session that could have displayed it. It does not any more: an unresolvable route delivers the
|
|
332
|
+
image. When a route really does refuse images the note reads
|
|
333
|
+
`[image not delivered: this model route declares no image input]`.
|
|
334
|
+
|
|
335
|
+
The same note covers every other failure — no attachment store, an unreadable file, a file over 20 MB, an
|
|
336
|
+
unsupported media type. **Nothing here can fail the delegation**; the answer the run produced always survives.
|
|
337
|
+
|
|
338
|
+
**A background call gets paths, not pictures.** A foreground delegation returns image blocks, so the model
|
|
339
|
+
sees the picture. A `run_in_background` delegation returns a job id, and everything read back through
|
|
340
|
+
`job_output` is text by contract — so its images are reported as paths in that text instead:
|
|
341
|
+
`[1 image: /Users/…/shot.png]`. The file is on this host either way. For a screenshot you want the model to
|
|
342
|
+
actually look at, delegate in the foreground.
|
|
343
|
+
|
|
344
|
+
**Sending pictures *in*.** The protocol accepts `input_image`, `local_image`, `image_url` and even audio, but this
|
|
345
|
+
tool currently sends text only. Until that changes, name the **path** in the prompt: Codex opens the file itself
|
|
346
|
+
with its own image tool, which is verified — asked to describe a screenshot by path, it read out the providers
|
|
347
|
+
listed on it.
|
|
348
|
+
|
|
349
|
+
## Overriding this row in a profile
|
|
350
|
+
|
|
351
|
+
The row this package declares is a bundle patch. A profile that wants different settings adds its own row with
|
|
352
|
+
the same `id`, and the loader **replaces the whole `config` map**: it walks the patch's top-level fields and
|
|
353
|
+
assigns each to the target, and `config` is one of those fields. It does not merge deeply, and it does not merge
|
|
354
|
+
arrays.
|
|
355
|
+
|
|
356
|
+
So every field has to be restated, or it is gone:
|
|
357
|
+
|
|
358
|
+
```yaml
|
|
359
|
+
# ✗ `presets` is gone, and with it the tool. The row still mounts — it just
|
|
360
|
+
# matches no preset, so `subagent_codex` never appears in any session.
|
|
361
|
+
- id: codex-pro
|
|
362
|
+
config:
|
|
363
|
+
runTimeoutMs: 5400000
|
|
364
|
+
|
|
365
|
+
# ✓ the whole map, every field
|
|
366
|
+
- id: codex-pro
|
|
367
|
+
name: 'dsh-subagent-codex-pro'
|
|
368
|
+
config:
|
|
369
|
+
providerName: codex-pro
|
|
370
|
+
presets: [daily, heavy]
|
|
371
|
+
runTimeoutMs: 5400000
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
This failure is quiet in the worst way. A patch whose `id` matches nothing, or whose `name` disagrees with the
|
|
375
|
+
target's, is **skipped with a warning** rather than an error; and a `config` that lost `presets` still mounts,
|
|
376
|
+
it simply serves no session. The `apply` line this plugin writes to `~/.dsh/codex-pro.log` names the presets it
|
|
377
|
+
actually received, which is where to look:
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
apply: v0.7.0 provider "codex-pro" registered … timeout=5400000ms; presets=["daily","heavy"]
|
|
381
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Thread lifetime
|
|
385
|
+
|
|
386
|
+
Every delegation used to open an ephemeral thread — the app-server deletes it when the connection ends, so
|
|
387
|
+
nothing was left to list or continue. `threadMode` chooses instead:
|
|
388
|
+
|
|
389
|
+
```yaml
|
|
390
|
+
- id: codex-pro
|
|
391
|
+
config:
|
|
392
|
+
threadMode: session # ephemeral (default) | persistent | session
|
|
393
|
+
runTimeoutMs: 600000 # deadline for one delegation; 0 disables it
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
A call may name its own `timeout_ms`, which overrides the configured default for that one delegation — `0`
|
|
397
|
+
meaning no deadline at all. That is what makes the deadline usable from a prompt: a release smoke test needs tens
|
|
398
|
+
of minutes, a quick lookup does not, and the model can say which this is because the value is a parameter rather
|
|
399
|
+
than a file.
|
|
400
|
+
|
|
401
|
+
**A delegation that hangs is otherwise invisible.** The job stays `running`, the session waits, and nothing is
|
|
402
|
+
logged — which is exactly what a stalled model request looks like from the outside. `runTimeoutMs` is the
|
|
403
|
+
deadline for one whole delegation, and it covers **startup too**: `initialize`, `thread/start`/`thread/resume`,
|
|
404
|
+
`thread/name/set`, and the turn all use one derived abort signal. When it passes, the run is cancelled, its
|
|
405
|
+
process range is torn down under a separate bounded grace, and the result says
|
|
406
|
+
`codex-pro: the delegation exceeded its 600000 ms deadline and was cancelled` instead of nothing at all. The
|
|
407
|
+
default is ten minutes, generous against a task class whose own documentation says "a minute or more"; `0`
|
|
408
|
+
restores the old behaviour of waiting forever.
|
|
409
|
+
|
|
410
|
+
The timeout, a caller cancellation, a protocol failure, and an unexpected process exit are distinct failure
|
|
411
|
+
categories. Settlement happens once; teardown is idempotent. A failed resume that is being repaired by a fresh
|
|
412
|
+
thread shares the original absolute deadline, so the retry cannot borrow a second full timeout.
|
|
413
|
+
|
|
414
|
+
**Cleanup is checked against the owned range.** On POSIX a guardian leads the process group and Codex
|
|
415
|
+
shares that group. An IPC lifeline is established during spawn; caller death triggers bounded guardian
|
|
416
|
+
cleanup. The live caller still confirms that the group has disappeared. Actual Codex and guardian
|
|
417
|
+
exit facts remain separate; an unknown exit is never fabricated as zero. Unconfirmed cleanup prevents
|
|
418
|
+
success and makes Host dispose reject. The Windows Host path controls its direct process only;
|
|
419
|
+
the machine entry requires POSIX. See [ownership and cancellation](docs/machine.md#cancellation-and-process-ownership).
|
|
420
|
+
|
|
421
|
+
| | |
|
|
422
|
+
|---|---|
|
|
423
|
+
| `ephemeral` | A throwaway run. Nothing is written that the Codex CLI or the Codex application can list. |
|
|
424
|
+
| `persistent` | Every delegation gets a durable thread: written to `~/.codex/sessions/`, listed by both, continuable. |
|
|
425
|
+
| `session` | One durable thread per Agent session, so ten delegations leave one thread and every call after the first continues the same conversation. |
|
|
426
|
+
|
|
427
|
+
A call overrides the choice with `thread_mode`, and continues a thread of its own with `thread_id`. A persistent
|
|
428
|
+
human-text result ends with the thread's id; a schema-constrained result stays pure JSON and reports the id
|
|
429
|
+
separately:
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
[codex thread 01a1212b-79cf-7241-8892-896094fad3d6 · persistent · pass it as thread_id to continue]
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Resuming **does not ignore the caller's choices on 0.165**: `model`, `cwd`, and `permission_mode` are sent as
|
|
436
|
+
`thread/resume` overrides and compared against the response. `reasoning_effort` is a per-turn setting and still
|
|
437
|
+
applies. A remembered thread with an incompatible configuration is replaced by a fresh thread; an explicit
|
|
438
|
+
`thread_id` reports the incompatibility instead of continuing somewhere else. Machine-readable JSON does not
|
|
439
|
+
get the thread marker described next.
|
|
440
|
+
|
|
441
|
+
**Two things the app-server decides, both measured.** A thread with no turn yet is not resumable — it answers
|
|
442
|
+
`no rollout found` — so a thread becomes continuable only after its first completed turn. And the Codex
|
|
443
|
+
application lists a thread only when its originator is exactly `Codex Desktop`, the single value its client
|
|
444
|
+
filters on; `originator` sets that and defaults to the literal. Overriding it trades application visibility for
|
|
445
|
+
an honest name, and the Codex CLI lists the thread either way.
|
|
446
|
+
|
|
447
|
+
Because the originator cannot say where a thread came from, a persistent one is named
|
|
448
|
+
`DSH · <the call's description>` through `thread/name/set`, which is what a person sees in the list.
|
|
449
|
+
|
|
450
|
+
**One thread carries one turn at a time.** Two delegations sent at once from the same session would both reach
|
|
451
|
+
for the remembered thread, and the app-server refuses the second with `thread-store conflict: … already has an
|
|
452
|
+
active writer`. A session-mode call therefore checks before it starts: a remembered thread that another
|
|
453
|
+
delegation is using is skipped, the call gets a thread of its own, and the skip is logged. Parallel work still
|
|
454
|
+
runs — it simply stops sharing one conversation, which is what `persistent` does anyway, so the mode falls back
|
|
455
|
+
to the mode that supports the shape of the work. Pick `session` for sequential work that benefits from a
|
|
456
|
+
continuing conversation, and `persistent` or `ephemeral` when delegations run at the same time.
|
|
457
|
+
|
|
458
|
+
**Why it is worth more than convenience.** A resumed thread keeps the same prompt prefix, so the endpoint's
|
|
459
|
+
cache keeps hitting. Measured on the Codex side, 93–99% of input tokens came from cache; the same work driven
|
|
460
|
+
through a plan-usage route measured 0%, because that route refuses every cache control.
|
|
461
|
+
|
|
462
|
+
## Permissions, including read-only review
|
|
463
|
+
|
|
464
|
+
The default is **workspace-write**: approvalPolicy=never with the explicit
|
|
465
|
+
workspace-write sandbox. Full access requires an explicit plugin configuration
|
|
466
|
+
or an explicit machine request; it is never a fallback for a failed sandbox.
|
|
467
|
+
|
|
468
|
+
| mode | Codex approvalPolicy | sandbox | use |
|
|
469
|
+
|---|---|---|---|
|
|
470
|
+
| workspace-write | never | workspace-write | default implementation work |
|
|
471
|
+
| read-only | never | read-only | independent review and inspection |
|
|
472
|
+
| dangerously-bypass-approvals-and-sandbox | never | danger-full-access | explicit full-access configuration only |
|
|
473
|
+
| approve-for-me | on-request | workspace-write | legacy policy using the approval reviewer |
|
|
474
|
+
| never | never | omitted | legacy no-prompt mode, not a known sandbox |
|
|
475
|
+
|
|
476
|
+
A per-call permission can keep its configured mode, narrow to read-only, or
|
|
477
|
+
narrow explicit full access to workspace-write. Other changes require editing
|
|
478
|
+
the plugin configuration. The machine API accepts only the three explicit
|
|
479
|
+
sandbox modes. Required cwd/model/approval/sandbox response fields must be
|
|
480
|
+
present, well typed and match. Complete observed sandbox policy remains in the
|
|
481
|
+
receipt; matched fields alone do not prove an OS sandbox was tested.
|
|
482
|
+
|
|
483
|
+
## Usage and rate limits
|
|
484
|
+
|
|
485
|
+
Codex reports `thread/tokenUsage/updated` with a thread-cumulative `total` and a last-model-call `last`. A
|
|
486
|
+
delegation can make several model calls, and `thread/resume` replays the latest persisted snapshot for the new
|
|
487
|
+
connection. Treating that replay as current spending is wrong.
|
|
488
|
+
|
|
489
|
+
The tracker folds notifications into one run report:
|
|
490
|
+
|
|
491
|
+
- `run` is the delta from the observed baseline, never a sum of notification snapshots.
|
|
492
|
+
- A replay after resume becomes the baseline and contributes no run usage, even
|
|
493
|
+
when Codex sends it after the turn/start request. A foreign-turn snapshot after
|
|
494
|
+
a live current-turn snapshot remains unattributed.
|
|
495
|
+
- A resume with no replay before its first live snapshot is `partial`; the report never invents a zero.
|
|
496
|
+
- Duplicate and out-of-order snapshots are ignored; malformed events or a counter that moves backwards mark the report `partial`
|
|
497
|
+
rather than producing negative usage.
|
|
498
|
+
- Usage from another thread/turn is not added.
|
|
499
|
+
- No observation is `unknown` with no `run`, never a fake `0`; an observation that arrived before a failure,
|
|
500
|
+
timeout, or cancellation is retained in the failed outcome.
|
|
501
|
+
- `cachedInputTokens` is a subset of `inputTokens` and `reasoningOutputTokens` is a subset of `outputTokens`;
|
|
502
|
+
the fields are reported as Codex states them and never added together. The
|
|
503
|
+
protocol-defaulted `cacheWriteInputTokens` may be omitted and is then recorded
|
|
504
|
+
as zero.
|
|
505
|
+
|
|
506
|
+
`account/rateLimits/updated` is stored separately as `rateLimits`; account limits are not token usage and are
|
|
507
|
+
not merged into it. Both are exposed on `CodexRunOutcome` / `delegateOnce` results and written to the diagnostic
|
|
508
|
+
log. The DSH `SubagentResult` and tool-result contracts have no `usage`/`rateLimits` field, so the provider and
|
|
509
|
+
custom tool do not invent host fields; the tool result remains content-only, and the background job result
|
|
510
|
+
remains the contract's string. DSH users can inspect the diagnostic log; external
|
|
511
|
+
orchestrators obtain the complete observations through the public machine API/CLI.
|
|
512
|
+
|
|
513
|
+
## Background runs and progress
|
|
514
|
+
|
|
515
|
+
A delegation takes a minute or more, so `run_in_background` exists whenever the
|
|
516
|
+
jobs service is loaded (`@deepseek-ai/dsh-jobs`). It returns a job id:
|
|
517
|
+
|
|
518
|
+
```js
|
|
519
|
+
tools.subagent_codex({ description: "…", prompt: "…", run_in_background: true })
|
|
520
|
+
// → { kind: "background", jobId: "subagent-3" } collect with job_output
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
The child's own activity is appended to the job's output ring as it happens —
|
|
524
|
+
completed commands and file changes, plus Codex's `commentary` messages — so
|
|
525
|
+
`job_output` shows how a long delegation is going, not only what it finally said.
|
|
526
|
+
|
|
527
|
+
## Capabilities
|
|
528
|
+
|
|
529
|
+
| | status |
|
|
530
|
+
|---|---|
|
|
531
|
+
| `model` / `reasoning_effort` per call | ✅ |
|
|
532
|
+
| final message returned | ✅ |
|
|
533
|
+
| images the run produced (screenshots, viewed files) | ✅ when the calling model route accepts images |
|
|
534
|
+
| images handed to the run (`images`, absolute paths) | ✅ |
|
|
535
|
+
| schema-enforced answer (`output_schema`, or the harness's row setting) | ✅ verified locally |
|
|
536
|
+
| `run_in_background` + progress via `job_output` | ✅ |
|
|
537
|
+
| read-only review permission (`read-only`) | ✅ `approvalPolicy=never` + `sandbox=read-only` |
|
|
538
|
+
| token usage / model context window | ✅ public machine receipt + internal outcome; DSH result/tool/job contracts retain their official shapes |
|
|
539
|
+
| account rate limits | ✅ separate from token usage; included in the public machine receipt |
|
|
540
|
+
| process-range cleanup with confirmation | ✅ POSIX; unconfirmed is reported |
|
|
541
|
+
| deadline covers startup and turn; user cancel distinct | ✅ |
|
|
542
|
+
| app-server protocol (handshake, thread lifecycle, approval decisions) | ✅ |
|
|
543
|
+
| interactive approvals | by design none — a delegated run is unattended, as the official provider is |
|
|
544
|
+
| continuable / thread reuse | ✅ `session` reuses one; `thread_id` continues any |
|
|
545
|
+
| `maxDepth` / persona / `toolFilter` | ❌ not applicable — see below |
|
|
546
|
+
|
|
547
|
+
The three refusals above are not gaps to be filled. A Codex delegation is a **leaf**: it has no DSH child
|
|
548
|
+
agent, so a delegation-depth cap has nothing to cap. Its tools are Codex's own — the shell, the patch tool, its
|
|
549
|
+
own MCP servers — not this harness's, so a **tool filter**, which names DSH tools, cannot describe them; restrict
|
|
550
|
+
Codex through its own configuration instead. And a **persona** is a system instruction for a child agent, where
|
|
551
|
+
this one already takes the task in `prompt`. Declaring any of them `true` would promise the harness an
|
|
552
|
+
enforcement that does not exist.
|
|
553
|
+
|
|
554
|
+
Use the official provider when its configuration and result contract meet your
|
|
555
|
+
needs. This package adds per-call configuration and a public machine receipt
|
|
556
|
+
for external orchestrators. Its compatibility is checked against the documented
|
|
557
|
+
DSH/Codex baselines; new upstream releases still require contract verification.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# This plugin's bundle patch.
|
|
2
|
+
#
|
|
3
|
+
# One root row: the plugin itself. It registers a Codex subagent provider and,
|
|
4
|
+
# per Agent, a `subagent_codex` tool that can carry a model and a reasoning
|
|
5
|
+
# effort — into the Agent's own context, the way the LCU plugin registers `js`.
|
|
6
|
+
#
|
|
7
|
+
# `presets` names the committed Agent presets that get the tool. Since DSH
|
|
8
|
+
# 0.2.1 the official `@deepseek-ai/dsh-subagent-codex` bundle contributes a
|
|
9
|
+
# *global* Host `subagent_codex` tool instead of a per-preset row, so a profile
|
|
10
|
+
# cannot switch it off by disabling a preset copy. This plugin registers its
|
|
11
|
+
# tool into the Agent's own scope on `agent/created`, and a scoped registration
|
|
12
|
+
# shadows the global one for its Agents — the official README documents the same
|
|
13
|
+
# precedence. Two registrations in one scope still collide (the later one throws
|
|
14
|
+
# and its tool silently never appears), so a profile must not also enable a
|
|
15
|
+
# same-name `tool-subagent` row beside this provider.
|
|
16
|
+
|
|
17
|
+
- insert:
|
|
18
|
+
- id: codex-pro
|
|
19
|
+
name: 'dsh-subagent-codex-pro'
|
|
20
|
+
config:
|
|
21
|
+
providerName: codex-pro
|
|
22
|
+
permissionMode: workspace-write
|
|
23
|
+
presets: [daily, heavy]
|
|
24
|
+
# `model` / `reasoningEffort` are deliberately unset. Whatever the user's
|
|
25
|
+
# prompt names is what the delegation uses; when it names nothing, the
|
|
26
|
+
# choice stays with Codex's own configuration (~/.codex/config.toml). A
|
|
27
|
+
# default here would silently override both.
|
|
28
|
+
#
|
|
29
|
+
# `modelSelection: false` hides `model` and `reasoning_effort` from the
|
|
30
|
+
# tool schema entirely.
|