# The dsh-web-app bundle patch: the browser surface over the dsh-base layer.
# Applied after dsh-base's insert; rows here override base rows by id, with
# the profile's own cordis.patch.yml and any --patch overlays still to come.
#
# A patch replaces the targeted row's whole `config`, so each row below
# restates every key it owns.
#
# The web-startup plugin injects `cmdlineArgs` and provides `webStartup` as an
# ordinary Cordis service. Rows configured from flags inject that service, so
# Loader resolves their expressions only after it exists. The web runtime then
# provides bind-dependent `webRuntime` values to the trust fence and client
# roster. `dsh --profile web --help` provides neither service, so no server binds.

# ── surface-specific values the base deliberately omits ─────────────────────

- id: system-prompt
  config:
    persona: >-
      You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.

# TODO: Re-enable shared HMR for Web after its reload lifecycle is tested.
- id: hmr
  disabled: true

# Web content search runs on an ephemeral in-memory index. The service
# activates at boot, while first-search defers the node:sqlite import and
# in-memory handle so Node 22 startup stays quiet until content search
# actually uses SQLite. That search then reconciles this boot's sources.
- id: session-query-sqlite
  config:
    path: ':memory:'
    openAt: first-search

- id: tools
  config:
    # TEMPORARY workaround: DSH_TOOLS_MODE (native|code|both) opts a whole dsh
    # process into Code Mode while per-session tool-mode selection is being
    # designed; unset keeps the schema default (native). Remove the env seam
    # once the web UI owns the choice per session.
    mode: !!js process.env.DSH_TOOLS_MODE

# ── web-only host rows, the transport layer, and the browser roster ─────────

