Jelajahi Sumber

Merge pull request #3602 from deepseek-harness/worktree/dsh-package-manifest

refactor(manifest): centralize package manifest types
Yichen Jiang 3 minggu lalu
induk
melakukan
62120daf94
45 mengubah file dengan 482 tambahan dan 83 penghapusan
  1. 6 0
      .agents/notes/implemented/architecture/2026-09-05-package-manifest-types.i18n.yaml
  2. 31 0
      .agents/notes/implemented/architecture/2026-09-05-package-manifest-types.md
  3. 31 0
      .agents/notes/implemented/architecture/2026-09-05-package-manifest-types.zh.md
  4. 2 2
      docs/config-catalog.i18n.yaml
  5. 1 0
      docs/config-catalog.md
  6. 1 0
      docs/config-catalog.zh.md
  7. 2 2
      docs/module-graph.i18n.yaml
  8. 2 0
      docs/module-graph.md
  9. 2 0
      docs/module-graph.zh.md
  10. 1 0
      package.json
  11. 2 2
      packages/boot/app-boot/README.i18n.yaml
  12. 2 0
      packages/boot/app-boot/README.md
  13. 2 0
      packages/boot/app-boot/README.zh.md
  14. 1 0
      packages/boot/app-boot/package.json
  15. 0 4
      packages/boot/app-boot/src/index.ts
  16. 3 30
      packages/boot/app-boot/src/profile.ts
  17. 3 0
      packages/boot/app-boot/tsconfig.json
  18. 2 2
      packages/client/modules/README.i18n.yaml
  19. 2 0
      packages/client/modules/README.md
  20. 2 0
      packages/client/modules/README.zh.md
  21. 2 1
      packages/client/modules/package.json
  22. 2 17
      packages/client/modules/src/index.ts
  23. 1 0
      packages/client/modules/tsconfig.json
  24. 2 2
      packages/experimental/webworker-packer/README.i18n.yaml
  25. 2 0
      packages/experimental/webworker-packer/README.md
  26. 2 0
      packages/experimental/webworker-packer/README.zh.md
  27. 2 1
      packages/experimental/webworker-packer/package.json
  28. 2 8
      packages/experimental/webworker-packer/src/repository.ts
  29. 3 0
      packages/experimental/webworker-packer/tsconfig.json
  30. 2 2
      packages/util/README.i18n.yaml
  31. 1 0
      packages/util/README.md
  32. 1 0
      packages/util/README.zh.md
  33. 6 0
      packages/util/package-manifest/README.i18n.yaml
  34. 85 0
      packages/util/package-manifest/README.md
  35. 85 0
      packages/util/package-manifest/README.zh.md
  36. 35 0
      packages/util/package-manifest/package.json
  37. 15 0
      packages/util/package-manifest/src/index.ts
  38. 99 0
      packages/util/package-manifest/src/types.ts
  39. 11 0
      packages/util/package-manifest/tsconfig.json
  40. 18 0
      pnpm-lock.yaml
  41. 1 0
      scripts/doc-standard.spec.ts
  42. 4 10
      scripts/gen-session-format-catalog.ts
  43. 1 0
      scripts/verify-package-readme-model-experience.ts
  44. 1 0
      tsconfig.base.json
  45. 1 0
      tsconfig.host.json

+ 6 - 0
.agents/notes/implemented/architecture/2026-09-05-package-manifest-types.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+#   pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-05-package-manifest-types.md
+2026-09-05-package-manifest-types.md: 94317a9317ba12059e726612840e4a001be2a892
+2026-09-05-package-manifest-types.zh.md: 2c40facd2591921123b5eae71b80c516f3b1f697

+ 31 - 0
.agents/notes/implemented/architecture/2026-09-05-package-manifest-types.md

@@ -0,0 +1,31 @@
+# Agent Note: Package manifest declaration ownership
+
+Status: implemented
+
+English | [中文](2026-09-05-package-manifest-types.zh.md)
+
+## Problem
+
+External packages need Harness manifest types without depending on boot or client implementations. Keeping declarations beside individual readers obscures the complete configuration API and lets overlapping fields diverge.
+
+## Decision
+
+[`@deepseek-ai/dsh-package-manifest`](../../../../packages/util/package-manifest/README.md) owns `DshManifest` and its member declarations in one type-only file. The package belongs to the existing utility group and exports no runtime values. Author declarations and launcher-generated module fallback metadata are explicitly distinguished.
+
+Readers import the shared declarations directly. Boot retains profile loading, raw JSON checks, defaults, and resolved runtime data. Client modules retain their normalized boot graph. The image packer resolves declared paths into directories. The Session catalog generator derives a read-only validated entry with a resolved import path; raw inputs and discovery rules remain local.
+
+App-boot declares a production dependency because its published declarations reference the shared types. Client modules, the private packer, and root scripts use development dependencies because their published APIs do not expose these types. Every package consumer has a TypeScript project reference. External authors import from the utility package; app-boot provides no compatibility re-exports.
+
+## Alternatives considered
+
+**Keep declarations in individual readers.** External authors would depend on runtime implementations, and a partial profile-only definition would omit existing client and build fields.
+
+**Create a separate types group.** The existing utility group accommodates a type-only library without another package category. Runtime service and event declarations stay with their owners.
+
+**Unify the JSON parsers.** Sharing declarations does not require changing validation, errors, defaults, or parsed results; those remain owned by each reader.
+
+## Consequences
+
+Authors gain one public import path at the cost of a published package and explicit dependency edges. Existing app-boot manifest type imports must use the new package. The [profile composition design](2026-08-05-profile-plugin-bundles.md) continues to own runtime semantics; type extraction does not change configuration acceptance or model-visible behavior.
+
+Compiler and packaged NodeNext consumer checks cover public imports. Existing profile, client, image configuration, and Session catalog tests cover reader behavior; documentation checks cover the utility classification and generated package catalogs. Optional declaration fields still require deliberate consumer updates when added.

+ 31 - 0
.agents/notes/implemented/architecture/2026-09-05-package-manifest-types.zh.md

