1
# The dsh-web-app bundle patch: the browser surface over the dsh-base layer.2
# Applied after dsh-base's insert; rows here override base rows by id, with3
# the profile's own cordis.patch.yml and any --patch overlays still to come.4
#5
# A patch replaces the targeted row's whole `config`, so each row below6
# restates every key it owns.7
#8
# The web-startup plugin injects `cmdlineArgs` and provides `webStartup` as an9
# ordinary Cordis service. Rows configured from flags inject that service, so10
# Loader resolves their expressions only after it exists. The web runtime then11
# provides bind-dependent `webRuntime` values to the trust fence.12
# `dsh --profile web --help` provides no `webStartup`, so no server binds.14
# ── surface-specific values the base deliberately omits ─────────────────────16
- id: system-prompt17
config:18
personaSuffix: Your working directory is {{cwd}}.19
personaPrefix: >-20
You are a coding agent powered by the {{model}} model.22
# Full-text session search is opt-in (the base row's `openAt: never`). This23
# restatement keeps the Web values on one ephemeral in-memory index; a24
# deployment enabling content search overrides `openAt` to `first-search` in a25
# later patch layer, which defers the node:sqlite import and in-memory handle26
# to the first search so Node 22 startup stays quiet.27
- id: session-query-sqlite28
config:29
path: ':memory:'30
openAt: never32
- id: tools33
config:34
# TEMPORARY workaround: DSH_TOOLS_MODE (native|ptc|both) opts a whole dsh35
# process into PTC mode while per-session tool-presentation selection is being36
# designed; unset keeps the schema default (native). Remove the env seam37
# once the web UI owns the choice per session.38
mode: !!js process.env.DSH_TOOLS_MODE40
# ── web-only host rows, the transport layer, and the browser roster ─────────42
# `dsh.client` rows are the browser roster the modules node half scans into43
# window.__DSH_BOOT__; the modules row is simultaneously a host row.44
- insert:45
- id: desktop-product-telemetry46
name: '@deepseek-ai/dsh-host-product-telemetry-otel'47
disabled: !!js "ctx.get('profileContext')?.name !== 'desktop'"48
config:49
scheduledDelayMillis: 3000050
timeoutMillis: 1500051
exportTimeoutMillis: 2000052
shutdownTimeoutMillis: 200053
serviceName: deepseek-harness-desktop54
serviceVersion: !!js process.env.DSH_CLIENT_VERSION55
endpoint: !!js process.env.DSH_PRODUCT_ANALYTICS_OTLP_URL57
- id: product-analytics58
name: '@deepseek-ai/dsh-client-product-analytics'59
disabled: !!js "ctx.get('profileContext')?.name !== 'desktop'"60
config:61
enabled: true62
appVersion: !!js process.env.DSH_CLIENT_VERSION64
# Host-owned opt-in sampled when a new Web session receives its preset65
# delegation tools. The Plugins page edits this settings namespace.66
- id: subagent-model-selection-settings67
name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'69
- id: message-feedback70
name: '@deepseek-ai/dsh-message-feedback'71
config:72
maxNoteBytes: 819274
# Browser Session export: `/export` command plus the shared download dialog.75
- id: session-log-download76
name: '@deepseek-ai/dsh-session-log-export'78
# Session-header "Open In..." split button over the host application79
# resolution (macOS/Windows/Linux; a host with no resolved application80
# renders no button). Two halves: the host routes and the browser surface.81
- id: open-in-app82
name: '@deepseek-ai/dsh-host-open-in-app'83
config:84
probeTimeoutMs: 1000085
iconTimeoutMs: 1000086
launchWatchMs: 100088
- id: ui-open-in-app89
name: '@deepseek-ai/dsh-client-ui-open-in-app'91
- id: workspace92
name: '@deepseek-ai/dsh-workspace'94
- id: session-reference95
name: '@deepseek-ai/dsh-session-reference'97
- id: file-reference-local98
name: '@deepseek-ai/dsh-file-reference-local'100
# Whole-log turn/step counts for the chat stats strip (the sessionStats101
# projection key); the projection registry itself is a base-layer row.102
- id: session-stats103
name: '@deepseek-ai/dsh-session-stats'105
# Whole-log turn outline for the chat turn rail (the turnOutline106
# projection key): every turn stays navigable before its events page in.107
- id: session-turn-outline108
name: '@deepseek-ai/dsh-session-turn-outline'110
# Resolve bind host, SSH launch, and display once at boot, then mount the111
# matching dual-face directory picker. Mount -native or -browse directly in112
# an overlay to pin the interaction.113
- id: directory-picker114
name: '@deepseek-ai/dsh-host-directory-picker-auto'116
# Read-only projection of current Loader entries for trusted client RPCs.117
- id: plugin-inventory118
name: '@deepseek-ai/dsh-host-plugin-inventory'120
# Session commands, cold reads, and live control over Typert Remote.121
- id: session-controller122
name: '@deepseek-ai/dsh-api-session-controller'124
# One background job's observation record streamed over Typert Remote;125
# the roster rides the session control stream above.126
- id: job-controller127
name: '@deepseek-ai/dsh-api-job-controller'128
# Workspace file service: bounded read, directory listing, and the129
# agent-write change feed inside the session workspace root.130
- id: terminal-controller131
name: '@deepseek-ai/dsh-api-terminal-controller'132
- id: workspace-files133
name: '@deepseek-ai/dsh-api-workspace-files'135
# The Schedule service owns the task store and delivers each due occurrence136
# as a follow-up in its original Session. The clock reading and the four137
# reminder tools belong to the `standard`, `cordis`, and `ptc` presets,138
# which declare them in their own scope.139
- id: schedule140
name: '@deepseek-ai/dsh-schedule'142
# Configuration-surface reads and writes over Typert Remote. Each method143
# reports an actionable error when its settings-domain provider is absent.144
- id: ui-settings-account145
name: '@deepseek-ai/dsh-client-ui-settings-account'147
- id: account-controller148
name: '@deepseek-ai/dsh-api-account-controller'150
- id: settings-controller151
name: '@deepseek-ai/dsh-api-settings-controller'153
# Workspace commands and reconnect-safe projection over Typert Remote.154
- id: workspace-controller155
name: '@deepseek-ai/dsh-api-workspace-controller'157
- id: cordis-host-runner158
name: '@deepseek-ai/dsh-cordis-host-runner'160
# The Host inspect providers are process-global: the registry rejects a161
# duplicate id, so they register here once and every preset's `tool-cordis`162
# row reads them.163
- id: cordis-inspect-providers164
name: '@deepseek-ai/dsh-tool-cordis/host'166
# Ordinary provider for the parsed Web flags. Its plugin-level injection167
# waits for cmdlineArgs; no launcher metadata or special row kind is needed.168
- id: web-startup169
name: '@deepseek-ai/dsh-web-app/startup'171
# ── layer 2: transport/service ──────────────────────────────────────────────173
# Plain route-registration carrier; host and port come from the app's174
# webStartup provider, with these deployment fallbacks. The dist is served175
# by the web-runtime row below through the fallback seat. Routing and176
# browser-facing asset paths do not depend on the advertised URL, which the177
# web-runtime row owns.178
- id: webserver179
name: '@deepseek-ai/dsh-host-webserver'180
inject: [webStartup]181
config:182
host: !!js ctx.webStartup.host ?? '127.0.0.1'183
port: !!js ctx.webStartup.port ?? 3080184
compression: gzip185
compressionLevel: 1186
compressionThresholdBytes: 1024188
# Web glue owned by this bundle: resolves the built frontend dist (an189
# assembly fact of dsh-web-app, never user config), mounts the190
# frontend-static fallback owner, registers the web-surface prompt191
# section and the bash runtime variable, prints the URL line, and opens the192
# canonical URL after the full Loader tree settles and the required-193
# entry audit passes. Invocation-only values come from the webStartup194
# provider; the advertised application URL is this row's `publicUrl` (the195
# loopback URL when unset). After the server binds, this row samples LAN196
# trust once and provides `webRuntime`. A complete agent-preset persona197
# suppresses the prompt section for that agent while retaining the198
# host-owned shell variable.199
- id: web-runtime200
name: '@deepseek-ai/dsh-web-app'201
inject: [webStartup]202
config:203
openBrowser: !!js ctx.webStartup.openBrowser204
printUrl: true205
publicUrl: !!js ctx.webStartup.publicUrl206
surfaceContext: true207
trustedHosts: !!js ctx.webStartup.trustedHosts209
# The client-plugin reload chain, always mounted: it is idle until a210
# rebuild watcher (pnpm run dev:web) actually rewrites client bundles. It211
# is a row rather than a child of web-runtime because its node half is a212
# client-side package, which a host-side bundle cannot import.213
- id: client-hmr214
name: '@deepseek-ai/dsh-client-hmr'216
# ── browser plugin roster (dsh.client rows; node halves are layer-2 hosts) ──218
# Dual-face: the node half scans this tree, composes window.__DSH_BOOT__,219
# and serves /plugins/<id>/client.js; the browser half is the module table220
# the shell kernel constructs before cordis exists (adopted as a plugin221
# entry by the kernel, never fetched).222
- id: modules223
name: '@deepseek-ai/dsh-client-modules'225
# Owns both ends of the web transport: node half binds the gateway to the226
# webserver under /api; browser half is the fetch/SSE client. Every API227
# request and upgrade passes the loopback plus trustedHosts policy; an228
# advertised public URL contributes nothing to it.229
- id: connection230
name: '@deepseek-ai/dsh-client-connection'231
inject: [webRuntime]232
config:233
# LAN literals derived from the active bind plus --trusted-host extras.234
# A deployment adding authorities keeps this expression and concatenates235
# its literals, for example: ['app.internal', ...ctx.webRuntime.trustedHosts].236
trustedHosts: !!js ctx.webRuntime.trustedHosts238
# Raw Blob and ReadableStream uploads run independently of Connection's239
# RPC and generation service.240
- id: file-upload241
name: '@deepseek-ai/dsh-client-file-upload'243
- id: api-remotes244
name: '@deepseek-ai/dsh-api-remotes'246
- id: cordis-client-runner247
name: '@deepseek-ai/dsh-cordis-client-runner'249
- id: ui-theme250
name: '@deepseek-ai/dsh-client-ui-theme'252
- id: locale253
name: '@deepseek-ai/dsh-client-locale'255
- id: shortcuts256
name: '@deepseek-ai/dsh-client-shortcuts'258
- id: ui-shortcuts259
name: '@deepseek-ai/dsh-client-ui-shortcuts'261
- id: ui-layout262
name: '@deepseek-ai/dsh-client-ui-layout'264
- id: ui-renderer265
name: '@deepseek-ai/dsh-client-ui-renderer'267
- id: ui-session268
name: '@deepseek-ai/dsh-client-ui-session'270
# Unified resource model: protocol providers behind the useResource hook.271
- id: resources272
name: '@deepseek-ai/dsh-client-resources'274
- id: ui-sidebar275
name: '@deepseek-ai/dsh-client-ui-sidebar'277
# The right Sidebar: one docking surface per session over ui-dockkit.278
- id: ui-sidebar-right279
name: '@deepseek-ai/dsh-client-ui-sidebar-right'282
- id: office-to-pdf283
name: '@deepseek-ai/dsh-office-to-pdf'285
# The right Sidebar's document tab: bounded file reads with selectable286
# Markdown, code, HTML, PDF, Office, and plain-text renderers.287
- id: ui-sidebar-documentpreview288
name: '@deepseek-ai/dsh-client-ui-sidebar-documentpreview'290
# Web profiles opt in; Desktop retains sandboxed HTTP(S) Browser tabs.291
- id: ui-sidebar-browser292
name: '@deepseek-ai/dsh-client-ui-sidebar-browser'293
disabled: !!js "ctx.get('profileContext')?.name !== 'desktop'"295
- id: ui-sidebar-terminal296
name: '@deepseek-ai/dsh-client-ui-sidebar-terminal'297
# The right Sidebar's workspace file tree tab type.298
- id: ui-sidebar-files299
name: '@deepseek-ai/dsh-client-ui-sidebar-files'301
- id: ui-settings302
name: '@deepseek-ai/dsh-client-ui-settings'304
- id: ui-settings-general305
name: '@deepseek-ai/dsh-client-ui-settings-general'307
- id: ui-settings-models308
name: '@deepseek-ai/dsh-client-ui-settings-models'310
# Plugin management: the sidebar Plugins page installs, enables, disables,311
# and removes the profile's bundles through the `pluginManager` Remote.312
# Without a profile-backed Host it reports itself unavailable.313
- id: ui-plugin-manager314
name: '@deepseek-ai/dsh-client-ui-plugin-manager'316
# Read-only Plugin list tab in the Settings Plugins section; the built-in317
# bundles the sidebar page leaves out are inspected here.318
- id: ui-settings-plugin-inventory319
name: '@deepseek-ai/dsh-client-ui-settings-plugin-inventory'321
- id: ui-conversation322
name: '@deepseek-ai/dsh-client-ui-conversation'324
- id: ui-approval325
name: '@deepseek-ai/dsh-client-ui-approval'327
- id: ui-chat328
name: '@deepseek-ai/dsh-client-ui-chat'330
# Official occupants for the generic sidebar and conversation brand slots.331
- id: ui-brand-official332
name: '@deepseek-ai/dsh-client-ui-brand-official'334
- id: ui-attachment335
name: '@deepseek-ai/dsh-client-ui-attachment'337
# Tool call tree, generic fallback, and keyed business Tool views.338
- id: ui-tool339
name: '@deepseek-ai/dsh-client-ui-tool'341
- id: ui-cordis342
name: '@deepseek-ai/dsh-client-ui-cordis'344
# Turn tail: the changed-files card and delivery cards under each closing345
# assistant message. Remove this entry to turn the surface off; the tail346
# hole renders empty.347
- id: ui-deliverables348
name: '@deepseek-ai/dsh-client-ui-deliverables'350
# Records each top-level turn's changed files from git working-tree351
# snapshots; the changed-files card above renders its events.352
- id: workspace-changes353
name: '@deepseek-ai/dsh-workspace-changes'356
- id: ui-workspace357
name: '@deepseek-ai/dsh-client-ui-workspace'359
# Durable workflow lifecycle as an independent Chat node after the360
# existing generic workflow tool row.361
- id: ui-workflow-run362
name: '@deepseek-ai/dsh-client-ui-workflow-run'364
# Input triggers: the '/' | '@' pipeline (ui-input-trigger), the command surface over365
# it (ui-commands), and the reference sources (ui-skill / ui-reference).366
- id: ui-input-trigger367
name: '@deepseek-ai/dsh-client-ui-input-trigger'369
- id: ui-commands370
name: '@deepseek-ai/dsh-client-ui-commands'372
- id: ui-skill373
name: '@deepseek-ai/dsh-client-ui-skill'375
- id: ui-subagent376
name: '@deepseek-ai/dsh-client-ui-subagent'378
- id: ui-reference379
name: '@deepseek-ai/dsh-client-ui-reference'382
# Background jobs: the session-header roster with on-demand streaming383
# record panels over the session job-output service.384
- id: ui-jobs385
name: '@deepseek-ai/dsh-client-ui-jobs'387
# Task management and Session reminder catalog over the Host Schedule388
# service mounted above.389
- id: ui-schedule390
name: '@deepseek-ai/dsh-client-ui-schedule'392
# Goal surface: GoalBar in the input dock over the goal session projection.393
- id: ui-goal394
name: '@deepseek-ai/dsh-client-ui-goal'396
# The feedback surface: Like/Dislike in the assistant-message action397
# strip, the feedback dialog behind Dislike and /feedback with its398
# acknowledgement toast, over the messageFeedback and sessionFeedback Remotes.399
- id: ui-message-feedback400
name: '@deepseek-ai/dsh-client-ui-message-feedback'402
# Model selection: the /model popupSelect + composer seat over session.models.403
- id: ui-model-selection404
name: '@deepseek-ai/dsh-client-ui-model-selection'406
- id: ui-permission407
name: '@deepseek-ai/dsh-client-ui-permission-presets'409
# The agent-preset row in General settings: the default preset for410
# sessions created later. Absent a roster it renders nothing.411
- id: ui-agent-preset412
name: '@deepseek-ai/dsh-client-ui-agent-preset'414
- id: ui-settings-session-log415
name: '@deepseek-ai/dsh-client-ui-settings-session-log'417
# The Built-in plugins settings section: the navigation entry and the tab418
# row the inventory tab registers into. The official configuration pages419
# are the companion rows below.420
- id: ui-settings-plugins421
name: '@deepseek-ai/dsh-client-ui-settings-plugins'423
# The official settings pages: one companion package per host-plane424
# namespace, each registering its page into the Plugins page while the425
# Host serves the namespace.426
- id: ui-settings-shell427
name: '@deepseek-ai/dsh-client-ui-settings-shell'429
- id: ui-settings-agent-loop430
name: '@deepseek-ai/dsh-client-ui-settings-agent-loop'432
- id: ui-settings-subagent433
name: '@deepseek-ai/dsh-client-ui-settings-subagent'435
- id: ui-settings-web-search436
name: '@deepseek-ai/dsh-client-ui-settings-web-search'438
# Plan control: the composer plan seat over the plan projection + /plan channel.439
- id: ui-plan440
name: '@deepseek-ai/dsh-client-ui-plan'442
- id: ui-user-questions443
name: '@deepseek-ai/dsh-client-ui-user-questions'445
- id: ui-trajectory446
name: '@deepseek-ai/dsh-client-ui-trajectory'448
# ── the agent plane moves behind agent presets ─────────────────────────────449
#450
# Every row below composes what ONE agent contributes to the host registries:451
# its tools, its prompt sections, its delegation backends. The base keeps them452
# for the TUI, which is single-session and composes its agent process-wide; the453
# Web surface disables them here and lets each session mount a preset instead.454
#455
# Disabling rather than deleting is deliberate: the base is shared, and a row456
# absent from a surface overlay would silently reappear the day someone reorders457
# the composition.459
# `shell-env` STAYS in the host plane: `apps/cli/src/web.ts` injects it to460
# publish `DSH_WEB_URL`/`DSH_WEB_MODE`, and a host row that injects a service is461
# the criterion for host-plane ownership — injection resolves before any session462
# exists, so there is no agent to key by. Behind a preset realm those variables463
# would never reach the model's shell at all.465
- id: tool-plugin-manager466
disabled: true468
- id: tool-bash469
disabled: true471
- id: tool-pwsh472
disabled: true474
# The background-job REGISTRY stays on the host plane; only the model-facing475
# `job_*` controls move. Its producers — `tool-bash` here, `tool-terminal` and a476
# non-continuable `tool-subagent` elsewhere — are preset rows that resolve it477
# with `ctx.get`, and an entry-local realm around the registry is invisible to478
# every sibling row outside that realm, so `run_in_background` answered479
# "background jobs unavailable" while the controls sat in the catalog. That is480
# the `goals` criterion read from inside the preset: a Service a row outside its481
# realm READS belongs to the plane both can see. The registry is keyed by owning482
# agent, so one host instance serves every session exactly as before presets.484
- id: tool-jobs485
disabled: true487
- id: tool-fs488
disabled: true490
- id: tool-fs-search491
disabled: true493
# The `skill` REGISTRY stays in the host plane. It is host+per-scope layered494
# (the tools-registry shape): deployment-level providers — repository plugins,495
# a host skill-filesystem row — register into its global layer, while a preset's496
# `skill-filesystem` registers into that preset's layer, and each agent reads the497
# merged catalog its scope chain selects. Only the per-agent rows move behind498
# presets: the base host `skill-filesystem` row is disabled here (presets own local499
# discovery), and `tool-skill` is what a preset mounts to give its agent the500
# catalog and loader at all.502
- id: skill-filesystem503
disabled: true505
- id: tool-skill506
disabled: true508
# The goal service and session driver stay on the host plane, where Gateway509
# remotes resolve them. Presets own the human command and model-facing tool.511
- id: command-goal512
disabled: true514
- id: tool-goal515
disabled: true517
- id: plan-mode518
disabled: true520
# The token METER stays on the host plane; only the compaction backend that521
# reads it moves. It owns the context-meter projection units, and that table is522
# process-wide, so preset ownership would make the meter a function of which523
# presets happen to be mounted rather than a per-session fact. Same criterion as524
# `tasks` and `goals`; the reasoning has one home in525
# `.agents/notes/implemented/architecture/2026-08-10-host-plane-ownership-after-presets.md`.527
- id: compaction-basic528
disabled: true530
- id: command-compact531
disabled: true533
- id: tool-result-pruner534
disabled: true536
# The subagent registry and its backends STAY in the host plane. `subagents` is537
# a process singleton with a cross-session query surface (`listChildren`,538
# `followup`) that the host api-proxy serves to the browser, and a provider539
# registers under a globally unique name, so a per-session copy would both540
# starve that host row and collide on the second session. What a preset541
# chooses is which delegation TOOLS its agent sees, below.543
- id: tool-subagent-control544
disabled: true546
- id: tool-subagent-list-agents547
disabled: true549
- id: tool-subagent550
disabled: true552
- id: tool-subagent-fork553
disabled: true555
- id: workflow-ptc556
disabled: true558
- id: tool-workflow559
disabled: true561
# `base` ships this row disabled; the preset-plane gate reads declared row ids562
# without regard to `disabled`, so this row must stay to keep `tool-ralph` off563
# the host plane and out of collision with every preset's row.564
- id: tool-ralph565
disabled: true567
- id: agent-instructions568
disabled: true570
- id: tool-todo571
disabled: true573
- id: tool-web574
disabled: true576
# The preset selection registry. The shipped preset declarations follow as577
# separate patch files under `presets/`, listed after this file in578
# `package.json` `dsh.bundle.patch`.579
- insert:580
- id: agent-preset-registry581
name: '@deepseek-ai/dsh-agent-preset-registry'582
config:583
default: standard