Upstream PR material: subagent worktree automatic isolation

August 14, 2026 · View on GitHub

dsh-worktree ships everything needed for manual and programmatic isolation. The one-liner model experience — subagent(prompt: ..., worktree: true) creating an isolated worktree per child run and cleaning it up afterwards — requires a small upstream capability in the DeepSeek Harness core. This file collects the exact changes; they are already implemented and tested in-tree (456 tests, 33 worktree/subagent-specific) and can be lifted into a PR as-is.

The changes

1. packages/subagent/subagent/src/types.ts — optional cwd on the start request

Add to SubagentStartRequest (after persona):

/**
 * Optional absolute working-directory override for the child session.
 * In-process backends stamp it as the child's durable `header.cwd` instead of
 * inheriting the parent's; out-of-process backends ignore it (see the
 * `dsh-worktree` Known Limitations). A relative value rejects the start.
 */
readonly cwd?: string

2. packages/subagent/subagent/src/child-agent.ts — cwd override in session meta

childSessionMeta(parent, childDepth, lineageSeedLength, cwdOverride?):

export function childSessionMeta(
  parent: Agent,
  childDepth: number,
  lineageSeedLength: number,
  cwdOverride?: string,
): NonNullable<CreateAgentOptions['meta']> {
  const parentHeader = parent.session.header
  const agentPreset = parent.ctx.get('agentPresets')?.composedPreset(parent.ctx)
  if (cwdOverride !== undefined && !isAbsolute(cwdOverride)) {
    throw new Error(`subagent cwd override must be an absolute path, got "${cwdOverride}"`)
  }
  const cwd = cwdOverride ?? parentHeader.cwd
  return {
    ...cwd !== undefined ? { cwd } : {},
    ...
  }
}

(import { isAbsolute } from 'node:path' added.)

3. packages/subagent/subagent-in-process-driver/src/index.ts — pass the override

meta: childSessionMeta(parent, childDepth, activationBoundary, request.cwd),

4. packages/subagent/tool-subagent/src/index.ts — the model-facing parameter

  • New optional parameter worktree: { oneOf: [{ type: 'boolean' }, { type: 'string' }] } (true → auto slug agent-<7 hex>; string → that slug).
  • In execute, before building the request: resolve ctx.get('worktree') (optional service), reject the combination with backgroundMode: continuable, acquire the lease from the parent's session.header.cwd, and stamp request.cwd = lease.entry.path.
  • Prefix the child prompt with a <worktree-context> notice (worktree path + branch, path-translation and re-read instructions).
  • After settlement: lease.release() — a clean worktree is removed; a dirty one is kept and its path/branch appended to the tool result text ([Worktree kept at <path>, branch <branch>]). Background one-shot runs release inside the job producer's completion path.

Suggested PR structure

  1. PR 1 (core capability): changes 1–3 + the child-agent-cwd.spec.ts tests.
  2. PR 2 (consumer): change 4 + the tool-subagent/tests/worktree.spec.ts tests, plus the dsh-worktree plugin repo linked from the description.

Both PRs keep the default behavior byte-identical: without cwd/worktree, child composition is unchanged (the regression suites prove it).