@deepseek-ai/dsh-subagent-codex 0.1.1-rc.1 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +119 -69
- package/README.zh.md +135 -85
- package/lib/index.js +32 -59
- package/lib/types/index.d.ts +4 -2
- package/lib/types/run.d.ts +2 -0
- package/lib/types/wire.d.ts +4 -10
- package/package.json +30 -22
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/subagent/subagent-codex/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: db1250987333f6431f9a05f5e440e9d47a1d36f2
|
|
6
|
+
README.zh.md: 4142c3ad54abc19ff3bd86a1b7fb6708eadfe9bf
|
package/README.md
CHANGED
|
@@ -1,43 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "The one-shot Codex subagent provider for users and maintainers choosing a product backend, installing a Profile bundle, or configuring an unattended Codex delegation."
|
|
3
|
+
kind: "package-bundle"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-subagent-codex
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Start and ownership
|
|
10
|
+
## Summary
|
|
8
11
|
|
|
9
|
-
`
|
|
12
|
+
`dsh-subagent-codex` registers a Profile-named Codex subagent provider (default `codex`) that runs a real Codex child through the official app-server protocol in the delegating session's workspace. Each accepted run starts the package-local Codex wrapper with `app-server --stdio`, creates one ephemeral Codex thread, submits one self-contained text task, and returns the selected final answer — or a separate safe failure diagnostic — through the shared subagent result contract. The provider ships as an optional Profile Bundle: installing it brings the official wrapper and one compatible native platform payload, while the registered provider stays dormant until a bound tool calls it. Native Codex configuration and authentication remain authoritative, and the Profile-selected `permissionMode` maps into the thread's approval, reviewer, and sandbox fields. Choose it when the child should be a genuine Codex session, fully isolated from the parent harness.
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
## Table of Contents
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
-----
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
Mount this provider when a delegation should run as a real Codex session in the parent's workspace. The common path is explicit: install the Bundle into a Profile, optionally configure the provider row, and expose it to the model through a delegation tool row.
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
### Installing the Bundle
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| Key | Default | Meaning |
|
|
26
|
-
|---|---|---|
|
|
27
|
-
| `providerName` | `codex` | Non-empty registry name on `ctx.subagents`; each mounted instance needs a unique value. |
|
|
28
|
-
| `env` | `{}` | Explicit child environment layered over the subprocess seam's credential-scrubbed parent environment. |
|
|
29
|
-
| `permissionMode` | `never` | Native non-interactive approval and sandbox mode fixed for every thread from this Provider instance. |
|
|
30
|
-
| `disposeGraceMs` | `3000` | Positive finite grace in milliseconds, no greater than [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.md), between the shared process-tree owner's termination tiers; disposal then waits for whole-tree exit. |
|
|
31
|
-
|
|
32
|
-
| `permissionMode` value | `thread/start` fields | Native behavior |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| `never` | `approvalPolicy: never`; sandbox omitted | Never ask for approval; execution failures return to the model under the native sandbox. |
|
|
35
|
-
| `approve-for-me` | `approvalPolicy: on-request`, `approvalsReviewer: auto_review`, `sandbox: workspace-write` | Route permission requests through Codex automatic review without a human. |
|
|
36
|
-
| `dangerously-bypass-approvals-and-sandbox` | `approvalPolicy: never`, `sandbox: danger-full-access` | Skip approval and sandbox enforcement; this value must be selected explicitly. |
|
|
37
|
-
|
|
38
|
-
Production resolves the `codex` bin declared by its pinned `@openai/codex@0.147.0` dependency and launches that JavaScript wrapper with the current Node executable. The wrapper selects the matching native platform payload; the provider neither inspects nor falls back to a host `codex` on `PATH`. Native Codex configuration and authentication remain authoritative through the parent cwd, `HOME`, and `CODEX_HOME`, while the Provider overrides only the selected thread approval/reviewer/sandbox fields. All other project, model, provider, MCP, hook, skill, and account settings remain native. The plugin does not select a model, create `CODEX_HOME`, log in, or probe an account. Credential-shaped ambient variables are removed by the subprocess seam before the explicit `env` overlay is applied.
|
|
39
|
-
|
|
40
|
-
This package is an optional Profile Bundle. Install it into the target Profile, then restart that Profile; installation brings the official wrapper and one compatible native platform payload into that Profile, while the declared `cordis.patch.yml` layer registers only the dormant `codex` Host provider and starts no Codex process. Removing the package withdraws that provider and its private runtime closure on the next Profile start.
|
|
32
|
+
Install the package into the target Profile, then restart that Profile. The installation brings the official wrapper and one compatible native platform payload into the Profile; the declared patch layer registers only the dormant provider and starts no Codex process.
|
|
41
33
|
|
|
42
34
|
```sh
|
|
43
35
|
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
|
|
@@ -45,68 +37,108 @@ dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
|
|
|
45
37
|
dsh --profile <name>
|
|
46
38
|
```
|
|
47
39
|
|
|
48
|
-
|
|
40
|
+
Removing the package withdraws the provider and its private runtime closure on the next Profile start. Installation controls Host availability, not model permission: the model can only reach the provider through a delegation tool row you compose.
|
|
49
41
|
|
|
50
|
-
|
|
42
|
+
### Configuration
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
|
|
44
|
+
| Field | Default | Meaning |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `providerName` | `codex` | Non-empty registry name on `ctx.subagents`; each mounted instance needs a unique value |
|
|
47
|
+
| `model` | native Codex settings | Optional non-empty model name fixed for every thread from this provider instance; omission sends no app-server override |
|
|
48
|
+
| `env` | `{}` | Explicit child environment layered over the credential-scrubbed parent environment |
|
|
49
|
+
| `permissionMode` | `never` | Native non-interactive approval and sandbox mode fixed for every thread from this provider instance |
|
|
50
|
+
| `disposeGraceMs` | `3000` | Grace between the shared process-tree owner's termination tiers |
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
| `permissionMode` value | `thread/start` fields | Native behavior |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `never` | `approvalPolicy: never`; sandbox omitted | Never ask for approval; execution failures return to the model under the native sandbox |
|
|
55
|
+
| `approve-for-me` | `approvalPolicy: on-request`, `approvalsReviewer: auto_review`, `sandbox: workspace-write` | Route permission requests through Codex automatic review without a human |
|
|
56
|
+
| `dangerously-bypass-approvals-and-sandbox` | `approvalPolicy: never`, `sandbox: danger-full-access` | Skip approval and sandbox enforcement; this value must be selected explicitly |
|
|
57
|
+
|
|
58
|
+
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-subagent-codex) is the exhaustive source for every accepted field and its JSDoc. A configured `model` passes unchanged on each ephemeral `thread/start`; omission leaves native model selection in force. The provider does not discover models, rewrite aliases, select `modelProvider` or `serviceTier`, or set a fallback. Credential-shaped ambient variables are removed before the explicit `env` overlay, so an API key intended for the child must be supplied there.
|
|
59
|
+
|
|
60
|
+
### Exposing the tool
|
|
61
|
+
|
|
62
|
+
Each delegation tool row names one provider and needs its own `toolName`, so the model sees static tools rather than a dynamic provider selector. Full Agent Presets carry a matching default tool row with `disabled: true`; copy a preset and remove that field to expose `subagent_codex` only to agents composed from the copy.
|
|
69
63
|
|
|
70
64
|
```yaml
|
|
71
65
|
- id: jobs
|
|
72
66
|
name: '@deepseek-ai/dsh-jobs-local'
|
|
73
|
-
|
|
74
67
|
- id: tool-jobs
|
|
75
68
|
name: '@deepseek-ai/dsh-tool-jobs'
|
|
76
|
-
|
|
77
|
-
- id: tool-subagent-codex-safe
|
|
78
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
79
|
-
disabled: true
|
|
80
|
-
config:
|
|
81
|
-
provider: codex-safe
|
|
82
|
-
toolName: subagent_codex_safe
|
|
83
|
-
backgroundMode: one-shot
|
|
84
|
-
maxDepth: provider-managed
|
|
85
|
-
|
|
86
|
-
- id: tool-subagent-codex-bypass
|
|
69
|
+
- id: tool-subagent-codex
|
|
87
70
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
88
71
|
config:
|
|
89
|
-
provider: codex
|
|
90
|
-
toolName:
|
|
72
|
+
provider: codex
|
|
73
|
+
toolName: subagent_codex
|
|
91
74
|
backgroundMode: one-shot
|
|
92
75
|
maxDepth: provider-managed
|
|
93
76
|
```
|
|
94
77
|
|
|
95
|
-
|
|
78
|
+
The `one-shot` policy keeps omitted or `false` `run_in_background` calls in the foreground, while explicit `true` returns a parent-owned Job id for `job_output` or `job_kill`; the base host and full presets already provide the generic Job registry and controls.
|
|
96
79
|
|
|
97
|
-
|
|
80
|
+
### What you get
|
|
98
81
|
|
|
99
|
-
|
|
82
|
+
A foreground call gives the model the selected final Codex answer, or an error with the stop reason and optional safe diagnostic for a failed run. A background call first returns a Job id; the generic job controls later deliver a completion notice and expose the same final answer or failed status through `job_output`. Codex commentary, reasoning, tool activity, raw stderr, and workspace diffs never enter the parent session.
|
|
100
83
|
|
|
101
|
-
|
|
84
|
+
### Failure and recovery
|
|
102
85
|
|
|
86
|
+
An install that omits optional dependencies, uses an unsupported platform, or loses the selected payload leaves the provider dormant and fails the first delegation at `initialize` with a safe `unknown` category and any observed process outcome; there is no host-CLI fallback. Raw wrapper text stays on Host stderr. A cancelled run settles as `aborted`.
|
|
87
|
+
|
|
88
|
+
-----
|
|
89
|
+
|
|
90
|
+
<a id="understand-the-implementation"></a>
|
|
91
|
+
## Understand the implementation
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary>Implementation internals — click to expand</summary>
|
|
95
|
+
|
|
96
|
+
This section explains how the provider drives a real Codex app-server and where the observable behavior comes from; the full contract lives in [Use this package](#use-this-package).
|
|
97
|
+
|
|
98
|
+
### Design concept
|
|
99
|
+
|
|
100
|
+
- **One fresh process, thread, and turn per run.** Every run spawns a fresh app-server, creates one ephemeral thread, and executes exactly one turn; there is no continuation, resume, or pooling.
|
|
101
|
+
- **Native configuration is authoritative.** Codex configuration and authentication stay native through the parent cwd, `HOME`, and `CODEX_HOME`; the provider overrides only the optional model and the thread's approval, reviewer, and sandbox fields.
|
|
102
|
+
- **Unattended by design.** Approval, user-input, and MCP requests are answered or declined without a human; unknown server requests fail the run.
|
|
103
|
+
|
|
104
|
+
### Source map
|
|
105
|
+
|
|
106
|
+
| File | Role |
|
|
107
|
+
|---|---|
|
|
108
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: config schema, provider registration |
|
|
109
|
+
| [`src/run.ts`](src/run.ts) | The run lifecycle, turn execution, result selection, and diagnostics |
|
|
110
|
+
| [`src/wire.ts`](src/wire.ts) | The minimal app-server JSON-RPC wire implementation |
|
|
111
|
+
| [`cordis.patch.yml`](cordis.patch.yml) | The Profile patch layer that registers the dormant provider |
|
|
112
|
+
|
|
113
|
+
### Run flow
|
|
114
|
+
|
|
115
|
+
A start accepts only a non-empty sequence of text blocks and derives the child cwd from the parent session. It spawns the fixed command through the subprocess seam, performs the `initialize` → `initialized` handshake, maps the Profile-selected mode and optional model into official `thread/start` fields beside `{ cwd, ephemeral: true }`, and publishes the run only after Codex returns a valid ephemeral thread. The published result starts exactly one turn, accepts only notifications for that run's thread and turn, and waits for the authoritative `turn/completed` terminal. The latest `agentMessage` with `phase: "final_answer"` wins; when Codex emits no explicit final phase, the latest message with `phase: null` is the compatibility fallback. A successful turn with no nonblank answer settles as an error. Failed turns use the coarse categories `limit`, `access-policy`, `service`, `transport`, `product-error`, `invalid-result`, or `unknown`; an early app-server exit uses `process`, and applicable connection and stream failures retain a numeric `httpStatusCode`.
|
|
116
|
+
|
|
117
|
+
</details>
|
|
118
|
+
|
|
119
|
+
-----
|
|
120
|
+
|
|
121
|
+
<a id="further-exploration"></a>
|
|
122
|
+
## Further Exploration
|
|
123
|
+
|
|
124
|
+
Read these pages when the package-level contract is not enough. They move from this provider to the seam it plugs into and the sibling product provider.
|
|
125
|
+
|
|
126
|
+
- [Subagent subsystem](../../../docs/subsystems/subagent.md) — the service contract, provider contract, and terminal result semantics.
|
|
127
|
+
- [dsh-subagent seam](../subagent/README.md) — the registry and start API this provider registers on.
|
|
128
|
+
- [Claude Code subagent provider](../subagent-claude-code/README.md) — the sibling product backend over the official Agent SDK.
|
|
129
|
+
- [Claude Code and Codex backends](../../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.md) — the design record for the product providers.
|
|
130
|
+
- [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-subagent-codex) — every accepted config field and its source declaration.
|
|
131
|
+
|
|
132
|
+
-----
|
|
133
|
+
|
|
134
|
+
<a id="model-experience"></a>
|
|
103
135
|
## Model Experience
|
|
104
136
|
|
|
105
137
|
### Child request
|
|
106
138
|
|
|
107
139
|
#### What the model sees
|
|
108
140
|
|
|
109
|
-
The Codex child receives the standalone text blocks as one turn in a fresh ephemeral thread. Its workspace is the parent Session cwd;
|
|
141
|
+
The Codex child receives the standalone text blocks as one turn in a fresh ephemeral thread. Its workspace is the parent Session cwd; the selected Provider instance fixes any configured model, environment, non-interactive approval policy, and sandbox mode, while an omitted model and every other product setting come from native Codex configuration. The executable version comes from the Bundle's pinned platform payload.
|
|
110
142
|
|
|
111
143
|
#### Token effect
|
|
112
144
|
|
|
@@ -120,7 +152,7 @@ Independent of the parent request cache. Reuse depends only on Codex's own provi
|
|
|
120
152
|
|
|
121
153
|
#### What the model sees
|
|
122
154
|
|
|
123
|
-
Through `dsh-tool-subagent`, a foreground call gives the parent the selected final Codex answer or an error containing the stop reason and optional safe diagnostic for a non-completed result. The diagnostic can distinguish
|
|
155
|
+
Through `dsh-tool-subagent`, a foreground call gives the parent the selected final Codex answer or an error containing the stop reason and optional safe diagnostic for a non-completed result. The diagnostic can distinguish a coarse action category, protocol stage, applicable numeric HTTP status, and observed process outcome without copying product prose or stderr. A background call first returns a Job id; the generic job controls later deliver a completion notice, expose the same final answer or failed status detail through `job_output`, and let `job_kill` request cancellation. Codex commentary, reasoning, tool activity, raw stderr, workspace diffs, usage, product ids, commands, paths, and protocol payloads are not copied into the parent Session.
|
|
124
156
|
|
|
125
157
|
#### Token effect
|
|
126
158
|
|
|
@@ -132,12 +164,30 @@ Append-only: foreground adds one result after the reusable parent prefix, while
|
|
|
132
164
|
|
|
133
165
|
## Known Limitations and Deferred Work
|
|
134
166
|
|
|
167
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
These limits define when this provider is a poor fit or needs special operational care. They are current package constraints, not a general Codex comparison or a task backlog.
|
|
171
|
+
|
|
135
172
|
- **One fresh process, thread, and turn per run** — there is no continuation, resume, pooling, progress stream, or product-session persistence.
|
|
136
|
-
- **Static instance selection** — Profile rows fix provider names and tool bindings; calls cannot choose a provider dynamically, and every exposed tool needs a unique `toolName`.
|
|
173
|
+
- **Static instance selection** — Profile rows fix provider names, optional models, and tool bindings; calls cannot choose or change either a provider or model dynamically, and every exposed tool needs a unique `toolName`.
|
|
137
174
|
- **Authentication and account state remain native** — the Bundle supplies the CLI but does not create an account, log in, trust a project, or rewrite Codex settings; configuration and authentication failures surface with their lifecycle stage and the safe `unknown` fallback rather than a separate public taxonomy.
|
|
138
175
|
- **The native platform payload is required at delegation time** — installs that omit optional dependencies, unsupported platforms, and missing or damaged payloads fail at the first run; there is no host-CLI fallback.
|
|
139
|
-
- **Compatibility is pinned by development evidence** — upgrading from the verified 0.
|
|
176
|
+
- **Compatibility is pinned by development evidence** — upgrading from the verified 0.149.1 protocol baseline requires regenerating upstream schema evidence and rerunning handshake, answer-selection, approval, cancellation, keyless real-product, and credentialed DeepSeek nonce tests.
|
|
140
177
|
- **No human approval path** — known unattended approval requests are denied and unknown server requests fail closed; the three Profile modes never create a DSH interaction channel or per-call allow policy.
|
|
141
178
|
- **Assistant payload is final text only** — a failed run may additionally expose the separate safe diagnostic; reasoning, commentary, intermediate messages, tool traffic, usage, raw stderr, and workspace diffs remain outside the parent Session, while generic Job ids, notices, and status come from the shared job runtime.
|
|
142
|
-
- **No optional shared capabilities** — output schemas, child personas, tool filtering, and harness depth enforcement are rejected by the shared service for this provider.
|
|
179
|
+
- **No optional shared capabilities** — `agentOptions`, output schemas, child personas, tool filtering, and harness depth enforcement are rejected by the shared service for this provider.
|
|
143
180
|
- **No wall-clock timeout or side-effect rollback** — the caller cancels long work, and files or external systems changed before cancellation are not restored.
|
|
181
|
+
|
|
182
|
+
<a id="dev-note"></a>
|
|
183
|
+
### Dev Note
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
187
|
+
|
|
188
|
+
This Dev Note is working context for maintainers: open questions and undecided directions. It is explicitly non-authoritative — shipped behavior and limits live in the sections above and in the package code.
|
|
189
|
+
|
|
190
|
+
- **Payload size disclosure** — the current darwin-arm64 platform payload packs to about 114 MB and unpacks to about 282 MB; these are disclosure numbers, not installation thresholds.
|
|
191
|
+
- **Version-pinned protocol** — the runtime dependency is pinned to `@openai/codex@0.149.1`; upgrading requires regenerating the upstream schema evidence and rerunning the credentialed nonce tests.
|
|
192
|
+
|
|
193
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,43 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向用户与维护者的一次性 Codex subagent 提供方,用于选择产品后端、安装 Profile bundle 或配置无人值守的 Codex 委派。"
|
|
3
|
+
kind: "package-bundle"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-subagent-codex
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## 启动与所有权
|
|
10
|
+
## 概述
|
|
8
11
|
|
|
9
|
-
`
|
|
12
|
+
`dsh-subagent-codex` 注册由 Profile 命名、默认名称为 `codex` 的 Codex subagent 提供方,它在发起委派的会话工作区中通过官方 app-server 协议运行真实的 Codex 子 agent(智能体)。每次接受的运行以 `app-server --stdio` 启动包内 Codex wrapper,创建一个临时 Codex 线程,提交一个自包含文本任务,并通过共享的 subagent 结果约定返回选定的最终答案——或独立的安全失败诊断。该提供方作为可选的 Profile Bundle 发布:安装会带入官方 wrapper 与一个兼容的原生平台载荷,而注册的提供方在绑定工具调用前保持休眠。原生 Codex 配置与身份验证继续是权威来源,Profile 选择的 `permissionMode` 会映射进线程的 approval、reviewer 与 sandbox 字段。当子 agent 应该是与父 harness 完全隔离的真实 Codex 会话时,选择它。
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
## 目录
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
14
22
|
|
|
15
|
-
|
|
23
|
+
-----
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
当委派应以父级工作区中的真实 Codex 会话运行时,挂载本提供方。常用路径是显式的:把 Bundle 安装进 Profile,可选地配置提供方行,并通过委派工具行把它暴露给模型。
|
|
20
29
|
|
|
21
|
-
|
|
30
|
+
### 安装 Bundle
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| 配置键 | 默认值 | 含义 |
|
|
26
|
-
|---|---|---|
|
|
27
|
-
| `providerName` | `codex` | `ctx.subagents` 中的非空注册名称;每个已挂载实例都需要唯一值。 |
|
|
28
|
-
| `env` | `{}` | 显式指定的子进程环境,叠加在由子进程 seam 清除凭证后的父环境之上。 |
|
|
29
|
-
| `permissionMode` | `never` | 为该提供方实例的每个线程固定原生非交互审批与沙箱模式。 |
|
|
30
|
-
| `disposeGraceMs` | `3000` | 共享进程树责任方各终止层级之间的宽限期,单位为毫秒且须为正有限值,并不得大于仓库共享的 [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.zh.md);随后资源释放会等待整棵进程树退出。 |
|
|
31
|
-
|
|
32
|
-
| `permissionMode` 值 | `thread/start` 字段 | 原生行为 |
|
|
33
|
-
|---|---|---|
|
|
34
|
-
| `never` | `approvalPolicy: never`;省略 sandbox | 永不请求审批;执行失败会在原生 sandbox 下返回模型。 |
|
|
35
|
-
| `approve-for-me` | `approvalPolicy: on-request`、`approvalsReviewer: auto_review`、`sandbox: workspace-write` | 由 Codex 自动评审权限请求,不等待人工。 |
|
|
36
|
-
| `dangerously-bypass-approvals-and-sandbox` | `approvalPolicy: never`、`sandbox: danger-full-access` | 跳过审批与 sandbox;必须显式选择该值。 |
|
|
37
|
-
|
|
38
|
-
生产环境会解析锁定的 `@openai/codex@0.147.0` 依赖所声明的 `codex` bin,并使用当前 Node 可执行文件启动该 JavaScript wrapper。Wrapper 会选择匹配的原生平台载荷;提供方既不检查也不回退 `PATH` 中的宿主 `codex`。父会话 cwd、`HOME` 与 `CODEX_HOME` 继续让原生 Codex 配置和身份验证保持权威,而提供方只覆盖选定线程的 approval/reviewer/sandbox 字段。其他项目、模型、provider、MCP、hook、skill 与账户设置仍由原生机制负责。本插件不选择模型、不创建 `CODEX_HOME`、不执行登录,也不探测账户。子进程 seam 会先移除具有凭证特征的环境变量,再应用显式 `env` 覆盖。
|
|
39
|
-
|
|
40
|
-
本包是可选的 Profile Bundle。将它安装进目标 Profile 后重启该 Profile;安装会把官方 wrapper 与一个兼容的原生平台载荷带入该 Profile,而包所声明的 `cordis.patch.yml` 层只注册休眠的 `codex` Host provider,不会启动 Codex 进程。移除该包后,下一次 Profile 启动会撤回这一 provider 及其私有运行时闭包。
|
|
32
|
+
把包安装进目标 Profile,然后重启该 Profile。安装会把官方 wrapper 与一个兼容的原生平台载荷带入 Profile;声明的 patch 层只注册休眠的提供方,不启动任何 Codex 进程。
|
|
41
33
|
|
|
42
34
|
```sh
|
|
43
35
|
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
|
|
@@ -45,99 +37,157 @@ dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
|
|
|
45
37
|
dsh --profile <name>
|
|
46
38
|
```
|
|
47
39
|
|
|
48
|
-
|
|
40
|
+
移除包后,下一次 Profile 启动会撤回提供方及其私有运行时闭包。安装决定 Host 可用性,而不是模型权限:模型只能通过你组合的委派工具行触达提供方。
|
|
49
41
|
|
|
50
|
-
|
|
42
|
+
### 配置
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
OPENAI_API_KEY: !!js process.env.OPENAI_API_KEY
|
|
44
|
+
| 字段 | 默认值 | 含义 |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `providerName` | `codex` | `ctx.subagents` 上的非空注册名称;每个已挂载实例都需要唯一值 |
|
|
47
|
+
| `model` | Codex 原生设置 | 为本提供方实例的每个线程固定的可选非空模型名称;省略时不发送 app-server 覆盖 |
|
|
48
|
+
| `env` | `{}` | 叠加在已清理凭据的父环境之上的显式子进程环境 |
|
|
49
|
+
| `permissionMode` | `never` | 为本提供方实例的每个线程固定的原生非交互审批与沙箱模式 |
|
|
50
|
+
| `disposeGraceMs` | `3000` | 共享进程树责任方各终止层级之间的宽限 |
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
| `permissionMode` 值 | `thread/start` 字段 | 原生行为 |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `never` | `approvalPolicy: never`;省略 sandbox | 永不请求审批;执行失败会在原生 sandbox 下返回模型 |
|
|
55
|
+
| `approve-for-me` | `approvalPolicy: on-request`、`approvalsReviewer: auto_review`、`sandbox: workspace-write` | 由 Codex 自动评审权限请求,不等待人工 |
|
|
56
|
+
| `dangerously-bypass-approvals-and-sandbox` | `approvalPolicy: never`、`sandbox: danger-full-access` | 跳过审批与 sandbox;必须显式选择该值 |
|
|
57
|
+
|
|
58
|
+
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-subagent-codex)是每个受支持字段及其 JSDoc 的穷尽式真源。已配置的 `model` 会原样传给每个临时 `thread/start`;省略时保留原生模型选择。提供方不会发现模型、改写别名、选择 `modelProvider` 或 `serviceTier`,也不会设置 fallback。具有凭证特征的环境变量会在显式 `env` 覆盖生效前被移除,因此供子进程使用的 API 密钥必须在该配置中显式提供。
|
|
59
|
+
|
|
60
|
+
### 暴露工具
|
|
61
|
+
|
|
62
|
+
每个委派工具行指名一个提供方,并需要独立的 `toolName`,因此模型看到的是静态工具,而不是动态提供方选择器。完整 Agent Preset 携带对应的默认工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_codex`。
|
|
69
63
|
|
|
70
64
|
```yaml
|
|
71
65
|
- id: jobs
|
|
72
66
|
name: '@deepseek-ai/dsh-jobs-local'
|
|
73
|
-
|
|
74
67
|
- id: tool-jobs
|
|
75
68
|
name: '@deepseek-ai/dsh-tool-jobs'
|
|
76
|
-
|
|
77
|
-
- id: tool-subagent-codex-safe
|
|
78
|
-
name: '@deepseek-ai/dsh-tool-subagent'
|
|
79
|
-
disabled: true
|
|
80
|
-
config:
|
|
81
|
-
provider: codex-safe
|
|
82
|
-
toolName: subagent_codex_safe
|
|
83
|
-
backgroundMode: one-shot
|
|
84
|
-
maxDepth: provider-managed
|
|
85
|
-
|
|
86
|
-
- id: tool-subagent-codex-bypass
|
|
69
|
+
- id: tool-subagent-codex
|
|
87
70
|
name: '@deepseek-ai/dsh-tool-subagent'
|
|
88
71
|
config:
|
|
89
|
-
provider: codex
|
|
90
|
-
toolName:
|
|
72
|
+
provider: codex
|
|
73
|
+
toolName: subagent_codex
|
|
91
74
|
backgroundMode: one-shot
|
|
92
75
|
maxDepth: provider-managed
|
|
93
76
|
```
|
|
94
77
|
|
|
95
|
-
|
|
78
|
+
`one-shot` 策略会让省略 `run_in_background` 或传入 `false` 的调用继续在前台等待,而显式传入 `true` 会返回由父 agent 拥有的 Job id,供 `job_output` 或 `job_kill` 使用;base host(基础宿主)与完整 preset 已提供通用作业注册表和控制工具。
|
|
96
79
|
|
|
97
|
-
|
|
80
|
+
### 你会得到什么
|
|
98
81
|
|
|
99
|
-
|
|
82
|
+
前台调用会把选定的最终 Codex 答案交给模型;运行失败时则返回带停止原因与可选安全诊断的错误。后台调用先返回 Job id;随后通用作业控制面会送达完成通知,并通过 `job_output` 公开同一最终答案或失败状态。Codex 的过程说明、推理、工具活动、原始 stderr 与工作区差异绝不会进入父级会话。
|
|
100
83
|
|
|
101
|
-
|
|
84
|
+
### 失败与恢复
|
|
102
85
|
|
|
86
|
+
省略 optional dependencies、当前平台不受支持或所选载荷缺失的安装会让提供方保持休眠,并在第一次委派时于 `initialize` 阶段以安全 `unknown` 类别和任何已观测进程结果失败;不存在宿主 CLI 回退。原始 wrapper 文本只保留在 Host stderr。被取消的运行以 `aborted` 结算。
|
|
87
|
+
|
|
88
|
+
-----
|
|
89
|
+
|
|
90
|
+
<a id="understand-the-implementation"></a>
|
|
91
|
+
## 理解实现
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary>实现细节——点击展开</summary>
|
|
95
|
+
|
|
96
|
+
本节解释提供方如何驱动真实 Codex app-server,以及可观察行为从何而来;完整约定见[使用本包](#use-this-package)。
|
|
97
|
+
|
|
98
|
+
### 设计理念
|
|
99
|
+
|
|
100
|
+
- **每次运行一个全新进程、线程与轮次。** 每次运行都会 spawn 全新 app-server、创建一个临时线程并恰好执行一个轮次;没有续接、恢复或池化。
|
|
101
|
+
- **原生配置是权威。** Codex 配置与身份验证经父级 cwd、`HOME` 与 `CODEX_HOME` 保持原生;提供方只覆盖可选模型以及线程的 approval、reviewer 与 sandbox 字段。
|
|
102
|
+
- **刻意无人值守。** 审批、用户输入与 MCP 请求都会在无人参与的情况下被应答或拒绝;未知服务器请求会使运行失败。
|
|
103
|
+
|
|
104
|
+
### 源码地图
|
|
105
|
+
|
|
106
|
+
| 文件 | 职责 |
|
|
107
|
+
|---|---|
|
|
108
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:config schema、提供方注册 |
|
|
109
|
+
| [`src/run.ts`](src/run.ts) | 运行生命周期、轮次执行、结果选择与诊断 |
|
|
110
|
+
| [`src/wire.ts`](src/wire.ts) | 最小的 app-server JSON-RPC 协议实现 |
|
|
111
|
+
| [`cordis.patch.yml`](cordis.patch.yml) | 注册休眠提供方的 Profile patch 层 |
|
|
112
|
+
|
|
113
|
+
### 运行流程
|
|
114
|
+
|
|
115
|
+
一次启动只接受非空的文本块序列,并根据父会话确定子级 cwd。它经子进程 seam spawn 固定命令,完成 `initialize` → `initialized` 握手,把 Profile 选择的模式与可选模型映射为官方 `thread/start` 字段并与 `{ cwd, ephemeral: true }` 一起发送,且仅在 Codex 返回有效的临时线程后发布运行。已发布的结果恰好启动一个轮次,只接受与此次运行的线程和轮次匹配的通知,并等待权威的 `turn/completed` 终态。以最后一条 `phase: "final_answer"` 的 `agentMessage` 为准;若 Codex 没有发出明确的最终阶段,则以最后一条 `phase: null` 的消息作为兼容性回退。成功完成的轮次若没有非空白答案,结果也会判为错误。失败轮次使用粗粒度类别 `limit`、`access-policy`、`service`、`transport`、`product-error`、`invalid-result` 或 `unknown`;app-server 提前退出使用 `process`,适用的连接与 stream 失败保留数值 `httpStatusCode`。
|
|
116
|
+
|
|
117
|
+
</details>
|
|
118
|
+
|
|
119
|
+
-----
|
|
120
|
+
|
|
121
|
+
<a id="further-exploration"></a>
|
|
122
|
+
## 进一步探索
|
|
123
|
+
|
|
124
|
+
当包级约定不够用时阅读以下页面。它们从本提供方逐步进入它接入的 seam 与兄弟产品提供方。
|
|
125
|
+
|
|
126
|
+
- [Subagent 子系统](../../../docs/subsystems/subagent.zh.md)——服务约定、提供方约定与终态结果语义。
|
|
127
|
+
- [dsh-subagent seam](../subagent/README.zh.md)——本提供方注册于其上的注册表与启动 API。
|
|
128
|
+
- [Claude Code subagent 提供方](../subagent-claude-code/README.zh.md)——经官方 Agent SDK 的兄弟产品后端。
|
|
129
|
+
- [Claude Code 与 Codex 后端](../../../.agents/notes/implemented/feature/2026-08-04-claude-code-and-codex-subagent-backends.zh.md)——产品提供方的设计记录。
|
|
130
|
+
- [生成配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-subagent-codex)——每个受支持配置字段及其源声明。
|
|
131
|
+
|
|
132
|
+
-----
|
|
133
|
+
|
|
134
|
+
<a id="model-experience"></a>
|
|
103
135
|
## 模型体验
|
|
104
136
|
|
|
105
137
|
### 子级请求
|
|
106
138
|
|
|
107
|
-
####
|
|
139
|
+
#### 模型看到什么
|
|
108
140
|
|
|
109
|
-
Codex 子级会在一个全新的临时线程中,以单个轮次接收这些独立文本块。它的工作区是父会话 cwd
|
|
141
|
+
Codex 子级会在一个全新的临时线程中,以单个轮次接收这些独立文本块。它的工作区是父会话 cwd;所选提供方实例会固定已配置的模型、环境、非交互审批策略与沙箱模式,而省略的模型及其余产品设置来自 Codex 原生配置。可执行版本来自 Bundle 锁定的平台载荷。
|
|
110
142
|
|
|
111
|
-
####
|
|
143
|
+
#### Token 影响
|
|
112
144
|
|
|
113
|
-
子级需为独立的 Codex 上下文和轮次承担 token
|
|
145
|
+
子级需为独立的 Codex 上下文和轮次承担 token 成本。子级 token 不会进入父级上下文。
|
|
114
146
|
|
|
115
|
-
####
|
|
147
|
+
#### KV Cache 影响
|
|
116
148
|
|
|
117
|
-
|
|
149
|
+
与父级请求缓存相互独立。能否复用只取决于 Codex 自身的提供方、模型、指令、工具和临时线程请求。
|
|
118
150
|
|
|
119
151
|
### 父级调度与结果(间接)
|
|
120
152
|
|
|
121
|
-
####
|
|
153
|
+
#### 模型看到什么
|
|
122
154
|
|
|
123
|
-
通过 `dsh-tool-subagent`,前台调用会让父级模型看到选定的 Codex
|
|
155
|
+
通过 `dsh-tool-subagent`,前台调用会让父级模型看到选定的 Codex 最终答案;若结果未完成,错误中会包含终止原因和可选的安全诊断。该诊断可以区分粗粒度行动类别、协议阶段、适用的数值 HTTP status 和已观测的进程结果,而不复制产品正文或 stderr。后台调用会先返回 Job id;随后通用作业控制面会送达完成通知,通过 `job_output` 公开同一最终答案或失败状态 detail,并允许 `job_kill` 请求取消。Codex 的过程说明、推理(reasoning)、工具活动、原始 stderr、工作区差异、用量信息、产品标识符、命令、路径和协议载荷均不会复制到父会话。
|
|
124
156
|
|
|
125
|
-
####
|
|
157
|
+
#### Token 影响
|
|
126
158
|
|
|
127
159
|
前台输入会增加工具结果中保留的最终答案或错误内容。后台输入还会包含启动确认、完成通知,以及 `job_output`、`job_kill` 或后续状态结果;子任务 token 仍不会进入父级上下文。本提供方自身不添加父级工具 schema。
|
|
128
160
|
|
|
129
|
-
####
|
|
161
|
+
#### KV Cache 影响
|
|
130
162
|
|
|
131
163
|
仅追加:前台会在可复用的父请求前缀后增加一个结果,后台则会继续追加 Job 启动确认、通知以及后续控制或收集结果。后台调度可能增加一个由通知唤醒的轮次,但这些消息都不会改写更早的前缀。
|
|
132
164
|
|
|
133
|
-
##
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
165
|
+
## 已知限制与延期工作
|
|
166
|
+
|
|
167
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
这些限制说明本提供方何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是通用 Codex 对比或任务积压。
|
|
171
|
+
|
|
172
|
+
- **每次运行均新建一个进程、一个线程和一个轮次**——不支持续接、恢复、池化、进度流或产品会话持久化。
|
|
173
|
+
- **静态选择实例**——Profile 配置项固定提供方名称、可选模型与工具绑定;调用无法动态选择或修改提供方与模型,而且每个公开工具都需要唯一的 `toolName`。
|
|
174
|
+
- **身份验证与账户状态仍由原生机制管理**——Bundle 会提供 CLI,但不会创建账户、登录、信任项目或改写 Codex 设置;配置与身份验证失败会公开其生命周期阶段与安全的 `unknown` 回退,而不会增加单独的公开分类体系。
|
|
175
|
+
- **委派时必须存在原生平台载荷**——省略 optional dependencies 的安装、不受支持的平台以及缺失或损坏的载荷都会在第一次运行时失败;不会回退到宿主 CLI。
|
|
176
|
+
- **兼容性由开发证据锁定**——若要从已验证的 0.149.1 协议基线升级,必须重新生成上游 schema 证据,并重新运行握手、答案选择、审批、取消、无密钥真实产品以及带密钥的 DeepSeek 随机数测试。
|
|
177
|
+
- **没有人工审批路径**——已知的无人值守审批请求会被拒绝,未知服务器请求会以默认拒绝方式使运行失败;三种 Profile 模式都不会创建 DSH 交互通道或逐次调用 allow 策略。
|
|
178
|
+
- **assistant 载荷仅包含最终文本**——失败运行可以额外公开独立的安全诊断;推理、过程说明、中间消息、工具通信、用量信息、原始 stderr 和工作区差异不会进入父会话,通用 Job id、通知与状态来自共享作业运行时。
|
|
179
|
+
- **没有可选的共享能力**——对于本提供方,共享服务会拒绝 `agentOptions`、输出 schema、子任务角色设定、工具筛选和 harness 深度强制约束。
|
|
180
|
+
- **没有按实际经过时间触发的超时或副作用回滚**——长时间运行的工作由调用方取消,且取消前已更改的文件或外部系统不会恢复原状。
|
|
181
|
+
|
|
182
|
+
<a id="dev-note"></a>
|
|
183
|
+
### 开发备注
|
|
184
|
+
|
|
185
|
+
<details>
|
|
186
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
187
|
+
|
|
188
|
+
本开发备注是维护者的工作上下文:开放问题与尚未决定的探索方向。它明确不具权威性——已交付的行为与限制以上文和包代码为准。
|
|
189
|
+
|
|
190
|
+
- **载荷体积披露**——当前 darwin-arm64 平台载荷压缩后约 114 MB、解包后约 282 MB;这些是披露数字,不是安装阈值。
|
|
191
|
+
- **版本锁定的协议**——运行时依赖锁定为 `@openai/codex@0.149.1`;升级需要重新生成上游 schema 证据并重新运行带凭证的随机数测试。
|
|
192
|
+
|
|
193
|
+
</details>
|
package/lib/index.js
CHANGED
|
@@ -5,11 +5,11 @@ import { NO_START_CAPABILITIES, assertPositiveFinite, resolveChildCwd, settleRun
|
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { dirname, resolve } from "node:path";
|
|
8
|
-
import {
|
|
8
|
+
import { brandString } from "@deepseek-ai/dsh-brand";
|
|
9
9
|
import { JsonRpcLineTransport } from "@deepseek-ai/dsh-sdk-protocol";
|
|
10
10
|
//#region lib/types/wire.js
|
|
11
11
|
/**
|
|
12
|
-
* Minimal Codex app-server 0.
|
|
12
|
+
* Minimal Codex app-server 0.149.1 protocol adapter. The shared JSON-RPC
|
|
13
13
|
* transport owns framing and request correlation; this module owns only the
|
|
14
14
|
* product methods, current thread/turn association, unattended approval
|
|
15
15
|
* responses, and terminal-answer selection.
|
|
@@ -28,25 +28,6 @@ const THREAD_PERMISSION_PARAMS = {
|
|
|
28
28
|
sandbox: "danger-full-access"
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
const STDERR_PERMISSION_SIGNATURES = [{
|
|
32
|
-
text: "approval policy is Never; reject command",
|
|
33
|
-
request: "command execution",
|
|
34
|
-
decision: "denied",
|
|
35
|
-
reason: "Codex rejected an escalation because the selected policy never asks for approval"
|
|
36
|
-
}, {
|
|
37
|
-
text: "recorded sandbox violation:",
|
|
38
|
-
request: "sandbox execution",
|
|
39
|
-
decision: "failed",
|
|
40
|
-
reason: "Codex reported a sandbox violation"
|
|
41
|
-
}];
|
|
42
|
-
const STDERR_SIGNATURE_TAIL_CHARS = Math.max(...STDERR_PERMISSION_SIGNATURES.map((signature) => signature.text.length)) - 1;
|
|
43
|
-
function stderrSignatureTail(value) {
|
|
44
|
-
for (let length = Math.min(STDERR_SIGNATURE_TAIL_CHARS, value.length); length > 0; length -= 1) {
|
|
45
|
-
const tail = value.slice(-length);
|
|
46
|
-
if (STDERR_PERMISSION_SIGNATURES.some((signature) => tail.length < signature.text.length && signature.text.startsWith(tail))) return tail;
|
|
47
|
-
}
|
|
48
|
-
return "";
|
|
49
|
-
}
|
|
50
31
|
function object(value, label) {
|
|
51
32
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`subagent-codex: app-server returned invalid ${label}`);
|
|
52
33
|
return value;
|
|
@@ -80,12 +61,12 @@ function objectFailureInfo(value) {
|
|
|
80
61
|
case "responseStreamDisconnected":
|
|
81
62
|
case "responseTooManyFailedAttempts": {
|
|
82
63
|
const httpStatus = numericHttpStatus(fields.httpStatusCode);
|
|
83
|
-
return httpStatus === void 0 ? { category } : {
|
|
84
|
-
category,
|
|
64
|
+
return httpStatus === void 0 ? { category: "transport" } : {
|
|
65
|
+
category: "transport",
|
|
85
66
|
httpStatus
|
|
86
67
|
};
|
|
87
68
|
}
|
|
88
|
-
case "activeTurnNotSteerable": return { category };
|
|
69
|
+
case "activeTurnNotSteerable": return { category: "product-error" };
|
|
89
70
|
default: return { category: "unknown" };
|
|
90
71
|
}
|
|
91
72
|
}
|
|
@@ -95,17 +76,24 @@ function failureInfo(turn) {
|
|
|
95
76
|
if (error === null || typeof error !== "object" || Array.isArray(error)) return { category: "unknown" };
|
|
96
77
|
const info = error.codexErrorInfo;
|
|
97
78
|
if (typeof info === "string") switch (info) {
|
|
98
|
-
case "contextWindowExceeded":
|
|
79
|
+
case "contextWindowExceeded": return {
|
|
80
|
+
category: "limit",
|
|
81
|
+
maxTokens: true
|
|
82
|
+
};
|
|
99
83
|
case "sessionBudgetExceeded":
|
|
100
|
-
case "usageLimitExceeded":
|
|
84
|
+
case "usageLimitExceeded": return { category: "limit" };
|
|
101
85
|
case "serverOverloaded":
|
|
86
|
+
case "internalServerError": return { category: "service" };
|
|
102
87
|
case "cyberPolicy":
|
|
103
|
-
case "
|
|
104
|
-
case "unauthorized":
|
|
88
|
+
case "misalignmentPolicyViolation":
|
|
89
|
+
case "unauthorized": return { category: "access-policy" };
|
|
105
90
|
case "badRequest":
|
|
106
91
|
case "threadRollbackFailed":
|
|
107
|
-
case "
|
|
108
|
-
case "
|
|
92
|
+
case "other": return { category: "product-error" };
|
|
93
|
+
case "sandboxError": return {
|
|
94
|
+
category: "access-policy",
|
|
95
|
+
sandboxFailure: true
|
|
96
|
+
};
|
|
109
97
|
default: return { category: "unknown" };
|
|
110
98
|
}
|
|
111
99
|
return info !== null && typeof info === "object" && !Array.isArray(info) ? objectFailureInfo(info) : { category: "unknown" };
|
|
@@ -148,6 +136,7 @@ async function raceAbort(pending, signal) {
|
|
|
148
136
|
var CodexAppServerWire = class {
|
|
149
137
|
input;
|
|
150
138
|
permissionMode;
|
|
139
|
+
model;
|
|
151
140
|
transport;
|
|
152
141
|
fatal = Promise.withResolvers();
|
|
153
142
|
threadId;
|
|
@@ -162,13 +151,13 @@ var CodexAppServerWire = class {
|
|
|
162
151
|
diagnosticOrder = 0;
|
|
163
152
|
observationOrder = 0;
|
|
164
153
|
pendingDiagnostic;
|
|
165
|
-
stderrTail = "";
|
|
166
154
|
inputEnded = false;
|
|
167
155
|
terminalObserved = false;
|
|
168
156
|
closed = false;
|
|
169
|
-
constructor(input, output, permissionMode) {
|
|
157
|
+
constructor(input, output, permissionMode, model) {
|
|
170
158
|
this.input = input;
|
|
171
159
|
this.permissionMode = permissionMode;
|
|
160
|
+
this.model = model;
|
|
172
161
|
this.transport = new JsonRpcLineTransport(input, output);
|
|
173
162
|
this.fatal.promise.catch(() => {});
|
|
174
163
|
this.transport.onRequest((method, params) => this.handleServerRequest(method, params));
|
|
@@ -222,6 +211,7 @@ var CodexAppServerWire = class {
|
|
|
222
211
|
const thread = object(object(await this.guarded(this.transport.request("thread/start", {
|
|
223
212
|
cwd,
|
|
224
213
|
ephemeral: true,
|
|
214
|
+
...this.model === void 0 ? {} : { model: this.model },
|
|
225
215
|
...THREAD_PERMISSION_PARAMS[this.permissionMode]
|
|
226
216
|
}, signal), signal), "thread/start response").thread, "thread/start thread");
|
|
227
217
|
const id = string(thread.id, "thread/start thread id");
|
|
@@ -279,8 +269,8 @@ var CodexAppServerWire = class {
|
|
|
279
269
|
category: parsed.category,
|
|
280
270
|
httpStatus: parsed.httpStatus
|
|
281
271
|
});
|
|
282
|
-
if (parsed.
|
|
283
|
-
if (parsed.
|
|
272
|
+
if (parsed.sandboxFailure) this.recordDiagnostic("sandbox execution", "failed", "Codex reported a sandbox failure", completed.order);
|
|
273
|
+
if (parsed.maxTokens) return {
|
|
284
274
|
output: this.collectOutput(),
|
|
285
275
|
stopReason: "max-tokens"
|
|
286
276
|
};
|
|
@@ -291,7 +281,7 @@ var CodexAppServerWire = class {
|
|
|
291
281
|
if (output.length === 0) {
|
|
292
282
|
this.recordFailure({
|
|
293
283
|
stage: "turn",
|
|
294
|
-
category: "
|
|
284
|
+
category: "invalid-result"
|
|
295
285
|
});
|
|
296
286
|
throw new Error("subagent-codex: Codex completed without a final answer");
|
|
297
287
|
}
|
|
@@ -337,25 +327,6 @@ var CodexAppServerWire = class {
|
|
|
337
327
|
collectFailure() {
|
|
338
328
|
return this.failure;
|
|
339
329
|
}
|
|
340
|
-
/**
|
|
341
|
-
* Observe product stderr while retaining only enough tail to recognize fixed
|
|
342
|
-
* permission signatures. The raw text is never copied into the diagnostic.
|
|
343
|
-
* @param chunk - one decoded stderr chunk already forwarded to the host.
|
|
344
|
-
*/
|
|
345
|
-
observeStderr(chunk) {
|
|
346
|
-
const observed = `${this.stderrTail}${chunk}`;
|
|
347
|
-
let latestIndex = -1;
|
|
348
|
-
let latest;
|
|
349
|
-
for (const signature of STDERR_PERMISSION_SIGNATURES) {
|
|
350
|
-
const index = observed.lastIndexOf(signature.text);
|
|
351
|
-
if (index > latestIndex) {
|
|
352
|
-
latestIndex = index;
|
|
353
|
-
latest = signature;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (latest !== void 0) this.recordDiagnostic(latest.request, latest.decision, latest.reason);
|
|
357
|
-
this.stderrTail = stderrSignatureTail(observed);
|
|
358
|
-
}
|
|
359
330
|
/** Detach JSON-RPC listeners and reject outstanding requests. Idempotent. */
|
|
360
331
|
close() {
|
|
361
332
|
if (this.closed) return;
|
|
@@ -691,10 +662,9 @@ async function startCodexRun(request, spec) {
|
|
|
691
662
|
category: "unknown"
|
|
692
663
|
}, thrown(error));
|
|
693
664
|
}
|
|
694
|
-
const wire = new CodexAppServerWire(child.stdout, child.stdin, spec.permissionMode);
|
|
665
|
+
const wire = new CodexAppServerWire(child.stdout, child.stdin, spec.permissionMode, spec.model);
|
|
695
666
|
const onStderr = (chunk) => {
|
|
696
667
|
const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
697
|
-
wire.observeStderr(bytes.toString());
|
|
698
668
|
try {
|
|
699
669
|
writeFileSync(process.stderr.fd, bytes);
|
|
700
670
|
} catch {}
|
|
@@ -717,7 +687,7 @@ async function startCodexRun(request, spec) {
|
|
|
717
687
|
const processFailure = child.done.then((outcome) => {
|
|
718
688
|
processFailureFacts = {
|
|
719
689
|
stage: "process",
|
|
720
|
-
category: "process
|
|
690
|
+
category: "process",
|
|
721
691
|
outcome
|
|
722
692
|
};
|
|
723
693
|
throw new CodexRunFailure(processFailureFacts);
|
|
@@ -825,7 +795,7 @@ async function startCodexRun(request, spec) {
|
|
|
825
795
|
onAbort
|
|
826
796
|
});
|
|
827
797
|
return subprocessRunHandle({
|
|
828
|
-
id:
|
|
798
|
+
id: brandString(randomUUID()),
|
|
829
799
|
result,
|
|
830
800
|
signal: request.signal,
|
|
831
801
|
onAbort,
|
|
@@ -847,6 +817,7 @@ const inject = ["subagents", "subprocess"];
|
|
|
847
817
|
const DEFAULT_PROVIDER_NAME = "codex";
|
|
848
818
|
const Config = z.object({
|
|
849
819
|
providerName: z.string().min(1).default(DEFAULT_PROVIDER_NAME),
|
|
820
|
+
model: z.string().min(1),
|
|
850
821
|
env: z.dict(z.string()).default({}),
|
|
851
822
|
permissionMode: z.union([...CODEX_PERMISSION_MODES]).default(DEFAULT_CODEX_PERMISSION_MODE),
|
|
852
823
|
disposeGraceMs: z.number().default(DEFAULT_DISPOSE_GRACE_MS)
|
|
@@ -874,6 +845,7 @@ var CodexProvider = class {
|
|
|
874
845
|
}
|
|
875
846
|
return startCodexRun(request, {
|
|
876
847
|
cwd,
|
|
848
|
+
...this.config.model === void 0 ? {} : { model: this.config.model },
|
|
877
849
|
permissionMode: this.config.permissionMode,
|
|
878
850
|
env: this.config.env,
|
|
879
851
|
disposeGraceMs: this.config.disposeGraceMs,
|
|
@@ -887,11 +859,12 @@ var CodexProvider = class {
|
|
|
887
859
|
/**
|
|
888
860
|
* Register one Profile-named Codex provider.
|
|
889
861
|
* @param ctx - context carrying shared subagent and subprocess services.
|
|
890
|
-
* @param config - registry name, permission mode, child environment, and disposal grace.
|
|
862
|
+
* @param config - registry name, optional model, permission mode, child environment, and disposal grace.
|
|
891
863
|
*/
|
|
892
864
|
function apply(ctx, config) {
|
|
893
865
|
const resolved = {
|
|
894
866
|
providerName: config.providerName ?? DEFAULT_PROVIDER_NAME,
|
|
867
|
+
...config.model === void 0 ? {} : { model: config.model },
|
|
895
868
|
env: config.env,
|
|
896
869
|
permissionMode: config.permissionMode ?? "never",
|
|
897
870
|
disposeGraceMs: config.disposeGraceMs
|
package/lib/types/index.d.ts
CHANGED
|
@@ -10,10 +10,12 @@ import z from '@deepseek-ai/schemastery';
|
|
|
10
10
|
import { type CodexPermissionMode } from './run.ts';
|
|
11
11
|
export declare const name = "subagent-codex";
|
|
12
12
|
export declare const inject: string[];
|
|
13
|
-
/** Deployment-owned permission, environment, and process-release settings. */
|
|
13
|
+
/** Deployment-owned model, permission, environment, and process-release settings. */
|
|
14
14
|
export interface Config {
|
|
15
15
|
/** Provider name on `ctx.subagents` (default `codex`). */
|
|
16
16
|
providerName?: string;
|
|
17
|
+
/** Native Codex model fixed for this instance; omitted to inherit Codex settings. */
|
|
18
|
+
model?: string;
|
|
17
19
|
/**
|
|
18
20
|
* Explicit environment entries layered over the subprocess seam's
|
|
19
21
|
* credential-scrubbed parent environment.
|
|
@@ -28,7 +30,7 @@ export declare const Config: z<Config>;
|
|
|
28
30
|
/**
|
|
29
31
|
* Register one Profile-named Codex provider.
|
|
30
32
|
* @param ctx - context carrying shared subagent and subprocess services.
|
|
31
|
-
* @param config - registry name, permission mode, child environment, and disposal grace.
|
|
33
|
+
* @param config - registry name, optional model, permission mode, child environment, and disposal grace.
|
|
32
34
|
*/
|
|
33
35
|
export declare function apply(ctx: Context, config: Config): void;
|
|
34
36
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/run.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare function codexAppServerArgv(): string[];
|
|
|
33
33
|
export interface CodexRunSpec {
|
|
34
34
|
/** Parent Session workspace, also supplied to `thread/start`. */
|
|
35
35
|
readonly cwd: string;
|
|
36
|
+
/** Profile-selected native model; omitted to preserve Codex settings. */
|
|
37
|
+
readonly model?: string;
|
|
36
38
|
/** Profile-selected native non-interactive permission mode. */
|
|
37
39
|
readonly permissionMode: CodexPermissionMode;
|
|
38
40
|
/** Explicit deployment/test environment layered after the shared scrub. */
|
package/lib/types/wire.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Minimal Codex app-server 0.
|
|
2
|
+
* Minimal Codex app-server 0.149.1 protocol adapter. The shared JSON-RPC
|
|
3
3
|
* transport owns framing and request correlation; this module owns only the
|
|
4
4
|
* product methods, current thread/turn association, unattended approval
|
|
5
5
|
* responses, and terminal-answer selection.
|
|
@@ -13,7 +13,7 @@ import type { CodexPermissionMode } from './run.ts';
|
|
|
13
13
|
/** Product facts owned by the Codex wire after publication. */
|
|
14
14
|
export interface CodexWireFailureFacts {
|
|
15
15
|
readonly stage: 'turn-start' | 'turn';
|
|
16
|
-
readonly category:
|
|
16
|
+
readonly category: 'limit' | 'access-policy' | 'service' | 'transport' | 'product-error' | 'invalid-result' | 'unknown';
|
|
17
17
|
readonly httpStatus?: number | undefined;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
@@ -25,6 +25,7 @@ export interface CodexWireFailureFacts {
|
|
|
25
25
|
export declare class CodexAppServerWire {
|
|
26
26
|
private readonly input;
|
|
27
27
|
private readonly permissionMode;
|
|
28
|
+
private readonly model?;
|
|
28
29
|
private readonly transport;
|
|
29
30
|
private readonly fatal;
|
|
30
31
|
private threadId;
|
|
@@ -39,11 +40,10 @@ export declare class CodexAppServerWire {
|
|
|
39
40
|
private diagnosticOrder;
|
|
40
41
|
private observationOrder;
|
|
41
42
|
private pendingDiagnostic;
|
|
42
|
-
private stderrTail;
|
|
43
43
|
private inputEnded;
|
|
44
44
|
private terminalObserved;
|
|
45
45
|
private closed;
|
|
46
|
-
constructor(input: Readable, output: Writable, permissionMode: CodexPermissionMode);
|
|
46
|
+
constructor(input: Readable, output: Writable, permissionMode: CodexPermissionMode, model?: string | undefined);
|
|
47
47
|
/** Start reading app-server frames. */
|
|
48
48
|
start(): void;
|
|
49
49
|
/**
|
|
@@ -91,12 +91,6 @@ export declare class CodexAppServerWire {
|
|
|
91
91
|
* @returns the fixed stage/category pair and optional HTTP status.
|
|
92
92
|
*/
|
|
93
93
|
collectFailure(): CodexWireFailureFacts;
|
|
94
|
-
/**
|
|
95
|
-
* Observe product stderr while retaining only enough tail to recognize fixed
|
|
96
|
-
* permission signatures. The raw text is never copied into the diagnostic.
|
|
97
|
-
* @param chunk - one decoded stderr chunk already forwarded to the host.
|
|
98
|
-
*/
|
|
99
|
-
observeStderr(chunk: string): void;
|
|
100
94
|
/** Detach JSON-RPC listeners and reject outstanding requests. Idempotent. */
|
|
101
95
|
close(): void;
|
|
102
96
|
private guarded;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-subagent-codex",
|
|
3
3
|
"description": "One-shot Codex subagent provider over the official app-server protocol",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -38,30 +38,38 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@deepseek-ai/
|
|
42
|
-
"@deepseek-ai/dsh-
|
|
43
|
-
"@deepseek-ai/dsh-
|
|
44
|
-
"@deepseek-ai/dsh-
|
|
45
|
-
"@deepseek-ai/
|
|
46
|
-
"@deepseek-ai/dsh-
|
|
47
|
-
"@deepseek-ai/dsh-
|
|
41
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
42
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
43
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
|
|
44
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
45
|
+
"@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.2"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@openai/codex": "0.
|
|
51
|
-
"@deepseek-ai/dsh-sdk-protocol": "^0.1.
|
|
52
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
50
|
+
"@openai/codex": "0.149.1",
|
|
51
|
+
"@deepseek-ai/dsh-sdk-protocol": "^0.1.2-alpha.2",
|
|
52
|
+
"@deepseek-ai/schemastery": "^3.18.2",
|
|
53
|
+
"@deepseek-ai/dsh-brand": "^0.1.2-alpha.2"
|
|
53
54
|
},
|
|
54
55
|
"devDependencies": {
|
|
55
|
-
"@deepseek-ai/cordis
|
|
56
|
-
"@deepseek-ai/
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-
|
|
63
|
-
"@deepseek-ai/dsh-
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/
|
|
56
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
57
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
58
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-agent-spine-demo": "^0.1.2-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-app-boot": "^0.1.2-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-llm": "^0.1.2-alpha.2",
|
|
63
|
+
"@deepseek-ai/dsh-loader-smoke": "^0.1.2-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2",
|
|
66
|
+
"@deepseek-ai/dsh-subagent": "^0.1.2-alpha.2",
|
|
67
|
+
"@deepseek-ai/dsh-subprocess": "^0.1.2-alpha.2",
|
|
68
|
+
"@deepseek-ai/dsh-subprocess-local": "^0.1.2-alpha.2",
|
|
69
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.2-alpha.2",
|
|
70
|
+
"@deepseek-ai/dsh-timeout": "^0.1.2-alpha.2",
|
|
71
|
+
"@deepseek-ai/dsh-session-checkpoint-policy": "^0.1.2-alpha.2",
|
|
72
|
+
"@deepseek-ai/dsh-tool-subagent": "^0.1.2-alpha.2",
|
|
73
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-alpha.2"
|
|
66
74
|
}
|
|
67
75
|
}
|