# The `liangshen` (梁神模式) agent preset: the builtin Minimal preset's
# one-line persona — extended with the mode's standing working discipline
# (thinking-disruption fuses, action-oriented steps, YAGNI/PDCA) and the
# session's workspace directory — as the WHOLE system prompt, with one tool
# presentation declared for the session's whole lifetime instead of a staged
# turn boundary.
#
# The presentation is the SDK's ToolPresentationMode, picked by the
# tool-catalog row's `presentation` key (default `'ptc'`): `'native'` keeps
# the assembled roster on the wire, `'ptc'` collapses the wire to the single
# `run_code` transport, and `'both'` keeps the full native roster AND the
# `run_code` transport co-resident — native calls carry ordinary work while
# `run_code` covers programmatic batch computation and wide fan-out. The
# declaration lands once per agent scope through agent.ctx.tools.presentAs as
# early as the scope exists, and degrades to the native surface with a
# one-time warning when the deployment has no code runtime or the host
# declines. The retired anchor-turn staging is gone: the legacy `anchorTools`
# and boolean `ptcPresentation` keys are still accepted (the latter mapping
# true -> 'ptc' and false -> 'native') but only warn once.
#
# Gentle tool paging keeps high-fan-out families out of reach until needed:
# tools matching `pagedToolPatterns` (default `mcp__*`) stay paged out, the
# durable tool-catalog message summarizes their namespaces with an activation
# pointer, and the preset's own `tool_activate` tool (the tool-activate row)
# loads one namespace back — at most three stay active, LRU eviction pages the
# oldest back out. The activation state replays from the durable session event
# stream on every assembly, so resume and compaction rebuild it without process
# memory. Paging is enforced twice, because the wire is not the only surface a
# tool can be reached through: the assembled tool list loses the paged families,
# and the agent scope is restricted (agent.ctx.tools.restrict) so the same names
# vanish from the generated SDK and from every program dispatch. The second half
# is what makes paging real under the default `ptc` presentation, where the wire
# has already collapsed to `run_code` and a wire-only filter would withhold
# nothing the model can observe.
#
# The capability facts the Standard system prompt would carry as tool-guidance
# prose arrive instead as a durable `tool-catalog` message appended after the
# user's own message, the way the skill catalog is injected: it names exactly
# the tools the current request's wire carries (each tool's argument signature
# plus a one-line summary) plus the paged-out namespace summaries, so the
# declared surface and the request's own wire never disagree. A second durable
# message, working-context, projects one minimal line of objective session
# state (plan mode, active namespaces, in-progress todos) folded from the
# session event stream, republished only when it changes.
#
# The persistent shell replaces the Standard ephemeral shell for the whole
# session — `bash` on POSIX, `pwsh` on win32, exactly one per host — and the
# editor inherits the host file sandbox instead of mounting a bare local
# filesystem — see the shell and filesystem sections below.
#
# Adapted from the builtin `minimal` and `standard` presets.
#
# This file is an AGENT-PLANE composition. The roster mounts it ONCE under a
# standing scope; every session naming it joins by scope parentage, so the
# tools and prompt sections registered here cover each joined agent while a
# session's own state stays keyed per Session/Agent inside the plugins. The
# host composition (`base.cordis.yml` + `web.cordis.yml`) keeps everything a
# preset must not own: the registries themselves, the sandbox and approval
# stack, persistence, and the model route.
#
# A service row here MUST sit inside a group carrying an `isolate` realm.
# Without one it publishes into the root realm, where it is process-global —
# another preset publishing the same name collides, and a host reader would
# resolve one preset's instance for every session; `dsh-agent-presets` rejects
# that at mount. `true` means an entry-local realm: this standing mount's own
# private instance, apart from every other preset's. (A shared label does NOT
# pool instances — `provide()` throws on the second registration under the
# same realm symbol; labels join REALMS, and are not what this file needs.)

# ── identity ────────────────────────────────────────────────────────────────