# `dsh.client` rows are the browser roster the modules node half scans into
# window.__DSH_BOOT__; the modules row is simultaneously a host row.
- insert:
    - id: code-runtime
      name: '@deepseek-ai/dsh-code-runtime-worker'

    - id: storage
      name: '@deepseek-ai/dsh-storage'

    - id: storage-json
      name: '@deepseek-ai/dsh-storage-json'
      config:
        root: !!js dshHomePath('storages')

    - id: storage-domain
      name: '@deepseek-ai/dsh-storage-domain'
      config:
        backend: json

    - id: workspace
      name: '@deepseek-ai/dsh-workspace'

    - id: session-projection-cache
      name: '@deepseek-ai/dsh-session-projection-cache'
      config:
        writeEveryEvents: 200
        writeIntervalMs: 5000

    # Resolve bind host, SSH launch, and display once at boot, then mount the
    # matching dual-face directory picker. Mount -native or -browse directly in
    # an overlay to pin the interaction.
    - id: directory-picker
      name: '@deepseek-ai/dsh-host-directory-picker-auto'

    # The API gateway: the transport-agnostic dispatch face every client shape
    # shares. The base layer's agent-default-model service owns the default model.
    - id: api-gateway
      name: '@deepseek-ai/dsh-host-apiproxy'

    # Ordinary provider for the parsed Web flags. Its plugin-level injection
    # waits for cmdlineArgs; no launcher metadata or special row kind is needed.
    - id: web-startup
      name: '@deepseek-ai/dsh-web-app/startup'

    # ── layer 2: transport/service ──────────────────────────────────────────────

    # Plain route-registration carrier; host and port come from the app's
    # webStartup provider, with these deployment fallbacks. The dist is served by
    # the web-runtime row below through the fallback seat.
    - id: webserver
      name: '@deepseek-ai/dsh-host-webserver'
      inject: [webStartup]
      config:
        host: !!js ctx.webStartup.host ?? '127.0.0.1'
        port: !!js ctx.webStartup.port ?? 3080

    # Web glue owned by this bundle: resolves the built frontend dist (an
    # assembly fact of dsh-web-app, never user config), mounts the
    # frontend-static fallback owner, registers the web-surface prompt
    # section and bash runtime variables, and prints the URL line. The webStartup
    # provider supplies invocation-only values; after the server binds, this row
    # samples LAN trust once and provides `webRuntime`. A complete agent-preset
    # persona suppresses the prompt section for that agent while retaining
    # these host-owned shell variables.
    - id: web-runtime
      name: '@deepseek-ai/dsh-web-app'
      inject: [webStartup]
      config:
        mode: !!js ctx.webStartup.mode
        printUrl: true
        surfaceContext: true
        trustedHosts: !!js ctx.webStartup.trustedHosts

    # The client-plugin reload chain: a dev-only row this bundle ships off,
    # which the runtime row turns on before client discovery. It is a row rather
    # than a child of web-runtime because its node half is a client-side package,
    # which a host-side bundle cannot import.
    - id: client-hmr
      name: '@deepseek-ai/dsh-client-hmr'
      inject: [webStartup]
      disabled: true

    # ── browser plugin roster (dsh.client rows; node halves are layer-2 hosts) ──

    # Dual-face: this waits for the runtime row to decide whether HMR belongs
    # in the first graph. The node half then scans this tree, composes
    # window.__DSH_BOOT__, and serves /plugins/<id>/client.js; the browser half
    # is the module table the shell kernel constructs before cordis exists
    # (adopted as a plugin entry by the kernel, never fetched).
    - id: modules
      name: '@deepseek-ai/dsh-client-modules'
      inject: [webRuntime]

    # Owns both ends of the web transport: node half binds the gateway to the
    # webserver under /api; browser half is the fetch/SSE client.
    - id: connection
      name: '@deepseek-ai/dsh-client-connection'
      inject: [webRuntime]
      config:
        # LAN literals derived from the active bind plus --trusted-host extras.
        # A deployment adding authorities keeps this expression and concatenates
        # its literals, for example: ['app.internal', ...ctx.webRuntime.trustedHosts].
        trustedHosts: !!js ctx.webRuntime.trustedHosts

    - id: api-remotes
      name: '@deepseek-ai/dsh-api-remotes'

    - id: client-runtime
      name: '@deepseek-ai/dsh-client-runtime'

    - id: ui-theme
      name: '@deepseek-ai/dsh-client-ui-theme'

    - id: locale
      name: '@deepseek-ai/dsh-client-locale'

    - id: ui-layout
      name: '@deepseek-ai/dsh-client-ui-layout'

    - id: ui-sidebar
      name: '@deepseek-ai/dsh-client-ui-sidebar'

    - id: ui-settings
      name: '@deepseek-ai/dsh-client-ui-settings'

    - id: ui-settings-general
      name: '@deepseek-ai/dsh-client-ui-settings-general'

    - id: ui-models
      name: '@deepseek-ai/dsh-client-ui-models'

    - id: ui-conversation
      name: '@deepseek-ai/dsh-client-ui-conversation'

    # Tool call tree, generic fallback, and keyed business Tool views.
    - id: ui-tool
      name: '@deepseek-ai/dsh-client-ui-tool'

    # Turn tail: the produced-files row under each closing assistant message.
    # Remove this entry to turn the surface off; the tail hole renders empty.
    - id: ui-deliverables
      name: '@deepseek-ai/dsh-client-ui-deliverables'


    - id: ui-workspace
      name: '@deepseek-ai/dsh-client-ui-workspace'

    # Input triggers: the '/' | '@' pipeline (ui-slash), the command surface over
    # it (ui-command), and the two reference sources (ui-skill / ui-subagent).
    - id: ui-slash
      name: '@deepseek-ai/dsh-client-ui-slash'

    - id: ui-command
      name: '@deepseek-ai/dsh-client-ui-command'

    - id: ui-skill
      name: '@deepseek-ai/dsh-client-ui-skill'

    - id: ui-subagent
      name: '@deepseek-ai/dsh-client-ui-subagent'

    # Goal surface: GoalBar in the input dock over the goal session projection.
    - id: ui-goal
      name: '@deepseek-ai/dsh-client-ui-goal'

    # Model selection: the /model popupSelect + composer seat over session.models.
    - id: ui-model
      name: '@deepseek-ai/dsh-client-ui-model'

    - id: ui-permission
      name: '@deepseek-ai/dsh-client-ui-permission'

    # The agent-preset row in General settings: the default preset for
    # sessions created later. Absent a roster it renders nothing.
    - id: ui-agent-preset
      name: '@deepseek-ai/dsh-client-ui-agent-preset'

    # Plan control: the composer plan seat over the plan projection + /plan channel.
    - id: ui-plan
      name: '@deepseek-ai/dsh-client-ui-plan'

    - id: ui-question
      name: '@deepseek-ai/dsh-client-ui-question'

    - id: ui-trajectory
      name: '@deepseek-ai/dsh-client-ui-trajectory'

