Skip to content

Agent Presets

pawaca edited this page Aug 30, 2026 · 2 revisions

Agent Presets

Edge's static single-preset composition vs upstream's dynamic per-session YAML presets.

No dedicated upstream documentation page. Package: @deepseek-ai/dsh-agent-presets. See also #43.

What Upstream Provides

The upstream preset system enables per-session agent composition — different sessions can use different tool sets, system prompts, and model selections:

  • dsh-agent-presets — reads cordis.yml preset files, resolves plugin dependencies, and composes a per-session cordis sub-graph. Supports multiple presets with a default, recompose() for hot-swapping, and authorable user-created presets.
  • cordis-plugin-loader — dynamic package loading at runtime. Reads YAML, resolves node_modules, calls import() to load plugin modules on demand.
  • dsh-host-plugin-inventory — exposes the current plugin graph to the client via Typert Remote, so the settings page can display which plugins are loaded.
  • dsh-client-ui-agent-preset — client UI for preset selection dropdown, composition viewer, and preset management (copy, remove, open document).

Current Edge State

One hardcoded preset

Edge does not install dsh-agent-presets, cordis-plugin-loader, or dsh-host-plugin-inventory. Instead, all ~30 plugins are installed statically in EdgeSessionStore.initialize() — every session gets the same composition. The apiproxy returns a fixed preset list:

agentPresets: {
  list: () => ok({ presets: [{
    id: 'dsh-edge',
    trust: 'system',
    isDefault: true,
    name: 'DSH Edge',
    description: 'DeepSeek Harness running in a Cloudflare Durable Object.',
  }], authorable: false, hasDocument: false }),
  select: (req) => req.payload.agentPreset === 'dsh-edge'
    ? ok({ agentPreset: 'dsh-edge' })
    : fail('agent-preset-not-found')
}

Client UI

dsh-client-ui-agent-preset is included in the 33-plugin client bundle. It renders the preset selector dropdown and composition viewer. With only one preset ('dsh-edge'), the selector shows a single option and the composition viewer displays the hardcoded preset description.

What Edge Did NOT Change

  • Agent lifecycle (create, dispose, scope isolation) — handled by AgentRegistry
  • Per-session model selection — works within the single preset
  • Client preset UI rendering — upstream code, just showing one option

Upstream vs Edge Comparison

Capability Upstream Edge
Preset count Multiple from YAML files One hardcoded
Plugin loading Dynamic import() Static ctx.plugin() at init
Per-session composition Different preset per session Same composition for all sessions
User-created presets Authorable via copy + edit YAML Not available
Hot-swap (recompose()) Change preset on blank session Not available
Plugin inventory UI Full plugin graph in settings Empty (no host-plugin-inventory)
Client bundle Runtime module scanning + HMR Build-time static assembly

Why Edge Uses Static Assembly

Cloudflare Workers have three hard constraints that prevent upstream's dynamic loading:

  • No filesystemcordis-plugin-loader reads cordis.yml via node:fs. Workers have no filesystem for config files.
  • No dynamic import() — Workers bundle all code at deploy time. Runtime import() of npm packages is not supported.
  • Single JS bundle — the Worker is one JavaScript file. All plugin code must be present in that bundle at deploy.

The static composition is not a temporary workaround — it's a deliberate design choice that trades flexibility for Cloudflare compatibility.

The Two Models Side by Side

Aspect Upstream (Dynamic) Edge (Static)
Plugin discovery cordis-plugin-loader reads cordis.yml at runtime assemble-standalone-web.mjs copies published packages at build time
Plugin loading import() dynamically loads packages All plugins bundled into one Worker JS file
Presets Multiple YAML presets; users create/copy/edit One hardcoded preset 'dsh-edge'; all sessions identical
Server plugins Loaded per-preset from YAML composition ~31 ctx.plugin() calls in EdgeSessionStore.initialize()
Client plugins Boot graph built at runtime; HMR in dev 33 plugins pre-built; expected-boot-graph.json frozen at review time
Plugin inventory dsh-host-plugin-inventory projects live state to UI Not available — settings page plugin list is empty
Hot reload HMR via dsh-client-hmr Full rebuild + redeploy (no HMR)
Adding a plugin Edit cordis.yml → reload Edit session-store.ts + package.json → rebuild → redeploy

Build Pipeline

Server side

session-store.ts (31 ctx.plugin() calls)
  → standalone/scripts/bundle-standalone.mjs
  → worker/direct/index.js (single Worker bundle)
  → gzip budget check: 921,600 bytes max

Client side

upstream published client packages
  → standalone/scripts/assemble-standalone-web.mjs
  → dist/plugins/@deepseek-ai/* (33 plugin directories)
  → expected-boot-graph.json (frozen contract)
  → verify-standalone.mjs (CI gate)

Excluded client packages

9 upstream client packages are explicitly excluded because they depend on features Edge doesn't have:

  • dsh-client-hmr — HMR (no dev server)
  • dsh-cordis-client-runner — dynamic loader client half
  • dsh-client-ui-cordis — composition inspector
  • dsh-client-ui-message-feedback — message rating (server not installed)
  • dsh-client-ui-plan — plan mode (server not installed)
  • dsh-client-ui-reference — @mentions (server not installed)
  • dsh-client-ui-settings-plugin-inventory — plugin list (no inventory service)
  • dsh-client-ui-settings-plugins — plugin settings (no preset editing)
  • dsh-session-log-export — session export (not wired)

Verification

verify-standalone.mjs runs at CI and checks:

  • All @deepseek-ai/dsh-* dependencies pin the exact upstream version
  • The assembled boot graph matches expected-boot-graph.json
  • The excluded client packages list is up to date
  • Worker gzip budget stays under 921,600 bytes

Any drift — an upstream upgrade adding a client plugin, a new exclusion, or a bundle size spike — fails CI before reaching production.

Performance Implications

Metric Upstream Edge
Cold start Plugin discovery + dynamic import + YAML parse Zero discovery — all code in the bundle, ctx.plugin() calls are synchronous
Bundle size N/A (loaded from disk) ~774 KB gzip (budget 900 KB) — all server + client code in one deploy
Dev iteration HMR — sub-second reload Full rebuild ~15s → wrangler dev auto-restart
Plugin change Edit YAML, no restart Code change → rebuild → redeploy

Architecture Summary

Component Category Edge Code
dsh-agent-presets Not installed Hardcoded single preset in edge-api.ts
cordis-plugin-loader Not installed Static ctx.plugin() calls
dsh-host-plugin-inventory Not installed Settings page shows no plugin list
dsh-client-ui-agent-preset Reuse In client bundle, shows single preset

TODO

Evaluate config-driven plugin activation. (#43) All plugin code is already in the bundle. A DO KV config could toggle which plugins are activated per-session without rebuilding — still static code, but dynamic composition. This would enable multiple "presets" as JSON configs rather than YAML files, sidestepping the dynamic loading constraint entirely.

English

中文

Clone this wiki locally