# The persona prefix below is the preset's entire system prompt base:
# `minimal-prompt` narrows the assembled sections down to it (plus plan mode's
# `plan:policy`, which no tool restriction backs — see that plugin) and
# appends the session's workspace directory at assembly time, so identity,
# harness, web-surface, and tool-guidance sections never reach the model.
# Besides the one-line persona it carries the standing working discipline the
# mode ships with: thinking-disruption fuses (a hypothesis gets at most two
# reasoning passes before a tool must settle it), action-oriented thinking
# (decide the next operation, never pre-rehearse implementations in thought),
# and YAGNI/PDCA with no redundant comments. Runtime context snapshots stay
# enabled: they are durable user-role messages, not system-prompt text.
- id: persona
  name: '@deepseek-ai/dsh-persona'
  config:
    prefix: |-
      You are a helpful software engineer assistant.

      - Thinking Disruption: Never reason through the same hypothesis more than twice. When a fact is missing, stop thinking and call a native inspection tool (read, grep, bash/pwsh) to settle it — no speculation without new evidence.
      - Action-Oriented: Use thinking only to pick the next concrete operation. Do not pre-rehearse implementations in thought; verify every claim against real tool output during execution.
      - Parallel Inspection: When several independent inspections, searches, or checks are needed, emit all of their tool calls in a single turn rather than across sequential rounds. The harness executes independent read-only calls in parallel.
      - Shell Discipline: Shell processes are ephemeral; directory changes do not persist across tool calls. Use compound commands (e.g. cd <path> && <cmd>) or pass workdir explicitly for operations in subdirectories.
      - Development Standard: Follow YAGNI and the PDCA loop. Each step verifies a single assumption. Do not write redundant comments.
      - Bounded Inspection & Convergence: Do not traverse dependency chains unbounded. Limit pre-action inspection to immediate target files (at most 2-3 inspection turns). Once core context is understood, immediately converge and begin answering or making edits. Verify edge cases during post-edit testing rather than over-reading upfront.
      - Bounded Output: never dump a large or unbounded command output into the conversation. Filter at the source with grep/head/tail/wc and read the narrow range you actually need; the model's long-range attention is a bounded budget and raw logs evict the session's own constraints from it.

# The mode's preset-local plugins:
#
# `minimal-prompt` keeps the system prompt on the persona above, plus the
# session's workspace directory appended at assembly time (`Your working
# directory is <cwd>.` — the one orientation fact the persona block carries),
# with no platform-conditional text: both platform shells are persistent, so
# the persona block is byte-identical on every host. The official `tools:sdk`
# and `tools:ptc-only` sections are kept exactly when the assembly's wire
# carries the `run_code` transport.
#
# `instructionSource` chooses where the AGENTS.md-style instruction files reach
# the model. The default, `host`, appends nothing and leaves the pre-step
# message batch untouched, so the `agent-instructions` row below delivers the
# baseline and the dynamic subdirectory instructions as the user-role messages
# the harness itself produces — the prompt stays the bare persona and the
# preset adds no instruction prose of its own. `system-prompt` instead reads
# the harness's baseline chain ($DSH_HOME/AGENTS.md, then the project root's
# ancestors down to the cwd) at assembly time and appends the content as one
# `workspace-instructions` section after the stable prefix, re-read on every
# request so file edits propagate, dropping the harness's own injections (they
# would duplicate the prompt). `hint` restores the one-time non-imperative
# pointer to the reference files (issue #388, upstream dsh-anchored-standard
# #49: a full-text dump as context measurably flips the trajectory). Both
# non-default modes trade the harness's own channel for a preset-owned one; the
# default keeps the upstream contract. Set `keepPlanPolicy: false` to drop
# plan mode's policy section as well.
#
# `tool-catalog` owns the wire presentation and the model-visible catalog.
# `presentation` selects the SDK ToolPresentationMode for the session's whole
# lifetime: `'ptc'` (default) collapses the wire to the `run_code` transport
# and reaches every other tool through the generated SDK, `'native'` keeps the
# assembled roster alone, and `'both'` keeps the roster and the transport
# co-resident. The shipped default is a static-context choice, not a measured
# win: the official scaffold comparison ranks this presentation below the native
# surface on both code-agent benchmarks, and the bundled benchmark matrix is how
# that trade would be settled locally. `pagedToolPatterns` (default `['mcp__*']`) names the
# high-fan-out families that stay paged out of the wire until activated; the
# active set replays from the session event stream, so resume and compaction
# rebuild it. `maxResidentTokens` (default 6000, calibrated above the shipped
# roster so the guard flags real growth rather than the factory configuration)
# guards the always-on-wire
# surface: crossing the estimate warns once and points at the patterns, rather
# than silently dropping a tool the session needs. The catalog itself is a
# durable user message appended after the
# user's own message, the way `dsh-tool-skill` injects the skill catalog: it
# names exactly the tools the current request's wire carries (each tool's
# argument signature plus a one-line summary capped by
# `descriptionMaxLength`; the full description stays in the tool schema) and
# summarizes the paged-out namespaces with their `tool_activate` pointer —
# including under `ptc`, where those namespaces really are unreachable until
# activated and the program contract says so. It republishes only when the
# content changes or the published copy left the visible surface (compaction,
# resume).
#
# `tool-activate` registers the `tool_activate` tool the catalog points at:
# it validates the target against the session's paged, inactive namespaces and
# activates one onto the wire, evicting the least recently used namespace past
# the cap of three. `working-context` appends the one-line recency projection
# (`[Working Context: ...]`) folded from the session events, republished only
# when it changes.
- id: minimal-prompt
  name: ./minimal-prompt.mjs
  config:
    keepPlanPolicy: true
    instructionSource: host
    instructionMaxBytes: 65536

