Files
deepseek-harness/.agents/notes/implemented/architecture/2026-08-22-single-dsh-application-launcher.md
T
Tianyi Cui 9edf1b9f10 fix(python-sdk): harden profile runtime startup
Resolve packaged profile proxies with Node ESM import conditions from each package installation, and fail loud when an explicit runtime export or legacy main entry is missing. Serialize the shared profile fallback under the existing cross-process writer lock so concurrent dsh processes cannot observe partial proxies; either carrier now replaces the other carrier’s managed entry without manual cleanup.

Give Python initialize its own 10-second default bound and name the selected profile in timeout diagnostics, while leaving ordinary agent turns unbounded by default. Package the dynamically resolved web frontend and skill-badge assets so the runtime wheel’s normal dsh profiles do not depend on pkg static-discovery accidents.

Rewrite the root launch rule and every active stale SDK-runtime note to the shipped dsh profile architecture in both languages. Focused tests prove import-only and transitive package exports, lock contention, cross-carrier transitions, missing-entry failures, asset inventory, and bounded initialization.
2026-08-24 17:28:27 +08:00

11 KiB

Agent Note: One dsh launcher for application profiles

Status: implemented

English | 中文

Problem

DeepSeek Harness application processes need one owner for composition, plugin resolution, environment discovery, shutdown, and user customization. A dedicated app bin with a complete cordis.yml creates a second lifecycle beside profile launch: plugins installed into a profile do not reach it, behavior drifts from dsh-base, and SDK callers learn arbitrary process argv instead of the product's composition model.

The Python SDK distributes a native executable and three platform wheels. Its packaged process must use the same profile launcher while preserving the closed VFS dependency tree, native sidecars, and installed-wheel evidence.

Decision

Launch scope

Every supported Node application starts through the dsh CLI and one named profile. The shipped application commands are dsh web, dsh --profile headless, dsh --profile sdk, and dsh --profile acp; dsh web is the deliberate convenience alias for --profile web, not another application entry.

Vendor CLIs, build-only and test-only executables, direct in-process plugin mounting, and the private browser WebWorker preview are outside the application-launch inventory. A package app bin or root demo that launches a package entry is not an accepted extension point.

Profile applications

@deepseek-ai/dsh-sdk-app and @deepseek-ai/dsh-acp-app compose the protocol applications over @deepseek-ai/dsh-base. The SDK bundle adds the JSON-RPC server plus app-owned help and stdio lifetime; the ACP bundle adds the automation-only ACP server plus the same application responsibilities. Both adopt the base model, tools, persistence, settings, credentials, policy, and environment behavior.

Profile manifests own patch reload:

Profile patchReload
web live
headless startup
sdk startup
acp startup

Custom profiles default to live. A startup profile still applies its bundle, profile, home-level, and invocation --patch layers, but it does not watch them after boot. dsh-base inserts the module-HMR row disabled; a profile with a tested source-module reload lifecycle must enable it explicitly. None of the shipped profiles enable server module HMR: patchReload: live uses the launcher's config-only watcher while the startup profiles install no watcher. SDK and ACP cannot safely replace their server, agents, persistence, or tool registry inside one owned stdio connection.

The shipped protocol profiles reserve stdout for protocol frames, expose help without starting transport, and route stdin EOF and signals through bounded root disposal. ACP remains automation-only. The SDK JSON-RPC methods, notification fields, and initialize.serverInfo.name remain stable. Model-visible tool and persistence defaults come from dsh-base, and runnable snapshots own those assembled application outputs.

TypeScript SDK customization

@deepseek-ai/dsh-sdk-client depends on the same-version @deepseek-ai/dsh package, resolves its installed CLI module, runs it through the current Node executable, and selects sdk by default. Both client layers expose dshBin, profile, ordered patches, dshHome, process cwd, environment, and timeouts; arbitrary command/argv launch remains an internal fake-runtime adapter.

SDK users customize plugins through profiles. dsh plugin --profile <name> ... manages persistent dependencies and bundle order, the profile's cordis.patch.yml owns persistent row changes, and launch patches supply ordered ephemeral overrides. A custom profile must retain @deepseek-ai/dsh-sdk-app or another SDK server row. Relative CLI-module, patch, explicit home, and process-cwd paths become absolute before spawn, and initialization has a finite bound whose diagnostic names the selected profile.

Direct SDK use follows normal Harness-home resolution: explicit dshHome, inherited DSH_HOME, then ~/.dsh. subagent-dsh-sdk instead requires an explicit absolute home, so a nested runtime cannot discover a person's profiles, installed plugins, credentials, or sessions through the operating-system home. DSH-specific ACP child examples also pass an isolated home; the ACP backend itself remains generic for non-DSH agents.

Python runtime

The Python runtime wheel packages the ordinary @deepseek-ai/dsh CLI from node_modules/@deepseek-ai/dsh/lib/bin.js through the private dsh-python-runtime-closure deploy manifest. The Python client selects dsh --profile sdk, ordered patch files, and an explicit Harness home; the installed dsh console command exposes the same profile grammar. The runnable Python example is examples/python-sdk-agent.