@@ -0,0 +1,31 @@
+# Agent Note: Package manifest 声明归属
+
+Status: implemented
+
+[English](2026-09-05-package-manifest-types.md) | 中文
+
+## 问题
+
+外部包需要 Harness manifest 类型,而无需依赖启动器或客户端实现。将声明放在各读取方旁边,会使完整配置 API 难以查找,也容易让重叠字段发生偏离。
+
+## 决策
+
+[`@deepseek-ai/dsh-package-manifest`](../../../../packages/util/package-manifest/README.zh.md) 在一个纯类型文件中拥有 `DshManifest` 及其成员声明。本包属于现有工具库分组,不导出运行时值。作者声明与启动器生成的模块后备元数据有明确区分。
+
+各读取方直接导入共享声明。启动器保留 profile 加载、原始 JSON 检查、默认值和解析后的运行时数据。客户端模块保留归一化的启动图。镜像打包器将声明路径解析为目录。Session 目录生成器派生带有已解析导入路径的只读校验结果;原始输入和发现规则仍由本地负责。
+
+App-boot 声明生产依赖,因为其发布的声明文件引用共享类型。客户端模块、私有打包器和根脚本使用开发依赖,因为其发布 API 不暴露这些类型。每个包消费方都有 TypeScript 项目引用。外部作者从工具包导入;app-boot 不提供兼容性再导出。
+
+## 考虑过的替代方案
+
+**将声明保留在各读取方。** 外部作者会依赖运行时实现,而仅覆盖 profile 的部分定义会遗漏已有的客户端和构建字段。
+
+**创建独立的 types 分组。** 现有工具库分组可以容纳纯类型库,无需增加包分类。运行时服务和事件声明仍由各自负责方拥有。
+
+**统一 JSON 解析器。** 共享声明不要求改变校验、错误、默认值或解析结果;这些仍由各读取方负责。
+
+## 后果
+
+作者获得统一的公共导入路径,代价是一个发布包和明确的依赖边。已有的 app-boot manifest 类型导入需要改用新包。[Profile 组合设计](2026-08-05-profile-plugin-bundles.zh.md) 继续负责运行时语义;类型提取不改变配置接受范围或模型可见行为。
+
+编译器与打包后的 NodeNext 消费方检查覆盖公共导入。已有 profile、客户端、镜像配置和 Session 目录测试覆盖读取行为;文档检查覆盖工具库分类与生成的包目录。新增可选声明字段时,仍需主动更新消费方。

+ 2 - 2
docs/config-catalog.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/config-catalog.md
-config-catalog.md: 884a45b7e165329a5d9dd6470d29812d95fb1b62
-config-catalog.zh.md: e8586045fb8727ccbc604b5345f8c1cac54d40e7
+config-catalog.md: 66657ff6c3f5874a4922f26fd61c65ebb7f37826
+config-catalog.zh.md: 6ae44ca258720dd1a7aa567f0ed71e996410b9ea

+ 1 - 0
docs/config-catalog.md

@@ -3468,6 +3468,7 @@ Imported as libraries by other packages; a `cordis.yml` cannot load them.
 - `@deepseek-ai/dsh-loader-smoke` ([`packages/test-support/loader-smoke/src/index.ts`](../packages/test-support/loader-smoke/src/index.ts))
 - `@deepseek-ai/dsh-native-command` ([`packages/util/native-command/src/index.ts`](../packages/util/native-command/src/index.ts))
 - `@deepseek-ai/dsh-output-retention` ([`packages/util/output-retention/src/index.ts`](../packages/util/output-retention/src/index.ts))
+- `@deepseek-ai/dsh-package-manifest` ([`packages/util/package-manifest/src/index.ts`](../packages/util/package-manifest/src/index.ts))
 - `@deepseek-ai/dsh-sandbox-windows-acl` ([`packages/sandbox/sandbox-windows-acl/src/index.ts`](../packages/sandbox/sandbox-windows-acl/src/index.ts))
 - `@deepseek-ai/dsh-scope` ([`packages/core/scope/src/index.ts`](../packages/core/scope/src/index.ts))
 - `@deepseek-ai/dsh-sdk-client` ([`packages/sdk/client/src/index.ts`](../packages/sdk/client/src/index.ts))

+ 1 - 0
docs/config-catalog.zh.md