- id: tool-catalog
  name: ./tool-catalog.mjs
  config:
    descriptionMaxLength: 200
    presentation: 'both'
    pagedToolPatterns: ['mcp__*']
    maxResidentTokens: 8000

- id: working-context
  name: ./working-context.mjs

# The runtime degeneration circuit breaker: folds a stall (consecutive zero-
# output long-reasoning steps) or echo (identical-argument tool failures)
# signal from the durable session event stream and fires once per episode —
# injecting a breaker message at pre-step and stepping the reasoning effort
# one notch down for the next few requests. With no signal it never rewrites
# a request, so the route's explicit effort and the prefix cache stay
# untouched. This is the outside force the persona's reflection fuse cannot
# be: see DSH discussion #5976 and the Agent Note that constrains dynamic
# reasoning effort to this triggered-only form.
- id: guard
  name: ./guard.mjs
  config:
    enabled: true
    sensitivity: 'balanced'
    stallReasoningChars: 8000
    globalStallCap: 4
    echoFailures: 3

# The paging activation handle the catalog points at: with `pagedToolPatterns`
# non-empty this row is what lets `tool_activate({ namespace })` bring one
# paged namespace back onto the wire (and the generated SDK) for the session.
- id: tool-activate
  name: ./tool-activate.mjs

# The key-fact register: the model pins a hard constraint / confirmed decision /
# failed path with `fact_register`, the fold rebuilds the register from the
# durable event stream, and working-context renders it as one field of the
# [Working Context: ...] line — the surface that lands inside the guaranteed
# local attention window every step. The counter to V4.1's long-session
# forgetting; the fold replays identically after resume and compaction.
- id: fact-ledger
  name: ./fact-ledger.mjs

# The harness's workspace-instruction loader. Under the default
# `instructionSource: host` this row owns the channel: its baseline and dynamic
# user-role messages reach the model untouched. Under
# `instructionSource: system-prompt` the content reaches the model through the
# system prompt instead and these injections are condensed or dropped by
# `minimal-prompt`; under `hint` the first is replaced by a pointer and the
# rest dropped. The row stays mounted in every mode: it is the default's whole
# delivery path, and its `maxBytes` remains the budget the other two consult.
- id: agent-instructions
  name: '@deepseek-ai/dsh-agent-instructions'
  config:
    maxBytes: 65536

# ── shell ───────────────────────────────────────────────────────────────────

# Standard Stdio shell tools: fresh subprocess per command with active-voice description
# cards, structured exit codes, and background job support. Exactly one mounts per host.
- id: tool-bash
  name: '@deepseek-ai/dsh-tool-bash'
  disabled: !!js process.platform === 'win32'

- id: tool-pwsh
  name: '@deepseek-ai/dsh-tool-pwsh'
  disabled: !!js process.platform !== 'win32'

# ── filesystem ──────────────────────────────────────────────────────────────

# The editor is a direct top-level row: it reuses the host's sandboxed
# `ctx.fs`, not a bare local filesystem. No `dsh-fs-local` realm is mounted, so
# it inherits the host file sandbox exactly like every Standard file tool, and
# the model-facing schema stays the builtin Minimal `str_replace_editor`.
- id: str-replace-editor
  name: '@deepseek-ai/dsh-tool-str-replace-editor'
  config:
    maxOutputChars: 16000

