Merge pull request #2374 from deepseek-harness/codex/product-subagent-one-shot-background

feat(agent-presets): enable background Codex and Claude Code subagent tasks
This commit is contained in:
pku-xht
2026-08-14 15:49:58 +08:00
committed by GitHub
39 changed files with 403 additions and 139 deletions

View File

@@ -198,16 +198,16 @@
toolName: subagent_fork
backgroundMode: continuable
# Product providers are host-plane singletons. Copy this preset, then
# remove `disabled` from either ordinary tool row to expose that product
# only to agents composed from the copy.
# Production dsh does not install these optional providers. An opting-in
# Profile mounts each provider once on the host plane; copy this preset,
# then remove `disabled` from the matching tool row.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -216,7 +216,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: workflow-worker-thread

View File

@@ -185,16 +185,16 @@
toolName: subagent_fork
backgroundMode: continuable
# Product providers are host-plane singletons. Copy this preset, then
# remove `disabled` from either ordinary tool row to expose that product
# only to agents composed from the copy.
# Production dsh does not install these optional providers. An opting-in
# Profile mounts each provider once on the host plane; copy this preset,
# then remove `disabled` from the matching tool row.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -203,7 +203,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: workflow-worker-thread

View File

@@ -123,7 +123,7 @@ After a clean mount-validation, ask the user to start a session on the new prese
## Native product subagents
Codex and Claude Code providers already live in the host composition. A preset chooses either product by contributing the same ordinary delegation-tool row used for spawn and fork; never move a product provider into the preset and never add a product-specific settings field.
Codex and Claude Code providers belong on the host plane but are not installed by production `dsh`. The active Profile must install and mount the selected provider before a preset can expose its ordinary delegation-tool row; never move a product provider into the preset and never add a product-specific settings field.
Copy these disabled templates from a shipped full preset and remove `disabled` only for the products the user requested:
@@ -134,7 +134,7 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -143,11 +143,11 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
```
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. The host must provide `codex` or `claude` on `PATH`; the preset does not install, authenticate, select a model for, or probe either product.
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. Production `dsh` does not install or mount either optional provider: before enabling a row, the Profile must install the matching `@deepseek-ai/dsh-subagent-codex` or `@deepseek-ai/dsh-subagent-claude-code` package and mount it once on the host plane. A preset cannot provide that host dependency. `backgroundMode: one-shot` keeps omitted or `false` calls in the foreground and lets explicit `run_in_background: true` return a generic Job id. Full presets already carry `tool-jobs`, while the base host carries the job registry; retain both so `job_output`, `job_list`, `job_kill`, cancellation, and completion notices stay available. The host must also provide `codex` or `claude` on `PATH`; the preset does not install, authenticate, select a model for, or probe either product.
## What not to move into a preset

View File

@@ -197,16 +197,16 @@
toolName: subagent_fork
backgroundMode: continuable
# Product providers are host-plane singletons. Copy this preset, then
# remove `disabled` from either ordinary tool row to expose that product
# only to agents composed from the copy.
# Production dsh does not install these optional providers. An opting-in
# Profile mounts each provider once on the host plane; copy this preset,
# then remove `disabled` from the matching tool row.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
config:
provider: codex
toolName: subagent_codex
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: tool-subagent-claude-code
@@ -215,7 +215,7 @@
config:
provider: claude-code
toolName: subagent_claude_code
enableRunInBackground: false
backgroundMode: one-shot
maxDepth: provider-managed
- id: workflow-worker-thread

View File

@@ -128,6 +128,16 @@ async function bootWeb(
const toolNames = (ctx: Context, agent?: Agent): string[] =>
ctx.tools.schemas(agent).map(schema => schema.name).sort()
function toolParameterNames(ctx: Context, agent: Agent, toolName: string): string[] {
const schema = ctx.tools.schemas(agent).find(tool => tool.name === toolName)
if (schema === undefined) throw new Error(`missing tool schema ${toolName}`)
const properties = schema.parameters.properties
if (typeof properties !== 'object' || properties === null || Array.isArray(properties)) {
throw new Error(`${toolName} has invalid parameter properties`)
}
return Object.keys(properties).sort()
}
function enablePresetTool(composition: string, id: string): string {
const row = ` - id: ${id}\n`
const start = composition.indexOf(row)
@@ -489,6 +499,12 @@ describe('product subagent rows in user presets', () => {
const tools = toolNames(productCtx, handle.agent)
expect(tools.filter(name => name === 'subagent_codex' || name === 'subagent_claude_code'))
.toEqual(productTools)
expect(tools).toEqual(expect.arrayContaining(['job_kill', 'job_list', 'job_output']))
for (const productTool of productTools) {
expect(toolParameterNames(productCtx, handle.agent, productTool)).toEqual([
'description', 'prompt', 'run_in_background',
])
}
} finally {
await handle.dispose()
}