The executable family is deepseek-harness-sdk-runtime-<platform>-<arch>. The SDK wire, wheel and import distribution names, sidecar names, and wire identity deepseek-harness-sdk-runtime remain stable. The SDK package family is @deepseek-ai/dsh-sdk-client, @deepseek-ai/dsh-sdk-protocol, and @deepseek-ai/dsh-sdk-jsonrpc-server; @deepseek-ai/dsh-acp remains the ACP protocol plugin. There is no Python-specific Node application, checked-in complete config, compatibility package, forwarding executable, fallback parser, or SDK/ACP launcher alias.

Enforcement

verify-application-entrypoints scans application/package manifests, executable sources, and root demo scripts. The allowlist classifies the dsh product bin, vendor-excluded scope, the private WebWorker build tool, and test support. An unclassified shebang, a new package bin, or a demo wrapper that bypasses apps/cli/src/bin.ts fails hygiene and the primary/static CI aggregates.

Existing decisions and supersession

This decision supersedes the application-launch and package-name facts in profile plugin bundles, TypeScript SDK client and subagent backend, remove the SDK project toolchain, and single-file Python SDK runtime distribution. Those notes retain independent authority for profile layering, client/wire semantics, deleted project tooling, and native packaging.

The ACP automation-only protocol remains authoritative for ACP wire and interaction scope. The repository naming contract remains authoritative for role-based package names. No active note is fully superseded or eligible for archival.

Alternatives considered

Keep direct bins and state that profiles are preferred. Rejected: documentation cannot make profiles own plugin installation, environment loading, shutdown, and tests while a supported executable bypasses them.

Keep forwarding compatibility bins. Rejected: a forwarding executable remains another public launch name and compatibility promise. The pre-release repository can move callers directly to profiles.

Put complete standalone Cordis trees behind profile wrappers. Rejected: that centralizes argv without centralizing application composition. dsh-base plus thin app bundles gives shared policy one owner while retaining protocol-specific negative guarantees.

Accept inline plugins or a complete cordis.yml in the TypeScript constructor. Rejected: the SDK would become another package installer and application composer. Named profiles and patch files already provide persistent and per-launch customization through one resolution model.

Resolve dsh only from PATH. Rejected: ordinary Node processes do not reliably inherit a project-local .bin path. A same-version package dependency provides a deterministic runtime.

Enable module HMR in dsh-base and make unsafe profiles disable it. Rejected: the shared base also underlies custom profiles, so an enabled default makes every new application remember to opt out of source-module replacement. A disabled base makes module HMR an explicit profile capability while leaving patchReload: live config watching available.

Hot-reload protocol profiles. Rejected: replacing a protocol server or its dependencies can invalidate pending frames and SDK-owned agents. Process restart is the adoption boundary for SDK and ACP configuration changes.

Move the Python executable through profiles without a separate packaging proof. Rejected: the native VFS closure, three platform wheels, ripgrep and spawn-helper sidecars, default config discovery, and clean-install behavior require their own migration evidence.

Verification

  • Source and built CLI acceptance cover sdk and acp help, transport startup, stdout purity, EOF, signals, and root disposal.
  • Bundle configuration tests pin module HMR disabled in dsh-base and absent from shipped mode overrides; the custom live-profile e2e pins config reload through the launcher's watch-only fallback.
  • Focused unit suites cover profile launch resolution, initialization bounds, SDK retries, server readiness, and nested isolated homes with 100% coverage on the changed runtime sources.
  • Keyless ACP and SDK snapshots boot real dsh profiles and pin protocol output plus persisted logs; the nested SDK composition boots a second real profile runtime.
  • The real-API workflow caps file parallelism at four because one profile e2e file can own several complete dsh subprocess trees; workflow tests pin that resource bound.
  • The Python suite exercises exe and node carriers; packaged-runtime scenarios, native macOS executable construction, both wheels, and clean-wheel default/MCP smokes pin the deepseek-harness-sdk-runtime-* artifacts and profile launch.
  • verify-application-entrypoints includes invalid fixtures for package bins, executable sources, package-launching demo wrappers, and unclassified demos.

Consequences

  • A user changes an SDK application's plugin composition through a named profile and ordered patches, using the same installation and resolution model as every other dsh application.
  • A custom profile receives live config watching without server module HMR and opts into source-module replacement only through an explicit row override.
  • SDK and ACP share the complete base application and one set of policy and tools; snapshots present intentional assembled differences explicitly.
  • Adding @deepseek-ai/dsh increases the TypeScript client's install size in exchange for a deterministic same-version runtime.
  • Trusted user patches can add a plugin that writes to stdout and corrupt their own protocol stream; shipped profiles guarantee purity, not arbitrary third-party composition.
  • Python packages the ordinary dsh profile launcher while retaining a closed native runtime and no system-Node requirement for wheel users.