# ── standard filesystem tools ───────────────────────────────────────────────

# Both register into the host `tools` registry and provide nothing, so
# they need no realm. The `fs` service and its policy stay in the host.
- id: tool-fs
  name: '@deepseek-ai/dsh-tool-fs'

- id: tool-fs-search
  name: '@deepseek-ai/dsh-tool-fs-search'
  config:
    sampleOverCapGlobResults: false

# ── background jobs ────────────────────────────────────────────────────────

# Only the model-facing controls. The task REGISTRY stays on the host plane:
# its producers sit outside any realm this file could put it in, and the
# registry is keyed by owning agent anyway. With the persistent shell, long
# commands run through `&`; these controls still expose whatever the host
# registry tracks.
- id: tool-jobs
  name: '@deepseek-ai/dsh-tool-jobs'

# ── skills ──────────────────────────────────────────────────────────────────

# The skill REGISTRY lives in the host composition and is layered per scope:
# these rows register into THIS preset's layer of it, so they need no realm.
# `skill-filesystem` contributes local-root discovery for agents on this preset, and
# `tool-skill` gives them the catalog and loader; the merged catalog also
# carries whatever the deployment registered globally (repository plugins).
# The `skill-catalog` pre-step message stays current like any other injection.
- id: skill-filesystem
  name: '@deepseek-ai/dsh-skill-filesystem'

- id: tool-skill
  name: '@deepseek-ai/dsh-tool-skill'

# ── goals ───────────────────────────────────────────────────────────────────

# Only the model-facing tool. The goal SERVICE, its session driver, and the
# `/goal` command stay on the host plane: the Gateway serves the goal domain as
# Remote endpoints whose receiver comes from a generated descriptor, so it
# resolves `goals` on the host and an entry-local realm here would hide it. The
# registry is keyed by session anyway, so one host instance serves every
# session. What a preset chooses is whether its agent can call the goal tool.
- id: tool-goal
  name: '@deepseek-ai/dsh-tool-goal'

# ── plan mode ───────────────────────────────────────────────────────────────

# Plan state is per-agent by nature, so an entry-local realm is not a
# workaround here — it is the correct lifetime. `minimal-prompt` keeps this
# row's `plan:policy` section in the otherwise one-line system prompt, because
# the section is the only thing that enforces plan mode: the exit tool stays
# registered in every mode and no tool restriction backs the policy.
- id: planning
  name: cordis:group
  group: true
  isolate:
    planMode: true
  config:
    - id: plan-mode
      name: '@deepseek-ai/dsh-plan-mode'
      config:
        section: |
              You are in plan mode. Stay in plan mode until exit_plan_mode succeeds or the user switches the session mode. Imperative language to implement changes means plan the implementation, not execute it. A user's conversational agreement — including an answer confirming something you asked — approves nothing and does not end plan mode; fold the confirmed decision into the plan and submit it through exit_plan_mode.

              Explore first. Use non-mutating reads, searches, static analysis, and checks to ground the plan in the actual repository. Do not edit or write files, change configuration, run formatters or code generation that rewrites tracked files, commit, or otherwise carry out the plan. Prefer existing functions and patterns over new machinery.

              The tool catalog stays the same across modes for request-cache stability. These plan-mode rules override any later tool description or guidance that suggests using mutation tools; those tools remain listed to keep the tool catalog unchanged. Do not use todo_write to track this planning phase: it tracks implementation after an approved plan, while the plan itself belongs in exit_plan_mode.

              Resolve discoverable facts by inspection. Use ask_user_question only for user-owned choices or material ambiguity that inspection cannot answer. Do not ask the user where code lives or how current behavior works when you can find out.

              Make the plan decision-complete: state the goal and success criteria; group implementation changes by subsystem; identify public API, schema, and data-flow changes; cover edge cases, failure modes, tests, acceptance criteria, and explicit assumptions. Keep it concise enough to review but detailed enough that another engineer can implement it without making design decisions.

              When ready, call exit_plan_mode with the complete plan markdown, starting with a # title. Make exit_plan_mode the only and final tool call in that assistant response: it presents the plan for approval, and implementation begins only in a later step after approval. Do not paste the final plan as a plain reply or ask "should I proceed?" through prose or ask_user_question. If review rejects it, incorporate the feedback and present again. If the review channel is unavailable or aborted, stay in plan mode and ask the user to switch modes manually; do not proceed with implementation.

