Files
deepseek-harness/packages/api/remotes
Turtle d1e22a7e24 feat(preset): declare Agent compositions in profile YAML (#4569)
* feat(preset): declare Agent compositions in profile YAML

* fix(preset): share scope binding cleanup and refresh module graph

* fix(preset): use host syntax colors in the YAML editor

* fix(preset): write new profile declarations as block YAML

* fix(preset): align eager activation with browser and worker surfaces

* fix(ui-agent-preset): retain the existing preset card layout

* fix(preset): keep rows waiting for a Host service mounted and re-audit after settlement

A preset row whose Host provider was still activating at declaration time
was reported as waiting for a missing service, and the registry recorded the
definition as broken for the rest of the process. Split the mount audit into
failed and pending rows: failures and root-realm leaks still reject the
mount, while pending rows stay mounted and every read and binding re-audits
them after the Host Loader tree settles.

Remove the accidental self-dependency of @deepseek-ai/dsh-web-app and its
lockfile self-link. Archive the copy-only preset authoring note, which the
declarative-preset note fully supersedes, and update the ralph note's
restore recipe to the profile-patch mechanism.

* chore(lint): re-key and prune the unknown-cast baseline after the master merge

* refactor(web-app): ship each Agent preset as its own bundle patch file

`dsh.bundle.patch` accepts an ordered file list, so the four shipped preset
declarations move out of the 1000-line web patch into
`presets/<id>.patch.yml`, one insert per file, applied after the host patch.
The host patch keeps the preset registry row. Gates and test scaffolds that
read the web layer resolve every patch file through `bundlePatchPaths`
instead of one hardcoded path.

* refactor(preset): drop the save notice injected into live Agents

The notice duplicated the save result the Web editor already shows, and
after the producer-owned message sources it would have needed its own
persisted source kind. Saving now only persists and reports the activation
diagnostic; the persistence catalog is unchanged from master.

* docs: regenerate the module graph after dropping the registry's llm dependency

* test(ui-agent-preset): cover the guide dialog's keyboard handling on the declarative section

* fix(preset): address the remaining review comments

Apply the lowercase id rule only when creating a declaration, so presets
declared with other ids stay editable. Export `isBuiltInPreset` from the
display fold and key the client card groups on it. Remove the dead `error`
and `retry` locale keys, the JSDoc orphaned by the `trust` removal, the
`trust` fields in the plugin-inventory test fixtures, and the stale
`dsh-agent-presets` package name in comments and READMEs.

* refactor(preset): make the Web preset page view-and-select only

Remove the child-plugin YAML editor, the `agent_preset` tool and the
registry's read/save Remote methods. Presets are declared and changed only by
bundle patches; the settings page keeps the roster, the new-task default and
the mode help, and its question icon explains that and offers to start a
Creator-mode task. The `editing-cordis-compositions` skill and the Creator
persona describe the bundle-based path, and the authored skill-load Session
and Web snapshots are refreshed for it.

* docs(skill): make the composition skill self-sufficient for preset authoring

State where the shipped declarations live, list every declaration field,
inline a complete minimal bundle and an override example, and name the
verification calls, so Creator mode no longer reads DSH package sources to
infer the format. Refresh the authored skill-load Session for the new text.

* test(tools): drop the removed agent_preset tool from the catalog expectation
2026-09-21 08:24:18 +00:00
..

description, kind
description kind
Application Remote assembly: selects typed Host capabilities and forwarded events for Client consumers. package-reference

@deepseek-ai/dsh-api-remotes

English | 中文

Summary

Two-sided BFF for Host Remote capabilities selected by this application. The Host entry owns the forwarded-event selection and registers its application event source with API Gateway; the Client entry imports generated /remote artifacts as runtime values, mounts each contribution through ctx.remote.$mount(), and re-exports their declaration merges. Client business packages depend on this facade rather than the Gateway implementation or individual Remote runtime entries.

Table of Contents


Use this package

@deepseek-ai/dsh-api-session-controller owns Agent and Session identity policy, including the Typert lookup resolvers used by other namespaces. This package only selects and mounts that generated Session contribution; it does not duplicate activation policy.

The Client assembly mounts Office conversion, Commands, credentials, settings, Goal, dynamic Cordis, file and Session references, read-only Host plugin inventory, message feedback, permission presets, Session Controller, subagents, and Workspace Controller contributions. The permissionPresets namespace returns the complete process-level catalog used by current-session controls. Cordis effect ownership withdraws every contribution when this assembly unloads, while @deepseek-ai/dsh-api-gateway/client owns descriptor validation, traced namespace Services, direct and scoped methods, invocation, streams, and cancellation. The Client entry consumes the shared TypertClientRemote interface through Cordis and does not import the concrete Gateway. It re-exports the Gateway Client face's declaration merges type-only, so a consumer reaching the forwarded-event vocabulary through this facade gains no runtime edge to the Gateway implementation.

This facade is also the front door for the wire type vocabulary a Client package names. It re-exports, type-only, the Remote failure vocabulary (RemoteResult, RemoteFailure, RemoteErrorCode, RemoteErrorDetailsMap), the Host facts (RemoteHostFacts), and each selected domain's client-safe payload types, so a Client feature package imports one specifier instead of reaching into dsh-typert-protocol, the Gateway, or an owner's Host entry. Two kinds of package deliberately skip this door: the API-layer packages this assembly itself selects — importing it back would close a dependency cycle — and their tests, which take the failure vocabulary from dsh-typert-protocol directly. A UI package's tests instead take the RemoteError constructor from dsh-client-test-runtime.

This package owns no physical transport or Host service discovery. It projects the application selection into generated Remote contributions and an independent Host event source per Client; API Gateway owns endpoints, carriers, cancellation, and reconnection. Its Client face can be reused by Web or a future TUI that provides the same React-free ctx.remote contract.


Forwarded Host events

src/remote-events.ts holds API_REMOTE_FORWARDED_EVENTS, the allowlist of Host Cordis events this application forwards without renaming, and therefore the legal key set of ctx.remote.$on; each entry also selects ordinary emission or Agent-scoped waterfall delivery. The type-only src/types.ts derives its selection face. Forwarding one more event requires one entry in that array: the type projection, consumer key face, and Host forwarding loop all derive from it.

The listener signature is not restated here. Each allowlisted event's Cordis Events declaration lives in its owner package's client-safe ./types export, and both faces of this package pull those declarations in. The Host face additionally asserts every entry against TypertForwardableEventEntry: an emit entry must be a declared one-way event, while a waterfall entry must be a declared Agent-scoped waterfall whose final parameter is its same-result next() callback.

The Host entry registers an independent allowlist listener set and queue for each Client stream. It rejects non-JSON ordinary-event arguments before enqueueing. For a waterfall, it projects only the top-level Agent identity and JSON request fields; a Client result must also be lossless JSON, while next() delegates to the following Host listener. Each scoped waterfall request must carry its routed Agent directly as request.agent; the Host rejects a missing or mismatched identity before forwarding. The source attaches all listeners synchronously before ctx.typertGateway.registerRemoteEvents() exposes Gateway's internal $events logical stream, so its first ready item proves that incremental delivery is active and carries the Host home for Client path display. Withdrawing the registration aborts active streams.

The payload-free permission-presets/catalog-changed event invalidates the process catalog. A Client subscribes before its first permissionPresets.catalog() read and re-reads the complete snapshot after a notification; the event carries no catalog state and changes no Session sequence.

Build boundary

Most repository packages belong to one TypeScript face: Host packages are registered in the root tsconfig.host.json, and Client packages in the root tsconfig.client.json. This package splits because its Host entry must participate in the Host Typert graph, while src/client/index.ts cannot compile until Host tsdown has generated the business packages' /remote declarations.

This package's root tsconfig.json is only a solution that references tsconfig.host.json and tsconfig.client.json. The Host aggregate and direct Host consumers reference the former, while the Client aggregate and direct Client consumers reference the latter; the package-root solution must not enter either aggregate's dependency graph. The two projects own disjoint source files and .tsbuildinfo files but share the lib/types output directory, with one deliberate exception: src/remote-events.ts and src/types.ts are listed in BOTH faces' files, because the forwarded-event allowlist is the single control point over what a consumer can receive, and the Host forwarding loop and the Client ctx.remote.$on key face must read one declaration rather than two that could drift.

That exception is not just a files entry. The root tsconfig.base.json maps @deepseek-ai/dsh-api-remotes/types to src/types.ts — the source plane, like every other workspace subpath and unlike the generated /remote artifacts, which have no paths entry and resolve through exports to built output. Both faces therefore admit the same allowlist and type projection into their own programs and emit byte-identical remote-events and types outputs into lib/types; the .tsbuildinfo files stay independent. No gate enforces the faces' source-file disjointness — scripts/project-reference-faces.ts only checks that a reference into a split project names the matching face — so this paragraph records why the double listing is intentional.

The package-local clientBundle(..., { hostPhase: true }) makes Host tsdown bundle the Host entry and the later Client tsdown bundle only the browser entry. Ordinary Client plugins remain single Client projects and produce both their Node loader entry and browser bundle during Client tsdown; split only when the two source sets require different compiler faces.

Model Experience

None, as this BFF selects Remote application methods and forwarded events but registers nothing model-facing.

KV Cache effect

No direct effect; mounted Host capabilities own any model-visible behavior they trigger.

Known Limitations and Deferred Work

  • The capability set is fixed by explicit build-time value imports; the Client does not discover the Host's active Services or Remote definitions at runtime.
  • Additional capabilities require an explicit /remote value import and mount in this assembly.
  • Only scoped waterfalls that are still pending are replayed after reconnection; ordinary one-way notifications remain isolated best-effort deliveries and are not replayed. State that requires reliable recovery needs an owner-provided query, cursor, or opening baseline.

Dev Note

Working context for maintainers — click to expand

None.

Runtime invariant: No companion is published. Typert and the Agent/Session registries own the observed relationships.