@@ -3469,6 +3469,7 @@ export interface Config {
 - `@deepseek-ai/dsh-loader-smoke`([`packages/test-support/loader-smoke/src/index.ts`](../packages/test-support/loader-smoke/src/index.ts))
 - `@deepseek-ai/dsh-native-command`([`packages/util/native-command/src/index.ts`](../packages/util/native-command/src/index.ts))
 - `@deepseek-ai/dsh-output-retention`([`packages/util/output-retention/src/index.ts`](../packages/util/output-retention/src/index.ts))
+- `@deepseek-ai/dsh-package-manifest` ([`packages/util/package-manifest/src/index.ts`](../packages/util/package-manifest/src/index.ts))
 - `@deepseek-ai/dsh-sandbox-windows-acl`([`packages/sandbox/sandbox-windows-acl/src/index.ts`](../packages/sandbox/sandbox-windows-acl/src/index.ts))
 - `@deepseek-ai/dsh-scope`([`packages/core/scope/src/index.ts`](../packages/core/scope/src/index.ts))
 - `@deepseek-ai/dsh-sdk-client`([`packages/sdk/client/src/index.ts`](../packages/sdk/client/src/index.ts))

+ 2 - 2
docs/module-graph.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/module-graph.md
-module-graph.md: be0bc1db1222ba7b95dff735a339bef680b86cf5
-module-graph.zh.md: e1fdaae73ac398076598316efcfa45822e378155
+module-graph.md: 5286f8729930be0abc4c5f11325c93408915b808
+module-graph.zh.md: 0a43108841558b68f376f6129b8f52b8bed2c244

+ 2 - 0
docs/module-graph.md

@@ -16,6 +16,7 @@ flowchart TD
     pkg_launch_environment["launch-environment"]
     pkg_native_command["native-command"]
     pkg_output_retention["output-retention"]
+    pkg_package_manifest["package-manifest"]
     pkg_timeout["timeout"]
     pkg_util_crypto["util-crypto"]
     pkg_util_time["util-time"]
@@ -1162,6 +1163,7 @@ flowchart TD
 | [`launch-environment`](../packages/util/launch-environment) | `util` | — |
 | [`native-command`](../packages/util/native-command) | `util` | — |
 | [`output-retention`](../packages/util/output-retention) | `util` | — |
+| [`package-manifest`](../packages/util/package-manifest) | `util` | — |
 | [`timeout`](../packages/util/timeout) | `util` | — |
 | [`util-crypto`](../packages/util/crypto) | `util` | — |
 | [`util-time`](../packages/util/time) | `util` | — |

+ 2 - 0
docs/module-graph.zh.md

@@ -18,6 +18,7 @@ flowchart TD
     pkg_launch_environment["launch-environment"]
     pkg_native_command["native-command"]
     pkg_output_retention["output-retention"]
+    pkg_package_manifest["package-manifest"]
     pkg_timeout["timeout"]
     pkg_util_crypto["util-crypto"]
     pkg_util_time["util-time"]
@@ -1164,6 +1165,7 @@ flowchart TD
 | [`launch-environment`](../packages/util/launch-environment) | `util` | — |
 | [`native-command`](../packages/util/native-command) | `util` | — |
 | [`output-retention`](../packages/util/output-retention) | `util` | — |
+| [`package-manifest`](../packages/util/package-manifest) | `util` | — |
 | [`timeout`](../packages/util/timeout) | `util` | — |
 | [`util-crypto`](../packages/util/crypto) | `util` | — |
 | [`util-time`](../packages/util/time) | `util` | — |

+ 1 - 0
package.json

@@ -161,6 +161,7 @@
     "postinstall": "node scripts/install-lefthook.mjs"
   },
   "devDependencies": {
+    "@deepseek-ai/dsh-package-manifest": "workspace:^",
     "@deepseek-ai/dsh-tool-session-query": "workspace:^",
     "@deepseek-ai/dsh-web-fetch-http": "workspace:^",
     "@stylistic/eslint-plugin": "^5.10.0",

+ 2 - 2
packages/boot/app-boot/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/boot/app-boot/README.md
-README.md: 72b10524b6632f90d3bf7852b0504718518a5c33
-README.zh.md: bbbae612a6fdc9c1060902691f4349ca0b9ae806
+README.md: 3cc8247eb366f278acc8bb86b30da75fe3ab3ee4
+README.zh.md: 1ea2ce4a90c5aa57edf9761ae2b85b04405e103c

+ 2 - 0
packages/boot/app-boot/README.md

@@ -45,6 +45,8 @@ With that entry point, success looks like a running app with every plugin active
 <a id="profiles"></a>
 ### Profiles
 
+Import profile and bundle declaration types from [`@deepseek-ai/dsh-package-manifest`](../../util/package-manifest/README.md). App-boot owns profile loading, JSON validation, and resolved runtime data.
+
 A profile is how one dsh installation ships different app surfaces: `web`, `headless`, `acp`, `sdk`, and `sdk-minimal` start distinct compositions from the same launcher. A profile lives at `$DSH_HOME/profiles/<name>` and combines installable bundles, its own `cordis.patch.yml`, and `patchReload: live | startup`; omitted reload policy keeps the historical `live` default for custom profiles. The shipped `web` template uses live reload, while the other shipped templates apply patches only at startup. `sdk-minimal` names only its standalone bundle; the other templates retain base-plus-mode stacks. `dsh plugin` creates custom profiles, and a missing bundle or one without a patch declaration fails startup loudly.
 
 Your machine-local preferences also live in the Harness home:

+ 2 - 0
packages/boot/app-boot/README.zh.md

@@ -45,6 +45,8 @@ const ctx = await boot('dsh', resolveConfigPath(argv[2], process.env.DSH_SNAPSHO
 <a id="profiles"></a>
 ### Profile
 
+Profile 与 bundle 的声明类型从 [`@deepseek-ai/dsh-package-manifest`](../../util/package-manifest/README.zh.md) 导入。App-boot 负责 profile 加载、JSON 校验和解析后的运行时数据。
+
 profile 是同一套 dsh 安装提供不同应用界面的方式:`web`、`headless`、`acp`、`sdk` 与 `sdk-minimal` 从同一 launcher 启动不同组合。profile 位于 `$DSH_HOME/profiles/<name>`,由可安装 bundle、自身 `cordis.patch.yml` 与 `patchReload: live | startup` 组成;自定义 profile 省略 reload 策略时保留历史 `live` 默认值。随产品交付的 `web` 模板实时重载,其他随附模板只在启动时应用 patch。`sdk-minimal` 只列出自身的独立 bundle,其他模板保留 base 加模式 bundle 的栈。`dsh plugin` 创建自定义 profile;缺失 bundle 或未声明 patch 的 bundle 会让启动明确失败。
 
 你的机器本地偏好同样位于 harness home 中:

+ 1 - 0
packages/boot/app-boot/package.json

@@ -28,6 +28,7 @@
   "license": "MIT",
   "dependencies": {
     "@deepseek-ai/dsh-atomic-write": "workspace:^",
+    "@deepseek-ai/dsh-package-manifest": "workspace:^",
     "js-yaml": "^4.2.0",
     "resolve.exports": "^2.0.3"
   },

+ 0 - 4
packages/boot/app-boot/src/index.ts

@@ -41,14 +41,10 @@ export {
   resolveBundleDir,
   resolveProfileDir,
   writeProfileManifest,
-  type DshBundleManifest,
-  type DshManifestSection,
-  type DshProfileManifest,
   type Profile,
   type ProfileLayer,
   type ProfileManifest,
   type ProfileModuleFallbackOptions,
-  type ProfilePatchReload,
   type ProfileTemplate,
 } from './profile.ts'
 

+ 3 - 30
packages/boot/app-boot/src/profile.ts

@@ -34,6 +34,7 @@ import { withFileLock } from '@deepseek-ai/dsh-atomic-write'
 import type { EntryOptions } from '@deepseek-ai/cordis-plugin-loader'
 import { applyEntryPatches, type PatchOptions } from '@deepseek-ai/cordis-plugin-include'
 import { resolveDshHome } from '@deepseek-ai/dsh-home-paths'
+import type { DshManifest, DshModuleFallbackManifest, ProfilePatchReload } from '@deepseek-ai/dsh-package-manifest'
 import { resolve as resolvePackage, type Package as ResolvePackageManifest } from 'resolve.exports'
 import { loadOverlayPatches } from './index.ts'
 
@@ -46,23 +47,6 @@ export const PROFILE_PATCH_FILENAME = 'cordis.patch.yml'
 /** Profile-private package links projected into its pnpm-managed node_modules. */
 const PROFILE_MODULE_FALLBACK_DIR = '.dsh-module-fallback'
 
-/** The bundle half of the `dsh` manifest section: what a bundle package exports. */
-export interface DshBundleManifest {
-  /** The patch layer this bundle exports, relative to its package root. */
-  patch: string
-}
-
-/** The profile half of the `dsh` manifest section: what a profile directory composes. */
-export interface DshProfileManifest {
-  /** Ordered bundle layer list (package names). */
-  bundles?: string[]
-  /** Whether user patch files reload while this profile remains active. */
-  patchReload?: ProfilePatchReload
-}
-
-/** User patch-file lifecycle selected by a profile. */
-export type ProfilePatchReload = 'live' | 'startup'
-
 /** Installation-owned defaults used when a shipped profile is first opened. */
 export interface ProfileTemplate {
   /** Ordered bundle layer list. */
@@ -71,23 +55,12 @@ export interface ProfileTemplate {
   patchReload: ProfilePatchReload
 }
 
-/**
- * The profile-launcher slice of the `dsh`-owned package.json section. A
- * manifest may declare both roles; other consumers own additional keys.
- */
-export interface DshManifestSection {
-  /** Bundle metadata consumed by the profile launcher. */
-  bundle?: DshBundleManifest
-  /** Profile metadata consumed by the profile launcher. */
-  profile?: DshProfileManifest
-}
-
 /** The slice of package.json both profiles and bundles use. */
 export interface ProfileManifest {
   name?: string
   dependencies?: Record<string, string>
   peerDependencies?: Record<string, string>
-  dsh?: DshManifestSection
+  dsh?: DshManifest
 }
 
 /** One resolved bundle layer of a profile. */
@@ -335,7 +308,7 @@ interface ModuleProxyManifest {
   private: true
   type: 'module'
   exports: Record<string, string>
-  dsh: { moduleFallback: { targets: Record<string, string> } }
+  dsh: { moduleFallback: DshModuleFallbackManifest }
 }
 
 interface ModuleProxyRecord {

+ 3 - 0
packages/boot/app-boot/tsconfig.json

@@ -34,6 +34,9 @@
     },
     {
       "path": "../../util/home-paths"
+    },
+    {
+      "path": "../../util/package-manifest"
     }
   ]
 }

+ 2 - 2
packages/client/modules/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/client/modules/README.md
-README.md: 414d34af202ee4ab24e1b7570697ad63bd8cdbc8
-README.zh.md: e6adc7b7d0265e52d5108e241a8f8a551976536d
+README.md: 7bfa39d183bb29f3f9481bb39280c5521786031a
+README.zh.md: 64dc5644f535722b464c867616b18ff557e921be

+ 2 - 0
packages/client/modules/README.md

@@ -25,6 +25,8 @@ English | [中文](README.zh.md)
 <a id="use-this-package"></a>
 ## Use this package
 
+Use [`DshClientManifest`](../../util/package-manifest/README.md) for the declaration type. Client-modules validates the JSON and owns the normalized boot graph.
+
 Use it when you compose or build a browser client plugin: the package turns a package's `dsh.client` declaration into a loadable browser bundle with no per-plugin wiring. It activates with the web composition; the shell boots it before any plugin runs.
 
 ### Declaring a client plugin

+ 2 - 0
packages/client/modules/README.zh.md

@@ -25,6 +25,8 @@ kind: "package-reference"
 <a id="use-this-package"></a>
 ## 使用本包
 
+声明类型使用 [`DshClientManifest`](../../util/package-manifest/README.zh.md)。Client-modules 负责 JSON 校验和归一化的启动图。
+
 组合或构建浏览器客户端插件时使用它:本包把包的 `dsh.client` 声明变成可加载的浏览器 bundle,无需任何逐插件接线。它随 web 组合激活;外壳在任何插件运行前启动它。
 
 ### 声明客户端插件

+ 2 - 1
packages/client/modules/package.json

@@ -45,7 +45,8 @@
     "@deepseek-ai/cordis-plugin-loader": "workspace:^",
     "@deepseek-ai/dsh-host-webserver": "workspace:^",
     "@deepseek-ai/dsh-invariants": "workspace:^",
-    "@deepseek-ai/cordis": "workspace:^"
+    "@deepseek-ai/cordis": "workspace:^",
+    "@deepseek-ai/dsh-package-manifest": "workspace:^"
   },
   "files": [
     "lib/index.js",

+ 2 - 17
packages/client/modules/src/index.ts

@@ -33,6 +33,7 @@ import { Service } from '@deepseek-ai/cordis'
 import type { Context } from '@deepseek-ai/cordis'
 import type { Entry } from '@deepseek-ai/cordis-plugin-loader'
 import type { IndexInjection } from '@deepseek-ai/dsh-host-webserver'
+import type { DshClientManifest } from '@deepseek-ai/dsh-package-manifest'
 import { optionalStringArray, stripClientSuffix } from './client/manifest.ts'
 import type { WebBootBatch, WebBootBatchPhase, WebBootEntry, WebBootGraph } from './client/manifest.ts'
 
@@ -48,22 +49,6 @@ declare module '@deepseek-ai/cordis' {
   }
 }
 
-/** package.json `dsh.client` declaration fields, validated one by one after reading the file. */
-interface DshClientDeclaration {
-  inject?: string[]
-  platform: string
-  /** Boot phase-one registration barrier; absent rows still ride the shared application batch. */
-  immediately?: boolean
-  /**
-   * Exact module-table requests beyond the implicit client baseline. Any
-   * specifier is valid, including subpaths such as `<pkg>/client`; each
-   * importing package declares its own exceptional requests. A type-only
-   * import is not a request because the transform erases it before resolution.
-   * Absent means the package uses only the baseline externals.
-   */
-  external?: string[]
-}
-
 /** The declared fields a graph row carries, normalized (absent array declarations become empty). */
 interface WebBootRowFields {
   inject?: string[]
@@ -198,7 +183,7 @@ function exactPackageSpecifier(specifier: string): string | undefined {
 }
 
 /** Narrow an unknown parsed JSON value to the `dsh.client` declaration, throwing on malformed fields. */
-function parseDshClient(pkgName: string, value: unknown): DshClientDeclaration | undefined {
+function parseDshClient(pkgName: string, value: unknown): DshClientManifest | undefined {
   if (value === undefined) return undefined
   if (typeof value !== 'object' || value === null) {
     throw new Error(`client-modules: ${pkgName} has a non-object dsh.client declaration`)

+ 1 - 0
packages/client/modules/tsconfig.json

@@ -11,6 +11,7 @@
     { "path": "../../../vendor/cordis" },
     { "path": "../../../vendor/loader" },
     { "path": "../../host/webserver" },
+    { "path": "../../util/package-manifest" },
     { "path": "../../runtime-diagnostics/invariants" }
   ]
 }

+ 2 - 2
packages/experimental/webworker-packer/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/experimental/webworker-packer/README.md
-README.md: 701f4a41db78fff6c6af532d1e4f8aa43d585577
-README.zh.md: 30682e314c36dbc08bcd895fb7a2277a38f7ea52
+README.md: 598a175637a6111001b07b0186c0f1a465d54a9c
+README.zh.md: 8f05e39d34bd33613065cf86fd9c6e51ff90d88e

+ 2 - 0
packages/experimental/webworker-packer/README.md

@@ -23,6 +23,8 @@ The VFS image packer: turns one composed profile into the gzip-compressed base t
 <a id="use-this-package"></a>
 ## Use this package
 
+The [`DshConfigTreeDeclaration`](../../util/package-manifest/README.md) type describes each `dsh.configTrees` entry; this packer validates it and resolves its source directory.
+
 The pack is a three-layer standard stack:
 
 1. **Roster** — the composed profile's plugin rows (standard YAML parse under Include's dialect, `!!js` intact), plus the rows of every config tree the CLI declares in its `package.json` `dsh.configTrees` (agent presets), materialized as a Node-style dependency closure. External peer edges never bind the worker; workspace peers stay on the chain.

+ 2 - 0
packages/experimental/webworker-packer/README.zh.md

@@ -23,6 +23,8 @@ VFS 镜像打包器:把一份合成 profile 变成浏览器 worker 挂载为
 <a id="use-this-package"></a>
 ## 使用本包
 
+[`DshConfigTreeDeclaration`](../../util/package-manifest/README.zh.md) 描述每个 `dsh.configTrees` 条目;本打包器负责校验并解析其源目录。
+
 打包是三层标准栈:
 
 1. **Roster**——合成 profile 的插件行(标准 YAML 解析、Include 方言、`!!js` 原样保留),加上 CLI 在 `package.json` `dsh.configTrees` 里声明的每棵配置树(agent presets)的行,按 Node 式依赖闭包物化。外部包的 peer 边不追,workspace peer 保留在链上。

+ 2 - 1
packages/experimental/webworker-packer/package.json

@@ -40,7 +40,8 @@
   "devDependencies": {
     "@deepseek-ai/cordis": "workspace:^",
     "@types/js-yaml": "^4.0.9",
-    "@types/picomatch": "^3.0.2"
+    "@types/picomatch": "^3.0.2",
+    "@deepseek-ai/dsh-package-manifest": "workspace:^"
   },
   "peerDependencies": {
     "@deepseek-ai/cordis": "workspace:^"

+ 2 - 8
packages/experimental/webworker-packer/src/repository.ts

@@ -12,6 +12,7 @@ import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node
 import { tmpdir } from 'node:os'
 import { join, relative } from 'node:path'
 import { DSH_HOME_ENV } from '@deepseek-ai/dsh-home-paths'
+import type { DshConfigTreeDeclaration } from '@deepseek-ai/dsh-package-manifest'
 import type { ConfigTree, ImageTree, PackResult } from './pack.ts'
 
 /**
@@ -96,13 +97,6 @@ export function composeProfile(repoRoot: string, profile: string): string {
   }
 }
 
-/** One `dsh.configTrees` declaration entry, validated field by field. */
-interface ConfigTreeDeclaration {
-  mount: string
-  path: string
-  scanRoster?: boolean
-}
-
 /**
  * Config trees the CLI package declares for deployment images
  * (`dsh.configTrees` in its package.json): `path` is relative to the CLI
@@ -125,7 +119,7 @@ export function configTrees(repoRoot: string): ConfigTree[] {
   }
   const mounts = new Set<string>()
   return declared.map((entry, index) => {
-    const tree = entry as Partial<ConfigTreeDeclaration> | null
+    const tree = entry as Partial<DshConfigTreeDeclaration> | null
     const at = `${CLI_PACKAGE} dsh.configTrees[${String(index)}]`
     if (tree === null || typeof tree !== 'object'
       || typeof tree.mount !== 'string' || tree.mount === ''

+ 3 - 0
packages/experimental/webworker-packer/tsconfig.json

@@ -19,6 +19,9 @@
     },
     {
       "path": "../../util/home-paths"
+    },
+    {
+      "path": "../../util/package-manifest"
     }
   ]
 }

+ 2 - 2
packages/util/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/util/README.md
-README.md: ab201cfbc33a51e1713804532f9c6d0a5003c19c
-README.zh.md: 751370c822a05da1b050d96bdcf9afbee1d0ea39
+README.md: 2e46d9d4573b31afa14d7f1380af106c391d19d6
+README.zh.md: c7d8b63dc3ee7e748e8a07a3d65315ff81d5476f

+ 1 - 0
packages/util/README.md

@@ -27,6 +27,7 @@ Each package provides one primitive; open a package page for how to use it.
 | Package | Role |
 |---|---|
 | [`brand/`](brand/README.md) | Nominal string types and their stateless constructor |
+| [`package-manifest/`](package-manifest/README.md) | Shared TypeScript declarations for package manifests |
 | [`crypto/`](crypto/README.md) | Mints RFC 9562 v4 UUIDs from the cross-runtime `crypto.getRandomValues` primitive |
 | [`deque/`](deque/README.md) | Provides amortized constant-time queue operations with bounded vacant storage |
 | [`values/`](values/README.md) | Validates, snapshots, compares, and freezes lossless JSON-compatible values |

+ 1 - 0
packages/util/README.zh.md

@@ -27,6 +27,7 @@ kind: "package-group"
 | 包 | 职责 |
 |---|---|
 | [`brand/`](brand/README.zh.md) | 提供名义字符串类型及其无状态构造函数 |
+| [`package-manifest/`](package-manifest/README.zh.md) | Package manifest 的共享 TypeScript 声明 |
 | [`crypto/`](crypto/README.zh.md) | 基于跨运行时 `crypto.getRandomValues` 原语生成 RFC 9562 v4 UUID |
 | [`deque/`](deque/README.zh.md) | 提供摊销常数时间的队列操作和有界空闲存储 |
 | [`values/`](values/README.zh.md) | 校验、创建快照、比较和冻结无损 JSON 兼容值 |

+ 6 - 0
packages/util/package-manifest/README.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+#   pnpm run verify-translation-pairing --write packages/util/package-manifest/README.md
+README.md: 971df84d7db3205eb8be75ba895046d4151780d2
+README.zh.md: 8989cbe9bc29bbf8112d5465eac9806c211fa88d

+ 85 - 0
packages/util/package-manifest/README.md

@@ -0,0 +1,85 @@
+---
+description: "Shared TypeScript declarations for package.json.dsh metadata, usable by boot, client, build, and external packages."
+kind: "package-library"
+---
+
+# @deepseek-ai/dsh-package-manifest
+
+English | [中文](README.zh.md)
+
+## Summary
+
+Use `DshManifest` to type a package's Harness metadata, or a member type such as `DshClientManifest` for one declaration. Boot, client, build, and external packages import the same types; each reader owns JSON validation and default resolution.
+
+## Table of Contents
+
+- [Use this package](#use-this-package)
+- [Understand the implementation](#understand-the-implementation)
+- [Further Exploration](#further-exploration)
+- [Model Experience](#model-experience)
+- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
+- [Dev Note](#dev-note)
+
+-----
+
+<a id="use-this-package"></a>
+## Use this package
+
+Import from the package root. Use a development dependency when only checking your own source; use a production dependency if your published declarations reference these types.
+
+```ts
+import type { DshClientManifest, DshManifest } from '@deepseek-ai/dsh-package-manifest'
+
+const client: DshClientManifest = { platform: 'web' }
+const dsh: DshManifest = {
+  bundle: { patch: './cordis.patch.yml' },
+  client,
+}
+```
+
+`DshManifest` describes `bundle`, `profile`, `client`, `configTrees`, `sessionFormatMigration`, and `moduleFallback`, not the surrounding npm manifest. `moduleFallback` is launcher-generated metadata and is not an author configuration entry. TypeScript checks this object and erases `import type` during compilation; JSON files cannot import types, and this example does not write a `package.json`. See [`src/types.ts`](src/types.ts) for the declarations.
+
+-----
+
+<a id="understand-the-implementation"></a>
+## Understand the implementation
+
+<details>
+<summary>Implementation internals — click to expand</summary>
+
+The package root only re-exports declarations from [`src/types.ts`](src/types.ts). No runtime invariant companion is published because the package has no runtime state or independently observable relationships.
+
+</details>
+
+-----
+
+<a id="further-exploration"></a>
+## Further Exploration
+
+- [Profile launcher](../../boot/app-boot/README.md#profiles) — manifest loading and composition.
+- [Declaration ownership](../../../.agents/notes/implemented/architecture/2026-09-05-package-manifest-types.md) — scope and dependency rationale.
+
+<a id="model-experience"></a>
+## Model Experience
+
+None, as this package only exports types.
+
+#### KV Cache effect
+
+Type declarations add no model input, so provider cache reuse is unaffected.
+
+## Known Limitations and Deferred Work
+
+<a id="known-limitations-and-deferred-work"></a>
+
+- **Static typing only.** These declarations do not validate JSON, check file existence, or supply defaults. `configTrees` serves the experimental image packer, and `sessionFormatMigration` is discovered only for workspace migration packages; declaring them does not register external plugin behavior.
+
+<a id="dev-note"></a>
+### Dev Note
+
+<details>
+<summary>Working context for maintainers — click to expand</summary>
+
+None.
+
+</details>

+ 85 - 0
packages/util/package-manifest/README.zh.md

@@ -0,0 +1,85 @@
+---
+description: "供启动器、客户端、构建工具和外部包共同使用的 package.json.dsh 元数据 TypeScript 声明。"
+kind: "package-library"
+---
+
+# @deepseek-ai/dsh-package-manifest
+
+[English](README.md) | 中文
+
+## 概述
+
+使用 `DshManifest` 为包的 Harness 元数据添加类型,也可用 `DshClientManifest` 等成员类型描述单项声明。启动器、客户端、构建工具和外部包导入同一组类型;各读取方负责 JSON 校验和默认值解析。
+
+## 目录
+
+- [使用本包](#use-this-package)
+- [理解实现](#understand-the-implementation)
+- [进一步探索](#further-exploration)
+- [模型体验](#model-experience)
+- [已知限制与后续工作](#known-limitations-and-deferred-work)
+- [开发备注](#dev-note)
+
+-----
+
+<a id="use-this-package"></a>
+## 使用本包
+
+从包根导入类型。仅检查自己的源码时使用开发依赖;若发布的声明文件引用这些类型,则使用生产依赖。
+
+```ts
+import type { DshClientManifest, DshManifest } from '@deepseek-ai/dsh-package-manifest'
+
+const client: DshClientManifest = { platform: 'web' }
+const dsh: DshManifest = {
+  bundle: { patch: './cordis.patch.yml' },
+  client,
+}
+```
+
+`DshManifest` 描述 `bundle`、`profile`、`client`、`configTrees`、`sessionFormatMigration` 和 `moduleFallback`,不包含外层 npm manifest。`moduleFallback` 是启动器生成的元数据,不是作者配置项。TypeScript 检查该对象,并在编译时删除 `import type`;JSON 文件不能导入类型,此示例也不会写入 `package.json`。声明见 [`src/types.ts`](src/types.ts)。
+
+-----
+
+<a id="understand-the-implementation"></a>
+## 理解实现
+
+<details>
+<summary>实现细节——点击展开</summary>
+
+包根仅重新导出 [`src/types.ts`](src/types.ts) 中的声明。本包不发布运行时不变量伴随模块,因为它没有运行时状态或可独立观察的关系。
+
+</details>
+
+-----
+
+<a id="further-exploration"></a>
+## 进一步探索
+
+- [Profile 启动器](../../boot/app-boot/README.zh.md#profiles)——manifest 加载与组合。
+- [声明归属](../../../.agents/notes/implemented/architecture/2026-09-05-package-manifest-types.zh.md)——范围与依赖依据。
+
+<a id="model-experience"></a>
+## 模型体验
+
+无,因为本包仅导出类型。
+
+#### KV Cache 影响
+
+类型声明不增加模型输入,因此不影响提供方的缓存复用。
+
+## 已知限制与后续工作
+
+<a id="known-limitations-and-deferred-work"></a>
+
+- **仅提供静态类型。** 这些声明不校验 JSON、不检查文件存在性,也不提供默认值。`configTrees` 服务于实验性镜像打包器,`sessionFormatMigration` 仅从工作区迁移包中发现;声明它们不会注册外部插件行为。
+
+<a id="dev-note"></a>
+### 开发备注
+
+<details>
+<summary>维护者的工作上下文——点击展开</summary>
+
+无。
+
+</details>

+ 35 - 0
packages/util/package-manifest/package.json

@@ -0,0 +1,35 @@
+{
+  "name": "@deepseek-ai/dsh-package-manifest",
+  "description": "Shared type declarations for package.json.dsh configuration fields",
+  "version": "0.1.3-alpha.1",
+  "publishConfig": {
+    "access": "public"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
+    "directory": "packages/util/package-manifest"
+  },
+  "type": "module",
+  "main": "lib/index.js",
+  "types": "lib/types/index.d.ts",
+  "exports": {
+    ".": {
+      "types": "./lib/types/index.d.ts",
+      "default": "./lib/index.js"
+    },
+    "./src/*": "./src/*",
+    "./package.json": "./package.json"
+  },
+  "files": [
+    "lib/index.js",
+    "lib/types/**/*.d.ts"
+  ],
+  "license": "MIT",
+  "peerDependencies": {
+    "@deepseek-ai/cordis": "workspace:^"
+  },
+  "devDependencies": {
+    "@deepseek-ai/cordis": "workspace:^"
+  }
+}

+ 15 - 0
packages/util/package-manifest/src/index.ts

@@ -0,0 +1,15 @@
+/**
+ * Public package manifest types, with no runtime exports.
+ * @module @deepseek-ai/dsh-package-manifest
+ */
+
+export type {
+  DshBundleManifest,
+  DshClientManifest,
+  DshConfigTreeDeclaration,
+  DshManifest,
+  DshModuleFallbackManifest,
+  DshProfileManifest,
+  DshSessionFormatMigrationManifest,
+  ProfilePatchReload,
+} from './types.ts'

+ 99 - 0
packages/util/package-manifest/src/types.ts

@@ -0,0 +1,99 @@
+/**
+ * Shared declarations for `package.json.dsh`.
+ * Each reader owns JSON validation and resolved defaults.
+ * @module @deepseek-ai/dsh-package-manifest/types
+ */
+
+/** The `dsh` property of an npm manifest; a package may declare several roles. */
+export interface DshManifest {
+  /** Bundle metadata consumed by the profile launcher. */
+  bundle?: DshBundleManifest
+  /** Profile metadata consumed by the profile launcher. */
+  profile?: DshProfileManifest
+  /** Client module loading and build metadata. */
+  client?: DshClientManifest
+  /** Config directories consumed by the experimental deployment-image packer. */
+  configTrees?: DshConfigTreeDeclaration[]
+  /** Adjacent Session migration metadata consumed by the workspace catalog generator. */
+  sessionFormatMigration?: DshSessionFormatMigrationManifest
+  /**
+   * Launcher-generated module proxy metadata, not an author configuration entry.
+   * @internal
+   */
+  moduleFallback?: DshModuleFallbackManifest
+}
+
+/** The configuration layer exported by a bundle package. */
+export interface DshBundleManifest {
+  /** Patch file path relative to the declaring package root. */
+  patch: string
+}
+
+/** The bundle composition declared by a profile directory. */
+export interface DshProfileManifest {
+  /** Ordered bundle layer list, using installed package names. */
+  bundles?: string[]
+  /** User patch lifecycle; omitted means `live` for custom profiles. */
+  patchReload?: ProfilePatchReload
+}
+
+/** Whether user patch files reload while a profile remains active or apply only at startup. */
+export type ProfilePatchReload = 'live' | 'startup'
+
+/** Client module declaration read by client-modules and the client build. */
+export interface DshClientManifest {
+  /** Client platform identifier; the Web consumer selects `web`. */
+  platform: string
+  /** Informational package-name dependencies, not Cordis service injection. */
+  inject?: string[]
+  /** Boot phase-one registration barrier; absent means the shared application batch. */
+  immediately?: boolean
+  /**
+   * Exact module-table requests beyond the implicit client baseline, including
+   * subpaths such as `<pkg>/client`; absent means baseline externals only.
+   * Type-only imports are erased and create no module request.
+   */
+  external?: string[]
+}
+
+/** One config directory read from the CLI package by the experimental image packer. */
+export interface DshConfigTreeDeclaration {
+  /** Non-empty destination path in the image; mount values must be unique. */
+  mount: string
+  /** Non-empty source directory path relative to the declaring package root. */
+  path: string
+  /** Include the directory's YAML plugin rows in the package roster; absent means false. */
+  scanRoster?: boolean
+}
+
+/**
+ * Adjacent Session migration metadata declared on disk. The catalog generator
+ * discovers only packages/session/session-format-vN-to-vN+1, not external plugins.
+ */
+export interface DshSessionFormatMigrationManifest {
+  /** Non-negative safe integer source version; negative zero is rejected. */
+  from: number
+  /** Non-negative safe integer target version, exactly from + 1. */
+  to: number
+  /** Non-empty package export path, such as `.` or `./migration`. */
+  export: string
+  /** Non-empty named export of the migration implementation. */
+  migration: string
+  /** Non-empty named export of the source version codec. */
+  sourceCodec: string
+  /** Non-empty named export of the target version codec. */
+  targetCodec: string
+  /** Non-empty named export of the target header validator. */
+  targetHeaderValidator: string
+  /** Non-empty named export of the target version restorer. */
+  targetRestorer: string
+}
+
+/**
+ * Metadata generated and read by the launcher's module fallback proxies.
+ * @internal
+ */
+export interface DshModuleFallbackManifest {
+  /** Package export subpaths mapped to resolved target file URLs. */
+  targets: Record<string, string>
+}

+ 11 - 0
packages/util/package-manifest/tsconfig.json

@@ -0,0 +1,11 @@
+{
+  "extends": "../../../tsconfig.base.json",
+  "compilerOptions": {
+    "rootDir": "src",
+    "outDir": "lib/types",
+    "types": []
+  },
+  "include": [
+    "src"
+  ]
+}

+ 18 - 0
pnpm-lock.yaml

@@ -16,6 +16,9 @@ importers:
 
   .:
     devDependencies:
+      '@deepseek-ai/dsh-package-manifest':
+        specifier: workspace:^
+        version: link:packages/util/package-manifest
       '@deepseek-ai/dsh-tool-session-query':
         specifier: workspace:^
         version: link:packages/session-query/tool-session-query
@@ -973,6 +976,9 @@ importers:
       '@deepseek-ai/dsh-atomic-write':
         specifier: workspace:^
         version: link:../../util/atomic-write
+      '@deepseek-ai/dsh-package-manifest':
+        specifier: workspace:^
+        version: link:../../util/package-manifest
       js-yaml:
         specifier: ^4.2.0
         version: 4.2.0
@@ -1863,6 +1869,9 @@ importers:
       '@deepseek-ai/dsh-invariants':
         specifier: workspace:^
         version: link:../../runtime-diagnostics/invariants
+      '@deepseek-ai/dsh-package-manifest':
+        specifier: workspace:^
+        version: link:../../util/package-manifest
 
   packages/client/store:
     dependencies:
@@ -4907,6 +4916,9 @@ importers:
       '@deepseek-ai/cordis':
         specifier: workspace:^
         version: link:../../../vendor/cordis
+      '@deepseek-ai/dsh-package-manifest':
+        specifier: workspace:^
+        version: link:../../util/package-manifest
       '@types/js-yaml':
         specifier: ^4.0.9
         version: 4.0.9
@@ -9413,6 +9425,12 @@ importers:
         specifier: workspace:^
         version: link:../../../vendor/cordis
 
+  packages/util/package-manifest:
+    devDependencies:
+      '@deepseek-ai/cordis':
+        specifier: workspace:^
+        version: link:../../../vendor/cordis
+
   packages/util/time:
     devDependencies:
       '@deepseek-ai/cordis':

+ 1 - 0
scripts/doc-standard.spec.ts

@@ -85,6 +85,7 @@ const PACKAGE_LIBRARIES: Readonly<Record<string, string>> = {
   'packages/util/launch-environment': 'Zero-dependency environment resolver.',
   'packages/util/native-command': 'Host-side subprocess runner utility.',
   'packages/util/output-retention': 'Zero-dependency retention utility.',
+  'packages/util/package-manifest': 'Shared package manifest declarations with type-only exports.',
   'packages/util/time': 'Zero-dependency time-zone canonicalization utility.',
   'packages/util/timeout': 'Zero-dependency timeout utility.',
   'packages/util/values': 'Stateless lossless-JSON and immutable-value helpers.',

+ 4 - 10
scripts/gen-session-format-catalog.ts

@@ -3,21 +3,15 @@
 import { globSync, readFileSync, writeFileSync } from 'node:fs'
 import { resolve } from 'node:path'
 import { pathToFileURL } from 'node:url'
+import type { DshSessionFormatMigrationManifest } from '@deepseek-ai/dsh-package-manifest'
 
 const root = resolve(import.meta.dirname, '..')
 const OUT = 'packages/session/session-format-catalog/src/generated.ts'
 
-/** One adjacent migration declaration read from a workspace manifest. */
-export interface SessionFormatMigrationManifest {
+/** Validated adjacent migration metadata with its resolved package import path. */
+export interface SessionFormatMigrationManifest extends Readonly<Omit<DshSessionFormatMigrationManifest, 'export'>> {
   readonly packageName: string
   readonly importPath: string
-  readonly from: number
-  readonly to: number
-  readonly migration: string
-  readonly sourceCodec: string
-  readonly targetCodec: string
-  readonly targetHeaderValidator: string
-  readonly targetRestorer: string
 }
 
 interface RawManifest {
@@ -78,7 +72,7 @@ export function collectSessionFormatMigrations(
     if (metadata === undefined) {
       throw new Error(`gen-session-format-catalog: ${rel} lacks dsh.sessionFormatMigration`)
     }
-    const allowed = new Set([
+    const allowed: ReadonlySet<string> = new Set<keyof DshSessionFormatMigrationManifest>([
       'from', 'to', 'export', 'migration', 'sourceCodec', 'targetCodec',
       'targetHeaderValidator', 'targetRestorer',
     ])

+ 1 - 0
scripts/verify-package-readme-model-experience.ts

@@ -57,6 +57,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
   'packages/client/ui-agent-preset': { kind: 'indirect', reason: 'Browser-side settings row; the preset it selects owns every model-facing effect.' },
   'packages/util/crypto': { kind: 'indirect', reason: 'Pure identifier minting; the ids consumers mint with it never enter prompts as semantic content.' },
   'packages/util/deque': { kind: 'none', reason: 'In-process collection primitive; registers nothing model-facing.' },
+  'packages/util/package-manifest': { kind: 'none', reason: 'Type declarations only; registers nothing model-facing.' },
   'packages/util/time': { kind: 'indirect', reason: 'Pure zone validation; the consumer that records a canonical zone owns the model-visible line derived from it.' },
   'packages/core/agent-default-model': { kind: 'indirect', reason: 'The service supplies a ModelSelection; request assembly and adapters own the model-visible request.' },
   'packages/llm/deepseek-llm-api-extensions': { kind: 'indirect', reason: 'The registry contributes model-hidden provider fields; dsh-llm-deepseek owns their wire placement.' },

+ 1 - 0
tsconfig.base.json

@@ -308,6 +308,7 @@
       "@deepseek-ai/dsh-message-feedback": ["./packages/feedback/message-feedback/src"],
       "@deepseek-ai/dsh-native-command": ["./packages/util/native-command/src"],
       "@deepseek-ai/dsh-output-retention": ["./packages/util/output-retention/src"],
+      "@deepseek-ai/dsh-package-manifest": ["./packages/util/package-manifest/src"],
       "@deepseek-ai/dsh-permission-presets": ["./packages/interaction/permission-presets/src"],
       "@deepseek-ai/dsh-permission-presets/invariant": ["./packages/interaction/permission-presets/src/invariant.ts"],
       "@deepseek-ai/dsh-persona": ["./packages/preset/persona/src"],

+ 1 - 0
tsconfig.host.json

@@ -131,6 +131,7 @@
     { "path": "./vendor/hmr" },
     { "path": "./vendor/logger-console" },
     { "path": "./packages/util/brand" },
+    { "path": "./packages/util/package-manifest" },
     { "path": "./packages/util/http-proxy" },
     { "path": "./packages/util/launch-environment" },
     { "path": "./packages/util/native-command" },