# ── compaction ──────────────────────────────────────────────────────────────

# `compaction-basic` reads `toolResultPrune` through `ctx.get`, so the pruner must
# share this realm rather than sit outside it.
#
# `tokenMeter` is deliberately NOT in this realm: the meter stays on the HOST
# plane, and the rows here resolve that one instance. It takes no configuration,
# keys every fold by Session, and owns the context-meter projection units the
# browser reads for every session — behind a realm those units would come and go
# with whichever presets happen to be mounted. What a preset chooses is whether
# its agent compacts at all, which is `compaction-basic` below.
- id: compaction
  name: cordis:group
  group: true
  isolate:
    compaction: true
    toolResultPruner: true
  config:
    - id: compaction-basic
      name: '@deepseek-ai/dsh-compaction-basic'

    - id: command-compact
      name: '@deepseek-ai/dsh-command-compact'

    - id: tool-result-pruner
      name: '@deepseek-ai/dsh-compaction-tool-result-pruner'
      config:
        thresholdChars: 4096
        headChars: 2048
        tailChars: 1024

# ── delegation and workflows ────────────────────────────────────────────────

# The `subagents` registry and its spawn/fork backends live in the HOST
# composition: the registry is a process singleton whose cross-session queries
# the api-proxy serves to the browser, and a provider name may only be
# registered once. This preset contributes the delegation TOOLS, which resolve
# that host registry.
#
# `workflows` is different — nothing outside an agent reads it — so every row
# that reaches it shares one entry-local realm here, and a consumer left
# outside would resolve a host registry this preset does not populate.
#
# `tool-subagent-report` is host-plane for the same reason as the registry,
# not because a preset may not want it: it registers a CONTINUABLE SETUP on
# that singleton rather than a tool this agent calls, and the setup list is
# not scope-aware — one copy per mounted preset means every child gets
# `report` registered once per live session, which throws on the second.
- id: delegation
  name: cordis:group
  group: true
  isolate:
    workflowEngine: true
  config:
    - id: tool-subagent-control
      name: '@deepseek-ai/dsh-tool-subagent-control'

    - id: tool-subagent-list-agents
      name: '@deepseek-ai/dsh-tool-subagent-control/list-agents'

    - id: tool-subagent
      name: '@deepseek-ai/dsh-tool-subagent'
      config:
        provider: spawn
        toolName: subagent
        backgroundMode: continuable

    - id: tool-subagent-fork
      name: '@deepseek-ai/dsh-tool-subagent'
      config:
        provider: fork
        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.
    - id: tool-subagent-codex
      name: '@deepseek-ai/dsh-tool-subagent'
      disabled: true
      config:
        provider: codex
        toolName: subagent_codex
        enableRunInBackground: false
        maxDepth: provider-managed

    - id: tool-subagent-claude-code
      name: '@deepseek-ai/dsh-tool-subagent'
      disabled: true
      config:
        provider: claude-code
        toolName: subagent_claude_code
        enableRunInBackground: false
        maxDepth: provider-managed

    - id: workflow-ptc
      name: '@deepseek-ai/dsh-workflow-ptc'
      config:
        provider: spawn

    # Keep the engine above for `ralph`, but do not publish a second
    # model-authored orchestration surface beside `run_code`: this is the one
    # roster difference the builtin `ptc` preset ships with, and the mode's
    # whole promoted surface is run_code.
    - id: tool-workflow
      name: '@deepseek-ai/dsh-tool-workflow'
      disabled: true

    - id: tool-ralph
      name: '@deepseek-ai/dsh-tool-ralph'
      config:
        subagentProvider: spawn
        maxRounds: 64

# ── remaining model-facing rows ─────────────────────────────────────────────

- id: tool-ask-user
  name: '@deepseek-ai/dsh-tool-ask-user'

- id: tool-todo
  name: '@deepseek-ai/dsh-tool-todo'
  config:
    allowParallelInProgress: true

# The `web` service and its search provider stay in the host composition; only
# the model-facing tool is per-session.
- id: tool-web
  name: '@deepseek-ai/dsh-tool-web'
  config:
    fetch: false
    searchTimeoutMs: 60000
