* feat(release): publish package source, declaration maps, and documentation Every release member publishes its TypeScript source and the declaration maps that resolve into it, and @deepseek-ai/dsh publishes the repository Markdown documentation staged from docs/ at prepack time. The ./src/* export most members already declared now resolves for an installed consumer, and a consumer editor follows a declaration to the implementation. Bundled JavaScript maps stay unpublished; the vendored family and the Desktop runtime file policy are unchanged. Rationale: .agents/notes/implemented/process/2026-10-08-published-source-and-docs.md * feat(release): publish JavaScript maps and compress bundles Every emitted JavaScript map ships beside the bundle it describes, and the workspace build and the client preset compress bundles without mangling identifiers. The documentation payload now mirrors the website publication manifest and is removed again by `postpack`; Desktop and the Python executable exclude the published source, documentation, and maps; `clean` removes a staged copy. Typert's publication contract gains the Remote declaration map. * fix(release): keep node bundles readable for the worker image The browser worker image lowers `yield*` only as a whole statement and rewrites `for await` loops by their source layout, so compressed node-side entries either fail that lowering or lower to unparseable code. Only the browser bundles keep compression, which removes about a third of their text. The bundle-purity assertion now matches the module-loader call regardless of the quote style compression picks, and the release-pack fixture exercises the installed package's new documentation requirement. * feat(release): publish the whole documentation tree `@deepseek-ai/dsh` copies `docs/` in full instead of the website's page set: the maintainer documents, the generated migration records, the translation guidelines, and the `*.i18n.yaml` consistency records ship with the installed package, so the documentation's own links resolve without a checkout. * feat(release): publish the upgrade guides with the documentation The staged documentation keeps the website's page set and adds every `docs/upgrade-guide/` document, which is what an installed reader consults when a released change breaks something. The rest of the maintainer tree stays out. * feat(agent-preset): ship plugin artwork skill with creator mode * docs(skills): clarify artwork icon lookup and official review steps * refactor(agent-preset): fold plugin artwork into cordis plugin development * feat(agent-preset): require detail documents for creator bundles * test(release): expect source and maps in master's new payload cases * refactor(agent-preset): consolidate creator skills into dsh-doc * feat(release): keep installed docs and source readable in Desktop Desktop keeps the published src/ and docs/ trees inside app.asar, and every model shell call receives DSH_INSTALL_DIR and DSH_NODE_EXECUTABLE so an agent can read them through Electron's Node mode. Staging also generates docs/upgrade-guide/README.md, an index of the upgrade guides grouped by the release they upgrade from. Minified client bundles print string literals with backticks: the worker image packer now recognizes those chunk requests, client license banners are prepended after minification so their text survives, and the affected artifact tests accept either quote. * docs(agent-preset): add dsh-doc install, upgrade, and fallback guidance * docs(agent-preset): align dsh-doc archive reads and upgrade start versions * docs(agent-preset): bound dsh-doc upgrade index reads and test documented commands * test(desktop): read archived docs through the Windows PowerShell form Regenerate the config catalog for the shifted shell-env source line. * docs(upgrade-guide): correct dsh-doc session verification step * test(agent-preset): use a block body for dsh-doc fixture cleanup * test(desktop): wait for the GUI-subsystem Electron process from PowerShell * docs(agent-preset): read the running DSH version from the installation * build(release): publish source and maps from the message-quotes package * docs(agent-preset): document the verified PowerShell runtime invocation * docs(agent-preset): keep PowerShell runtime output streams and exit code * docs(tool-cordis): describe the shipped dsh-doc skill * docs(skills): load configuration guidance separately * fix(skills): declare shared template details in every locale * fix(skills): verify dsh-doc guidance through recorded sessions * test(skills): retain current runtime reminder in dsh-doc replay * test(snapshot): refresh terminal panel runtime context
18 KiB
Cookbook: adding a workspace package
English | 中文
The file-by-file checklist for a new @deepseek-ai/dsh-<name> package. This checklist is validated against the bash and adapter packages as templates; if it drifts from them, fix it here.
1. Create the package
packages/<group>/<pkg>/
package.json # copy from packages/core/tools, adjust name/description/deps
tsconfig.json # extends ../../../tsconfig.base.json, rootDir src,
# outDir lib/types, references: ../../../vendor/cosmokit,
# ../../../vendor/cordis (+ ../../../vendor/schemastery if
# you use Config, + ../../<group>/<dep> for each dsh dep)
src/index.ts # service default export or plugin (name/inject/apply/Config)
locale/en.json # optional display metadata: meta.title, meta.description, and meta.detail
locale/zh.json # translations using the same fields
README.md # service API, events, extension points, design notes,
# + gated Model Experience context blocks or short form
# + the gated "Known Limitations and Deferred Work" section
# (or a whitelist entry in scripts/verify-package-readme-limitations.ts)
Choose an existing group when one matches the package's role (core, llm, shell, compaction, subagent, todo, session, client/host, util, or test-support). A new group is allowed, but it is a pure container: no package.json, no source files, and packages still sit exactly one level below it.
package.json invariants (enforced by pnpm run constraints / scripts/check-workspace-constraints.ts): private: true, a version matching the root package.json, type: module, main: "lib/index.js", types: "lib/types/index.d.ts", exports["."].types: "./lib/types/index.d.ts", exports["."].default: "./lib/index.js", @deepseek-ai/cordis in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. @deepseek-ai/schemastery goes in dependencies (it is a runtime validator), matching agent-loop. The files list contains exactly lib/index.js, lib/types/**/*.d.ts, lib/types/**/*.d.ts.map, src, and package-specific runtime artifacts recognized by the gate. A package whose runtime export points into the emitted tree also includes lib/types/**/*.js. Declaration and JavaScript maps publish beside the files they describe, and src publishes with them, so a consumer editor and a plugin author read the implementation behind an exported declaration; do not publish stale root declaration files. CLI app packages with a package bin include lib/bin.js immediately after lib/index.js in files.
In-package relative imports use explicit .ts specifiers in source (for example, export * from './types.ts'). The compiler rewrites those to .js in emitted JS and leaves explicit .ts specifiers in declarations, which standard NodeNext/Node16 TypeScript consumers resolve to the sibling .d.ts files.
2. Register it in the root configs
| File | Change |
|---|---|
tsconfig.base.json |
no edit for an existing group; for a new group, add a ./packages/<group>/*/src candidate to the @deepseek-ai/dsh-* wildcard |
tsconfig.host.json (Host package) or tsconfig.client.json (Client package) |
add { "path": "./packages/<group>/<pkg>" } to references — an ordinary package belongs to exactly one aggregate, never both. api/remotes uses a repository-specific split because the Host generates a contract that the Client consumes in a later phase; new packages must not copy it (layout) |
A packages/client/* package additionally extends tsconfig.base.client.json instead of tsconfig.base.json, and a client plugin package declares dsh.client in package.json, exports ./client, and calls the shared tsdown preset (packages/client/tsdown.client.ts) — see packages/client/AGENTS.md for the client-side contract.
Covered automatically by globs or package-manifest discovery — no edits needed: root package.json workspaces, scripts/publint-all.ts, tsdown.config.ts, .oxlintrc.json, scripts/check-workspace-constraints.ts.
3. Decide the package topology
For a swappable capability, separate Service Definition / Service Provider / Consumer roles into packages when they evolve independently (see docs/architecture.md § "Capability seams" — the shell trio is the template). A single-purpose plugin stays one package.
Name the role that exists
Name the stable current responsibility. Do not name the first implementation, a possible future expansion, or the Cordis base class. An interface package names the capability. An implementation package adds the mechanism, protocol, environment, or vendor that distinguishes it. Use local only when same-host execution is part of the contract.
Use a singular ctx key for one engine, runtime, policy, controller, resolver, store, or current configuration. Use a plural key for a registry or a service that owns multiple named members. The class role and key number must agree. Do not reuse one Cordis Context key for incompatible host and client declarations. TypeScript declaration merging sees both faces even when they use separate runtime contexts. Add the role suffix when the natural plural already belongs to another face.
| Word | Use it when | Do not use it when |
|---|---|---|
Controller |
It accepts commands or user intent and changes one existing domain or presentation state. | It executes arbitrary work, owns a provider fleet, or only converts values for display. |
Store |
It owns one data set and mainly offers CRUD, snapshot, or subscription operations for that data. | It validates a state machine, arbitrates authority, dispatches work, or owns provider precedence. A map does not make a class a store. |
Directory |
It exposes entries and metadata for discovery or selection. | Producers register arbitrary implementations into it, or callers execute work through it. |
Presenter |
It is a pure conversion from domain values or tool arguments to render intent. | It performs I/O, subscribes, mutates state, or owns lifecycle. |
Registry |
It owns a dynamic set of named registrations, including lookup, duplicate or precedence rules, lifetime, and disposal. | Its main contract is dispatch, execution, cancellation, policy, or orchestration. |
Runtime |
It runs live work and owns dispatch, cancellation, provider coordination, or operation lifecycle across calls. | It only stores records, returns a catalog, resolves one value, or holds configuration. |
Resolver |
It computes or locates one answer from supplied inputs without owning that answer's lifecycle. | It owns a mutable collection or long-running execution. |
Binder |
It attaches one declared interface to a caller context or lifecycle and returns the bound value. | It owns the value as a collection, controls its domain state, or only converts data. |
Engine |
It implements a domain algorithm or stateful execution model. | It only selects a provider or forwards across a protocol boundary. |
Policy |
It decides what is allowed, selected, limited, or observed. | It performs the mechanism that the decision permits. |
Executor |
It runs one explicit request or resolved specification in one capability. | It owns a broad application lifecycle or provider catalog. |
Gateway |
It adapts a process, network, RPC, or API boundary. | It only registers same-process services or stores metadata. |
Provider |
It supplies one implementation of a capability definition. Add a mechanism or vendor qualifier when several can exist. | It is the capability definition, provider registry, or consumer runtime. |
Backend |
It implements replaceable lower-level persistence, transport, or execution behind a defined interface. | It is a user-facing service or one returned live-resource reference. |
Handle |
It refers to one live resource and controls or observes that resource. | It creates and manages the complete resource pool. |
Config |
It owns one resolved configuration value or one tightly bounded record and its update contract. | It stores a general collection, executes work, or exposes unrelated settings. |
Service |
It owns a cohesive domain service that no sharper role above states honestly. | The name exists only because the class extends Cordis Service. |
Use SDK only for the JSON-RPC client/server protocol used by the supported Python and TypeScript SDKs. DeepSeek Harness itself is an agent harness, not an SDK project. Use the canonical product spelling Typert, never TypeRT or typeRT.
4. Write the package README
Keep package-specific service API, config, events, extension points, and design notes first. Choose the frontmatter kind from the four kind labels in the dsh-doc-writing metadata reference — group, reference, library, or bundle — matching the package's repository position and entry shape; each kind selects one README template. The limitations section records durable consumer gaps and non-obvious maintainer constraints owned by this package; ordinary cleanup stays in its source TODO or Agent Note. An indirect Model Experience sentence may name the consumer that surfaces this package's contribution, but it does not restate that consumer's implementation. End a package README with this canonical sequence:
## Model Experience
### Request context and condition
#### What the model sees
The exact data-dependent fields, an anchored generated-catalog link, or an introduction to the verbatim literal below.
##### Verbatim text for this field, when needed
```markdown
Stable system-prompt prose of any length, or another long non-generated literal, copied exactly from source.
```
#### Token effect
Fixed, conditional, retained, replaced, capped, or zero-direct token effect.
#### KV Cache effect
Append-only, prefix-stable, replacing, or independent behavior, including the exact conditions that may invalidate reuse.
## Known Limitations and Deferred Work
- **Consumer-visible gap** — exact missing operation or case, its consequence, and any maintainer constraint.
Fill Model Experience from the implementation. Use one H3 per direct, conditional, capped, lifetime, or auxiliary model-context entry, with the three ordered H4 fields shown above and one prose paragraph under each. Quote stable text owned by the package: system-prompt prose goes in a titled H5 plus markdown fence under the field that introduces it—normally What the model sees—other short literals stay inline with named placeholders, and other long literals use the same nested form. Summarize only data-dependent or provider-owned text. A tool-schema entry links its anchored section in the generated tool catalog and states only deltas absent there. Keep prompt and schema entries separate when scoping can hide one without the other. In KV Cache effect, distinguish append-only growth, a stable repeated prefix, replacement of earlier request tokens, and an independent model request, then name the package-owned changes that can invalidate reuse. “Does not invalidate” means the package preserves an already-reusable prefix; provider cache availability and eviction remain outside the package contract. The prose standard governs completeness and ownership; the verifier enforces the required section structure.
A package with no context effect or one consumer-owned path uses the audited None, as or Indirectly, through sentence in SENTENCE_MODEL_EXPERIENCE, followed by a KV Cache effect H4 and one non-empty paragraph; a model-agnostic generic package may instead join NO_MODEL_EXPERIENCE_SECTION. Do not expand either case into a description of another package's work. The limitations allowlist is independent. The archived Model Experience Agent Note records the rationale.
5. Add optional plugin display metadata
For an npm package plugin, define its title and description in locale/en.json. Other language files, such as locale/zh.json, use the same fields:
{
"meta": {
"title": "Workspace Tools",
"description": "Tools for your workspace."
}
}
Merge these entries into package.json, retaining existing runtime exports and publication files:
{
"exports": {
"./package.json": "./package.json",
"./locale/*.json": "./locale/*.json"
},
"files": ["locale/*.json"]
}
Keep language files together in locale/, with en.json as the discovery entry. Fields are optional; present values must be non-empty strings. English meta.title and meta.description use sentence case: capitalize only the first word, proper nouns, and product names, as in Git worktrees (rule). At runtime, missing files or fields permit fallback, while malformed JSON or invalid fields produce a per-plugin diagnostic. First-party packages and plugin templates must provide ja.json, es.json, fr.json, de.json, ru.json, and pt.json covering every field present in en.json or optional zh.json, preserving placeholders, slash commands, and API field names; detail paths may point to the existing English document. Subpath resources under locale/<subpath>/ follow the same checks. English descriptions explain user-visible behavior and end with sentence punctuation. Run pnpm run check:i18n to verify JSON metadata and UI dictionary completeness; the JSON checks also run in check:all, CI static checks, and hygiene.
Fields fall back independently before view-specific name formatting, using the existing locale language chain first:
- Title: locale
meta.title→package.json.namefor a package-root plugin → complete Cordis plugin name. - Description: locale
meta.description→package.json.descriptionfor a package-root plugin → no description.
Export <package name>/locale/en.json for locale lookup; expose <package name>/package.json for package-field fallback or an icon declaration. A subpath plugin such as <package name>/search is not a package and never reads a package.json, even one exported at ./search/package.json; it uses ./search/locale/*.json for text and ./search/icon for its image.
For an image on bundle cards, details, and component rows, a package-root plugin sets top-level "icon": "./icon.svg" in its exported manifest, or exports ./icon when that field is omitted. A subpath plugin exports its own icon, such as "./search/icon": "./search.svg". Include each image in files. A manifest icon is relative to the manifest's directory; absolute paths, URLs, paths outside that directory, and symlinks resolving outside it are rejected. An exported icon resolves through Node exports like locale resources and must remain inside its package. SVG, PNG, JPEG (.jpg/.jpeg), and WebP files are supported up to 256 KiB. Images must be self-contained; SVG is rendered as an image, not inline HTML. The Host returns a data URL without activating the plugin. Invalid declarations or unreadable files produce a metadata diagnostic while retaining valid text, without trying the export; missing or undecodable images use the panel's default artwork.
A bundle can also set meta.detail to a Markdown file path relative to the package root, such as "docs/DETAIL.md" in locale/en.json and "docs/DETAIL.zh.md" in locale/zh.json; it is read from these files at the package root. The path uses /, stays inside the package, ends in .md or .markdown, and has no package-field fallback; keep its images beside it, and list its top-level directory, such as docs, in files right after the locale resources, as check-workspace-constraints requires. The named file must exist in a subdirectory of the package; a root-level document is rejected because its neighboring images would not be published. A bundle's page in Plugins, and its index entry page when the custom plugin indexes bundle is on, show the file for the active UI language as the plugin's detail document. Without meta.detail they show the package README, and an index entry page then falls back to the declared repository's README; the document order defines the rules. Write it for users, and reference images by paths relative to the file.
Installed bundle cards and details, component lists and configuration details, and Settings' plugin inventory display this metadata, including disabled and preset plugins. Reads do not activate plugins.
Only Settings shortens literal package-name and module-name fallbacks by removing npm scope and Cordis/DSH prefixes; Plugin Manager keeps complete names. Locale titles and descriptions remain unchanged. A row configuration page can use its registered summary when the plugin has no display description.
The Install view still uses npm registry information from pnpm view, not locale metadata.
Verify the result:
- Run
pnpm run verify-package-metafrom the repository root to check fields, resource exports, and publication coverage. - Switch an installed plugin's applicable Plugin Manager and Settings entries between English and Chinese; check the title, description, per-field fallback, and Settings-only compact technical names.
6. Verify
pnpm install # registers the workspace
pnpm run doc-sync
pnpm run constraints && pnpm run typecheck && pnpm run lint
pnpm run build && pnpm run hygiene
Follow the repository testing policy for the behavior-specific checks and coverage required by the new package.