feat(persona): make the agent persona a composable row

`dsh-system-prompt` owns the deployment persona as its own config and registers
that section unconditionally, so a process has exactly one. An agent preset
cannot mount the prompt registry itself, which means that without a row of its
own a preset could change an agent's tools but never its identity — and a
roster of presets that all sound the same is not worth having.

The row is scope-only by construction: mounted outside an agent scope it
collides with the registry's own `deployment:persona` registration and fails
loud. That is the correct shape rather than a gap, because the unscoped slot
already has an owner and this row exists to shadow it for one agent.

Two behaviours are pinned by test because both read the other way at a glance:
an empty persona still occupies the slot (shadowing the deployment persona away
entirely, then disappearing at render), and `assemble()` keeps section text
uninterpolated — `renderPrompt()` is the stage that resolves `{{…}}`.
This commit is contained in:
Yichen Jiang
2026-08-03 22:22:57 +08:00
parent e27d38efd6
commit 739042a804
14 changed files with 371 additions and 2 deletions

View File

@@ -1022,6 +1022,24 @@ Depends on: [`ApprovalPolicy`](core-data-structures/approval.md) · [`SandboxMod
Source: [`packages/ui/permission/src/index.ts:140`](../packages/ui/permission/src/index.ts)
## `@deepseek-ai/dsh-persona`
Requires: `systemPrompt`
```ts config-catalog
/** Plugin config: the persona text this composition contributes. */
export interface Config {
/**
* Persona prose rendered as the `deployment:persona` section. A template:
* complete `{{…}}` groups interpolate strictly against registered prompt
* variables. Empty text drops the section at render, matching the registry.
*/
text: string
}
```
Source: [`packages/preset/persona/src/index.ts:33`](../packages/preset/persona/src/index.ts)
## `@deepseek-ai/dsh-plan-mode`
Requires: `tools` · `systemPrompt`