# ── the agent plane moves behind agent presets ─────────────────────────────
#
# Every row below composes what ONE agent contributes to the host registries:
# its tools, its prompt sections, its delegation backends. The base keeps them
# for the TUI, which is single-session and composes its agent process-wide; the
# Web surface disables them here and lets each session mount a preset instead.
#
# Disabling rather than deleting is deliberate: the base is shared, and a row
# absent from a surface overlay would silently reappear the day someone reorders
# the composition.

# `bash-env` STAYS in the host plane: `apps/cli/src/web.ts` injects it to
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is
# the criterion for host-plane ownership — injection resolves before any session
# exists, so there is no agent to key by. Behind a preset realm those variables
# would never reach the model's shell at all.

- id: tool-bash
  disabled: true

# The background-task REGISTRY stays on the host plane; only the model-facing
# `task_*` controls move. Its producers — `tool-bash` here, `tool-pty` and a
# non-continuable `tool-subagent` elsewhere — are preset rows that resolve it
# with `ctx.get`, and an entry-local realm around the registry is invisible to
# every sibling row outside that realm, so `run_in_background` answered
# "background tasks unavailable" while the controls sat in the catalog. That is
# the `goals` criterion read from inside the preset: a Service a row outside its
# realm READS belongs to the plane both can see. The registry is keyed by owning
# agent, so one host instance serves every session exactly as before presets.

- id: tool-tasks
  disabled: true

- id: tool-fs
  disabled: true

- id: tool-fs-search
  disabled: true

- id: tool-str-replace-editor
  disabled: true

# The `skill` REGISTRY stays in the host plane. It is host+per-scope layered
# (the tools-registry shape): deployment-level providers — repository plugins,
# a host skill-local row — register into its global layer, while a preset's
# `skill-local` registers into that preset's layer, and each agent reads the
# merged catalog its scope chain selects. Only the per-agent rows move behind
# presets: the base host `skill-local` row is disabled here (presets own local
# discovery), and `tool-skill` is what a preset mounts to give its agent the
# catalog and loader at all.

- id: skill-local
  disabled: true

- id: tool-skill
  disabled: true

# The goal SERVICE, its session driver, and the `/goal` command STAY on the
# host plane; only the model-facing tool moves. The Gateway serves the goal
# domain as Remote endpoints, and a Remote method picks its receiver Service
# from a generated descriptor — it resolves `goals` on the host, so a
# per-session realm would answer `service-unavailable` for every browser call.
# That is the `bash-env` criterion read from the other side: injection is not
# the only host relationship a Service can have. The registry is keyed by
# session, so one host instance serves every session exactly as before presets.

- id: tool-goal
  disabled: true

- id: plan-mode
  disabled: true

- id: token-meter
  disabled: true

- id: compact-basic
  disabled: true

- id: command-compact
  disabled: true

- id: tool-result-prune
  disabled: true

# The subagent registry and its backends STAY in the host plane. `subagents` is
# a process singleton with a cross-session query surface (`listChildren`,
# `followup`) that the host api-proxy serves to the browser, and a provider
# registers under a globally unique name, so a per-session copy would both
# starve that host row and collide on the second session. What a preset
# chooses is which delegation TOOLS its agent sees, below.

- id: tool-subagent-control
  disabled: true

- id: tool-subagent-list-agents
  disabled: true

- id: tool-subagent
  disabled: true

- id: tool-subagent-fork
  disabled: true

# `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: workflow-workerthread
  disabled: true

- id: tool-workflow
  disabled: true

- id: tool-ralph
  disabled: true

- id: workspace-context
  disabled: true

- id: tool-todo
  disabled: true

- id: tool-web
  disabled: true

# The preset roster. `config/agent-presets/` ships with the deployment and is
# read-only (its entries carry `system` trust);
# `$DSH_HOME/.agent-presets` is where a person — or an agent — authors their own, and
# carries the same trust as shell access because a preset IS a composition.
# `roots` is an assembly fact, not user config: the shipped preset directory
# ships beside this file, so AppCLIEntry resolves it and patches it in — the
# same treatment `distIndex` gets on the webserver row.
- insert:
    - id: agent-presets
      name: '@deepseek-ai/dsh-agent-presets'
      config:
        default: standard
