Merge pull request #2279 from deepseek-harness/worktree/preset-user-root-in-package

agent-presets owns the writable preset root instead of awaiting an app
This commit is contained in:
Yichen Jiang
2026-08-12 11:53:55 +08:00
committed by GitHub
28 changed files with 387 additions and 58 deletions
@@ -25,13 +25,13 @@ Two planes, and the choice is not about how "agent-related" something feels —
A preset is a directory holding one `agent.cordis.yml`, optionally beside a `preset.yml` carrying display metadata — `name` and `description` (and, for shipped presets, a roster `order`). Write the metadata too: a preset without it shows up in every picker as its bare directory name.
Locally authored presets live one directory per preset under `${DSH_HOME:-$HOME/.dsh}/.agent-presets/`, and the shipped set sits beside the deployment's own config. Use those when the user asks where to look. Both roots are configuration rather than fixed locations, though, and no call reports them — `authorable` says only whether a writable one exists — so take the path you actually read or edit from `list()` or `resolve()`, which is also where `copy()` reports what it just created.
Locally authored presets live one directory per preset under `${DSH_HOME:-$HOME/.dsh}/.agent-presets/`, and the shipped set sits beside the deployment's own config. Use those when the user asks where to look. A deployment can configure other roots, so the path you read or edit comes from `list()` or `resolve()` which is also where `copy()` reports what it just created.
## The roster service
`ctx.agentPresets` owns discovery, authoring, and mounting. You reach it by mounting a temporary plugin that injects it and registers a tool for yourself — `cordis_mount` returns only the mount acknowledgement, so a registered tool is how a service answer gets back to you, and it becomes callable on your next step.
Read `cordis_inspect what:"api" name:"agentPresets"` for the current signatures before writing the code. The four calls this skill relies on:
Read `cordis_inspect what:"api" name:"agentPresets"` for the current signatures before writing the code. What this skill relies on:
- `list()` — every preset with its `id`, `trust` (`system` for the shipped set, `user` for authored ones), and the absolute `path` of its composition file. This is how you locate any composition without knowing the install layout; the directory is that path's parent.
- `read(id)` — one preset's composition text, without a file tool or a path.
+6 -8
View File
@@ -29,13 +29,11 @@ import {
watchUserPatches,
type Profile,
} from '@deepseek-ai/dsh-app-boot'
import { dshHomePath, resolveDshHome } from '@deepseek-ai/dsh-paths'
import { resolveDshHome } from '@deepseek-ai/dsh-paths'
/** Shipped agent-preset root: beside this app's own config, in both source and built layouts. */
const SHIPPED_PRESET_ROOT = fileURLToPath(new URL('../config/agent-presets/', import.meta.url))
/** Harness-home directory holding locally authored agent presets. */
const USER_PRESET_DIR = '.agent-presets'
import { DSH_ENVIRONMENT_KEY, type EnvironmentSnapshot } from '@deepseek-ai/dsh-environment'
import { provideCmdline } from '@deepseek-ai/dsh-cmdline'
import { createProcessShutdown, type ProcessShutdown } from './process-shutdown.ts'
@@ -154,16 +152,16 @@ function composeProfile(
if (typeof row.id === 'string') rows.set(row.id, row)
}
const composedOverlays = [...overlays]
// Preset roots belong to every dsh composition that mounts the roster.
// The SHIPPED root is the part of the roster only this app can resolve: it
// sits beside this app's own config, in both the source and built layouts.
// The writable root the roster appends is `dsh-agent-presets`' own, so a
// launcher that never reaches this patch still finds a person's presets.
if (rows.has('agent-presets')) {
composedOverlays.push({
id: 'agent-presets',
config: {
...(rows.get('agent-presets')?.config ?? {}) as Record<string, unknown>,
roots: [
{ path: SHIPPED_PRESET_ROOT, trust: 'system' },
{ path: dshHomePath(USER_PRESET_DIR), trust: 'user' },
],
roots: [{ path: SHIPPED_PRESET_ROOT, trust: 'system' }],
},
})
}
+67 -1
View File
@@ -96,7 +96,11 @@ async function bootWeb(settingsFile: string, extra: PatchOptions[] = []): Promis
// document overrides.
{
id: 'agent-presets',
config: { default: 'standard', roots: [{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' }] },
config: {
default: 'standard',
roots: [{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' }],
includeUserRoot: false,
},
},
...extra,
]
@@ -442,6 +446,7 @@ describe('product subagent rows in user presets', () => {
{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' },
{ path: userRoot, trust: 'user' },
],
includeUserRoot: false,
},
}])
}, 120_000)
@@ -624,6 +629,66 @@ describe('a delegated child', () => {
})
})
describe('a launcher that configures no writable root', () => {
// The claim this default exists for, asserted through the real shipped
// bundles rather than a hand-built context: `apps/cli` patches in only the
// system root, and a person's own presets are found anyway because the
// roster derives `<dshHome>/.agent-presets` itself. `$DSH_HOME` is pointed
// at a temp home BEFORE boot — the derived root is resolved when the plugin
// is constructed, and an unpinned run would read the developer's own.
let derivedCtx: Context
let previousHome: string | undefined
beforeAll(async () => {
const home = await mkdtemp(join(tmpdir(), 'dsh-preset-derived-'))
previousHome = process.env.DSH_HOME
process.env.DSH_HOME = home
await mkdir(join(home, '.agent-presets', 'derived-mine'), { recursive: true })
await writeFile(
join(home, '.agent-presets', 'derived-mine', 'agent.cordis.yml'),
'- id: tool-todo\n name: \'@deepseek-ai/dsh-tool-todo\'\n config:\n allowParallelInProgress: true\n',
)
const settingsFile = join(await mkdtemp(join(tmpdir(), 'dsh-preset-derived-settings-')), 'settings.yaml')
await writeFile(settingsFile, '{}\n')
// Only the shipped root, exactly what `composeProfile` supplies; the
// writable one is the roster's own default rather than this patch's job.
derivedCtx = await bootWeb(settingsFile, [{
id: 'agent-presets',
config: {
default: 'standard',
roots: [{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' }],
includeUserRoot: true,
},
}])
}, 120_000)
afterAll(async () => {
if (previousHome === undefined) delete process.env.DSH_HOME
else process.env.DSH_HOME = previousHome
await derivedCtx.fiber.dispose()
})
it('discovers and mounts a preset the person authored under the harness home', async () => {
const listed = await derivedCtx.agentPresets.list()
const mine = listed.find(preset => preset.id === 'derived-mine')
expect(mine).toMatchObject({ trust: 'user' })
// Omitted rather than undefined: a healthy row carries no `broken` key.
expect(mine?.broken).toBeUndefined()
expect(derivedCtx.agentPresets.authorable).toBe(true)
const handle = await derivedCtx.agents.create({
sessionId: SessionId('preset-derived-root'),
setup: agentCtx => derivedCtx.agentPresets.mount(agentCtx, 'derived-mine').then(() => undefined),
})
try {
expect(toolNames(derivedCtx, handle.agent)).toContain('todo_write')
} finally {
await handle.dispose()
}
})
})
describe('authoring a preset on the shipped composition', () => {
let authorCtx: Context
let userRoot: string
@@ -642,6 +707,7 @@ describe('authoring a preset on the shipped composition', () => {
// nothing is the normal first-run state.
{ path: userRoot, trust: 'user' },
],
includeUserRoot: false,
},
}])
})