Merge pull request #3165 from deepseek-harness/worktree-npmdepresolve

fix(npm): bound published peer dependency relays
This commit is contained in:
imccyu
2026-08-29 15:20:25 +08:00
committed by GitHub
86 changed files with 3813 additions and 2163 deletions
@@ -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 .agents/notes/implemented/architecture/2026-08-15-client-shells-and-dynamic-packages.md
2026-08-15-client-shells-and-dynamic-packages.md: 016314d10f55e0b590e98944ca417bae658ab56a
2026-08-15-client-shells-and-dynamic-packages.zh.md: 4e0277d1becab8467521dc21d0e5b7509d1ee993
2026-08-15-client-shells-and-dynamic-packages.md: 1d67c778b6a06849324dd6095a98d57dc41b94f9
2026-08-15-client-shells-and-dynamic-packages.zh.md: db1e4e7e7b319c283ae39a94535d88d4dc71d60a
@@ -57,11 +57,11 @@ After the `immediately` tier has registered its factories, the kernel creates al
### Dependency declarations
Every client package keeps Cordis in matching `peerDependencies` and `devDependencies`. A dynamic package that imports, re-exports, augments, or names an internal dynamic package in `dsh.client.inject` keeps that package as matching peer and development dependencies. Static client inputs and React modules are development-only inputs for a dynamic package because the shell supplies their runtime identities.
Every Client package keeps Cordis in matching `peerDependencies` and `devDependencies`; Cordis is its only peer. Browser imports, type references, module augmentations, and `dsh.client.inject` are development inputs because the Client build and shipped profile supply their runtime identities. A package that also publishes a Host entry keeps that entry's runtime value imports in `dependencies`. [Published dependency faces](../process/2026-08-26-published-dependency-faces.md) owns package discovery, exceptions, and the explicit Host roster.
Ordinary installed libraries remain `dependencies`: a dynamic build may bundle a private implementation, while a `staticLinked` library retains its bare import for the final host. Each build face decides externality independently from npm sections. Published file lists cover every runtime entry, relative asset, and declaration file reached by the artifact.
`verify-client-packages` enforces these classifications, dependency sections, build forms, parser-preload alignment, shared-module requests, and module-graph acyclicity. The repository publint pass enforces publication closure. The verifier's `--fix` mode repairs only unambiguous manifest drift.
`verify-package-dependencies` enforces and repairs dependency sections. `verify-client-packages` enforces build forms, parser-preload alignment, shared-module requests, and module-graph acyclicity. The repository publint pass enforces publication closure.
## Alternatives considered
@@ -77,7 +77,7 @@ Ordinary installed libraries remain `dependencies`: a dynamic build may bundle a
## Consequences
Bundle contents stay stable when an npm dependency moves between peer and development sections, because each build face declares externality directly. Static libraries remain host-assembled, while dynamic packages retain uniform artifacts and lifecycle governance.
Bundle contents stay stable when an internal DSH relationship is development-only, because each build face declares externality directly. Static libraries remain host-assembled, while dynamic packages retain uniform artifacts and lifecycle governance. The shipped profile owns the complete Client package roster, so individual Client packages do not ask npm to solve the same graph again through peer placement.
The startup protocol depends on the modules package id, and modules must remain self-contained at runtime. Combo generation preserves its ordinary package artifact and gives every other row one shared initial transport; HMR uses the same route with that row as its sole resource. A missing bootstrap registration fails before Cordis starts; later plugin import, apply, and service-wait failures remain visible through the boot page's ACTIVE scan.
@@ -57,11 +57,11 @@ Bootstrap combo 当前只登记 modules factory。启动内核把原始图与外
### 依赖声明
每个 client 包都把 Cordis 保持为 matching `peerDependencies``devDependencies`。动态包若 import、re-export、augment 内部动态包,或在 `dsh.client.inject` 中命名它,就把该包保持为 matching peer 与开发依赖。静态 client 输入和 React 模块对动态包只是开发依赖,因为外壳提供其运行期身份
每个 Client 包都把 Cordis 保持为范围一致的 `peerDependencies``devDependencies`Cordis 是唯一的 peer。Browser import、类型引用、模块扩充与 `dsh.client.inject` 都是开发输入,因为 Client 构建与发布 profile 会提供其运行期身份。同时发布 Host 入口的包把该入口的运行期 value import 放在 `dependencies`。[发布依赖门面](../process/2026-08-26-published-dependency-faces.zh.md)负责包发现、例外与显式 Host 名册
普通安装库仍放在 `dependencies`:动态构建可以内联私有实现,而 `staticLinked` 库会保留 bare import 交给最终宿主。各构建 face 独立决定 external,不由 npm 区段推导。发布文件列表覆盖产物实际可达的每个运行期入口、相对资产和声明文件。
`verify-client-packages` 检查这些分类、依赖区段、构建形态、parser preload 对齐、共享模块请求和模块图无环性。仓库 publint pass 负责检查发布闭包。该验证器的 `--fix` 模式只修复无歧义的 manifest 漂移。
`verify-package-dependencies` 检查并修复依赖区段。`verify-client-packages` 检查构建形态、parser preload 对齐、共享模块请求和模块图无环性。仓库 publint pass 负责检查发布闭包。
## Alternatives considered
@@ -77,7 +77,7 @@ Bootstrap combo 当前只登记 modules factory。启动内核把原始图与外
## Consequences
Npm 依赖在 peer 与开发区段间移动时,bundle 内容保持稳定,因为每个构建 face 都直接声明 external。静态库继续由宿主装配,动态包则保留统一产物与生命周期治理。
内部 DSH 关系仅放在开发区段时,bundle 内容保持稳定,因为每个构建 face 都直接声明 external。静态库继续由宿主装配,动态包则保留统一产物与生命周期治理。发布 profile 拥有完整 Client 包名册,因此各 Client 包不再要求 npm 通过 peer placement 重复求解同一张图。
启动协议依赖 modules 的 package idmodules 还必须保持运行期自包含。Combo 生成保留其普通 package 产物,并为其他全部 row 提供一条共享初始传输;HMR 使用同一条路由,并只把该 row 作为资源。缺少 bootstrap registration 会在 Cordis 启动前失败;后续插件 import、apply 与 service 等待失败仍由启动页的 ACTIVE 扫描呈现。
@@ -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 .agents/notes/implemented/process/2026-08-10-npm-release-sequences.md
2026-08-10-npm-release-sequences.md: 014bfe3abb2548a369cbfc6a5f11263303e0656a
2026-08-10-npm-release-sequences.zh.md: a67311afd93d3f4f9f0a396237c9ce0b04db0a06
2026-08-10-npm-release-sequences.md: f83bce914bae4cfc5a047bf975b623579dc9156f
2026-08-10-npm-release-sequences.zh.md: c24e5c0a29af2f875136d439ce3fe06577c061b1
@@ -80,6 +80,12 @@ Every reference to a workspace member uses `workspace:^`, so `pnpm pack` substit
`scripts/check-workspace-constraints.ts` requires the protocol, so a new package cannot reintroduce a hand-written range; the invariant-companion rule requires `workspace:^` for `@deepseek-ai/dsh-invariants` for the same reason.
### Published dependency faces use an explicit policy
[`verify-package-dependencies`](../../../../scripts/verify-package-dependencies.ts) classifies workspace relationships by their published Client and Host use, keeps only Cordis as a peer in covered packages, and applies a small explicit Host roster. [Published dependency faces and bounded peer relays](2026-08-26-published-dependency-faces.md) owns the selection rules and rationale.
`pnpm run benchmark:npm-resolution` measures this graph manually with the installed npm executable. `pnpm run benchmark:npm-resolution:next` additionally tries each reachable unconfigured Host package and serially remeasures the leading candidates. Both commands use a loopback metadata registry and reject archive requests, so their duration excludes package downloads. Neither command is an aggregate gate because scheduler load and metadata completion order make wall-clock thresholds nondeterministic.
### An optional dependency is never loaded at module scope
A dependency in `optionalDependencies`, or a peer carrying `peerDependenciesMeta.<name>.optional`, may be absent from an installed tree — that absence is the whole promise of "optional". A static import is evaluated when the importing module loads, so one absent package stops being "this capability is unavailable" and becomes a load failure for everything that reaches the importing module. The failure appears only in an installed tree missing that package, and no test here constructs one: a workspace install always has every package, so the unit tests, the snapshots, and the packed-install probe all pass while the published package is broken for the consumer who declined the optional peer.
@@ -80,6 +80,12 @@ registry 的两个行为决定了「怎么尝试一次发布」。写入之间
`scripts/check-workspace-constraints.ts` 要求这个协议,所以新包无法再引入硬写的范围;同理,invariant companion 规则要求 `@deepseek-ai/dsh-invariants``workspace:^`
### 发布依赖门面使用显式策略
[`verify-package-dependencies`](../../../../scripts/verify-package-dependencies.ts) 按已发布的 Client 与 Host 用法分类 workspace 关系,让受管包只保留 Cordis peer,并应用一份较小的显式 Host 名册。[发布依赖门面与有限 peer 中继](2026-08-26-published-dependency-faces.zh.md)记录选包规则与理由。
`pnpm run benchmark:npm-resolution` 使用当前安装的 npm 手动测量该依赖图。`pnpm run benchmark:npm-resolution:next` 还会逐个尝试每个可达且未配置的 Host 包,再串行复测领先候选。两个命令都使用回环 metadata registry 并拒绝包归档请求,因此耗时不包含包下载。调度器负载与 metadata 完成顺序会使墙钟阈值失去确定性,所以两个命令都不进入聚合门禁。
### optional 依赖绝不在模块作用域被加载
`optionalDependencies` 里的依赖,或带 `peerDependenciesMeta.<name>.optional` 的 peer,在安装出来的树里可以不存在——这份「可以不存在」正是 optional 的全部承诺。而静态 import 在引入方模块加载时就求值,于是一个缺失的包不再表现为「这个能力不可用」,而是变成所有能走到该模块的代码的加载失败。这种失败只在「缺了该包的安装树」里出现,而本仓没有任何测试构造这种树:workspace 安装总是把每个包都装上,所以单测、快照、打包安装探针全都会过,而那个拒绝了这个 optional peer 的消费者拿到的却是坏的包。
@@ -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/process/2026-08-26-published-dependency-faces.md
2026-08-26-published-dependency-faces.md: 668d4c721446a87388ae50951156d1b24f91138c
2026-08-26-published-dependency-faces.zh.md: b10a285a81490f8eb66d70d3e73c2f676ad1b35c
@@ -0,0 +1,93 @@
# Agent Note: Published dependency faces and bounded peer relays
Status: implemented
English | [中文](2026-08-26-published-dependency-faces.zh.md)
## Problem
A package may contain a browser bundle, a Host entry, shared TypeScript declarations, and Cordis injection metadata. Encoding all of those relationships as required npm peers made the published CLI expensive to install: npm installs peers automatically and repeatedly evaluates placement through deep, converging peer paths. Changing ranges or making the peers optional did not remove that traversal.
The package that chooses a Client build input is the shipped profile, while a Host value import is loaded by Node from the importing package. Those relationships need different npm sections. Applying one rule to every Host package would reduce the graph but would also create a large migration with no corresponding installation benefit.
## Decision
### Package selection
[`verify-package-dependencies`](../../../../scripts/verify-package-dependencies.ts) owns dependency-section policy. It always covers packages under `packages/client/` and every non-experimental package that declares `dsh.client`. Inside the directory, `dsh.client` marks a Client/Host package whose Host entry is scanned; a package without that declaration is a Client-only static build input. Outside the directory, `dsh.client` selects the same Client/Host scan. A `"./client"` export alone is an API and does not select npm dependency policy.
[`package-dependency-policy.ts`](../../../../scripts/package-dependency-policy.ts) provides explicit Client-face include and exclude lists. An include handles an exceptional package without `dsh.client`, while an exclude removes an automatically discovered dual-face package outside `packages/client/`. The verifier rejects unknown, stale, redundant, duplicate, overlapping, and ineffective entries. The include list is empty; the exclude list contains `@deepseek-ai/dsh-api-session-controller` and `@deepseek-ai/dsh-api-workspace-controller`. Adding Session Controller back would migrate nine more Host edges while its five-run candidate retest improved median resolution by only 0.15 seconds.
Host-only packages join the same policy through a separate explicit list. The list contains `@deepseek-ai/dsh-llm` and `@deepseek-ai/dsh-session`; source imports do not expand it.
### Dependency sections
Every covered package keeps `@deepseek-ai/cordis` in matching `peerDependencies` and `devDependencies`. Cordis is the shared plugin runtime whose identity the application controls.
A workspace package reached by a runtime value import from the Host entry closure belongs only in `dependencies` when every imported runtime export appears in the policy's `safeHostDependencyExports` table. An export whose constructor identity or module state must be shared appears in `peerRequiredHostExports`; importing one such export keeps the whole package edge in matching `peerDependencies` and `devDependencies`. Each table key is an exact module specifier and each value is a reviewed export set. The verifier follows runtime local imports from the Host entry, records named and default imports and re-exports, and rejects exports present in neither table; namespace, dynamic, and side-effect imports remain unbounded and cannot enter either table.
Workspace imports used by the Client bundle, type-only imports, module augmentations, `dsh.client.inject`, invariant companions, and existing metadata-only peers belong only in `devDependencies`. Ordinary third-party packages imported by the Host runtime belong in `dependencies`; other third-party relationships keep their declared section. Workspace references use `workspace:^`.
Some development relationships exist only in `dsh.client.inject` or TypeScript project references. The policy's `configurationOnlyDevDependencies` table names only those reviewed edges and keeps them in `devDependencies`.
The verifier reads source manifests and source files, so it runs on a clean tree without built `lib/`. Every selected Host face must have `src/index.ts`. An unclassified Host runtime export is a policy violation that blocks all `--fix` writes; a maintainer must review the export and classify it, change the source relationship, or change the package selection. Once source safety passes, `--fix` performs only the section and range changes implied by the classification and removes stale peer metadata.
### Maintainer workflow
Run the verifier without `--fix` for a read-only check of package selection, export classifications, dependency sections, workspace ranges, and peer metadata. An unclassified runtime import reports one clickable `path:line:column` diagnostic per imported export.
```sh
pnpm run verify-package-dependencies
```
Classify each new Host runtime export in [`package-dependency-policy.ts`](../../../../scripts/package-dependency-policy.ts) before generating manifests. `safeHostDependencyExports` permits an ordinary dependency; `peerRequiredHostExports` keeps the whole provider package edge in matching peer and development sections. An export may appear in exactly one table. After refactoring a peer-required export so duplicate package copies are safe, move that exact specifier and export to the safe table; an edge becomes an ordinary dependency only after none of its imported exports remain peer-required.
Generate the managed manifests and every directly derived artifact with one command. `--fix` writes nothing while a policy violation exists; after success it refreshes `pnpm-lock.yaml`, regenerates both module-graph languages and their pairing record, and prints the ordinary-dependency and peer-required edge lists.
```sh
pnpm run verify-package-dependencies -- --fix
git diff -- packages pnpm-lock.yaml docs/module-graph.md docs/module-graph.zh.md docs/module-graph.i18n.yaml
```
Measure the working-tree graph and a Git ref through the local metadata-only registry. Each run creates a fresh consumer and npm cache, replaces inherited npm configuration with explicit peer, hoisting, and registry settings, executes `npm install --package-lock-only`, rejects archive downloads, and leaves the repository unchanged. `--runs` controls repetitions, `--timeout-ms` terminates the npm process tree after its deadline, and optional `--max-ms` makes the command fail when the slowest run exceeds a threshold.
```sh
pnpm run benchmark:npm-resolution -- --runs=5 --timeout-ms=300000
pnpm run benchmark:npm-resolution -- --ref=origin/master --runs=5 --timeout-ms=300000
```
Verify package placement through two incompatible synthetic DSH releases. The verifier copies every current DSH manifest into `0.1.0` and `0.2.0`, asks npm for a package lock only, and rejects cross-release DSH resolution, unexpected DSH locations, unequal release inventories, multiple Cordis installations, and package archive requests. The local index contains only installed current-platform metadata, so npm-accepted probes for unavailable optional packages are reported without failing the check.
```sh
pnpm run verify-npm-install-layout
```
Rank the next Host package by applying the current policy in memory, measuring a baseline, trying each reachable unconfigured package, and serially retesting the fastest coarse candidates. Positive `gainSeconds` is `baseline median - candidate median`; `--candidates` limits the roster, `--jobs` controls coarse concurrency, and neither phase writes manifests. A selected candidate still requires export classification before it joins `hostPackages`.
```sh
pnpm run benchmark:npm-resolution:next -- --runs=1 --finalist-runs=5 --finalists=5 --jobs=8 --timeout-ms=120000
```
### Performance verification
[`verify-npm-install-layout`](../../../../scripts/verify-npm-install-layout.ts) is a deterministic package-path and version check in the `Release (dsh)` workflow on every pull request and master push; it does not enforce resolver duration. [`benchmark-npm-resolution`](../../../../scripts/benchmark-npm-resolution.ts) and [`benchmark-next-package-dependency`](../../../../scripts/benchmark-next-package-dependency.ts) remain manual because resolver time varies with machine load and metadata completion order. Their fresh-consumer, metadata-only runs isolate npm's dependency-tree calculation from registry latency and archive downloads, so relative results identify peer relays without creating a release-time performance promise.
## Alternatives considered
**Keep internal relationships as peers.** npm must place and validate each required peer along converging ancestry paths, which recreates the reported install-time failure even when all internal versions are compatible.
**Use the `"./client"` export as the Client-face roster.** A package may publish Client-facing types or a browser API without contributing a dynamically loaded row. Selecting that package broadens the migration to unrelated Host packages such as Goal, Session Title, and Todo. `dsh.client` identifies dynamic rows, while the `packages/client/` directory independently covers static Client inputs.
**Flatten every Host package.** This removes more peer work but expands the migration to packages whose individual benchmark result is negligible. The explicit Host list preserves the remaining peer contracts until measurement justifies another entry.
**Move every Client-related declaration to development-only.** A dual-face package's Host value imports remain real Node loads. Omitting them from the published dependency graph makes the package depend on accidental hoisting by a profile.
**Enforce a wall-clock threshold in CI.** Resolver time varies with machine load and metadata completion order. Deterministic manifest classification belongs in CI; timing remains a maintainer benchmark.
## Consequences
The published dependency graph follows artifact ownership instead of source-directory coupling. Client bundles and shipped profiles provide browser identities, Host modules install duplicate-safe values they load, and Cordis plus explicitly peer-required Host exports retain shared package instances.
Moving a public type-only relationship to `devDependencies` means a standalone TypeScript consumer must install the referenced type package when it consumes that declaration. The shipped profiles install the complete supported package family; supporting independently assembled TypeScript consumers would require a different policy.
The explicit overrides, Host list, and export classifications are reviewable decisions. Class constructors used by `instanceof`, symbols, and accessors for module-private registries require peers when their identity or state crosses package boundaries; being a value import alone does not make an export duplicate-safe. Changing a classification changes the installed graph and requires the focused verifier tests, the two-release layout check, and a fresh next-package benchmark. The metadata-only benchmark is diagnostic evidence, not a release-time performance promise.
@@ -0,0 +1,93 @@
# Agent Note: 发布依赖门面与有限 peer 中继
Status: implemented
[English](2026-08-26-published-dependency-faces.md) | 中文
## 问题
一个包可能同时包含浏览器 bundle、Host 入口、共享 TypeScript 声明和 Cordis 注入元数据。把这些关系全部编码成必需 npm peer 会使已发布 CLI 的安装代价过高:npm 会自动安装 peer,并沿深层、反复汇合的 peer 路径重复执行放置检查。修改版本范围或把 peer 标成 optional 都不会消除这类遍历。
Client 构建输入由发布 profile 选择,而 Host value import 由导入它的包通过 Node 加载;两者需要不同的 npm 区段。把规则应用到每个 Host 包虽然也能缩小依赖图,却会制造一个没有对应安装收益的大范围迁移。
## 决策
### 包选择
[`verify-package-dependencies`](../../../../scripts/verify-package-dependencies.ts) 统一负责依赖区段策略。它始终覆盖 `packages/client/` 下的包,以及声明 `dsh.client` 的每个非实验包。在该目录内,`dsh.client` 标记需要扫描 Host 入口的 Client/Host 包;没有该声明的包是仅供 Client 编译的静态输入。在目录外,`dsh.client` 选择相同的 Client/Host 扫描。仅有 `"./client"` export 只是 API,不参与 npm 依赖策略选包。
[`package-dependency-policy.ts`](../../../../scripts/package-dependency-policy.ts) 提供显式 Client 门面 include 与 exclude 列表。include 用于没有 `dsh.client` 的例外包,exclude 用于移除 `packages/client/` 之外自动发现的双面包。验证器拒绝未知、失效、冗余、重复、相互重叠和无法生效的配置项。include 列表为空;exclude 列表包含 `@deepseek-ai/dsh-api-session-controller``@deepseek-ai/dsh-api-workspace-controller`。把 Session Controller 加回会多迁移九条 Host 边,而五次候选复测的 resolver 中位数仅改善 0.15 秒。
Host-only 包通过另一份显式列表加入同一策略。该列表包含 `@deepseek-ai/dsh-llm``@deepseek-ai/dsh-session`;源码 import 不会自动扩大列表。
### 依赖区段
每个受管包都把 `@deepseek-ai/cordis` 保持在范围一致的 `peerDependencies``devDependencies` 中。Cordis 是由应用控制身份的共享插件运行时。
Host 入口闭包中的运行期 value import 所到达的 workspace 包,只有在每个运行期导出都列入策略的 `safeHostDependencyExports` 表时才只属于 `dependencies`。constructor 身份或模块状态必须共享的导出列入 `peerRequiredHostExports`;一旦使用这类导出,整条包依赖边就保留在范围一致的 `peerDependencies``devDependencies` 中。表的每个 key 都是精确 module specifier,每个 value 都是经审查的导出集合。验证器从 Host 入口沿运行期本地 import 扫描,记录具名与默认 import 和 re-export,并拒绝两个表都未收录的导出;namespace、dynamic 和 side-effect import 无法限定导出范围,因此不能进入任一表。
Client bundle 使用的 workspace import、纯类型 import、模块扩充、`dsh.client.inject`、invariant companion 和仅有元数据的现存 peer 只属于 `devDependencies`。Host 运行时导入的普通第三方包属于 `dependencies`;其他第三方关系保持原区段。Workspace 引用使用 `workspace:^`
部分开发期关系只存在于 `dsh.client.inject` 或 TypeScript project reference 中。策略的 `configurationOnlyDevDependencies` 表只列出这些已评审的依赖边,并将它们保留在 `devDependencies` 中。
验证器读取源码 manifest 和源码文件,因此可以在没有已构建 `lib/` 的干净工作树上运行。每个被选中的 Host face 都必须存在 `src/index.ts`。未分类的 Host 运行期导出属于策略违规,会阻止 `--fix` 的全部写入;维护者必须审查该导出,并选择分类该导出、修改源码关系或修改选包范围。源码安全检查通过后,`--fix` 只执行分类所确定的区段与范围变更,并删除失效的 peer 元数据。
### 维护流程
不带 `--fix` 运行验证器,会以只读方式检查选包范围、导出分类、依赖区段、workspace range 与 peer metadata。未分类的运行期 import 会按每个导出分别报告可点击的 `path:line:column` 诊断。
```sh
pnpm run verify-package-dependencies
```
生成 manifest 前,在 [`package-dependency-policy.ts`](../../../../scripts/package-dependency-policy.ts) 中分类每个新增 Host 运行期导出。`safeHostDependencyExports` 允许普通 dependency`peerRequiredHostExports` 让整个提供包依赖边保留在范围一致的 peer 与开发区段。一个导出只能出现在一个表中。把 peer-required 导出重构到重复安装安全后,将该精确 specifier 与导出移入 safe 表;只有当一条依赖边的所有 import 都不再使用 peer-required 导出时,它才会成为普通 dependency。
用一条命令生成受管 manifest 和所有直接派生产物。存在策略违规时,`--fix` 不写任何文件;成功后,它会刷新 `pnpm-lock.yaml`、重新生成中英文 module graph 及其配对记录,并打印普通 dependency 与 peer-required 依赖边。
```sh
pnpm run verify-package-dependencies -- --fix
git diff -- packages pnpm-lock.yaml docs/module-graph.md docs/module-graph.zh.md docs/module-graph.i18n.yaml
```
通过仅 metadata 的本地 registry 测量工作树依赖图与 Git ref。每轮都会创建全新 consumer 与 npm cache,用明确的 peer、hoisting 和 registry 设置替换继承的 npm 配置,执行 `npm install --package-lock-only`,拒绝下载包归档,并保持仓库不变。`--runs` 控制重复次数,`--timeout-ms` 会在期限到达后终止 npm 进程树,可选 `--max-ms` 会在最慢一轮超过阈值时让命令失败。
```sh
pnpm run benchmark:npm-resolution -- --runs=5 --timeout-ms=300000
pnpm run benchmark:npm-resolution -- --ref=origin/master --runs=5 --timeout-ms=300000
```
通过两个互不兼容的 DSH 合成版本验证包落位。验证器把每份当前 DSH manifest 分别复制为 `0.1.0``0.2.0`,只要求 npm 生成 package lock,并拒绝跨版本 DSH 解析、非预期 DSH 路径、两套版本清单不一致、多个 Cordis 实例以及包归档请求。本地索引只包含当前平台已安装的 metadata,因此只报告而不拒绝 npm 已接受的不可用可选包探测。
```sh
pnpm run verify-npm-install-layout
```
计算下一项 Host 包时,命令会在内存中应用当前策略、测量 baseline、逐个尝试可达且未配置的包,并串行复测粗筛中最快的候选。正数 `gainSeconds` 等于 `baseline median - candidate median``--candidates` 限定名册,`--jobs` 控制粗筛并发度,两个阶段都不写 manifest。选中的候选仍需先完成导出分类,才能加入 `hostPackages`
```sh
pnpm run benchmark:npm-resolution:next -- --runs=1 --finalist-runs=5 --finalists=5 --jobs=8 --timeout-ms=120000
```
### 性能验证
[`verify-npm-install-layout`](../../../../scripts/verify-npm-install-layout.ts) 是 `Release (dsh)` workflow 在每个 pull request 和 master push 上运行的确定性包路径与版本检查;它不限制 resolver 耗时。[`benchmark-npm-resolution`](../../../../scripts/benchmark-npm-resolution.ts) 与 [`benchmark-next-package-dependency`](../../../../scripts/benchmark-next-package-dependency.ts) 保持为手动工具,因为 resolver 耗时会随机器负载和 metadata 完成顺序变化。它们通过全新 consumer 和仅 metadata 的运行,把 npm 依赖树计算与 registry 延迟、包归档下载分离,因此相对结果可以定位 peer 中继,但不构成发布时性能承诺。
## 考虑过的替代方案
**把内部关系继续保留为 peer。** npm 必须沿汇合的祖先路径放置并验证每个必需 peer;即使内部版本全部兼容,也会重新产生已报告的安装耗时问题。
**用 `"./client"` export 作为 Client 门面名册。** 包可能发布 Client 类型或浏览器 API,却不贡献动态装载 row。选中这类包会把迁移扩大到 Goal、Session Title 和 Todo 等无关 Host 包。`dsh.client` 标识动态 row,而 `packages/client/` 目录独立覆盖静态 Client 输入。
**拍平全部 Host 包。** 这会移除更多 peer 工作,却把迁移扩大到单包 benchmark 收益可忽略的包。显式 Host 列表会保留其余 peer 约束,直到测量结果证明应增加新成员。
**把所有 Client 相关声明都改为仅开发依赖。** 双面包的 Host value import 仍是实际的 Node 加载;从发布依赖图中删掉它们,会让包依赖 profile 的偶然提升。
**在 CI 中强制墙钟阈值。** Resolver 耗时会随机器负载和 metadata 完成顺序变化。确定性的 manifest 分类进入 CI,耗时测量保留为维护者 benchmark。
## 结果
发布依赖图按产物归属而不是源码目录耦合分类。Client bundle 与发布 profile 提供浏览器运行时身份,Host 模块安装自己加载的可重复实体,而 Cordis 和显式标为 peer-required 的 Host 导出继续共享包实例。
把公开纯类型关系放进 `devDependencies`,意味着独立 TypeScript 消费者在使用该声明时必须自行安装被引用的类型包。发布 profile 会安装完整的受支持包族;若要支持独立组装的 TypeScript 消费者,需要另一套策略。
显式 override、Host 列表与导出分类都是需要评审的决策。当 class constructor、symbol 和访问模块私有 registry 的函数跨包传递身份或状态时,它们要求 peer;仅仅属于 value import 并不能证明导出可重复安装。修改分类会改变安装图,因此需要运行聚焦 verifier 测试、双版本布局检查并重新执行 next-package benchmark。仅 metadata benchmark 是诊断证据,不是发布时安装耗时承诺。
+43 -3
View File
@@ -2,9 +2,9 @@
# entries, all on one version. The vendored framework and the native packages are
# separate sequences with their own workflows and version lines.
#
# Pack runs without credentials on every pull request and master push, so a
# pull request proves the whole publish set still packs. Publication is a manual
# workflow_dispatch of release-publish.yml from a dsh-v* tag.
# Pack and dependency-layout verification run without credentials on every pull
# request and master push. Publication is a manual workflow_dispatch of
# release-publish.yml from a dsh-v* tag.
name: Release (dsh)
on:
@@ -26,6 +26,46 @@ env:
DSH_TELEMETRY_DISABLED: '1'
jobs:
dependencies:
name: Dependency layout
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: pnpm/action-setup@v4
with:
dest: ${{ runner.temp }}/setup-pnpm
- uses: actions/setup-node@v6
with:
node-version: ${{ env.PRIMARY_NODE_VERSION }}
- name: Configure pnpm store path
id: pnpm-store
run: |
store_root="$HOME/.local/share/pnpm/store"
echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV"
store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent)
echo "path=$store_path" >> "$GITHUB_OUTPUT"
- uses: actions/cache/restore@v4
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-
- name: Install (immutable)
run: pnpm install --frozen-lockfile
- name: Verify dependency policy
run: pnpm run verify-package-dependencies
- name: Verify npm install layout
run: pnpm run verify-npm-install-layout
pack:
name: Pack npm tarballs
runs-on: ubuntu-24.04
+2 -2
View File
@@ -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: 7c4ccbf6841070d37116641c1404a70cbb598769
module-graph.zh.md: 4581a5bdda1e75a9678a731ab109d3b3c71b7434
module-graph.md: e5225db8618a3d056257bf96e57e3ad99fb73134
module-graph.zh.md: 9a5f72ef3ddcc8c3823dd59812869611dec77169
+153 -556
View File
@@ -1,9 +1,9 @@
<!-- Generated by scripts/gen-module-graph.ts — do not edit by hand.
Run `pnpm run gen-module-graph` to regenerate. -->
# Module dependency graph
# Shared-instance dependency graph
Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package's `peerDependencies` (the canonical runtime-dependency signal) and grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.
Peer dependencies among the `@deepseek-ai/dsh-*` harness packages. A peer means the consumer requires a shared instance; ordinary runtime dependencies and development-only relationships are not shown. The graph is grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` has package `b` as a peer. Names omit the `@deepseek-ai/dsh-` prefix.
```mermaid
flowchart TD
@@ -375,11 +375,6 @@ flowchart TD
pkg_base --> pkg_invariants
pkg_sdk_app --> pkg_invariants
pkg_sdk_minimal --> pkg_invariants
pkg_client_store --> pkg_invariants
pkg_client_ui_primitives --> pkg_invariants
pkg_client_ui_renderer --> pkg_invariants
pkg_client_ui_slots --> pkg_invariants
pkg_client_web --> pkg_invariants
pkg_code_runtime --> pkg_invariants
pkg_code_runtime_python --> pkg_invariants
pkg_e2b --> pkg_invariants
@@ -396,18 +391,37 @@ flowchart TD
pkg_win32_process --> pkg_invariants
pkg_llm_mock_server --> pkg_invariants
pkg_typert_generator --> pkg_invariants
pkg_typert_loader --> pkg_invariants
pkg_typert_loader --> pkg_typert_registry
pkg_typert_protocol --> pkg_invariants
pkg_typert_registry --> pkg_invariants
pkg_llm --> pkg_typert_protocol
pkg_session --> pkg_scope
pkg_api_gateway --> pkg_typert_protocol
pkg_api_remotes --> pkg_scope
pkg_attachment --> pkg_brand
pkg_attachment --> pkg_invariants
pkg_client_modules --> pkg_host_webserver
pkg_client_modules --> pkg_invariants
pkg_credentials --> pkg_brand
pkg_credentials --> pkg_invariants
pkg_subprocess_e2b --> pkg_e2b
pkg_subprocess_e2b --> pkg_invariants
pkg_subprocess_e2b --> pkg_subprocess
pkg_subprocess_e2b --> pkg_timeout
pkg_experimental_inspector --> pkg_client_modules
pkg_experimental_inspector --> pkg_host_webserver
pkg_experimental_inspector --> pkg_invariants
pkg_experimental_webworker_runtime --> pkg_client_connection
pkg_experimental_webworker_runtime --> pkg_client_modules
pkg_experimental_webworker_runtime --> pkg_host_webserver
pkg_experimental_webworker_runtime --> pkg_invariants
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_host_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_webserver
pkg_host_directory_picker_auto --> pkg_invariants
pkg_host_frontend_static --> pkg_client_connection
pkg_host_frontend_static --> pkg_host_webserver
pkg_host_frontend_static --> pkg_invariants
pkg_host_plugin_inventory --> pkg_brand
pkg_host_plugin_inventory --> pkg_invariants
pkg_host_plugin_inventory --> pkg_typert_protocol
@@ -423,32 +437,6 @@ flowchart TD
pkg_subprocess_local --> pkg_invariants
pkg_subprocess_local --> pkg_subprocess
pkg_subprocess_local --> pkg_timeout
pkg_typert_loader --> pkg_invariants
pkg_typert_loader --> pkg_typert_registry
pkg_llm --> pkg_attachment
pkg_llm --> pkg_brand
pkg_llm --> pkg_invariants
pkg_llm --> pkg_timeout
pkg_llm --> pkg_typert_protocol
pkg_attachment_local --> pkg_attachment
pkg_attachment_local --> pkg_home_paths
pkg_attachment_local --> pkg_invariants
pkg_client_hmr --> pkg_client_modules
pkg_client_hmr --> pkg_host_webserver
pkg_client_hmr --> pkg_invariants
pkg_credentials_local --> pkg_atomic_write
pkg_credentials_local --> pkg_credentials
pkg_credentials_local --> pkg_home_paths
pkg_credentials_local --> pkg_invariants
pkg_credentials_local --> pkg_launch_environment
pkg_experimental_inspector --> pkg_client_modules
pkg_experimental_inspector --> pkg_host_webserver
pkg_experimental_inspector --> pkg_invariants
pkg_session --> pkg_brand
pkg_session --> pkg_invariants
pkg_session --> pkg_llm
pkg_session --> pkg_scope
pkg_session --> pkg_typert_protocol
pkg_system_prompt --> pkg_invariants
pkg_system_prompt --> pkg_llm
pkg_system_prompt --> pkg_scope
@@ -457,37 +445,28 @@ flowchart TD
pkg_skill --> pkg_scope
pkg_web --> pkg_invariants
pkg_web --> pkg_llm
pkg_authorization --> pkg_credentials
pkg_authorization --> pkg_invariants
pkg_authorization --> pkg_llm
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_skill_badge --> pkg_invariants
pkg_skill_badge --> pkg_skill
pkg_web_fetch_http --> pkg_invariants
pkg_web_fetch_http --> pkg_timeout
pkg_web_fetch_http --> pkg_web
pkg_web_search_exa --> pkg_invariants
pkg_web_search_exa --> pkg_launch_environment
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_invariants
pkg_web_search_perplexity --> pkg_launch_environment
pkg_web_search_perplexity --> pkg_web
pkg_spill --> pkg_brand
pkg_spill --> pkg_invariants
pkg_spill --> pkg_llm
pkg_spill --> pkg_session
pkg_app_boot --> pkg_home_paths
pkg_app_boot --> pkg_invariants
pkg_app_boot --> pkg_launch_environment
pkg_app_boot --> pkg_system_prompt
pkg_attachment_local --> pkg_attachment
pkg_attachment_local --> pkg_home_paths
pkg_attachment_local --> pkg_invariants
pkg_code_runtime_worker_thread --> pkg_code_runtime
pkg_code_runtime_worker_thread --> pkg_invariants
pkg_code_runtime_worker_thread --> pkg_session
pkg_code_runtime_worker_thread --> pkg_timeout
pkg_persona --> pkg_invariants
pkg_persona --> pkg_system_prompt
pkg_authorization --> pkg_credentials
pkg_authorization --> pkg_invariants
pkg_authorization --> pkg_llm
pkg_credentials_local --> pkg_atomic_write
pkg_credentials_local --> pkg_credentials
pkg_credentials_local --> pkg_home_paths
pkg_credentials_local --> pkg_invariants
pkg_credentials_local --> pkg_launch_environment
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_sandbox --> pkg_invariants
pkg_sandbox --> pkg_llm
pkg_sandbox --> pkg_session
@@ -516,8 +495,23 @@ flowchart TD
pkg_fs --> pkg_invariants
pkg_fs --> pkg_llm
pkg_fs --> pkg_sandbox
pkg_skill_badge --> pkg_invariants
pkg_skill_badge --> pkg_skill
pkg_web_fetch_http --> pkg_invariants
pkg_web_fetch_http --> pkg_timeout
pkg_web_fetch_http --> pkg_web
pkg_web_search_exa --> pkg_invariants
pkg_web_search_exa --> pkg_launch_environment
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_invariants
pkg_web_search_perplexity --> pkg_launch_environment
pkg_web_search_perplexity --> pkg_web
pkg_spill_local --> pkg_invariants
pkg_spill_local --> pkg_spill
pkg_app_boot --> pkg_home_paths
pkg_app_boot --> pkg_invariants
pkg_app_boot --> pkg_launch_environment
pkg_app_boot --> pkg_system_prompt
pkg_message_feedback --> pkg_brand
pkg_message_feedback --> pkg_invariants
pkg_message_feedback --> pkg_llm
@@ -525,6 +519,8 @@ flowchart TD
pkg_message_feedback --> pkg_session_persistence
pkg_message_feedback --> pkg_storage_domain
pkg_message_feedback --> pkg_typert_protocol
pkg_persona --> pkg_invariants
pkg_persona --> pkg_system_prompt
pkg_sandbox_local --> pkg_invariants
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
@@ -618,6 +614,14 @@ flowchart TD
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_hook_protocol --> pkg_shell
pkg_api_workspace_controller --> pkg_api_gateway
pkg_api_workspace_controller --> pkg_client_connection
pkg_api_workspace_controller --> pkg_host_directory_picker
pkg_api_workspace_controller --> pkg_invariants
pkg_api_workspace_controller --> pkg_session
pkg_api_workspace_controller --> pkg_storage_domain
pkg_api_workspace_controller --> pkg_typert_protocol
pkg_api_workspace_controller --> pkg_workspace
pkg_file_reference --> pkg_agent
pkg_file_reference --> pkg_invariants
pkg_time_context --> pkg_agent
@@ -1032,17 +1036,6 @@ flowchart TD
pkg_web_app --> pkg_invariants
pkg_web_app --> pkg_shell_env
pkg_web_app --> pkg_system_prompt
pkg_client_connection --> pkg_attachment
pkg_client_connection --> pkg_brand
pkg_client_connection --> pkg_commands
pkg_client_connection --> pkg_credentials
pkg_client_connection --> pkg_host_directory_picker
pkg_client_connection --> pkg_host_webserver
pkg_client_connection --> pkg_invariants
pkg_client_connection --> pkg_llm
pkg_client_connection --> pkg_session
pkg_client_connection --> pkg_settings
pkg_client_connection --> pkg_tool_todo
pkg_compaction_tool_result_pruner --> pkg_compaction
pkg_compaction_tool_result_pruner --> pkg_invariants
pkg_compaction_tool_result_pruner --> pkg_llm
@@ -1121,11 +1114,6 @@ flowchart TD
pkg_tool_session_query --> pkg_system_prompt
pkg_tool_session_query --> pkg_timeout
pkg_tool_session_query --> pkg_tools
pkg_api_gateway --> pkg_brand
pkg_api_gateway --> pkg_client_connection
pkg_api_gateway --> pkg_host_webserver
pkg_api_gateway --> pkg_invariants
pkg_api_gateway --> pkg_typert_registry
pkg_compaction_basic --> pkg_agent
pkg_compaction_basic --> pkg_commands
pkg_compaction_basic --> pkg_compaction
@@ -1167,13 +1155,6 @@ flowchart TD
pkg_agent_spine_demo --> pkg_tool_jobs
pkg_agent_spine_demo --> pkg_tool_skill
pkg_agent_spine_demo --> pkg_tools
pkg_experimental_webworker_runtime --> pkg_client_connection
pkg_experimental_webworker_runtime --> pkg_client_modules
pkg_experimental_webworker_runtime --> pkg_host_webserver
pkg_experimental_webworker_runtime --> pkg_invariants
pkg_host_frontend_static --> pkg_client_connection
pkg_host_frontend_static --> pkg_host_webserver
pkg_host_frontend_static --> pkg_invariants
pkg_webhook_github --> pkg_credentials
pkg_webhook_github --> pkg_host_webserver
pkg_webhook_github --> pkg_invariants
@@ -1261,14 +1242,6 @@ flowchart TD
pkg_api_session_controller --> pkg_util_time
pkg_api_session_controller --> pkg_util_workspace_path
pkg_api_session_controller --> pkg_workspace
pkg_api_workspace_controller --> pkg_api_gateway
pkg_api_workspace_controller --> pkg_client_connection
pkg_api_workspace_controller --> pkg_host_directory_picker
pkg_api_workspace_controller --> pkg_invariants
pkg_api_workspace_controller --> pkg_session
pkg_api_workspace_controller --> pkg_storage_domain
pkg_api_workspace_controller --> pkg_typert_protocol
pkg_api_workspace_controller --> pkg_workspace
pkg_experimental_agent_team --> pkg_agent
pkg_experimental_agent_team --> pkg_brand
pkg_experimental_agent_team --> pkg_invariants
@@ -1305,30 +1278,18 @@ flowchart TD
pkg_subagent_spawn_in_process --> pkg_invariants
pkg_subagent_spawn_in_process --> pkg_subagent
pkg_subagent_spawn_in_process --> pkg_subagent_in_process_driver
pkg_api_remotes --> pkg_agent_presets
pkg_api_remotes --> pkg_api_gateway
pkg_api_remotes --> pkg_api_session_controller
pkg_api_remotes --> pkg_api_settings_controller
pkg_api_remotes --> pkg_api_workspace_controller
pkg_api_remotes --> pkg_commands
pkg_api_remotes --> pkg_cordis_host_runner
pkg_api_remotes --> pkg_credentials
pkg_api_remotes --> pkg_file_reference
pkg_api_remotes --> pkg_goal
pkg_api_remotes --> pkg_host_plugin_inventory
pkg_api_remotes --> pkg_invariants
pkg_api_remotes --> pkg_llm
pkg_api_remotes --> pkg_message_feedback
pkg_api_remotes --> pkg_session
pkg_api_remotes --> pkg_session_reference
pkg_api_remotes --> pkg_settings
pkg_api_remotes --> pkg_subagent
pkg_api_remotes --> pkg_user_approval
pkg_api_remotes --> pkg_user_questions
pkg_client_ui_session --> pkg_api_session_controller
pkg_client_ui_session --> pkg_client_ui_renderer
pkg_client_ui_session --> pkg_invariants
pkg_client_ui_session --> pkg_session
pkg_experimental_client_ui_agent_team --> pkg_api_remotes
pkg_experimental_client_ui_agent_team --> pkg_api_session_controller
pkg_experimental_client_ui_agent_team --> pkg_client_locale
pkg_experimental_client_ui_agent_team --> pkg_client_ui_conversation
pkg_experimental_client_ui_agent_team --> pkg_client_ui_primitives
pkg_experimental_client_ui_agent_team --> pkg_client_ui_renderer
pkg_experimental_client_ui_agent_team --> pkg_client_ui_session
pkg_experimental_client_ui_agent_team --> pkg_client_ui_slots
pkg_experimental_client_ui_agent_team --> pkg_experimental_agent_team
pkg_experimental_client_ui_agent_team --> pkg_invariants
pkg_experimental_client_ui_agent_team --> pkg_session
pkg_experimental_client_ui_agent_team --> pkg_typert_protocol
pkg_experimental_tool_agent_team --> pkg_agent
pkg_experimental_tool_agent_team --> pkg_experimental_agent_team
pkg_experimental_tool_agent_team --> pkg_invariants
@@ -1348,358 +1309,6 @@ flowchart TD
pkg_sdk_jsonrpc_server --> pkg_sdk_protocol
pkg_sdk_jsonrpc_server --> pkg_session
pkg_sdk_jsonrpc_server --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
pkg_client_ui_settings --> pkg_api_remotes
pkg_client_ui_settings --> pkg_invariants
pkg_client_ui_settings --> pkg_settings
pkg_client_locale --> pkg_api_remotes
pkg_client_locale --> pkg_client_connection
pkg_client_locale --> pkg_client_ui_renderer
pkg_client_locale --> pkg_client_ui_settings
pkg_client_locale --> pkg_invariants
pkg_client_locale --> pkg_settings
pkg_client_ui_settings_models --> pkg_api_remotes
pkg_client_ui_settings_models --> pkg_client_locale
pkg_client_ui_settings_models --> pkg_client_ui_renderer
pkg_client_ui_settings_models --> pkg_client_ui_settings
pkg_client_ui_settings_models --> pkg_invariants
pkg_client_ui_settings_plugin_inventory --> pkg_api_remotes
pkg_client_ui_settings_plugin_inventory --> pkg_client_locale
pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_renderer
pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_settings
pkg_client_ui_settings_plugin_inventory --> pkg_invariants
pkg_client_ui_settings_plugins --> pkg_api_remotes
pkg_client_ui_settings_plugins --> pkg_client_locale
pkg_client_ui_settings_plugins --> pkg_client_ui_renderer
pkg_client_ui_settings_plugins --> pkg_client_ui_settings
pkg_client_ui_settings_plugins --> pkg_invariants
pkg_client_ui_theme --> pkg_api_remotes
pkg_client_ui_theme --> pkg_client_connection
pkg_client_ui_theme --> pkg_client_locale
pkg_client_ui_theme --> pkg_client_ui_renderer
pkg_client_ui_theme --> pkg_client_ui_settings
pkg_client_ui_theme --> pkg_host_webserver
pkg_client_ui_theme --> pkg_invariants
pkg_client_ui_theme --> pkg_settings
pkg_client_ui_layout --> pkg_client_locale
pkg_client_ui_layout --> pkg_client_ui_renderer
pkg_client_ui_layout --> pkg_client_ui_session
pkg_client_ui_layout --> pkg_client_ui_theme
pkg_client_ui_layout --> pkg_invariants
pkg_cordis_client_runner --> pkg_api_remotes
pkg_cordis_client_runner --> pkg_client_modules
pkg_cordis_client_runner --> pkg_client_ui_renderer
pkg_cordis_client_runner --> pkg_client_ui_theme
pkg_cordis_client_runner --> pkg_invariants
pkg_client_ui_conversation --> pkg_api_remotes
pkg_client_ui_conversation --> pkg_api_session_controller
pkg_client_ui_conversation --> pkg_api_workspace_controller
pkg_client_ui_conversation --> pkg_attachment
pkg_client_ui_conversation --> pkg_brand
pkg_client_ui_conversation --> pkg_client_locale
pkg_client_ui_conversation --> pkg_client_ui_layout
pkg_client_ui_conversation --> pkg_client_ui_renderer
pkg_client_ui_conversation --> pkg_client_ui_session
pkg_client_ui_conversation --> pkg_client_ui_settings
pkg_client_ui_conversation --> pkg_client_ui_workspace
pkg_client_ui_conversation --> pkg_commands
pkg_client_ui_conversation --> pkg_goal
pkg_client_ui_conversation --> pkg_invariants
pkg_client_ui_conversation --> pkg_llm
pkg_client_ui_conversation --> pkg_llm_retry
pkg_client_ui_conversation --> pkg_permission_presets
pkg_client_ui_conversation --> pkg_plan_mode
pkg_client_ui_conversation --> pkg_session
pkg_client_ui_conversation --> pkg_settings
pkg_client_ui_conversation --> pkg_token_meter
pkg_client_ui_conversation --> pkg_tool_todo
pkg_client_ui_conversation --> pkg_util_crypto
pkg_client_ui_conversation --> pkg_util_workspace_path
pkg_client_ui_conversation --> pkg_workspace
pkg_client_ui_sidebar --> pkg_api_workspace_controller
pkg_client_ui_sidebar --> pkg_client_locale
pkg_client_ui_sidebar --> pkg_client_ui_layout
pkg_client_ui_sidebar --> pkg_client_ui_renderer
pkg_client_ui_sidebar --> pkg_client_ui_session
pkg_client_ui_sidebar --> pkg_client_ui_workspace
pkg_client_ui_sidebar --> pkg_invariants
pkg_client_ui_workspace --> pkg_api_remotes
pkg_client_ui_workspace --> pkg_api_session_controller
pkg_client_ui_workspace --> pkg_api_workspace_controller
pkg_client_ui_workspace --> pkg_client_connection
pkg_client_ui_workspace --> pkg_client_locale
pkg_client_ui_workspace --> pkg_client_ui_conversation
pkg_client_ui_workspace --> pkg_client_ui_renderer
pkg_client_ui_workspace --> pkg_client_ui_session
pkg_client_ui_workspace --> pkg_client_ui_sidebar
pkg_client_ui_workspace --> pkg_invariants
pkg_client_ui_workspace --> pkg_schedule
pkg_client_ui_workspace --> pkg_session
pkg_client_ui_workspace --> pkg_typert_protocol
pkg_client_ui_workspace --> pkg_util_workspace_path
pkg_client_ui_agent_preset --> pkg_agent_presets
pkg_client_ui_agent_preset --> pkg_api_remotes
pkg_client_ui_agent_preset --> pkg_api_session_controller
pkg_client_ui_agent_preset --> pkg_client_connection
pkg_client_ui_agent_preset --> pkg_client_locale
pkg_client_ui_agent_preset --> pkg_client_ui_conversation
pkg_client_ui_agent_preset --> pkg_client_ui_renderer
pkg_client_ui_agent_preset --> pkg_client_ui_session
pkg_client_ui_agent_preset --> pkg_client_ui_settings
pkg_client_ui_agent_preset --> pkg_client_ui_workspace
pkg_client_ui_agent_preset --> pkg_invariants
pkg_client_ui_agent_preset --> pkg_session
pkg_client_ui_approval --> pkg_api_remotes
pkg_client_ui_approval --> pkg_api_session_controller
pkg_client_ui_approval --> pkg_client_locale
pkg_client_ui_approval --> pkg_client_ui_conversation
pkg_client_ui_approval --> pkg_client_ui_renderer
pkg_client_ui_approval --> pkg_client_ui_session
pkg_client_ui_approval --> pkg_invariants
pkg_client_ui_approval --> pkg_llm
pkg_client_ui_approval --> pkg_session
pkg_client_ui_approval --> pkg_typert_protocol
pkg_client_ui_brand_official --> pkg_client_ui_renderer
pkg_client_ui_brand_official --> pkg_client_ui_sidebar
pkg_client_ui_brand_official --> pkg_invariants
pkg_client_ui_directory_picker_browse --> pkg_api_remotes
pkg_client_ui_directory_picker_browse --> pkg_client_locale
pkg_client_ui_directory_picker_browse --> pkg_client_ui_renderer
pkg_client_ui_directory_picker_browse --> pkg_client_ui_workspace
pkg_client_ui_directory_picker_browse --> pkg_invariants
pkg_client_ui_directory_picker_native --> pkg_client_ui_renderer
pkg_client_ui_directory_picker_native --> pkg_client_ui_workspace
pkg_client_ui_directory_picker_native --> pkg_invariants
pkg_client_ui_input_trigger --> pkg_api_session_controller
pkg_client_ui_input_trigger --> pkg_client_locale
pkg_client_ui_input_trigger --> pkg_client_ui_conversation
pkg_client_ui_input_trigger --> pkg_client_ui_renderer
pkg_client_ui_input_trigger --> pkg_client_ui_session
pkg_client_ui_input_trigger --> pkg_file_reference
pkg_client_ui_input_trigger --> pkg_invariants
pkg_client_ui_input_trigger --> pkg_session
pkg_client_ui_jobs --> pkg_api_session_controller
pkg_client_ui_jobs --> pkg_client_locale
pkg_client_ui_jobs --> pkg_client_ui_conversation
pkg_client_ui_jobs --> pkg_client_ui_renderer
pkg_client_ui_jobs --> pkg_client_ui_session
pkg_client_ui_jobs --> pkg_invariants
pkg_client_ui_plan --> pkg_api_remotes
pkg_client_ui_plan --> pkg_client_locale
pkg_client_ui_plan --> pkg_client_ui_conversation
pkg_client_ui_plan --> pkg_client_ui_renderer
pkg_client_ui_plan --> pkg_client_ui_session
pkg_client_ui_plan --> pkg_invariants
pkg_client_ui_plan --> pkg_plan_mode
pkg_client_ui_plan --> pkg_session
pkg_client_ui_schedule --> pkg_api_session_controller
pkg_client_ui_schedule --> pkg_client_locale
pkg_client_ui_schedule --> pkg_client_ui_conversation
pkg_client_ui_schedule --> pkg_client_ui_renderer
pkg_client_ui_schedule --> pkg_client_ui_session
pkg_client_ui_schedule --> pkg_invariants
pkg_client_ui_schedule --> pkg_schedule
pkg_client_ui_settings_general --> pkg_api_remotes
pkg_client_ui_settings_general --> pkg_client_connection
pkg_client_ui_settings_general --> pkg_client_locale
pkg_client_ui_settings_general --> pkg_client_ui_renderer
pkg_client_ui_settings_general --> pkg_client_ui_session
pkg_client_ui_settings_general --> pkg_client_ui_settings
pkg_client_ui_settings_general --> pkg_client_ui_sidebar
pkg_client_ui_settings_general --> pkg_invariants
pkg_client_ui_settings_general --> pkg_settings
pkg_client_ui_trajectory --> pkg_agent
pkg_client_ui_trajectory --> pkg_api_session_controller
pkg_client_ui_trajectory --> pkg_attachment
pkg_client_ui_trajectory --> pkg_client_locale
pkg_client_ui_trajectory --> pkg_client_ui_conversation
pkg_client_ui_trajectory --> pkg_client_ui_renderer
pkg_client_ui_trajectory --> pkg_client_ui_session
pkg_client_ui_trajectory --> pkg_compaction
pkg_client_ui_trajectory --> pkg_invariants
pkg_client_ui_trajectory --> pkg_llm
pkg_client_ui_trajectory --> pkg_session
pkg_client_ui_trajectory --> pkg_tools
pkg_client_ui_user_questions --> pkg_api_remotes
pkg_client_ui_user_questions --> pkg_api_session_controller
pkg_client_ui_user_questions --> pkg_client_locale
pkg_client_ui_user_questions --> pkg_client_ui_conversation
pkg_client_ui_user_questions --> pkg_client_ui_renderer
pkg_client_ui_user_questions --> pkg_client_ui_session
pkg_client_ui_user_questions --> pkg_invariants
pkg_client_ui_user_questions --> pkg_session
pkg_client_ui_user_questions --> pkg_typert_protocol
pkg_client_ui_user_questions --> pkg_user_questions
pkg_experimental_client_ui_agent_team --> pkg_api_remotes
pkg_experimental_client_ui_agent_team --> pkg_api_session_controller
pkg_experimental_client_ui_agent_team --> pkg_client_locale
pkg_experimental_client_ui_agent_team --> pkg_client_ui_conversation
pkg_experimental_client_ui_agent_team --> pkg_client_ui_primitives
pkg_experimental_client_ui_agent_team --> pkg_client_ui_renderer
pkg_experimental_client_ui_agent_team --> pkg_client_ui_session
pkg_experimental_client_ui_agent_team --> pkg_client_ui_slots
pkg_experimental_client_ui_agent_team --> pkg_experimental_agent_team
pkg_experimental_client_ui_agent_team --> pkg_invariants
pkg_experimental_client_ui_agent_team --> pkg_session
pkg_experimental_client_ui_agent_team --> pkg_typert_protocol
pkg_client_ui_chat --> pkg_agent
pkg_client_ui_chat --> pkg_api_remotes
pkg_client_ui_chat --> pkg_api_session_controller
pkg_client_ui_chat --> pkg_api_workspace_controller
pkg_client_ui_chat --> pkg_attachment
pkg_client_ui_chat --> pkg_client_locale
pkg_client_ui_chat --> pkg_client_ui_approval
pkg_client_ui_chat --> pkg_client_ui_conversation
pkg_client_ui_chat --> pkg_client_ui_layout
pkg_client_ui_chat --> pkg_client_ui_renderer
pkg_client_ui_chat --> pkg_client_ui_session
pkg_client_ui_chat --> pkg_client_ui_settings
pkg_client_ui_chat --> pkg_client_ui_workspace
pkg_client_ui_chat --> pkg_commands
pkg_client_ui_chat --> pkg_compaction
pkg_client_ui_chat --> pkg_invariants
pkg_client_ui_chat --> pkg_llm
pkg_client_ui_chat --> pkg_llm_retry
pkg_client_ui_chat --> pkg_session
pkg_client_ui_chat --> pkg_session_stats
pkg_client_ui_chat --> pkg_settings
pkg_client_ui_chat --> pkg_token_meter
pkg_client_ui_chat --> pkg_tools
pkg_client_ui_chat --> pkg_util_workspace_path
pkg_client_ui_commands --> pkg_api_remotes
pkg_client_ui_commands --> pkg_api_session_controller
pkg_client_ui_commands --> pkg_client_locale
pkg_client_ui_commands --> pkg_client_ui_conversation
pkg_client_ui_commands --> pkg_client_ui_input_trigger
pkg_client_ui_commands --> pkg_client_ui_renderer
pkg_client_ui_commands --> pkg_client_ui_session
pkg_client_ui_commands --> pkg_commands
pkg_client_ui_commands --> pkg_invariants
pkg_client_ui_commands --> pkg_session
pkg_client_ui_reference --> pkg_api_remotes
pkg_client_ui_reference --> pkg_api_session_controller
pkg_client_ui_reference --> pkg_client_connection
pkg_client_ui_reference --> pkg_client_locale
pkg_client_ui_reference --> pkg_client_ui_input_trigger
pkg_client_ui_reference --> pkg_file_reference
pkg_client_ui_reference --> pkg_invariants
pkg_client_ui_reference --> pkg_session_reference
pkg_client_ui_reference --> pkg_typert_protocol
pkg_client_ui_reference --> pkg_util_workspace_path
pkg_client_ui_subagent --> pkg_api_session_controller
pkg_client_ui_subagent --> pkg_client_connection
pkg_client_ui_subagent --> pkg_client_locale
pkg_client_ui_subagent --> pkg_client_ui_conversation
pkg_client_ui_subagent --> pkg_client_ui_input_trigger
pkg_client_ui_subagent --> pkg_client_ui_renderer
pkg_client_ui_subagent --> pkg_client_ui_session
pkg_client_ui_subagent --> pkg_invariants
pkg_client_ui_subagent --> pkg_session
pkg_client_ui_subagent --> pkg_subagent
pkg_client_ui_subagent --> pkg_token_meter
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_host_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_webserver
pkg_host_directory_picker_auto --> pkg_invariants
pkg_session_log_export --> pkg_attachment
pkg_session_log_export --> pkg_client_connection
pkg_session_log_export --> pkg_client_locale
pkg_session_log_export --> pkg_client_ui_commands
pkg_session_log_export --> pkg_client_ui_conversation
pkg_session_log_export --> pkg_client_ui_renderer
pkg_session_log_export --> pkg_client_ui_session
pkg_session_log_export --> pkg_commands
pkg_session_log_export --> pkg_invariants
pkg_session_log_export --> pkg_session
pkg_session_log_export --> pkg_session_persistence
pkg_session_log_export --> pkg_session_query
pkg_client_ui_attachment --> pkg_attachment
pkg_client_ui_attachment --> pkg_client_ui_chat
pkg_client_ui_attachment --> pkg_client_ui_conversation
pkg_client_ui_attachment --> pkg_client_ui_renderer
pkg_client_ui_attachment --> pkg_client_ui_trajectory
pkg_client_ui_attachment --> pkg_invariants
pkg_client_ui_deliverables --> pkg_api_remotes
pkg_client_ui_deliverables --> pkg_client_connection
pkg_client_ui_deliverables --> pkg_client_locale
pkg_client_ui_deliverables --> pkg_client_ui_chat
pkg_client_ui_deliverables --> pkg_client_ui_conversation
pkg_client_ui_deliverables --> pkg_client_ui_renderer
pkg_client_ui_deliverables --> pkg_invariants
pkg_client_ui_deliverables --> pkg_session
pkg_client_ui_deliverables --> pkg_system_prompt
pkg_client_ui_goal --> pkg_api_remotes
pkg_client_ui_goal --> pkg_api_session_controller
pkg_client_ui_goal --> pkg_client_locale
pkg_client_ui_goal --> pkg_client_ui_chat
pkg_client_ui_goal --> pkg_client_ui_conversation
pkg_client_ui_goal --> pkg_client_ui_renderer
pkg_client_ui_goal --> pkg_client_ui_session
pkg_client_ui_goal --> pkg_commands
pkg_client_ui_goal --> pkg_goal
pkg_client_ui_goal --> pkg_invariants
pkg_client_ui_goal --> pkg_session
pkg_client_ui_goal --> pkg_typert_protocol
pkg_client_ui_message_feedback --> pkg_api_remotes
pkg_client_ui_message_feedback --> pkg_client_connection
pkg_client_ui_message_feedback --> pkg_client_locale
pkg_client_ui_message_feedback --> pkg_client_ui_chat
pkg_client_ui_message_feedback --> pkg_client_ui_conversation
pkg_client_ui_message_feedback --> pkg_client_ui_renderer
pkg_client_ui_message_feedback --> pkg_client_ui_session
pkg_client_ui_message_feedback --> pkg_invariants
pkg_client_ui_message_feedback --> pkg_message_feedback
pkg_client_ui_message_feedback --> pkg_session
pkg_client_ui_message_feedback --> pkg_typert_protocol
pkg_client_ui_model_selection --> pkg_api_remotes
pkg_client_ui_model_selection --> pkg_api_session_controller
pkg_client_ui_model_selection --> pkg_client_locale
pkg_client_ui_model_selection --> pkg_client_ui_commands
pkg_client_ui_model_selection --> pkg_client_ui_conversation
pkg_client_ui_model_selection --> pkg_client_ui_input_trigger
pkg_client_ui_model_selection --> pkg_client_ui_renderer
pkg_client_ui_model_selection --> pkg_client_ui_session
pkg_client_ui_model_selection --> pkg_invariants
pkg_client_ui_model_selection --> pkg_session
pkg_client_ui_model_selection --> pkg_typert_protocol
pkg_client_ui_permission_presets --> pkg_api_remotes
pkg_client_ui_permission_presets --> pkg_api_session_controller
pkg_client_ui_permission_presets --> pkg_client_locale
pkg_client_ui_permission_presets --> pkg_client_ui_commands
pkg_client_ui_permission_presets --> pkg_client_ui_input_trigger
pkg_client_ui_permission_presets --> pkg_client_ui_renderer
pkg_client_ui_permission_presets --> pkg_client_ui_session
pkg_client_ui_permission_presets --> pkg_client_ui_settings
pkg_client_ui_permission_presets --> pkg_invariants
pkg_client_ui_permission_presets --> pkg_permission_presets
pkg_client_ui_tool --> pkg_api_remotes
pkg_client_ui_tool --> pkg_api_workspace_controller
pkg_client_ui_tool --> pkg_client_connection
pkg_client_ui_tool --> pkg_client_locale
pkg_client_ui_tool --> pkg_client_ui_chat
pkg_client_ui_tool --> pkg_client_ui_conversation
pkg_client_ui_tool --> pkg_client_ui_renderer
pkg_client_ui_tool --> pkg_client_ui_session
pkg_client_ui_tool --> pkg_invariants
pkg_client_ui_tool --> pkg_util_workspace_path
pkg_client_ui_workflow_run --> pkg_api_session_controller
pkg_client_ui_workflow_run --> pkg_client_locale
pkg_client_ui_workflow_run --> pkg_client_ui_chat
pkg_client_ui_workflow_run --> pkg_client_ui_conversation
pkg_client_ui_workflow_run --> pkg_client_ui_renderer
pkg_client_ui_workflow_run --> pkg_client_ui_session
pkg_client_ui_workflow_run --> pkg_invariants
pkg_client_ui_workflow_run --> pkg_session
pkg_client_ui_workflow_run --> pkg_tool_workflow
pkg_client_ui_workflow_run --> pkg_workflow
pkg_client_test_runtime --> pkg_api_session_controller
pkg_client_test_runtime --> pkg_api_workspace_controller
pkg_client_test_runtime --> pkg_attachment
@@ -1715,30 +1324,66 @@ flowchart TD
pkg_client_test_runtime --> pkg_session
pkg_client_test_runtime --> pkg_subagent
pkg_client_test_runtime --> pkg_typert_protocol
pkg_client_ui_skill --> pkg_api_remotes
pkg_client_ui_skill --> pkg_api_session_controller
pkg_client_ui_skill --> pkg_client_connection
pkg_client_ui_skill --> pkg_client_locale
pkg_client_ui_skill --> pkg_client_ui_input_trigger
pkg_client_ui_skill --> pkg_client_ui_renderer
pkg_client_ui_skill --> pkg_client_ui_tool
pkg_client_ui_skill --> pkg_invariants
pkg_client_ui_skill --> pkg_session
pkg_client_ui_cordis --> pkg_api_remotes
pkg_client_ui_cordis --> pkg_client_connection
pkg_client_ui_cordis --> pkg_client_locale
pkg_client_ui_cordis --> pkg_client_ui_input_trigger
pkg_client_ui_cordis --> pkg_client_ui_renderer
pkg_client_ui_cordis --> pkg_client_ui_session
pkg_client_ui_cordis --> pkg_client_ui_sidebar
pkg_client_ui_cordis --> pkg_client_ui_tool
pkg_client_ui_cordis --> pkg_cordis_client_runner
pkg_client_ui_cordis --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
```
| Package | Group | Depends on |
| Package | Group | Peer dependencies |
| --- | --- | --- |
| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | — |
| [`client-connection`](../packages/client/connection) | `client` | — |
| [`client-hmr`](../packages/client/hmr) | `client` | — |
| [`client-locale`](../packages/client/locale) | `client` | — |
| [`client-modules`](../packages/client/modules) | `client` | — |
| [`client-store`](../packages/client/store) | `client` | — |
| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | — |
| [`client-ui-approval`](../packages/client/ui-approval) | `client` | — |
| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | — |
| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | — |
| [`client-ui-chat`](../packages/client/ui-chat) | `client` | — |
| [`client-ui-commands`](../packages/client/ui-commands) | `client` | — |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | — |
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | — |
| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | — |
| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | — |
| [`client-ui-goal`](../packages/client/ui-goal) | `client` | — |
| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | — |
| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | — |
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | — |
| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | — |
| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | — |
| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | — |
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | — |
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | — |
| [`client-ui-reference`](../packages/client/ui-reference) | `client` | — |
| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | — |
| [`client-ui-schedule`](../packages/client/ui-schedule) | `client` | — |
| [`client-ui-session`](../packages/client/ui-session) | `client` | — |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | — |
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | — |
| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | — |
| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | — |
| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | — |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | — |
| [`client-ui-skill`](../packages/client/ui-skill) | `client` | — |
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | — |
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | — |
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | — |
| [`client-ui-tool`](../packages/client/ui-tool) | `client` | — |
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | — |
| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | — |
| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | — |
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | — |
| [`client-web`](../packages/client/web) | `client` | — |
| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | — |
| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | — |
| [`invariants`](../packages/runtime-diagnostics/invariants) | `runtime-diagnostics` | — |
| [`typert-registry`](../packages/typert/registry) | `typert` | — |
| [`atomic-write`](../packages/util/atomic-write) | `util` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`brand`](../packages/util/brand) | `util` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`home-paths`](../packages/util/home-paths) | `util` | [`invariants`](../packages/runtime-diagnostics/invariants) |
@@ -1756,11 +1401,6 @@ flowchart TD
| [`base`](../packages/bundle/base) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`sdk-app`](../packages/bundle/sdk-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`sdk-minimal`](../packages/bundle/sdk-minimal) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-store`](../packages/client/store) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`code-runtime-python`](../packages/code-runtime/code-runtime-python) | `code-runtime` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`e2b`](../packages/e2b/e2b) | `e2b` | [`invariants`](../packages/runtime-diagnostics/invariants) |
@@ -1777,38 +1417,34 @@ flowchart TD
| [`win32-process`](../packages/subprocess/win32-process) | `subprocess` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`llm-mock-server`](../packages/test-support/llm-mock-server) | `test-support` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`typert-generator`](../packages/typert/generator) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-registry`](../packages/typert/registry) |
| [`typert-protocol`](../packages/typert/protocol) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`typert-registry`](../packages/typert/registry) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`llm`](../packages/llm/llm) | `llm` | [`typert-protocol`](../packages/typert/protocol) |
| [`session`](../packages/core/session) | `core` | [`scope`](../packages/core/scope) |
| [`api-gateway`](../packages/api/gateway) | `api` | [`typert-protocol`](../packages/typert/protocol) |
| [`api-remotes`](../packages/api/remotes) | `api` | [`scope`](../packages/core/scope) |
| [`attachment`](../packages/attachment/attachment) | `attachment` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-modules`](../packages/client/modules) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`credentials`](../packages/credentials/credentials) | `credentials` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`subprocess-e2b`](../packages/e2b/subprocess-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`invariants`](../packages/runtime-diagnostics/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`experimental-inspector`](../packages/experimental/inspector) | `experimental` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`experimental-webworker-runtime`](../packages/experimental/webworker-runtime) | `experimental` | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-frontend-static`](../packages/host/frontend-static) | `host` | [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-plugin-inventory`](../packages/host/plugin-inventory) | `host` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-protocol`](../packages/typert/protocol) |
| [`anonymous-user-id`](../packages/identity/anonymous-user-id) | `identity` | [`brand`](../packages/util/brand), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`storage-domain`](../packages/storage/storage-domain) | `storage` | [`invariants`](../packages/runtime-diagnostics/invariants), [`storage`](../packages/storage/storage) |
| [`storage-json`](../packages/storage/storage-json) | `storage` | [`invariants`](../packages/runtime-diagnostics/invariants), [`storage`](../packages/storage/storage) |
| [`storage-sqlite`](../packages/storage/storage-sqlite) | `storage` | [`invariants`](../packages/runtime-diagnostics/invariants), [`storage`](../packages/storage/storage) |
| [`subprocess-local`](../packages/subprocess/subprocess-local) | `subprocess` | [`invariants`](../packages/runtime-diagnostics/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-registry`](../packages/typert/registry) |
| [`llm`](../packages/llm/llm) | `llm` | [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`typert-protocol`](../packages/typert/protocol) |
| [`attachment-local`](../packages/attachment/attachment-local) | `attachment` | [`attachment`](../packages/attachment/attachment), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment) |
| [`experimental-inspector`](../packages/experimental/inspector) | `experimental` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`typert-protocol`](../packages/typert/protocol) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`authorization`](../packages/credentials/authorization) | `credentials` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-fetch-http`](../packages/web/web-fetch-http) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`spill`](../packages/spill/spill) | `spill` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`app-boot`](../packages/boot/app-boot) | `boot` | [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`system-prompt`](../packages/core/system-prompt) |
| [`attachment-local`](../packages/attachment/attachment-local) | `attachment` | [`attachment`](../packages/attachment/attachment), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`code-runtime-worker-thread`](../packages/code-runtime/code-runtime-worker-thread) | `code-runtime` | [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`persona`](../packages/preset/persona) | `preset` | [`invariants`](../packages/runtime-diagnostics/invariants), [`system-prompt`](../packages/core/system-prompt) |
| [`authorization`](../packages/credentials/authorization) | `credentials` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`session-log-deepseek`](../packages/session/session-log-deepseek) | `session` | [`deepseek-llm-api-extensions`](../packages/llm/deepseek-llm-api-extensions), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
@@ -1817,8 +1453,14 @@ flowchart TD
| [`session-snapshot`](../packages/test-support/session-snapshot) | `test-support` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-fetch-http`](../packages/web/web-fetch-http) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`spill`](../packages/spill/spill) |
| [`app-boot`](../packages/boot/app-boot) | `boot` | [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`system-prompt`](../packages/core/system-prompt) |
| [`message-feedback`](../packages/feedback/message-feedback) | `feedback` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol) |
| [`persona`](../packages/preset/persona) | `preset` | [`invariants`](../packages/runtime-diagnostics/invariants), [`system-prompt`](../packages/core/system-prompt) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
@@ -1837,6 +1479,7 @@ flowchart TD
| [`skill-filesystem`](../packages/skill/skill-filesystem) | `skill` | [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`shell`](../packages/shell/shell) |
| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`client-connection`](../packages/client/connection), [`host-directory-picker`](../packages/host/directory-picker), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) |
| [`file-reference`](../packages/context/file-reference) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`shell`](../packages/shell/shell) |
@@ -1908,7 +1551,6 @@ flowchart TD
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`mcp-client`](../packages/mcp/mcp-client), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`token-meter`](../packages/llm/token-meter), [`user-approval`](../packages/interaction/user-approval) |
| [`api-settings-controller`](../packages/api/settings-controller) | `api` | [`agent-presets`](../packages/preset/agent-presets), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`native-command`](../packages/util/native-command), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`typert-protocol`](../packages/typert/protocol) |
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt) |
| [`client-connection`](../packages/client/connection) | `client` | [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`host-directory-picker`](../packages/host/directory-picker), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`tool-todo`](../packages/todo/tool-todo) |
| [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-bash`](../packages/shell/tool-bash) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
@@ -1917,12 +1559,9 @@ flowchart TD
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval), [`util-time`](../packages/util/time) |
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`api-gateway`](../packages/api/gateway) | `api` | [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-registry`](../packages/typert/registry) |
| [`compaction-basic`](../packages/compaction/compaction-basic) | `compaction` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`typert-protocol`](../packages/typert/protocol) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs-local`](../packages/jobs/jobs-local), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`shell-env`](../packages/shell/shell-env), [`skill`](../packages/skill/skill), [`skill-filesystem`](../packages/skill/skill-filesystem), [`system-prompt`](../packages/core/system-prompt), [`tool-bash`](../packages/shell/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-jobs`](../packages/jobs/tool-jobs), [`tool-skill`](../packages/skill/tool-skill), [`tools`](../packages/core/tools) |
| [`experimental-webworker-runtime`](../packages/experimental/webworker-runtime) | `experimental` | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-frontend-static`](../packages/host/frontend-static) | `host` | [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`webhook-github`](../packages/webhook/webhook-github) | `webhook` | [`credentials`](../packages/credentials/credentials), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`webhook`](../packages/webhook/webhook) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
@@ -1933,57 +1572,15 @@ flowchart TD
| [`tool-subagent-report`](../packages/subagent/tool-subagent-report) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`hooks-claude-code`](../packages/hooks/hooks-claude-code) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`native-command`](../packages/util/native-command), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`skill`](../packages/skill/skill), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-time`](../packages/util/time), [`util-workspace-path`](../packages/util/workspace-path), [`workspace`](../packages/workspace/workspace) |
| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`client-connection`](../packages/client/connection), [`host-directory-picker`](../packages/host/directory-picker), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) |
| [`experimental-agent-team`](../packages/experimental/agent-team) | `experimental` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) |
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-worker-thread`](../packages/workflow/workflow-worker-thread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`api-remotes`](../packages/api/remotes) | `api` | [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`api-session-controller`](../packages/api/session-controller), [`api-settings-controller`](../packages/api/settings-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`commands`](../packages/interaction/commands), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`goal`](../packages/goal/goal), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`session-reference`](../packages/context/session-reference), [`settings`](../packages/settings/settings), [`subagent`](../packages/subagent/subagent), [`user-approval`](../packages/interaction/user-approval), [`user-questions`](../packages/interaction/user-questions) |
| [`client-ui-session`](../packages/client/ui-session) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | `experimental` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-slots`](../packages/client/ui-slots), [`experimental-agent-team`](../packages/experimental/agent-team), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | `experimental` | [`agent`](../packages/core/agent), [`experimental-agent-team`](../packages/experimental/agent-team), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`sdk-client`](../packages/sdk/client) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) |
| [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-remotes`](../packages/api/remotes), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-modules`](../packages/client/modules), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`util-crypto`](../packages/util/crypto), [`util-workspace-path`](../packages/util/workspace-path), [`workspace`](../packages/workspace/workspace) |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`api-workspace-controller`](../packages/api/workspace-controller), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`schedule`](../packages/schedule/schedule), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`agent-presets`](../packages/preset/agent-presets), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-approval`](../packages/client/ui-approval) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session) |
| [`client-ui-schedule`](../packages/client/ui-schedule) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`schedule`](../packages/schedule/schedule) |
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`api-session-controller`](../packages/api/session-controller), [`attachment`](../packages/attachment/attachment), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions) |
| [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | `experimental` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-slots`](../packages/client/ui-slots), [`experimental-agent-team`](../packages/experimental/agent-team), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-chat`](../packages/client/ui-chat) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-locale`](../packages/client/locale), [`client-ui-approval`](../packages/client/ui-approval), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-stats`](../packages/session/session-stats), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-commands`](../packages/client/ui-commands) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-reference`](../packages/client/ui-reference) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-reference`](../packages/context/session-reference), [`typert-protocol`](../packages/typert/protocol), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) |
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`attachment`](../packages/attachment/attachment), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-trajectory`](../packages/client/ui-trajectory), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`permission-presets`](../packages/interaction/permission-presets) |
| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`api-remotes`](../packages/api/remotes), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) |
| [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-tool`](../packages/client/ui-tool), [`cordis-client-runner`](../packages/extensions/cordis-client-runner), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
+155 -558
View File
@@ -1,11 +1,11 @@
<!-- 英文源文件由 scripts/gen-module-graph.ts 生成;本中文文件是通过双语配对维护的经评审对侧
更新时先运行 `pnpm run gen-module-graph` 更新英文,再更新本文件并运行 `pnpm run verify-translation-pairing --write docs/module-graph.md` 重新记录配对。 -->
<!-- 由 scripts/gen-module-graph.ts 生成——请勿手工编辑
运行 `pnpm run gen-module-graph` 重新生成。 -->
# 模块依赖关系图
# 共享实例依赖关系图
[English](module-graph.md) | 中文
`@deepseek-ai/dsh-*` harness 包之间的依赖关系。该关系图根据各包的 `peerDependencies`(规范的运行时依赖信号)生成,并`packages/<group>/<pkg>` 层级分组`a --> b` 表示包 `a` 依赖包 `b`。名称中的 `@deepseek-ai/dsh-` 前缀已移除。
`@deepseek-ai/dsh-*` harness 包之间的 peer 依赖关系。peer 表示消费端需要提供共享实例,不包括普通运行时 dependency 或仅开发期关系。该图`packages/<group>/<pkg>` 层级分组`a --> b` 表示包 `a` peer 依赖包 `b`。名称中的 `@deepseek-ai/dsh-` 前缀已移除。
```mermaid
flowchart TD
@@ -377,11 +377,6 @@ flowchart TD
pkg_base --> pkg_invariants
pkg_sdk_app --> pkg_invariants
pkg_sdk_minimal --> pkg_invariants
pkg_client_store --> pkg_invariants
pkg_client_ui_primitives --> pkg_invariants
pkg_client_ui_renderer --> pkg_invariants
pkg_client_ui_slots --> pkg_invariants
pkg_client_web --> pkg_invariants
pkg_code_runtime --> pkg_invariants
pkg_code_runtime_python --> pkg_invariants
pkg_e2b --> pkg_invariants
@@ -398,18 +393,37 @@ flowchart TD
pkg_win32_process --> pkg_invariants
pkg_llm_mock_server --> pkg_invariants
pkg_typert_generator --> pkg_invariants
pkg_typert_loader --> pkg_invariants
pkg_typert_loader --> pkg_typert_registry
pkg_typert_protocol --> pkg_invariants
pkg_typert_registry --> pkg_invariants
pkg_llm --> pkg_typert_protocol
pkg_session --> pkg_scope
pkg_api_gateway --> pkg_typert_protocol
pkg_api_remotes --> pkg_scope
pkg_attachment --> pkg_brand
pkg_attachment --> pkg_invariants
pkg_client_modules --> pkg_host_webserver
pkg_client_modules --> pkg_invariants
pkg_credentials --> pkg_brand
pkg_credentials --> pkg_invariants
pkg_subprocess_e2b --> pkg_e2b
pkg_subprocess_e2b --> pkg_invariants
pkg_subprocess_e2b --> pkg_subprocess
pkg_subprocess_e2b --> pkg_timeout
pkg_experimental_inspector --> pkg_client_modules
pkg_experimental_inspector --> pkg_host_webserver
pkg_experimental_inspector --> pkg_invariants
pkg_experimental_webworker_runtime --> pkg_client_connection
pkg_experimental_webworker_runtime --> pkg_client_modules
pkg_experimental_webworker_runtime --> pkg_host_webserver
pkg_experimental_webworker_runtime --> pkg_invariants
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_host_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_webserver
pkg_host_directory_picker_auto --> pkg_invariants
pkg_host_frontend_static --> pkg_client_connection
pkg_host_frontend_static --> pkg_host_webserver
pkg_host_frontend_static --> pkg_invariants
pkg_host_plugin_inventory --> pkg_brand
pkg_host_plugin_inventory --> pkg_invariants
pkg_host_plugin_inventory --> pkg_typert_protocol
@@ -425,32 +439,6 @@ flowchart TD
pkg_subprocess_local --> pkg_invariants
pkg_subprocess_local --> pkg_subprocess
pkg_subprocess_local --> pkg_timeout
pkg_typert_loader --> pkg_invariants
pkg_typert_loader --> pkg_typert_registry
pkg_llm --> pkg_attachment
pkg_llm --> pkg_brand
pkg_llm --> pkg_invariants
pkg_llm --> pkg_timeout
pkg_llm --> pkg_typert_protocol
pkg_attachment_local --> pkg_attachment
pkg_attachment_local --> pkg_home_paths
pkg_attachment_local --> pkg_invariants
pkg_client_hmr --> pkg_client_modules
pkg_client_hmr --> pkg_host_webserver
pkg_client_hmr --> pkg_invariants
pkg_credentials_local --> pkg_atomic_write
pkg_credentials_local --> pkg_credentials
pkg_credentials_local --> pkg_home_paths
pkg_credentials_local --> pkg_invariants
pkg_credentials_local --> pkg_launch_environment
pkg_experimental_inspector --> pkg_client_modules
pkg_experimental_inspector --> pkg_host_webserver
pkg_experimental_inspector --> pkg_invariants
pkg_session --> pkg_brand
pkg_session --> pkg_invariants
pkg_session --> pkg_llm
pkg_session --> pkg_scope
pkg_session --> pkg_typert_protocol
pkg_system_prompt --> pkg_invariants
pkg_system_prompt --> pkg_llm
pkg_system_prompt --> pkg_scope
@@ -459,37 +447,28 @@ flowchart TD
pkg_skill --> pkg_scope
pkg_web --> pkg_invariants
pkg_web --> pkg_llm
pkg_authorization --> pkg_credentials
pkg_authorization --> pkg_invariants
pkg_authorization --> pkg_llm
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_skill_badge --> pkg_invariants
pkg_skill_badge --> pkg_skill
pkg_web_fetch_http --> pkg_invariants
pkg_web_fetch_http --> pkg_timeout
pkg_web_fetch_http --> pkg_web
pkg_web_search_exa --> pkg_invariants
pkg_web_search_exa --> pkg_launch_environment
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_invariants
pkg_web_search_perplexity --> pkg_launch_environment
pkg_web_search_perplexity --> pkg_web
pkg_spill --> pkg_brand
pkg_spill --> pkg_invariants
pkg_spill --> pkg_llm
pkg_spill --> pkg_session
pkg_app_boot --> pkg_home_paths
pkg_app_boot --> pkg_invariants
pkg_app_boot --> pkg_launch_environment
pkg_app_boot --> pkg_system_prompt
pkg_attachment_local --> pkg_attachment
pkg_attachment_local --> pkg_home_paths
pkg_attachment_local --> pkg_invariants
pkg_code_runtime_worker_thread --> pkg_code_runtime
pkg_code_runtime_worker_thread --> pkg_invariants
pkg_code_runtime_worker_thread --> pkg_session
pkg_code_runtime_worker_thread --> pkg_timeout
pkg_persona --> pkg_invariants
pkg_persona --> pkg_system_prompt
pkg_authorization --> pkg_credentials
pkg_authorization --> pkg_invariants
pkg_authorization --> pkg_llm
pkg_credentials_local --> pkg_atomic_write
pkg_credentials_local --> pkg_credentials
pkg_credentials_local --> pkg_home_paths
pkg_credentials_local --> pkg_invariants
pkg_credentials_local --> pkg_launch_environment
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_sandbox --> pkg_invariants
pkg_sandbox --> pkg_llm
pkg_sandbox --> pkg_session
@@ -518,8 +497,23 @@ flowchart TD
pkg_fs --> pkg_invariants
pkg_fs --> pkg_llm
pkg_fs --> pkg_sandbox
pkg_skill_badge --> pkg_invariants
pkg_skill_badge --> pkg_skill
pkg_web_fetch_http --> pkg_invariants
pkg_web_fetch_http --> pkg_timeout
pkg_web_fetch_http --> pkg_web
pkg_web_search_exa --> pkg_invariants
pkg_web_search_exa --> pkg_launch_environment
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_invariants
pkg_web_search_perplexity --> pkg_launch_environment
pkg_web_search_perplexity --> pkg_web
pkg_spill_local --> pkg_invariants
pkg_spill_local --> pkg_spill
pkg_app_boot --> pkg_home_paths
pkg_app_boot --> pkg_invariants
pkg_app_boot --> pkg_launch_environment
pkg_app_boot --> pkg_system_prompt
pkg_message_feedback --> pkg_brand
pkg_message_feedback --> pkg_invariants
pkg_message_feedback --> pkg_llm
@@ -527,6 +521,8 @@ flowchart TD
pkg_message_feedback --> pkg_session_persistence
pkg_message_feedback --> pkg_storage_domain
pkg_message_feedback --> pkg_typert_protocol
pkg_persona --> pkg_invariants
pkg_persona --> pkg_system_prompt
pkg_sandbox_local --> pkg_invariants
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
@@ -620,6 +616,14 @@ flowchart TD
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_hook_protocol --> pkg_shell
pkg_api_workspace_controller --> pkg_api_gateway
pkg_api_workspace_controller --> pkg_client_connection
pkg_api_workspace_controller --> pkg_host_directory_picker
pkg_api_workspace_controller --> pkg_invariants
pkg_api_workspace_controller --> pkg_session
pkg_api_workspace_controller --> pkg_storage_domain
pkg_api_workspace_controller --> pkg_typert_protocol
pkg_api_workspace_controller --> pkg_workspace
pkg_file_reference --> pkg_agent
pkg_file_reference --> pkg_invariants
pkg_time_context --> pkg_agent
@@ -1034,17 +1038,6 @@ flowchart TD
pkg_web_app --> pkg_invariants
pkg_web_app --> pkg_shell_env
pkg_web_app --> pkg_system_prompt
pkg_client_connection --> pkg_attachment
pkg_client_connection --> pkg_brand
pkg_client_connection --> pkg_commands
pkg_client_connection --> pkg_credentials
pkg_client_connection --> pkg_host_directory_picker
pkg_client_connection --> pkg_host_webserver
pkg_client_connection --> pkg_invariants
pkg_client_connection --> pkg_llm
pkg_client_connection --> pkg_session
pkg_client_connection --> pkg_settings
pkg_client_connection --> pkg_tool_todo
pkg_compaction_tool_result_pruner --> pkg_compaction
pkg_compaction_tool_result_pruner --> pkg_invariants
pkg_compaction_tool_result_pruner --> pkg_llm
@@ -1123,11 +1116,6 @@ flowchart TD
pkg_tool_session_query --> pkg_system_prompt
pkg_tool_session_query --> pkg_timeout
pkg_tool_session_query --> pkg_tools
pkg_api_gateway --> pkg_brand
pkg_api_gateway --> pkg_client_connection
pkg_api_gateway --> pkg_host_webserver
pkg_api_gateway --> pkg_invariants
pkg_api_gateway --> pkg_typert_registry
pkg_compaction_basic --> pkg_agent
pkg_compaction_basic --> pkg_commands
pkg_compaction_basic --> pkg_compaction
@@ -1169,13 +1157,6 @@ flowchart TD
pkg_agent_spine_demo --> pkg_tool_jobs
pkg_agent_spine_demo --> pkg_tool_skill
pkg_agent_spine_demo --> pkg_tools
pkg_experimental_webworker_runtime --> pkg_client_connection
pkg_experimental_webworker_runtime --> pkg_client_modules
pkg_experimental_webworker_runtime --> pkg_host_webserver
pkg_experimental_webworker_runtime --> pkg_invariants
pkg_host_frontend_static --> pkg_client_connection
pkg_host_frontend_static --> pkg_host_webserver
pkg_host_frontend_static --> pkg_invariants
pkg_webhook_github --> pkg_credentials
pkg_webhook_github --> pkg_host_webserver
pkg_webhook_github --> pkg_invariants
@@ -1263,14 +1244,6 @@ flowchart TD
pkg_api_session_controller --> pkg_util_time
pkg_api_session_controller --> pkg_util_workspace_path
pkg_api_session_controller --> pkg_workspace
pkg_api_workspace_controller --> pkg_api_gateway
pkg_api_workspace_controller --> pkg_client_connection
pkg_api_workspace_controller --> pkg_host_directory_picker
pkg_api_workspace_controller --> pkg_invariants
pkg_api_workspace_controller --> pkg_session
pkg_api_workspace_controller --> pkg_storage_domain
pkg_api_workspace_controller --> pkg_typert_protocol
pkg_api_workspace_controller --> pkg_workspace
pkg_experimental_agent_team --> pkg_agent
pkg_experimental_agent_team --> pkg_brand
pkg_experimental_agent_team --> pkg_invariants
@@ -1307,30 +1280,18 @@ flowchart TD
pkg_subagent_spawn_in_process --> pkg_invariants
pkg_subagent_spawn_in_process --> pkg_subagent
pkg_subagent_spawn_in_process --> pkg_subagent_in_process_driver
pkg_api_remotes --> pkg_agent_presets
pkg_api_remotes --> pkg_api_gateway
pkg_api_remotes --> pkg_api_session_controller
pkg_api_remotes --> pkg_api_settings_controller
pkg_api_remotes --> pkg_api_workspace_controller
pkg_api_remotes --> pkg_commands
pkg_api_remotes --> pkg_cordis_host_runner
pkg_api_remotes --> pkg_credentials
pkg_api_remotes --> pkg_file_reference
pkg_api_remotes --> pkg_goal
pkg_api_remotes --> pkg_host_plugin_inventory
pkg_api_remotes --> pkg_invariants
pkg_api_remotes --> pkg_llm
pkg_api_remotes --> pkg_message_feedback
pkg_api_remotes --> pkg_session
pkg_api_remotes --> pkg_session_reference
pkg_api_remotes --> pkg_settings
pkg_api_remotes --> pkg_subagent
pkg_api_remotes --> pkg_user_approval
pkg_api_remotes --> pkg_user_questions
pkg_client_ui_session --> pkg_api_session_controller
pkg_client_ui_session --> pkg_client_ui_renderer
pkg_client_ui_session --> pkg_invariants
pkg_client_ui_session --> pkg_session
pkg_experimental_client_ui_agent_team --> pkg_api_remotes
pkg_experimental_client_ui_agent_team --> pkg_api_session_controller
pkg_experimental_client_ui_agent_team --> pkg_client_locale
pkg_experimental_client_ui_agent_team --> pkg_client_ui_conversation
pkg_experimental_client_ui_agent_team --> pkg_client_ui_primitives
pkg_experimental_client_ui_agent_team --> pkg_client_ui_renderer
pkg_experimental_client_ui_agent_team --> pkg_client_ui_session
pkg_experimental_client_ui_agent_team --> pkg_client_ui_slots
pkg_experimental_client_ui_agent_team --> pkg_experimental_agent_team
pkg_experimental_client_ui_agent_team --> pkg_invariants
pkg_experimental_client_ui_agent_team --> pkg_session
pkg_experimental_client_ui_agent_team --> pkg_typert_protocol
pkg_experimental_tool_agent_team --> pkg_agent
pkg_experimental_tool_agent_team --> pkg_experimental_agent_team
pkg_experimental_tool_agent_team --> pkg_invariants
@@ -1350,358 +1311,6 @@ flowchart TD
pkg_sdk_jsonrpc_server --> pkg_sdk_protocol
pkg_sdk_jsonrpc_server --> pkg_session
pkg_sdk_jsonrpc_server --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
pkg_client_ui_settings --> pkg_api_remotes
pkg_client_ui_settings --> pkg_invariants
pkg_client_ui_settings --> pkg_settings
pkg_client_locale --> pkg_api_remotes
pkg_client_locale --> pkg_client_connection
pkg_client_locale --> pkg_client_ui_renderer
pkg_client_locale --> pkg_client_ui_settings
pkg_client_locale --> pkg_invariants
pkg_client_locale --> pkg_settings
pkg_client_ui_settings_models --> pkg_api_remotes
pkg_client_ui_settings_models --> pkg_client_locale
pkg_client_ui_settings_models --> pkg_client_ui_renderer
pkg_client_ui_settings_models --> pkg_client_ui_settings
pkg_client_ui_settings_models --> pkg_invariants
pkg_client_ui_settings_plugin_inventory --> pkg_api_remotes
pkg_client_ui_settings_plugin_inventory --> pkg_client_locale
pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_renderer
pkg_client_ui_settings_plugin_inventory --> pkg_client_ui_settings
pkg_client_ui_settings_plugin_inventory --> pkg_invariants
pkg_client_ui_settings_plugins --> pkg_api_remotes
pkg_client_ui_settings_plugins --> pkg_client_locale
pkg_client_ui_settings_plugins --> pkg_client_ui_renderer
pkg_client_ui_settings_plugins --> pkg_client_ui_settings
pkg_client_ui_settings_plugins --> pkg_invariants
pkg_client_ui_theme --> pkg_api_remotes
pkg_client_ui_theme --> pkg_client_connection
pkg_client_ui_theme --> pkg_client_locale
pkg_client_ui_theme --> pkg_client_ui_renderer
pkg_client_ui_theme --> pkg_client_ui_settings
pkg_client_ui_theme --> pkg_host_webserver
pkg_client_ui_theme --> pkg_invariants
pkg_client_ui_theme --> pkg_settings
pkg_client_ui_layout --> pkg_client_locale
pkg_client_ui_layout --> pkg_client_ui_renderer
pkg_client_ui_layout --> pkg_client_ui_session
pkg_client_ui_layout --> pkg_client_ui_theme
pkg_client_ui_layout --> pkg_invariants
pkg_cordis_client_runner --> pkg_api_remotes
pkg_cordis_client_runner --> pkg_client_modules
pkg_cordis_client_runner --> pkg_client_ui_renderer
pkg_cordis_client_runner --> pkg_client_ui_theme
pkg_cordis_client_runner --> pkg_invariants
pkg_client_ui_conversation --> pkg_api_remotes
pkg_client_ui_conversation --> pkg_api_session_controller
pkg_client_ui_conversation --> pkg_api_workspace_controller
pkg_client_ui_conversation --> pkg_attachment
pkg_client_ui_conversation --> pkg_brand
pkg_client_ui_conversation --> pkg_client_locale
pkg_client_ui_conversation --> pkg_client_ui_layout
pkg_client_ui_conversation --> pkg_client_ui_renderer
pkg_client_ui_conversation --> pkg_client_ui_session
pkg_client_ui_conversation --> pkg_client_ui_settings
pkg_client_ui_conversation --> pkg_client_ui_workspace
pkg_client_ui_conversation --> pkg_commands
pkg_client_ui_conversation --> pkg_goal
pkg_client_ui_conversation --> pkg_invariants
pkg_client_ui_conversation --> pkg_llm
pkg_client_ui_conversation --> pkg_llm_retry
pkg_client_ui_conversation --> pkg_permission_presets
pkg_client_ui_conversation --> pkg_plan_mode
pkg_client_ui_conversation --> pkg_session
pkg_client_ui_conversation --> pkg_settings
pkg_client_ui_conversation --> pkg_token_meter
pkg_client_ui_conversation --> pkg_tool_todo
pkg_client_ui_conversation --> pkg_util_crypto
pkg_client_ui_conversation --> pkg_util_workspace_path
pkg_client_ui_conversation --> pkg_workspace
pkg_client_ui_sidebar --> pkg_api_workspace_controller
pkg_client_ui_sidebar --> pkg_client_locale
pkg_client_ui_sidebar --> pkg_client_ui_layout
pkg_client_ui_sidebar --> pkg_client_ui_renderer
pkg_client_ui_sidebar --> pkg_client_ui_session
pkg_client_ui_sidebar --> pkg_client_ui_workspace
pkg_client_ui_sidebar --> pkg_invariants
pkg_client_ui_workspace --> pkg_api_remotes
pkg_client_ui_workspace --> pkg_api_session_controller
pkg_client_ui_workspace --> pkg_api_workspace_controller
pkg_client_ui_workspace --> pkg_client_connection
pkg_client_ui_workspace --> pkg_client_locale
pkg_client_ui_workspace --> pkg_client_ui_conversation
pkg_client_ui_workspace --> pkg_client_ui_renderer
pkg_client_ui_workspace --> pkg_client_ui_session
pkg_client_ui_workspace --> pkg_client_ui_sidebar
pkg_client_ui_workspace --> pkg_invariants
pkg_client_ui_workspace --> pkg_schedule
pkg_client_ui_workspace --> pkg_session
pkg_client_ui_workspace --> pkg_typert_protocol
pkg_client_ui_workspace --> pkg_util_workspace_path
pkg_client_ui_agent_preset --> pkg_agent_presets
pkg_client_ui_agent_preset --> pkg_api_remotes
pkg_client_ui_agent_preset --> pkg_api_session_controller
pkg_client_ui_agent_preset --> pkg_client_connection
pkg_client_ui_agent_preset --> pkg_client_locale
pkg_client_ui_agent_preset --> pkg_client_ui_conversation
pkg_client_ui_agent_preset --> pkg_client_ui_renderer
pkg_client_ui_agent_preset --> pkg_client_ui_session
pkg_client_ui_agent_preset --> pkg_client_ui_settings
pkg_client_ui_agent_preset --> pkg_client_ui_workspace
pkg_client_ui_agent_preset --> pkg_invariants
pkg_client_ui_agent_preset --> pkg_session
pkg_client_ui_approval --> pkg_api_remotes
pkg_client_ui_approval --> pkg_api_session_controller
pkg_client_ui_approval --> pkg_client_locale
pkg_client_ui_approval --> pkg_client_ui_conversation
pkg_client_ui_approval --> pkg_client_ui_renderer
pkg_client_ui_approval --> pkg_client_ui_session
pkg_client_ui_approval --> pkg_invariants
pkg_client_ui_approval --> pkg_llm
pkg_client_ui_approval --> pkg_session
pkg_client_ui_approval --> pkg_typert_protocol
pkg_client_ui_brand_official --> pkg_client_ui_renderer
pkg_client_ui_brand_official --> pkg_client_ui_sidebar
pkg_client_ui_brand_official --> pkg_invariants
pkg_client_ui_directory_picker_browse --> pkg_api_remotes
pkg_client_ui_directory_picker_browse --> pkg_client_locale
pkg_client_ui_directory_picker_browse --> pkg_client_ui_renderer
pkg_client_ui_directory_picker_browse --> pkg_client_ui_workspace
pkg_client_ui_directory_picker_browse --> pkg_invariants
pkg_client_ui_directory_picker_native --> pkg_client_ui_renderer
pkg_client_ui_directory_picker_native --> pkg_client_ui_workspace
pkg_client_ui_directory_picker_native --> pkg_invariants
pkg_client_ui_input_trigger --> pkg_api_session_controller
pkg_client_ui_input_trigger --> pkg_client_locale
pkg_client_ui_input_trigger --> pkg_client_ui_conversation
pkg_client_ui_input_trigger --> pkg_client_ui_renderer
pkg_client_ui_input_trigger --> pkg_client_ui_session
pkg_client_ui_input_trigger --> pkg_file_reference
pkg_client_ui_input_trigger --> pkg_invariants
pkg_client_ui_input_trigger --> pkg_session
pkg_client_ui_jobs --> pkg_api_session_controller
pkg_client_ui_jobs --> pkg_client_locale
pkg_client_ui_jobs --> pkg_client_ui_conversation
pkg_client_ui_jobs --> pkg_client_ui_renderer
pkg_client_ui_jobs --> pkg_client_ui_session
pkg_client_ui_jobs --> pkg_invariants
pkg_client_ui_plan --> pkg_api_remotes
pkg_client_ui_plan --> pkg_client_locale
pkg_client_ui_plan --> pkg_client_ui_conversation
pkg_client_ui_plan --> pkg_client_ui_renderer
pkg_client_ui_plan --> pkg_client_ui_session
pkg_client_ui_plan --> pkg_invariants
pkg_client_ui_plan --> pkg_plan_mode
pkg_client_ui_plan --> pkg_session
pkg_client_ui_schedule --> pkg_api_session_controller
pkg_client_ui_schedule --> pkg_client_locale
pkg_client_ui_schedule --> pkg_client_ui_conversation
pkg_client_ui_schedule --> pkg_client_ui_renderer
pkg_client_ui_schedule --> pkg_client_ui_session
pkg_client_ui_schedule --> pkg_invariants
pkg_client_ui_schedule --> pkg_schedule
pkg_client_ui_settings_general --> pkg_api_remotes
pkg_client_ui_settings_general --> pkg_client_connection
pkg_client_ui_settings_general --> pkg_client_locale
pkg_client_ui_settings_general --> pkg_client_ui_renderer
pkg_client_ui_settings_general --> pkg_client_ui_session
pkg_client_ui_settings_general --> pkg_client_ui_settings
pkg_client_ui_settings_general --> pkg_client_ui_sidebar
pkg_client_ui_settings_general --> pkg_invariants
pkg_client_ui_settings_general --> pkg_settings
pkg_client_ui_trajectory --> pkg_agent
pkg_client_ui_trajectory --> pkg_api_session_controller
pkg_client_ui_trajectory --> pkg_attachment
pkg_client_ui_trajectory --> pkg_client_locale
pkg_client_ui_trajectory --> pkg_client_ui_conversation
pkg_client_ui_trajectory --> pkg_client_ui_renderer
pkg_client_ui_trajectory --> pkg_client_ui_session
pkg_client_ui_trajectory --> pkg_compaction
pkg_client_ui_trajectory --> pkg_invariants
pkg_client_ui_trajectory --> pkg_llm
pkg_client_ui_trajectory --> pkg_session
pkg_client_ui_trajectory --> pkg_tools
pkg_client_ui_user_questions --> pkg_api_remotes
pkg_client_ui_user_questions --> pkg_api_session_controller
pkg_client_ui_user_questions --> pkg_client_locale
pkg_client_ui_user_questions --> pkg_client_ui_conversation
pkg_client_ui_user_questions --> pkg_client_ui_renderer
pkg_client_ui_user_questions --> pkg_client_ui_session
pkg_client_ui_user_questions --> pkg_invariants
pkg_client_ui_user_questions --> pkg_session
pkg_client_ui_user_questions --> pkg_typert_protocol
pkg_client_ui_user_questions --> pkg_user_questions
pkg_experimental_client_ui_agent_team --> pkg_api_remotes
pkg_experimental_client_ui_agent_team --> pkg_api_session_controller
pkg_experimental_client_ui_agent_team --> pkg_client_locale
pkg_experimental_client_ui_agent_team --> pkg_client_ui_conversation
pkg_experimental_client_ui_agent_team --> pkg_client_ui_primitives
pkg_experimental_client_ui_agent_team --> pkg_client_ui_renderer
pkg_experimental_client_ui_agent_team --> pkg_client_ui_session
pkg_experimental_client_ui_agent_team --> pkg_client_ui_slots
pkg_experimental_client_ui_agent_team --> pkg_experimental_agent_team
pkg_experimental_client_ui_agent_team --> pkg_invariants
pkg_experimental_client_ui_agent_team --> pkg_session
pkg_experimental_client_ui_agent_team --> pkg_typert_protocol
pkg_client_ui_chat --> pkg_agent
pkg_client_ui_chat --> pkg_api_remotes
pkg_client_ui_chat --> pkg_api_session_controller
pkg_client_ui_chat --> pkg_api_workspace_controller
pkg_client_ui_chat --> pkg_attachment
pkg_client_ui_chat --> pkg_client_locale
pkg_client_ui_chat --> pkg_client_ui_approval
pkg_client_ui_chat --> pkg_client_ui_conversation
pkg_client_ui_chat --> pkg_client_ui_layout
pkg_client_ui_chat --> pkg_client_ui_renderer
pkg_client_ui_chat --> pkg_client_ui_session
pkg_client_ui_chat --> pkg_client_ui_settings
pkg_client_ui_chat --> pkg_client_ui_workspace
pkg_client_ui_chat --> pkg_commands
pkg_client_ui_chat --> pkg_compaction
pkg_client_ui_chat --> pkg_invariants
pkg_client_ui_chat --> pkg_llm
pkg_client_ui_chat --> pkg_llm_retry
pkg_client_ui_chat --> pkg_session
pkg_client_ui_chat --> pkg_session_stats
pkg_client_ui_chat --> pkg_settings
pkg_client_ui_chat --> pkg_token_meter
pkg_client_ui_chat --> pkg_tools
pkg_client_ui_chat --> pkg_util_workspace_path
pkg_client_ui_commands --> pkg_api_remotes
pkg_client_ui_commands --> pkg_api_session_controller
pkg_client_ui_commands --> pkg_client_locale
pkg_client_ui_commands --> pkg_client_ui_conversation
pkg_client_ui_commands --> pkg_client_ui_input_trigger
pkg_client_ui_commands --> pkg_client_ui_renderer
pkg_client_ui_commands --> pkg_client_ui_session
pkg_client_ui_commands --> pkg_commands
pkg_client_ui_commands --> pkg_invariants
pkg_client_ui_commands --> pkg_session
pkg_client_ui_reference --> pkg_api_remotes
pkg_client_ui_reference --> pkg_api_session_controller
pkg_client_ui_reference --> pkg_client_connection
pkg_client_ui_reference --> pkg_client_locale
pkg_client_ui_reference --> pkg_client_ui_input_trigger
pkg_client_ui_reference --> pkg_file_reference
pkg_client_ui_reference --> pkg_invariants
pkg_client_ui_reference --> pkg_session_reference
pkg_client_ui_reference --> pkg_typert_protocol
pkg_client_ui_reference --> pkg_util_workspace_path
pkg_client_ui_subagent --> pkg_api_session_controller
pkg_client_ui_subagent --> pkg_client_connection
pkg_client_ui_subagent --> pkg_client_locale
pkg_client_ui_subagent --> pkg_client_ui_conversation
pkg_client_ui_subagent --> pkg_client_ui_input_trigger
pkg_client_ui_subagent --> pkg_client_ui_renderer
pkg_client_ui_subagent --> pkg_client_ui_session
pkg_client_ui_subagent --> pkg_invariants
pkg_client_ui_subagent --> pkg_session
pkg_client_ui_subagent --> pkg_subagent
pkg_client_ui_subagent --> pkg_token_meter
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_client_ui_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_host_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_webserver
pkg_host_directory_picker_auto --> pkg_invariants
pkg_session_log_export --> pkg_attachment
pkg_session_log_export --> pkg_client_connection
pkg_session_log_export --> pkg_client_locale
pkg_session_log_export --> pkg_client_ui_commands
pkg_session_log_export --> pkg_client_ui_conversation
pkg_session_log_export --> pkg_client_ui_renderer
pkg_session_log_export --> pkg_client_ui_session
pkg_session_log_export --> pkg_commands
pkg_session_log_export --> pkg_invariants
pkg_session_log_export --> pkg_session
pkg_session_log_export --> pkg_session_persistence
pkg_session_log_export --> pkg_session_query
pkg_client_ui_attachment --> pkg_attachment
pkg_client_ui_attachment --> pkg_client_ui_chat
pkg_client_ui_attachment --> pkg_client_ui_conversation
pkg_client_ui_attachment --> pkg_client_ui_renderer
pkg_client_ui_attachment --> pkg_client_ui_trajectory
pkg_client_ui_attachment --> pkg_invariants
pkg_client_ui_deliverables --> pkg_api_remotes
pkg_client_ui_deliverables --> pkg_client_connection
pkg_client_ui_deliverables --> pkg_client_locale
pkg_client_ui_deliverables --> pkg_client_ui_chat
pkg_client_ui_deliverables --> pkg_client_ui_conversation
pkg_client_ui_deliverables --> pkg_client_ui_renderer
pkg_client_ui_deliverables --> pkg_invariants
pkg_client_ui_deliverables --> pkg_session
pkg_client_ui_deliverables --> pkg_system_prompt
pkg_client_ui_goal --> pkg_api_remotes
pkg_client_ui_goal --> pkg_api_session_controller
pkg_client_ui_goal --> pkg_client_locale
pkg_client_ui_goal --> pkg_client_ui_chat
pkg_client_ui_goal --> pkg_client_ui_conversation
pkg_client_ui_goal --> pkg_client_ui_renderer
pkg_client_ui_goal --> pkg_client_ui_session
pkg_client_ui_goal --> pkg_commands
pkg_client_ui_goal --> pkg_goal
pkg_client_ui_goal --> pkg_invariants
pkg_client_ui_goal --> pkg_session
pkg_client_ui_goal --> pkg_typert_protocol
pkg_client_ui_message_feedback --> pkg_api_remotes
pkg_client_ui_message_feedback --> pkg_client_connection
pkg_client_ui_message_feedback --> pkg_client_locale
pkg_client_ui_message_feedback --> pkg_client_ui_chat
pkg_client_ui_message_feedback --> pkg_client_ui_conversation
pkg_client_ui_message_feedback --> pkg_client_ui_renderer
pkg_client_ui_message_feedback --> pkg_client_ui_session
pkg_client_ui_message_feedback --> pkg_invariants
pkg_client_ui_message_feedback --> pkg_message_feedback
pkg_client_ui_message_feedback --> pkg_session
pkg_client_ui_message_feedback --> pkg_typert_protocol
pkg_client_ui_model_selection --> pkg_api_remotes
pkg_client_ui_model_selection --> pkg_api_session_controller
pkg_client_ui_model_selection --> pkg_client_locale
pkg_client_ui_model_selection --> pkg_client_ui_commands
pkg_client_ui_model_selection --> pkg_client_ui_conversation
pkg_client_ui_model_selection --> pkg_client_ui_input_trigger
pkg_client_ui_model_selection --> pkg_client_ui_renderer
pkg_client_ui_model_selection --> pkg_client_ui_session
pkg_client_ui_model_selection --> pkg_invariants
pkg_client_ui_model_selection --> pkg_session
pkg_client_ui_model_selection --> pkg_typert_protocol
pkg_client_ui_permission_presets --> pkg_api_remotes
pkg_client_ui_permission_presets --> pkg_api_session_controller
pkg_client_ui_permission_presets --> pkg_client_locale
pkg_client_ui_permission_presets --> pkg_client_ui_commands
pkg_client_ui_permission_presets --> pkg_client_ui_input_trigger
pkg_client_ui_permission_presets --> pkg_client_ui_renderer
pkg_client_ui_permission_presets --> pkg_client_ui_session
pkg_client_ui_permission_presets --> pkg_client_ui_settings
pkg_client_ui_permission_presets --> pkg_invariants
pkg_client_ui_permission_presets --> pkg_permission_presets
pkg_client_ui_tool --> pkg_api_remotes
pkg_client_ui_tool --> pkg_api_workspace_controller
pkg_client_ui_tool --> pkg_client_connection
pkg_client_ui_tool --> pkg_client_locale
pkg_client_ui_tool --> pkg_client_ui_chat
pkg_client_ui_tool --> pkg_client_ui_conversation
pkg_client_ui_tool --> pkg_client_ui_renderer
pkg_client_ui_tool --> pkg_client_ui_session
pkg_client_ui_tool --> pkg_invariants
pkg_client_ui_tool --> pkg_util_workspace_path
pkg_client_ui_workflow_run --> pkg_api_session_controller
pkg_client_ui_workflow_run --> pkg_client_locale
pkg_client_ui_workflow_run --> pkg_client_ui_chat
pkg_client_ui_workflow_run --> pkg_client_ui_conversation
pkg_client_ui_workflow_run --> pkg_client_ui_renderer
pkg_client_ui_workflow_run --> pkg_client_ui_session
pkg_client_ui_workflow_run --> pkg_invariants
pkg_client_ui_workflow_run --> pkg_session
pkg_client_ui_workflow_run --> pkg_tool_workflow
pkg_client_ui_workflow_run --> pkg_workflow
pkg_client_test_runtime --> pkg_api_session_controller
pkg_client_test_runtime --> pkg_api_workspace_controller
pkg_client_test_runtime --> pkg_attachment
@@ -1717,30 +1326,66 @@ flowchart TD
pkg_client_test_runtime --> pkg_session
pkg_client_test_runtime --> pkg_subagent
pkg_client_test_runtime --> pkg_typert_protocol
pkg_client_ui_skill --> pkg_api_remotes
pkg_client_ui_skill --> pkg_api_session_controller
pkg_client_ui_skill --> pkg_client_connection
pkg_client_ui_skill --> pkg_client_locale
pkg_client_ui_skill --> pkg_client_ui_input_trigger
pkg_client_ui_skill --> pkg_client_ui_renderer
pkg_client_ui_skill --> pkg_client_ui_tool
pkg_client_ui_skill --> pkg_invariants
pkg_client_ui_skill --> pkg_session
pkg_client_ui_cordis --> pkg_api_remotes
pkg_client_ui_cordis --> pkg_client_connection
pkg_client_ui_cordis --> pkg_client_locale
pkg_client_ui_cordis --> pkg_client_ui_input_trigger
pkg_client_ui_cordis --> pkg_client_ui_renderer
pkg_client_ui_cordis --> pkg_client_ui_session
pkg_client_ui_cordis --> pkg_client_ui_sidebar
pkg_client_ui_cordis --> pkg_client_ui_tool
pkg_client_ui_cordis --> pkg_cordis_client_runner
pkg_client_ui_cordis --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
```
| Package | Group | Depends on |
| 包 | 分组 | Peer 依赖 |
| --- | --- | --- |
| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | — |
| [`client-connection`](../packages/client/connection) | `client` | — |
| [`client-hmr`](../packages/client/hmr) | `client` | — |
| [`client-locale`](../packages/client/locale) | `client` | — |
| [`client-modules`](../packages/client/modules) | `client` | — |
| [`client-store`](../packages/client/store) | `client` | — |
| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | — |
| [`client-ui-approval`](../packages/client/ui-approval) | `client` | — |
| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | — |
| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | — |
| [`client-ui-chat`](../packages/client/ui-chat) | `client` | — |
| [`client-ui-commands`](../packages/client/ui-commands) | `client` | — |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | — |
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | — |
| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | — |
| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | — |
| [`client-ui-goal`](../packages/client/ui-goal) | `client` | — |
| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | — |
| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | — |
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | — |
| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | — |
| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | — |
| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | — |
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | — |
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | — |
| [`client-ui-reference`](../packages/client/ui-reference) | `client` | — |
| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | — |
| [`client-ui-schedule`](../packages/client/ui-schedule) | `client` | — |
| [`client-ui-session`](../packages/client/ui-session) | `client` | — |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | — |
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | — |
| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | — |
| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | — |
| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | — |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | — |
| [`client-ui-skill`](../packages/client/ui-skill) | `client` | — |
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | — |
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | — |
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | — |
| [`client-ui-tool`](../packages/client/ui-tool) | `client` | — |
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | — |
| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | — |
| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | — |
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | — |
| [`client-web`](../packages/client/web) | `client` | — |
| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | — |
| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | — |
| [`invariants`](../packages/runtime-diagnostics/invariants) | `runtime-diagnostics` | — |
| [`typert-registry`](../packages/typert/registry) | `typert` | — |
| [`atomic-write`](../packages/util/atomic-write) | `util` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`brand`](../packages/util/brand) | `util` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`home-paths`](../packages/util/home-paths) | `util` | [`invariants`](../packages/runtime-diagnostics/invariants) |
@@ -1758,11 +1403,6 @@ flowchart TD
| [`base`](../packages/bundle/base) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`sdk-app`](../packages/bundle/sdk-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`sdk-minimal`](../packages/bundle/sdk-minimal) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-store`](../packages/client/store) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-renderer`](../packages/client/ui-renderer) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`code-runtime-python`](../packages/code-runtime/code-runtime-python) | `code-runtime` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`e2b`](../packages/e2b/e2b) | `e2b` | [`invariants`](../packages/runtime-diagnostics/invariants) |
@@ -1779,38 +1419,34 @@ flowchart TD
| [`win32-process`](../packages/subprocess/win32-process) | `subprocess` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`llm-mock-server`](../packages/test-support/llm-mock-server) | `test-support` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`typert-generator`](../packages/typert/generator) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-registry`](../packages/typert/registry) |
| [`typert-protocol`](../packages/typert/protocol) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`typert-registry`](../packages/typert/registry) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`llm`](../packages/llm/llm) | `llm` | [`typert-protocol`](../packages/typert/protocol) |
| [`session`](../packages/core/session) | `core` | [`scope`](../packages/core/scope) |
| [`api-gateway`](../packages/api/gateway) | `api` | [`typert-protocol`](../packages/typert/protocol) |
| [`api-remotes`](../packages/api/remotes) | `api` | [`scope`](../packages/core/scope) |
| [`attachment`](../packages/attachment/attachment) | `attachment` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-modules`](../packages/client/modules) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`credentials`](../packages/credentials/credentials) | `credentials` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`subprocess-e2b`](../packages/e2b/subprocess-e2b) | `e2b` | [`e2b`](../packages/e2b/e2b), [`invariants`](../packages/runtime-diagnostics/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`experimental-inspector`](../packages/experimental/inspector) | `experimental` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`experimental-webworker-runtime`](../packages/experimental/webworker-runtime) | `experimental` | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-frontend-static`](../packages/host/frontend-static) | `host` | [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-plugin-inventory`](../packages/host/plugin-inventory) | `host` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-protocol`](../packages/typert/protocol) |
| [`anonymous-user-id`](../packages/identity/anonymous-user-id) | `identity` | [`brand`](../packages/util/brand), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`storage-domain`](../packages/storage/storage-domain) | `storage` | [`invariants`](../packages/runtime-diagnostics/invariants), [`storage`](../packages/storage/storage) |
| [`storage-json`](../packages/storage/storage-json) | `storage` | [`invariants`](../packages/runtime-diagnostics/invariants), [`storage`](../packages/storage/storage) |
| [`storage-sqlite`](../packages/storage/storage-sqlite) | `storage` | [`invariants`](../packages/runtime-diagnostics/invariants), [`storage`](../packages/storage/storage) |
| [`subprocess-local`](../packages/subprocess/subprocess-local) | `subprocess` | [`invariants`](../packages/runtime-diagnostics/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-registry`](../packages/typert/registry) |
| [`llm`](../packages/llm/llm) | `llm` | [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`typert-protocol`](../packages/typert/protocol) |
| [`attachment-local`](../packages/attachment/attachment-local) | `attachment` | [`attachment`](../packages/attachment/attachment), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment) |
| [`experimental-inspector`](../packages/experimental/inspector) | `experimental` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`typert-protocol`](../packages/typert/protocol) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`authorization`](../packages/credentials/authorization) | `credentials` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-fetch-http`](../packages/web/web-fetch-http) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`spill`](../packages/spill/spill) | `spill` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`app-boot`](../packages/boot/app-boot) | `boot` | [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`system-prompt`](../packages/core/system-prompt) |
| [`attachment-local`](../packages/attachment/attachment-local) | `attachment` | [`attachment`](../packages/attachment/attachment), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`code-runtime-worker-thread`](../packages/code-runtime/code-runtime-worker-thread) | `code-runtime` | [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
| [`persona`](../packages/preset/persona) | `preset` | [`invariants`](../packages/runtime-diagnostics/invariants), [`system-prompt`](../packages/core/system-prompt) |
| [`authorization`](../packages/credentials/authorization) | `credentials` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm) |
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`session-log-deepseek`](../packages/session/session-log-deepseek) | `session` | [`deepseek-llm-api-extensions`](../packages/llm/deepseek-llm-api-extensions), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`session-persistence`](../packages/session/session-persistence) | `session` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
@@ -1819,8 +1455,14 @@ flowchart TD
| [`session-snapshot`](../packages/test-support/session-snapshot) | `test-support` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`agent`](../packages/core/agent) | `core` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`system-prompt`](../packages/core/system-prompt), [`typert-protocol`](../packages/typert/protocol) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`skill-badge`](../packages/skill/skill-badge) | `skill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-fetch-http`](../packages/web/web-fetch-http) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`web`](../packages/web/web) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/runtime-diagnostics/invariants), [`spill`](../packages/spill/spill) |
| [`app-boot`](../packages/boot/app-boot) | `boot` | [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`system-prompt`](../packages/core/system-prompt) |
| [`message-feedback`](../packages/feedback/message-feedback) | `feedback` | [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol) |
| [`persona`](../packages/preset/persona) | `preset` | [`invariants`](../packages/runtime-diagnostics/invariants), [`system-prompt`](../packages/core/system-prompt) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
| [`session-persistence-sqlite`](../packages/session/session-persistence-sqlite) | `session` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence) |
@@ -1839,6 +1481,7 @@ flowchart TD
| [`skill-filesystem`](../packages/skill/skill-filesystem) | `skill` | [`fs`](../packages/fs/fs), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`skill`](../packages/skill/skill) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`launch-environment`](../packages/util/launch-environment), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`web`](../packages/web/web) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`shell`](../packages/shell/shell) |
| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`client-connection`](../packages/client/connection), [`host-directory-picker`](../packages/host/directory-picker), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) |
| [`file-reference`](../packages/context/file-reference) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`shell`](../packages/shell/shell) |
@@ -1910,7 +1553,6 @@ flowchart TD
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`mcp-client`](../packages/mcp/mcp-client), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`token-meter`](../packages/llm/token-meter), [`user-approval`](../packages/interaction/user-approval) |
| [`api-settings-controller`](../packages/api/settings-controller) | `api` | [`agent-presets`](../packages/preset/agent-presets), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/runtime-diagnostics/invariants), [`native-command`](../packages/util/native-command), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`typert-protocol`](../packages/typert/protocol) |
| [`web-app`](../packages/bundle/web-app) | `bundle` | [`invariants`](../packages/runtime-diagnostics/invariants), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt) |
| [`client-connection`](../packages/client/connection) | `client` | [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`commands`](../packages/interaction/commands), [`credentials`](../packages/credentials/credentials), [`host-directory-picker`](../packages/host/directory-picker), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`tool-todo`](../packages/todo/tool-todo) |
| [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner) | `compaction` | [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`tool-cordis`](../packages/extensions/tool-cordis) | `extensions` | [`agent`](../packages/core/agent), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-bash`](../packages/shell/tool-bash) | `shell` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`shell`](../packages/shell/shell), [`shell-env`](../packages/shell/shell-env), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
@@ -1919,12 +1561,9 @@ flowchart TD
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`agent-presets`](../packages/preset/agent-presets), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`typert-protocol`](../packages/typert/protocol), [`user-approval`](../packages/interaction/user-approval), [`util-time`](../packages/util/time) |
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`api-gateway`](../packages/api/gateway) | `api` | [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`typert-registry`](../packages/typert/registry) |
| [`compaction-basic`](../packages/compaction/compaction-basic) | `compaction` | [`agent`](../packages/core/agent), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`compaction-tool-result-pruner`](../packages/compaction/compaction-tool-result-pruner), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`output-retention`](../packages/util/output-retention), [`session`](../packages/core/session), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`typert-protocol`](../packages/typert/protocol) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`agent-loop`](../packages/core/agent-loop), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`home-paths`](../packages/util/home-paths), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs-local`](../packages/jobs/jobs-local), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session/session-title), [`shell-env`](../packages/shell/shell-env), [`skill`](../packages/skill/skill), [`skill-filesystem`](../packages/skill/skill-filesystem), [`system-prompt`](../packages/core/system-prompt), [`tool-bash`](../packages/shell/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-jobs`](../packages/jobs/tool-jobs), [`tool-skill`](../packages/skill/tool-skill), [`tools`](../packages/core/tools) |
| [`experimental-webworker-runtime`](../packages/experimental/webworker-runtime) | `experimental` | [`client-connection`](../packages/client/connection), [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`host-frontend-static`](../packages/host/frontend-static) | `host` | [`client-connection`](../packages/client/connection), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`webhook-github`](../packages/webhook/webhook-github) | `webhook` | [`credentials`](../packages/credentials/credentials), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`webhook`](../packages/webhook/webhook) |
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`subagent-claude-code`](../packages/subagent/subagent-claude-code) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
@@ -1935,57 +1574,15 @@ flowchart TD
| [`tool-subagent-report`](../packages/subagent/tool-subagent-report) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`hooks-claude-code`](../packages/hooks/hooks-claude-code) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`api-session-controller`](../packages/api/session-controller) | `api` | [`agent`](../packages/core/agent), [`agent-default-model`](../packages/core/agent-default-model), [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-connection`](../packages/client/connection), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`jobs`](../packages/jobs/jobs), [`llm`](../packages/llm/llm), [`native-command`](../packages/util/native-command), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-query`](../packages/session-query/session-query), [`session-title`](../packages/session/session-title), [`skill`](../packages/skill/skill), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol), [`typert-registry`](../packages/typert/registry), [`util-time`](../packages/util/time), [`util-workspace-path`](../packages/util/workspace-path), [`workspace`](../packages/workspace/workspace) |
| [`api-workspace-controller`](../packages/api/workspace-controller) | `api` | [`api-gateway`](../packages/api/gateway), [`client-connection`](../packages/client/connection), [`host-directory-picker`](../packages/host/directory-picker), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`storage-domain`](../packages/storage/storage-domain), [`typert-protocol`](../packages/typert/protocol), [`workspace`](../packages/workspace/workspace) |
| [`experimental-agent-team`](../packages/experimental/agent-team) | `experimental` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-projection`](../packages/session/session-projection), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) |
| [`sdk-protocol`](../packages/sdk/protocol) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-worker-thread`](../packages/workflow/workflow-worker-thread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`invariants`](../packages/runtime-diagnostics/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
| [`api-remotes`](../packages/api/remotes) | `api` | [`agent-presets`](../packages/preset/agent-presets), [`api-gateway`](../packages/api/gateway), [`api-session-controller`](../packages/api/session-controller), [`api-settings-controller`](../packages/api/settings-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`commands`](../packages/interaction/commands), [`cordis-host-runner`](../packages/extensions/cordis-host-runner), [`credentials`](../packages/credentials/credentials), [`file-reference`](../packages/context/file-reference), [`goal`](../packages/goal/goal), [`host-plugin-inventory`](../packages/host/plugin-inventory), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`session-reference`](../packages/context/session-reference), [`settings`](../packages/settings/settings), [`subagent`](../packages/subagent/subagent), [`user-approval`](../packages/interaction/user-approval), [`user-questions`](../packages/interaction/user-questions) |
| [`client-ui-session`](../packages/client/ui-session) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | `experimental` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-slots`](../packages/client/ui-slots), [`experimental-agent-team`](../packages/experimental/agent-team), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | `experimental` | [`agent`](../packages/core/agent), [`experimental-agent-team`](../packages/experimental/agent-team), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`sdk-client`](../packages/sdk/client) | `sdk` | [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) |
| [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`api-remotes`](../packages/api/remotes), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-locale`](../packages/client/locale) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-ui-settings-models`](../packages/client/ui-settings-models) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-settings-plugin-inventory`](../packages/client/ui-settings-plugin-inventory) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-settings-plugins`](../packages/client/ui-settings-plugins) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-settings`](../packages/client/ui-settings), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`cordis-client-runner`](../packages/extensions/cordis-client-runner) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-modules`](../packages/client/modules), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`brand`](../packages/util/brand), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tool-todo`](../packages/todo/tool-todo), [`util-crypto`](../packages/util/crypto), [`util-workspace-path`](../packages/util/workspace-path), [`workspace`](../packages/workspace/workspace) |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`api-workspace-controller`](../packages/api/workspace-controller), [`client-locale`](../packages/client/locale), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`schedule`](../packages/schedule/schedule), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-agent-preset`](../packages/client/ui-agent-preset) | `client` | [`agent-presets`](../packages/preset/agent-presets), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-approval`](../packages/client/ui-approval) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-brand-official`](../packages/client/ui-brand-official) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native) | `client` | [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-input-trigger`](../packages/client/ui-input-trigger) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-jobs`](../packages/client/ui-jobs) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`api-remotes`](../packages/api/remotes), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`plan-mode`](../packages/plan/plan-mode), [`session`](../packages/core/session) |
| [`client-ui-schedule`](../packages/client/ui-schedule) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`schedule`](../packages/schedule/schedule) |
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`invariants`](../packages/runtime-diagnostics/invariants), [`settings`](../packages/settings/settings) |
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`agent`](../packages/core/agent), [`api-session-controller`](../packages/api/session-controller), [`attachment`](../packages/attachment/attachment), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`client-ui-user-questions`](../packages/client/ui-user-questions) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol), [`user-questions`](../packages/interaction/user-questions) |
| [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | `experimental` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-slots`](../packages/client/ui-slots), [`experimental-agent-team`](../packages/experimental/agent-team), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-chat`](../packages/client/ui-chat) | `client` | [`agent`](../packages/core/agent), [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-locale`](../packages/client/locale), [`client-ui-approval`](../packages/client/ui-approval), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-layout`](../packages/client/ui-layout), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-workspace`](../packages/client/ui-workspace), [`commands`](../packages/interaction/commands), [`compaction`](../packages/compaction/compaction), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-stats`](../packages/session/session-stats), [`settings`](../packages/settings/settings), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-commands`](../packages/client/ui-commands) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-reference`](../packages/client/ui-reference) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`file-reference`](../packages/context/file-reference), [`invariants`](../packages/runtime-diagnostics/invariants), [`session-reference`](../packages/context/session-reference), [`typert-protocol`](../packages/typert/protocol), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`token-meter`](../packages/llm/token-meter) |
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`client-ui-directory-picker-browse`](../packages/client/ui-directory-picker-browse), [`client-ui-directory-picker-native`](../packages/client/ui-directory-picker-native), [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`session-log-export`](../packages/session-query/session-log-export) | `session-query` | [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`client-ui-attachment`](../packages/client/ui-attachment) | `client` | [`attachment`](../packages/attachment/attachment), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-trajectory`](../packages/client/ui-trajectory), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`client-ui-deliverables`](../packages/client/ui-deliverables) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-message-feedback`](../packages/client/ui-message-feedback) | `client` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`message-feedback`](../packages/feedback/message-feedback), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-model-selection`](../packages/client/ui-model-selection) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-permission-presets`](../packages/client/ui-permission-presets) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-commands`](../packages/client/ui-commands), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`invariants`](../packages/runtime-diagnostics/invariants), [`permission-presets`](../packages/interaction/permission-presets) |
| [`client-ui-tool`](../packages/client/ui-tool) | `client` | [`api-remotes`](../packages/api/remotes), [`api-workspace-controller`](../packages/api/workspace-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`util-workspace-path`](../packages/util/workspace-path) |
| [`client-ui-workflow-run`](../packages/client/ui-workflow-run) | `client` | [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`tool-workflow`](../packages/workflow/tool-workflow), [`workflow`](../packages/workflow/workflow) |
| [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) |
| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-tool`](../packages/client/ui-tool), [`invariants`](../packages/runtime-diagnostics/invariants), [`session`](../packages/core/session) |
| [`client-ui-cordis`](../packages/extensions/ui-cordis) | `extensions` | [`api-remotes`](../packages/api/remotes), [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-ui-input-trigger`](../packages/client/ui-input-trigger), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-sidebar`](../packages/client/ui-sidebar), [`client-ui-tool`](../packages/client/ui-tool), [`cordis-client-runner`](../packages/extensions/cordis-client-runner), [`invariants`](../packages/runtime-diagnostics/invariants) |
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
+4
View File
@@ -50,6 +50,8 @@
"test:web:perf": "npm run build && npm run test:web:perf:built",
"test:web:perf:built": "DSH_SNAPSHOT=replay vitest run --config vitest.web.perf.config.ts",
"test:web:stress": "npm run build && vitest run --config vitest.web-stress.config.ts",
"benchmark:npm-resolution": "tsx scripts/benchmark-npm-resolution.ts",
"benchmark:npm-resolution:next": "tsx scripts/benchmark-next-package-dependency.ts",
"test:gui": "vitest run packages/client packages/host",
"check:all": "tsx scripts/run-gates.ts check-all",
"check:ci": "tsx scripts/run-gates.ts ci-primary",
@@ -104,6 +106,8 @@
"verify-optional-dependency-imports": "tsx scripts/verify-optional-dependency-imports.ts",
"verify-runtime-closure": "tsx scripts/verify-runtime-closure.ts",
"verify-application-entrypoints": "tsx scripts/verify-application-entrypoints.ts",
"verify-package-dependencies": "tsx scripts/verify-package-dependencies.ts",
"verify-npm-install-layout": "tsx scripts/verify-npm-install-layout.ts",
"verify-client-packages": "tsx scripts/verify-client-packages.ts",
"verify-client-ui-i18n": "tsx scripts/verify-client-ui-i18n.ts",
"verify-vendored-links": "tsx scripts/verify-vendored-links.ts",
+4 -8
View File
@@ -57,17 +57,12 @@
"license": "MIT",
"dependencies": {
"@deepseek-ai/dsh-timeout": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/schemastery": "workspace:^",
"ws": "^8.21.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-typert-registry": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
@@ -78,6 +73,7 @@
"@deepseek-ai/dsh-util-crypto": "workspace:^",
"@types/ws": "^8.18.1",
"@deepseek-ai/cordis": "workspace:^",
"zod": "^4.4.3"
"zod": "^4.4.3",
"@deepseek-ai/dsh-typert-protocol": "workspace:^"
}
}
+7 -25
View File
@@ -55,37 +55,17 @@
"lib/types/**/*.d.ts"
],
"dependencies": {
"@deepseek-ai/dsh-scope": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^"
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-agent-presets": "workspace:^",
"@deepseek-ai/dsh-api-gateway": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-api-settings-controller": "workspace:^",
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-cordis-host-runner": "workspace:^",
"@deepseek-ai/dsh-credentials": "workspace:^",
"@deepseek-ai/dsh-file-reference": "workspace:^",
"@deepseek-ai/dsh-goal": "workspace:^",
"@deepseek-ai/dsh-host-plugin-inventory": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-message-feedback": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-reference": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/dsh-user-questions": "workspace:^"
"@deepseek-ai/dsh-scope": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-agent-presets": "workspace:^",
"@deepseek-ai/dsh-api-gateway": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-api-settings-controller": "workspace:^",
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
@@ -97,11 +77,13 @@
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-message-feedback": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-reference": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-user-approval": "workspace:^",
"@deepseek-ai/dsh-user-questions": "workspace:^"
"@deepseek-ai/dsh-user-questions": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/dsh-scope": "workspace:^"
}
}
+6 -6
View File
@@ -56,13 +56,13 @@ Non-negotiables across the layers:
## Dependency declaration
Npm sections describe installation and development relationships; each build face independently decides what its artifact contains. [`verify-client-packages`](../../scripts/verify-client-packages.ts) checks the client-specific rules and can repair unambiguous manifest drift with `--fix`.
Npm sections describe installation and development relationships; each build face independently decides what its artifact contains. [`verify-package-dependencies`](../../scripts/verify-package-dependencies.ts) checks and repairs these rules; [`verify-client-packages`](../../scripts/verify-client-packages.ts) owns Client loading and module requests.
1. **Every client package keeps Cordis in matching `peerDependencies` and `devDependencies`.** This includes the static packages because their Node face participates in the same Cordis plugin contract.
2. **A dynamic package declares internal dynamic relationships as peer plus dev.** Production source imports, re-exports, module augmentations, and type-only references to an `@deepseek-ai/dsh-*` package count, as does a package named by `dsh.client.inject`. A test-only internal dependency stays dev-only.
3. **Static client inputs are dev-only for a dynamic consumer.** A package without `dsh.client`, plus the React modules seeded by the web shell, belongs only in the consumer's `devDependencies`; it never belongs in that dynamic package's `dependencies` or `peerDependencies`. `packages/client/web` likewise keeps Loader, modules, and static UI inputs as development inputs; Cordis remains peer plus dev.
4. **Ordinary installed libraries stay in `dependencies`.** This includes private implementation libraries bundled into `lib/client.js` and bare imports left in a statically linked `lib/index.js`; the final Vite host, not the library build, merges and splits the latter. A dynamic package never puts an `@deepseek-ai/dsh-*` package in `dependencies`.
5. **Every peer has a matching development range.** npm dependency and peer cycles are allowed; only the synchronous module-request graph has the separate acyclicity rule below.
2. **A package under `packages/client/` is always covered; `dsh.client` marks a Client/Host package outside that directory.** Explicit include/exclude entries handle exceptions. Every covered package's Host entry is scanned, while a `./client` export alone does not select dependency policy.
3. **Browser and type relationships are development-only.** Client imports, type-only imports, module augmentations, TypeScript project references, `dsh.client.inject`, invariant companions, and metadata-only peers belong only in `devDependencies`. Configuration-only entries that Knip cannot infer from imports are listed in the dependency policy and projected into `knip.json` by `--fix`.
4. **Host value imports require classified exports.** A workspace value reached from the package's Host entry belongs only in `dependencies` when its exact module specifier and runtime export appear in `safeHostDependencyExports`. Exports whose identity or module state must be shared appear in `peerRequiredHostExports` and keep the whole package edge in matching `peerDependencies` and `devDependencies`. The verifier rejects unclassified exports before `--fix` writes manifests.
5. **Ordinary installed libraries stay in `dependencies`.** This includes private implementation libraries bundled into `lib/client.js` and bare imports left in a statically linked `lib/index.js`; the final Vite host, not the library build, merges and splits the latter.
6. **Browser and Node build faces declare externality independently.** A dynamic browser half uses the baseline plus `dsh.client.external`; a statically linked face externalizes every bare specifier; a Node face externalizes its production dependencies ([`tsdown.client.ts`](tsdown.client.ts)). Moving a name between npm sections must not silently change bundle contents.
7. **Keep the published payload closed.** Every relative runtime import and emitted asset must be covered by `files`; the repository publint pass checks the exact publication view.
@@ -140,7 +140,7 @@ Bringing up a new `packages/client/<name>` plugin package (ui-workspace is a com
3. **dsh.client manifest semantics**: `platform: 'web'` always, and the declaration requires a `./client` export (the scan throws without one); `immediately: true` only for stage-one-prefetch infrastructure rows. `inject` lists package-name dependency edges — they are **informational only** (preflight display, HMR diffing); they do not sequence entry activation or apply order. Activation order is Cordis fiber inject waiting on *services*, nothing else. A non-baseline `external` request sequences its dynamic supplier ahead of the consumer — see [shared modules](#shared-modules-and-the-module-graph).
4. **Registering into another package's slot**: apply order is unconstrained, and a business service is not a declaration barrier. Use `ctx.slots.inject(name, () => ctx.slots.register(...))`; it waits on the actual declaration, removes the contribution when that declaration collapses, reruns after redeclaration, and leaves with the caller's plugin fiber. Return a generator yielding each registration when several contributions must install and roll back atomically. A bare `slots.register` into an undeclared slot remains an error; keep service edges only for services the contribution actually reads.
5. Rebuild the bundle (`pnpm --filter <pkg> bundle`) before probing a live `dsh web` server — the registry serves `lib/client.js`, not sources.
6. **Declaration decisions**, each settled by [dependency declaration](#dependency-declaration) and [shared modules](#shared-modules-and-the-module-graph): does the package ship a `./client` export; which non-baseline value imports require `dsh.client.external`; which dynamic value dependencies are peer plus dev; which static compile inputs are dev-only; and whether `files` covers every relative runtime import and emitted asset.
6. **Declaration decisions**, each settled by [dependency declaration](#dependency-declaration) and [shared modules](#shared-modules-and-the-module-graph): does the package ship a `./client` export; which non-baseline value imports require `dsh.client.external`; which Host value imports are ordinary dependencies; which Browser and type inputs are dev-only; and whether `files` covers every relative runtime import and emitted asset.
## New component checklist
+3 -14
View File
@@ -39,7 +39,8 @@
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^",
"zod": "^4.4.3"
"zod": "^4.4.3",
"@deepseek-ai/dsh-credentials": "workspace:^"
},
"files": [
"lib/index.js",
@@ -48,24 +49,12 @@
"lib/types/**/*.d.ts"
],
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-credentials": "workspace:^",
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-host-directory-picker": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-tool-todo": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-credentials": "workspace:^",
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-host-directory-picker": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
-4
View File
@@ -41,10 +41,6 @@
"@deepseek-ai/schemastery": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-client-modules": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
+4 -10
View File
@@ -43,13 +43,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -63,11 +57,11 @@
"@deepseek-ai/dsh-invariants": "workspace:^",
"@types/react": "~18.3.1",
"react": "^18.2.0",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
"@deepseek-ai/dsh-client-connection": "workspace:^"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
},
"files": [
"lib/index.js",
+1 -4
View File
@@ -54,9 +54,6 @@
"lib/types/**/*.d.ts"
],
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
}
}
+1 -2
View File
@@ -31,8 +31,7 @@
"zustand": "~4.4.7"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
+1 -13
View File
@@ -50,19 +50,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-agent-presets": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
+1 -11
View File
@@ -48,17 +48,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
+1 -7
View File
@@ -70,12 +70,6 @@
"lib/types/**/*.d.ts"
],
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-client-ui-chat": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-trajectory": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
}
}
@@ -44,9 +44,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-sidebar": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
+3 -27
View File
@@ -51,31 +51,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-approval": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-compaction": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-llm-retry": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-stats": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-util-workspace-path": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -102,7 +78,6 @@
"@deepseek-ai/dsh-llm-retry": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-stats": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-util-workspace-path": "workspace:^",
@@ -112,7 +87,8 @@
"react-dom": "^18.2.0"
},
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
},
"files": [
"lib/index.js",
+1 -11
View File
@@ -49,17 +49,7 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
+3 -28
View File
@@ -55,35 +55,11 @@
"@lexical/plain-text": "^0.49.0",
"@lexical/text": "^0.49.0",
"@lexical/utils": "^0.49.0",
"lexical": "^0.49.0"
"lexical": "^0.49.0",
"@deepseek-ai/dsh-settings": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-goal": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-llm-retry": "workspace:^",
"@deepseek-ai/dsh-permission-presets": "workspace:^",
"@deepseek-ai/dsh-plan-mode": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/dsh-tool-todo": "workspace:^",
"@deepseek-ai/dsh-util-crypto": "workspace:^",
"@deepseek-ai/dsh-workspace": "workspace:^",
"@deepseek-ai/dsh-util-workspace-path": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -110,7 +86,6 @@
"@deepseek-ai/dsh-permission-presets": "workspace:^",
"@deepseek-ai/dsh-plan-mode": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/dsh-tool-todo": "workspace:^",
"@deepseek-ai/dsh-util-crypto": "workspace:^",
+5 -12
View File
@@ -48,16 +48,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-chat": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -70,7 +61,6 @@
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-system-prompt": "workspace:^",
"@types/react": "~18.3.1",
"@deepseek-ai/cordis": "workspace:^",
"react": "^18.2.0",
@@ -82,5 +72,8 @@
"lib/invariant.js",
"lib/client.js",
"lib/types/**/*.d.ts"
]
],
"dependencies": {
"@deepseek-ai/dsh-system-prompt": "workspace:^"
}
}
@@ -49,11 +49,6 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
@@ -44,9 +44,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
+1 -13
View File
@@ -49,19 +49,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-chat": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-goal": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -49,15 +49,7 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-file-reference": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
+1 -7
View File
@@ -45,13 +45,7 @@
"access": "public"
},
"peerDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
-5
View File
@@ -46,11 +46,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-theme": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
@@ -46,18 +46,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-message-feedback": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-client-ui-chat": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -46,18 +46,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-commands": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -47,17 +47,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-commands": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-permission-presets": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
+1 -9
View File
@@ -45,15 +45,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-plan-mode": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -61,7 +61,6 @@
"lib/types/**/*.d.ts"
],
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
}
}
-10
View File
@@ -47,16 +47,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-file-reference": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session-reference": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/dsh-util-workspace-path": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
-1
View File
@@ -44,7 +44,6 @@
"use-sync-external-store": "1.2.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
-7
View File
@@ -45,13 +45,6 @@
"access": "public"
},
"peerDependencies": {
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-schedule": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
+1 -5
View File
@@ -44,11 +44,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -48,19 +48,11 @@
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^",
"clsx": "^2.0.0"
"clsx": "^2.0.0",
"@deepseek-ai/dsh-settings": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-sidebar": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -76,7 +68,6 @@
"@deepseek-ai/cordis": "workspace:^",
"@types/react": "~18.3.1",
"react": "^18.2.0",
"@deepseek-ai/dsh-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
},
@@ -45,12 +45,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -45,12 +45,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
@@ -45,12 +45,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
+3 -8
View File
@@ -42,14 +42,8 @@
"watch": "tsdown --watch"
},
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -60,7 +54,8 @@
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@types/react": "~18.3.1",
"react": "^18.2.0"
"react": "^18.2.0",
"@deepseek-ai/schemastery": "workspace:^"
},
"files": [
"lib/index.js",
+1 -8
View File
@@ -51,14 +51,7 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-workspace": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-client-ui-layout": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
-9
View File
@@ -48,15 +48,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-tool": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
-1
View File
@@ -38,7 +38,6 @@
"lib/types/**/*.d.ts"
],
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
}
}
+1 -12
View File
@@ -47,18 +47,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-subagent": "workspace:^",
"@deepseek-ai/dsh-token-meter": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-client-locale": "workspace:^",
+3 -11
View File
@@ -44,15 +44,7 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-settings": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -67,7 +59,6 @@
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
"@deepseek-ai/dsh-host-webserver": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^",
"@types/react": "~18.3.1",
"react": "^18.2.0"
},
@@ -84,6 +75,7 @@
},
"dependencies": {
"clsx": "^2.0.0",
"@deepseek-ai/schemastery": "workspace:^"
"@deepseek-ai/schemastery": "workspace:^",
"@deepseek-ai/dsh-settings": "workspace:^"
}
}
+1 -11
View File
@@ -49,17 +49,7 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-client-ui-chat": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-util-workspace-path": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
+1 -13
View File
@@ -51,19 +51,7 @@
"diff": "^9.0.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-compaction": "workspace:^",
"@deepseek-ai/dsh-tools": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-agent": "workspace:^",
+1 -11
View File
@@ -51,17 +51,7 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/dsh-user-questions": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -54,16 +54,6 @@
],
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-chat": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-tool-workflow": "workspace:^",
"@deepseek-ai/dsh-workflow": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
+1 -15
View File
@@ -54,21 +54,7 @@
"clsx": "^2.0.0"
},
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-api-session-controller": "workspace:^",
"@deepseek-ai/dsh-api-workspace-controller": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-sidebar": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-schedule": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-util-workspace-path": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
-1
View File
@@ -42,7 +42,6 @@
"typescript": "^6.0.3"
},
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"files": [
+3 -5
View File
@@ -45,20 +45,18 @@
],
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-scope": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-llm": "workspace:^",
"@deepseek-ai/dsh-scope": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/dsh-typert-registry": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"@deepseek-ai/dsh-llm": "workspace:^"
}
}
@@ -46,12 +46,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/cordis-plugin-loader": "workspace:^",
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-modules": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-theme": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
@@ -51,16 +51,6 @@
},
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-api-remotes": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-cordis-client-runner": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-sidebar": "workspace:^",
"@deepseek-ai/dsh-client-ui-input-trigger": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-client-ui-tool": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
+2 -6
View File
@@ -57,23 +57,19 @@
],
"license": "MIT",
"peerDependencies": {
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-timeout": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"dependencies": {
"@deepseek-ai/dsh-util-crypto": "workspace:^",
"@deepseek-ai/schemastery": "workspace:^",
"zod": "^4.4.3"
"zod": "^4.4.3",
"@deepseek-ai/dsh-timeout": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-brand": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-timeout": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
}
@@ -2,7 +2,9 @@
"name": "@deepseek-ai/dsh-session-log-export",
"description": "Web Session-log export command and shared download dialog",
"version": "0.1.2-alpha.1",
"publishConfig": { "access": "public" },
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
@@ -12,33 +14,39 @@
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
"exports": {
".": { "types": "./lib/types/index.d.ts", "default": "./lib/index.js" },
"./invariant": { "types": "./lib/types/invariant.d.ts", "default": "./lib/invariant.js" },
"./client": { "types": "./lib/types/client/index.d.ts", "default": "./lib/client.js" },
".": {
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./client": {
"types": "./lib/types/client/index.d.ts",
"default": "./lib/client.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": ["lib/index.js", "lib/invariant.js", "lib/client.js", "lib/types/**/*.d.ts"],
"scripts": { "bundle": "tsdown", "watch": "tsdown --watch" },
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/client.js",
"lib/types/**/*.d.ts"
],
"scripts": {
"bundle": "tsdown",
"watch": "tsdown --watch"
},
"license": "MIT",
"dependencies": {
"@deepseek-ai/schemastery": "workspace:^",
"fflate": "^0.8.2"
"fflate": "^0.8.2",
"@deepseek-ai/dsh-session": "workspace:^"
},
"peerDependencies": {
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-attachment": "workspace:^",
"@deepseek-ai/dsh-client-connection": "workspace:^",
"@deepseek-ai/dsh-client-locale": "workspace:^",
"@deepseek-ai/dsh-client-ui-commands": "workspace:^",
"@deepseek-ai/dsh-client-ui-conversation": "workspace:^",
"@deepseek-ai/dsh-client-ui-renderer": "workspace:^",
"@deepseek-ai/dsh-client-ui-session": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-session-query": "workspace:^"
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/cordis": "workspace:^",
@@ -56,7 +64,6 @@
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
"@deepseek-ai/dsh-commands": "workspace:^",
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"@deepseek-ai/dsh-session-query": "workspace:^",
"@types/react": "~18.3.1",
+2 -3
View File
@@ -53,15 +53,14 @@
],
"license": "MIT",
"dependencies": {
"@deepseek-ai/dsh-typert-protocol": "workspace:^",
"zod": "^4.4.3"
},
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
},
"devDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/cordis": "workspace:^"
"@deepseek-ai/cordis": "workspace:^",
"@deepseek-ai/dsh-typert-protocol": "workspace:^"
}
}
+53 -49
View File
@@ -627,9 +627,6 @@ importers:
'@deepseek-ai/dsh-timeout':
specifier: workspace:^
version: link:../../util/timeout
'@deepseek-ai/dsh-typert-protocol':
specifier: workspace:^
version: link:../../typert/protocol
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -652,6 +649,9 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-typert-protocol':
specifier: workspace:^
version: link:../../typert/protocol
'@deepseek-ai/dsh-typert-registry':
specifier: workspace:^
version: link:../../typert/registry
@@ -667,12 +667,12 @@ importers:
packages/api/remotes:
dependencies:
'@deepseek-ai/dsh-scope':
'@deepseek-ai/dsh-api-session-controller':
specifier: workspace:^
version: link:../../core/scope
'@deepseek-ai/dsh-typert-protocol':
version: link:../session-controller
'@deepseek-ai/dsh-session':
specifier: workspace:^
version: link:../../typert/protocol
version: link:../../core/session
devDependencies:
'@deepseek-ai/cordis':
specifier: workspace:^
@@ -683,15 +683,15 @@ importers:
'@deepseek-ai/dsh-api-gateway':
specifier: workspace:^
version: link:../gateway
'@deepseek-ai/dsh-api-session-controller':
specifier: workspace:^
version: link:../session-controller
'@deepseek-ai/dsh-api-settings-controller':
specifier: workspace:^
version: link:../settings-controller
'@deepseek-ai/dsh-api-workspace-controller':
specifier: workspace:^
version: link:../workspace-controller
'@deepseek-ai/dsh-client-connection':
specifier: workspace:^
version: link:../../client/connection
'@deepseek-ai/dsh-commands':
specifier: workspace:^
version: link:../../interaction/commands
@@ -719,9 +719,9 @@ importers:
'@deepseek-ai/dsh-message-feedback':
specifier: workspace:^
version: link:../../feedback/message-feedback
'@deepseek-ai/dsh-session':
'@deepseek-ai/dsh-scope':
specifier: workspace:^
version: link:../../core/session
version: link:../../core/scope
'@deepseek-ai/dsh-session-reference':
specifier: workspace:^
version: link:../../context/session-reference
@@ -731,6 +731,9 @@ importers:
'@deepseek-ai/dsh-subagent':
specifier: workspace:^
version: link:../../subagent/subagent
'@deepseek-ai/dsh-typert-protocol':
specifier: workspace:^
version: link:../../typert/protocol
'@deepseek-ai/dsh-user-approval':
specifier: workspace:^
version: link:../../interaction/user-approval
@@ -1663,6 +1666,9 @@ importers:
packages/client/connection:
dependencies:
'@deepseek-ai/dsh-credentials':
specifier: workspace:^
version: link:../../credentials/credentials
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -1682,9 +1688,6 @@ importers:
'@deepseek-ai/dsh-commands':
specifier: workspace:^
version: link:../../interaction/commands
'@deepseek-ai/dsh-credentials':
specifier: workspace:^
version: link:../../credentials/credentials
'@deepseek-ai/dsh-host-directory-picker':
specifier: workspace:^
version: link:../../host/directory-picker
@@ -1731,6 +1734,9 @@ importers:
packages/client/locale:
dependencies:
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -1765,9 +1771,6 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@types/react':
specifier: ~18.3.1
version: 18.3.31
@@ -1992,6 +1995,9 @@ importers:
packages/client/ui-chat:
dependencies:
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -2068,9 +2074,6 @@ importers:
'@deepseek-ai/dsh-session-stats':
specifier: workspace:^
version: link:../../session/session-stats
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/dsh-token-meter':
specifier: workspace:^
version: link:../../llm/token-meter
@@ -2156,6 +2159,9 @@ importers:
packages/client/ui-conversation:
dependencies:
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -2250,9 +2256,6 @@ importers:
'@deepseek-ai/dsh-session':
specifier: workspace:^
version: link:../../core/session
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/dsh-token-meter':
specifier: workspace:^
version: link:../../llm/token-meter
@@ -2288,6 +2291,10 @@ importers:
version: 4.4.3
packages/client/ui-deliverables:
dependencies:
'@deepseek-ai/dsh-system-prompt':
specifier: workspace:^
version: link:../../core/system-prompt
devDependencies:
'@deepseek-ai/cordis':
specifier: workspace:^
@@ -2328,9 +2335,6 @@ importers:
'@deepseek-ai/dsh-session':
specifier: workspace:^
version: link:../../core/session
'@deepseek-ai/dsh-system-prompt':
specifier: workspace:^
version: link:../../core/system-prompt
'@types/react':
specifier: ~18.3.1
version: 18.3.31
@@ -3058,10 +3062,6 @@ importers:
version: 18.3.1
packages/client/ui-settings:
dependencies:
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
devDependencies:
'@deepseek-ai/cordis':
specifier: workspace:^
@@ -3084,6 +3084,9 @@ importers:
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
'@types/react':
specifier: ~18.3.1
version: 18.3.31
@@ -3093,6 +3096,9 @@ importers:
packages/client/ui-settings-general:
dependencies:
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -3139,9 +3145,6 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@types/react':
specifier: ~18.3.1
version: 18.3.31
@@ -3450,6 +3453,9 @@ importers:
packages/client/ui-theme:
dependencies:
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -3493,9 +3499,6 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-settings':
specifier: workspace:^
version: link:../../settings/settings
'@types/react':
specifier: ~18.3.1
version: 18.3.31
@@ -4422,6 +4425,10 @@ importers:
version: link:../../runtime-diagnostics/invariants
packages/core/session:
dependencies:
'@deepseek-ai/dsh-llm':
specifier: workspace:^
version: link:../../llm/llm
devDependencies:
'@deepseek-ai/cordis':
specifier: workspace:^
@@ -4432,9 +4439,6 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-llm':
specifier: workspace:^
version: link:../../llm/llm
'@deepseek-ai/dsh-scope':
specifier: workspace:^
version: link:../scope
@@ -6369,6 +6373,9 @@ importers:
packages/llm/llm:
dependencies:
'@deepseek-ai/dsh-timeout':
specifier: workspace:^
version: link:../../util/timeout
'@deepseek-ai/dsh-util-crypto':
specifier: workspace:^
version: link:../../util/crypto
@@ -6391,9 +6398,6 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-timeout':
specifier: workspace:^
version: link:../../util/timeout
'@deepseek-ai/dsh-typert-protocol':
specifier: workspace:^
version: link:../../typert/protocol
@@ -7162,6 +7166,9 @@ importers:
packages/session-query/session-log-export:
dependencies:
'@deepseek-ai/dsh-session':
specifier: workspace:^
version: link:../../core/session
'@deepseek-ai/schemastery':
specifier: link:../../../vendor/schemastery
version: link:../../../vendor/schemastery
@@ -7214,9 +7221,6 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-session':
specifier: workspace:^
version: link:../../core/session
'@deepseek-ai/dsh-session-persistence':
specifier: workspace:^
version: link:../../session/session-persistence
@@ -9645,9 +9649,6 @@ importers:
packages/typert/registry:
dependencies:
'@deepseek-ai/dsh-typert-protocol':
specifier: workspace:^
version: link:../protocol
zod:
specifier: ^4.4.3
version: 4.4.3
@@ -9658,6 +9659,9 @@ importers:
'@deepseek-ai/dsh-invariants':
specifier: workspace:^
version: link:../../runtime-diagnostics/invariants
'@deepseek-ai/dsh-typert-protocol':
specifier: workspace:^
version: link:../protocol
packages/util/atomic-write:
devDependencies:
@@ -0,0 +1,125 @@
import { describe, expect, it } from 'vitest'
import {
applyFactsToRegistry,
discoverBenchmarkCandidates,
parseNextPackageBenchmarkOptions,
type MutableRegistryManifest,
} from './benchmark-next-package-dependency.ts'
import type {
PackageDependencyFacts,
PackageDependencyManifest,
WorkspacePackageManifest,
} from './verify-package-dependencies.ts'
import type { RegistryIndex } from './benchmark-npm-resolution.ts'
describe('next package benchmark options', () => {
it('parses candidate and repetition controls', () => {
expect(parseNextPackageBenchmarkOptions([
'--',
'--candidates=@f/a,@f/b',
'--runs=2',
'--finalist-runs=4',
'--finalists=3',
'--jobs=6',
'--timeout-ms=9000',
])).toEqual({
candidates: ['@f/a', '@f/b'],
coarseRuns: 2,
finalistRuns: 4,
finalists: 3,
jobs: 6,
timeoutMs: 9000,
})
})
it('rejects invalid positive integers', () => {
expect(() => parseNextPackageBenchmarkOptions(['--jobs=0'])).toThrow('--jobs must be a positive integer')
})
})
describe('next package benchmark graph', () => {
it('applies a source-derived candidate without changing the filesystem', () => {
const manifest: PackageDependencyManifest & { version: string } = {
name: '@f/probe',
version: '1.0.0',
peerDependencies: {
'@deepseek-ai/cordis': 'workspace:^',
'@f/runtime': 'workspace:^',
'@f/types': 'workspace:^',
},
devDependencies: {
'@deepseek-ai/cordis': 'workspace:^',
'@f/runtime': 'workspace:^',
'@f/types': 'workspace:^',
},
}
const facts: PackageDependencyFacts = {
manifestPath: 'packages/g/probe/package.json',
role: 'configured-host',
manifest,
workspaceNames: new Set(['@deepseek-ai/cordis', '@f/probe', '@f/runtime', '@f/types']),
allSourceUses: new Map([
['@f/runtime', ['packages/g/probe/src/index.ts']],
['@f/types', ['packages/g/probe/src/types.ts']],
]),
hostRuntimeSourceUses: new Map([['@f/runtime', ['packages/g/probe/src/index.ts']]]),
hostRuntimeExportUses: [{
packageName: '@f/runtime',
specifier: '@f/runtime',
exportName: 'runtimeValue',
sourcePath: 'packages/g/probe/src/index.ts',
line: 1,
column: 10,
sourceLine: "import { runtimeValue } from '@f/runtime'",
}],
peerRequiredHostDependencies: new Set(),
configurationOnlyDevDependencies: new Set(),
clientInject: new Set(),
}
const index = new Map<string, Map<string, MutableRegistryManifest>>([
['@f/probe', new Map([['1.0.0', structuredClone(manifest) as MutableRegistryManifest]])],
])
applyFactsToRegistry(index, facts, new Map([
['@deepseek-ai/cordis', '4.0.1'],
['@f/probe', '1.0.0'],
['@f/runtime', '2.0.0'],
['@f/types', '3.0.0'],
]))
expect(index.get('@f/probe')?.get('1.0.0')).toMatchObject({
dependencies: { '@f/runtime': '^2.0.0' },
peerDependencies: { '@deepseek-ai/cordis': '^4.0.1' },
})
expect(index.get('@f/probe')?.get('1.0.0')?.dependencies).not.toHaveProperty('@f/types')
})
it('finds reachable unconfigured packages with non-Cordis peers', () => {
const index = new Map([
['@deepseek-ai/dsh', new Map([['1.0.0', {
name: '@deepseek-ai/dsh', version: '1.0.0', dependencies: { '@f/a': '^1.0.0', '@f/b': '^1.0.0' },
}]])],
['@f/a', new Map([['1.0.0', {
name: '@f/a', version: '1.0.0', peerDependencies: { '@f/runtime': '^1.0.0' },
}]])],
['@f/b', new Map([['1.0.0', {
name: '@f/b', version: '1.0.0', peerDependencies: { '@deepseek-ai/cordis': '^4.0.0' },
}]])],
['@f/runtime', new Map([['1.0.0', { name: '@f/runtime', version: '1.0.0' }]])],
]) as RegistryIndex
const release = new Map<string, WorkspacePackageManifest>([
['@f/a', {
name: '@f/a', dir: 'packages/g/a', manifestPath: 'packages/g/a/package.json', manifest: { name: '@f/a' },
}],
['@f/b', {
name: '@f/b', dir: 'packages/g/b', manifestPath: 'packages/g/b/package.json', manifest: { name: '@f/b' },
}],
])
expect(discoverBenchmarkCandidates(
index,
new Map([['@deepseek-ai/dsh', '1.0.0'], ['@f/a', '1.0.0'], ['@f/b', '1.0.0']]),
release,
new Set(),
)).toEqual(['@f/a'])
})
})
@@ -0,0 +1,271 @@
/** Benchmark which additional Host package most reduces npm peer resolution. */
import { availableParallelism } from 'node:os'
import { resolve } from 'node:path'
import { parseArgs } from 'node:util'
import {
benchmarkNpmResolution,
buildRegistryIndex,
parsePositiveIntegerOption,
publishWorkspaceRange,
type RegistryIndex,
} from './benchmark-npm-resolution.ts'
import {
readPackageDependencyFacts,
readPackageDependencyState,
readWorkspacePackageManifests,
repairPackageDependencyManifest,
type PackageDependencyFacts,
type WorkspacePackageManifest,
} from './verify-package-dependencies.ts'
const TARGET_PACKAGE = '@deepseek-ai/dsh'
const CORDIS = '@deepseek-ai/cordis'
interface Options {
readonly candidates?: readonly string[]
readonly coarseRuns: number
readonly finalistRuns: number
readonly finalists: number
readonly jobs: number
readonly timeoutMs: number
}
export interface MutableRegistryManifest {
name: string
version: string
dependencies?: Record<string, string>
optionalDependencies?: Record<string, string>
peerDependencies?: Record<string, string>
peerDependenciesMeta?: Record<string, { optional?: boolean }>
}
interface Measurement {
readonly package: string
readonly seconds: readonly number[]
readonly medianSeconds: number
}
/** Parse benchmark selection and repetition options. */
export function parseNextPackageBenchmarkOptions(args: readonly string[]): Options {
const normalized = args[0] === '--' ? args.slice(1) : args
const { values } = parseArgs({
args: [...normalized],
options: {
candidates: { type: 'string' },
runs: { type: 'string' },
'finalist-runs': { type: 'string' },
finalists: { type: 'string' },
jobs: { type: 'string' },
'timeout-ms': { type: 'string' },
},
allowPositionals: false,
})
return {
...(values.candidates === undefined
? {}
: { candidates: values.candidates.split(',').filter(Boolean) }),
coarseRuns: parsePositiveIntegerOption(values.runs, 1, '--runs'),
finalistRuns: parsePositiveIntegerOption(values['finalist-runs'], 3, '--finalist-runs'),
finalists: parsePositiveIntegerOption(values.finalists, 5, '--finalists'),
jobs: parsePositiveIntegerOption(values.jobs, Math.min(8, availableParallelism()), '--jobs'),
timeoutMs: parsePositiveIntegerOption(values['timeout-ms'], 120_000, '--timeout-ms'),
}
}
function median(values: readonly number[]): number {
const sorted = [...values].sort((left, right) => left - right)
const middle = Math.floor(sorted.length / 2)
return sorted.length % 2 === 0
? ((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2
: sorted[middle] ?? 0
}
function cloneIndex(index: RegistryIndex): Map<string, Map<string, MutableRegistryManifest>> {
return new Map([...index].map(([name, versions]) => [
name,
new Map([...versions].map(([version, manifest]) => [
version,
structuredClone(manifest) as MutableRegistryManifest,
])),
]))
}
function publishedSection(
values: Readonly<Record<string, string>> | undefined,
workspaceVersions: ReadonlyMap<string, string>,
): Record<string, string> | undefined {
if (values === undefined) return undefined
return Object.fromEntries(Object.entries(values).map(([name, range]) => {
const version = workspaceVersions.get(name)
return [name, version === undefined ? range : publishWorkspaceRange(range, version)]
}))
}
/** Apply one source-derived policy result to an in-memory registry manifest. */
export function applyFactsToRegistry(
index: Map<string, Map<string, MutableRegistryManifest>>,
facts: PackageDependencyFacts,
workspaceVersions: ReadonlyMap<string, string>,
): void {
const source = structuredClone(facts.manifest)
repairPackageDependencyManifest({ ...facts, manifest: source })
const version = workspaceVersions.get(source.name ?? '')
const target = version === undefined ? undefined : index.get(source.name ?? '')?.get(version)
if (target === undefined) throw new Error(`local registry has no ${source.name ?? 'unnamed package'}@${version ?? 'unknown'}`)
for (const field of ['dependencies', 'optionalDependencies', 'peerDependencies'] as const) {
const values = publishedSection(source[field], workspaceVersions)
if (values !== undefined) target[field] = values
else if (field === 'dependencies') delete target.dependencies
else if (field === 'optionalDependencies') delete target.optionalDependencies
else delete target.peerDependencies
}
if (source.peerDependenciesMeta === undefined) delete target.peerDependenciesMeta
else target.peerDependenciesMeta = structuredClone(source.peerDependenciesMeta) as Record<string, { optional?: boolean }>
}
function currentVersion(pkg: WorkspacePackageManifest): string {
const version = pkg.manifest.version
if (typeof version !== 'string') throw new Error(`${pkg.manifestPath}: missing package version`)
return version
}
/** Find reachable Host candidates whose published manifests still carry non-Cordis peers. */
export function discoverBenchmarkCandidates(
index: RegistryIndex,
workspaceVersions: ReadonlyMap<string, string>,
releasePackages: ReadonlyMap<string, WorkspacePackageManifest>,
policyPackages: ReadonlySet<string>,
): string[] {
const reached = new Set<string>()
const queue = [TARGET_PACKAGE]
for (let cursor = 0; cursor < queue.length; cursor += 1) {
const name = queue[cursor]
if (name === undefined || reached.has(name)) continue
const version = workspaceVersions.get(name)
const manifest = version === undefined ? undefined : index.get(name)?.get(version)
if (manifest === undefined) continue
reached.add(name)
const installed = {
...manifest.dependencies,
...manifest.optionalDependencies,
...Object.fromEntries(Object.entries(manifest.peerDependencies ?? {})
.filter(([peer]) => (manifest.peerDependenciesMeta?.[peer] as { optional?: boolean } | undefined)?.optional !== true)),
}
for (const dependency of Object.keys(installed).sort()) {
if (!reached.has(dependency)) queue.push(dependency)
}
}
return [...reached].filter((name) => {
if (policyPackages.has(name) || !releasePackages.has(name)) return false
const version = workspaceVersions.get(name)
const manifest = version === undefined ? undefined : index.get(name)?.get(version)
return Object.keys(manifest?.peerDependencies ?? {}).some(peer => peer !== CORDIS)
}).sort()
}
async function measure(
index: RegistryIndex,
targetVersion: string,
runs: number,
timeoutMs: number,
): Promise<number[]> {
const seconds: number[] = []
for (let run = 0; run < runs; run += 1) {
const result = await benchmarkNpmResolution(index, targetVersion, timeoutMs)
if (result.archiveRequests > 0) throw new Error('metadata-only benchmark requested package archives')
seconds.push(Number((result.durationMs / 1000).toFixed(2)))
}
return seconds
}
async function mapConcurrent<T, R>(
values: readonly T[],
jobs: number,
operation: (value: T) => Promise<R>,
): Promise<R[]> {
const results: R[] = []
let next = 0
await Promise.all(Array.from({ length: Math.min(jobs, values.length) }, async () => {
while (next < values.length) {
const index = next
next += 1
const value = values[index]
if (value === undefined) return
results[index] = await operation(value)
}
}))
return results
}
async function main(): Promise<void> {
const options = parseNextPackageBenchmarkOptions(process.argv.slice(2))
const root = resolve(import.meta.dirname, '..')
const packages = readWorkspacePackageManifests(root)
const workspaceVersions = new Map(packages.all.map(pkg => [pkg.name, currentVersion(pkg)]))
const releaseByName = new Map(packages.release.map(pkg => [pkg.name, pkg]))
const state = readPackageDependencyState(root)
if (state.policyViolations.length > 0) throw new Error(state.policyViolations.join('\n'))
const base = cloneIndex(buildRegistryIndex(root))
for (const facts of state.facts) applyFactsToRegistry(base, facts, workspaceVersions)
const targetVersion = workspaceVersions.get(TARGET_PACKAGE)
if (targetVersion === undefined) throw new Error(`workspace has no ${TARGET_PACKAGE}`)
const policyNames = new Set(state.facts.map(facts => facts.manifest.name).filter(name => name !== undefined))
const discovered = discoverBenchmarkCandidates(base, workspaceVersions, releaseByName, policyNames)
const candidates = options.candidates ?? discovered
for (const name of candidates) {
if (!discovered.includes(name)) throw new Error(`${name} is not a reachable unconfigured Host candidate`)
}
const candidateFacts = new Map(candidates.map((name) => {
const pkg = releaseByName.get(name)
if (pkg === undefined) throw new Error(`release set has no ${name}`)
return [name, readPackageDependencyFacts(root, pkg, 'configured-host', state.workspaceNames)]
}))
const baselineSeconds = await measure(base, targetVersion, options.finalistRuns, options.timeoutMs)
const baseline = median(baselineSeconds)
console.log(JSON.stringify({ type: 'baseline', seconds: baselineSeconds, medianSeconds: baseline }))
const coarse = await mapConcurrent(candidates, options.jobs, async (name): Promise<Measurement> => {
const index = cloneIndex(base)
const facts = candidateFacts.get(name)
if (facts === undefined) throw new Error(`missing source facts for ${name}`)
applyFactsToRegistry(index, facts, workspaceVersions)
const seconds = await measure(index, targetVersion, options.coarseRuns, options.timeoutMs)
const result = { package: name, seconds, medianSeconds: median(seconds) }
console.log(JSON.stringify({ type: 'coarse', ...result }))
return result
})
const finalists = coarse.sort((left, right) => left.medianSeconds - right.medianSeconds)
.slice(0, options.finalists)
const measured: Measurement[] = []
for (const finalist of finalists) {
const index = cloneIndex(base)
const facts = candidateFacts.get(finalist.package)
if (facts === undefined) throw new Error(`missing source facts for ${finalist.package}`)
applyFactsToRegistry(index, facts, workspaceVersions)
const seconds = await measure(index, targetVersion, options.finalistRuns, options.timeoutMs)
measured.push({ package: finalist.package, seconds, medianSeconds: median(seconds) })
}
const ranking = measured.sort((left, right) => left.medianSeconds - right.medianSeconds)
.map(result => ({
...result,
gainSeconds: Number((baseline - result.medianSeconds).toFixed(2)),
}))
console.log(JSON.stringify({
type: 'result',
baselineSeconds,
baselineMedianSeconds: baseline,
candidateCount: candidates.length,
ranking,
}, null, 2))
}
if (import.meta.main) {
try {
await main()
} catch (error) {
console.error(`benchmark-next-package-dependency: ${error instanceof Error ? error.message : String(error)}`)
process.exitCode = 1
}
}
+182
View File
@@ -0,0 +1,182 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
benchmarkNpmResolution,
buildRegistryIndex,
parseBenchmarkOptions,
publishWorkspaceRange,
resolveNpmPackageLock,
runCommandWithTimeout,
type RegistryIndex,
} from './benchmark-npm-resolution.ts'
const roots: string[] = []
afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
})
function writeJson(root: string, path: string, value: unknown): void {
const absolute = join(root, path)
mkdirSync(dirname(absolute), { recursive: true })
writeFileSync(absolute, `${JSON.stringify(value, null, 2)}\n`)
}
describe('npm resolution benchmark', () => {
it('parses repeat, timeout, threshold, and ref options', () => {
expect(parseBenchmarkOptions([])).toEqual({ runs: 1, timeoutMs: 300_000 })
expect(parseBenchmarkOptions([
'--runs', '3', '--timeout-ms', '45000', '--max-ms', '20000', '--ref', 'master',
])).toEqual({ runs: 3, timeoutMs: 45_000, maxMs: 20_000, ref: 'master' })
expect(parseBenchmarkOptions(['--', '--runs', '2'])).toEqual({ runs: 2, timeoutMs: 300_000 })
expect(() => parseBenchmarkOptions(['--runs', '0'])).toThrow('--runs must be a positive integer')
})
it('projects workspace protocols to published ranges', () => {
expect(publishWorkspaceRange('workspace:^', '1.2.3')).toBe('^1.2.3')
expect(publishWorkspaceRange('workspace:~', '1.2.3')).toBe('~1.2.3')
expect(publishWorkspaceRange('workspace:*', '1.2.3')).toBe('1.2.3')
expect(publishWorkspaceRange('^4.0.0', '1.2.3')).toBe('^4.0.0')
})
it('combines installed metadata with current publishable workspace fields', () => {
const root = mkdtempSync(join(tmpdir(), 'dsh-npm-registry-index-'))
roots.push(root)
writeJson(root, 'node_modules/.pnpm/external@2.0.0/node_modules/external/package.json', {
name: 'external',
version: '2.0.0',
dependencies: { child: '^1.0.0' },
devDependencies: { ignored: '^1.0.0' },
})
writeJson(root, 'apps/cli/package.json', {
name: '@deepseek-ai/dsh',
version: '0.1.0',
dependencies: { '@deepseek-ai/dsh-child': 'workspace:^', external: '^2.0.0' },
devDependencies: { ignored: 'workspace:^' },
})
writeJson(root, 'packages/core/child/package.json', {
name: '@deepseek-ai/dsh-child',
version: '0.1.0',
})
const index = buildRegistryIndex(root)
expect(index.get('external')?.get('2.0.0')).toMatchObject({ dependencies: { child: '^1.0.0' } })
expect(index.get('@deepseek-ai/dsh')?.get('0.1.0')).toEqual({
name: '@deepseek-ai/dsh',
version: '0.1.0',
dependencies: { '@deepseek-ai/dsh-child': '^0.1.0', external: '^2.0.0' },
})
})
it('runs npm against the local registry without requesting an archive', async () => {
const index: RegistryIndex = new Map([[
'@deepseek-ai/dsh',
new Map([['0.1.0', { name: '@deepseek-ai/dsh', version: '0.1.0' }]]),
]])
const result = await benchmarkNpmResolution(index, '0.1.0', 10_000)
expect(result.durationMs).toBeGreaterThan(0)
expect(result.registryRequests).toBeGreaterThan(0)
expect(result.archiveRequests).toBe(0)
expect(result.unknownPackages).toEqual([])
})
it('returns npm placement for two aliased package versions without requesting archives', async () => {
const index: RegistryIndex = new Map([[
'@deepseek-ai/dsh',
new Map([
['0.1.0', { name: '@deepseek-ai/dsh', version: '0.1.0' }],
['0.2.0', { name: '@deepseek-ai/dsh', version: '0.2.0' }],
]),
]])
const result = await resolveNpmPackageLock(index, {
'@deepseek-ai/dsh': '0.2.0',
'dsh-previous': 'npm:@deepseek-ai/dsh@0.1.0',
}, 10_000)
expect(result.archiveRequests).toBe(0)
expect(result.packageLock.packages['node_modules/@deepseek-ai/dsh']?.version).toBe('0.2.0')
expect(result.packageLock.packages['node_modules/dsh-previous']).toMatchObject({
name: '@deepseek-ai/dsh',
version: '0.1.0',
})
})
it('isolates peer resolution from inherited npm configuration', async () => {
const root = mkdtempSync(join(tmpdir(), 'dsh-hostile-npm-config-'))
roots.push(root)
const userConfig = join(root, 'user.npmrc')
writeFileSync(userConfig, '@deepseek-ai:registry=http://127.0.0.1:1/\nlegacy-peer-deps=true\nomit=peer\n')
const previous = {
userConfig: process.env.npm_config_userconfig,
legacyPeerDeps: process.env.npm_config_legacy_peer_deps,
omit: process.env.npm_config_omit,
}
process.env.npm_config_userconfig = userConfig
process.env.npm_config_legacy_peer_deps = 'true'
process.env.npm_config_omit = 'peer'
try {
const index: RegistryIndex = new Map([
['@deepseek-ai/dsh', new Map([['0.1.0', {
name: '@deepseek-ai/dsh',
version: '0.1.0',
peerDependencies: { '@deepseek-ai/dsh-peer': '1.0.0' },
}]])],
['@deepseek-ai/dsh-peer', new Map([['1.0.0', {
name: '@deepseek-ai/dsh-peer',
version: '1.0.0',
}]])],
])
const result = await resolveNpmPackageLock(index, { '@deepseek-ai/dsh': '0.1.0' }, 10_000)
expect(result.archiveRequests).toBe(0)
expect(result.packageLock.packages['node_modules/@deepseek-ai/dsh-peer']?.version).toBe('1.0.0')
} finally {
if (previous.userConfig === undefined) delete process.env.npm_config_userconfig
else process.env.npm_config_userconfig = previous.userConfig
if (previous.legacyPeerDeps === undefined) delete process.env.npm_config_legacy_peer_deps
else process.env.npm_config_legacy_peer_deps = previous.legacyPeerDeps
if (previous.omit === undefined) delete process.env.npm_config_omit
else process.env.npm_config_omit = previous.omit
}
})
it.skipIf(process.platform === 'win32')('force-kills a timed-out process tree', async () => {
const source = [
"const { spawn } = require('node:child_process')",
"process.on('SIGTERM', () => {})",
'const child = spawn(process.execPath, [\'-e\', "process.on(\'SIGTERM\', () => {}); setInterval(() => {}, 1000)"], { stdio: \'ignore\' })',
'console.log(child.pid)',
'setInterval(() => {}, 1000)',
].join(';')
let descendantPid: number | undefined
try {
const result = await runCommandWithTimeout(process.execPath, ['-e', source], {
cwd: process.cwd(),
env: process.env,
timeoutMs: 1_000,
terminationGraceMs: 100,
})
const reportedPid = Number.parseInt(result.output.trim(), 10)
if (!Number.isSafeInteger(reportedPid)) throw new Error(`child reported invalid pid ${result.output.trim()}`)
descendantPid = reportedPid
expect(result.timedOut).toBe(true)
expect(result.signal).toBe('SIGKILL')
expect(() => { process.kill(reportedPid, 0) }).toThrow()
} finally {
if (descendantPid !== undefined && Number.isSafeInteger(descendantPid)) {
try {
process.kill(descendantPid, 'SIGKILL')
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ESRCH') throw error
}
}
}
})
})
+551
View File
@@ -0,0 +1,551 @@
/** Benchmark npm's dependency-tree resolution against an all-local registry. */
import { execFileSync, spawn, spawnSync, type ChildProcess } from 'node:child_process'
import { globSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { createServer, type Server } from 'node:http'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { performance } from 'node:perf_hooks'
import { parseArgs } from 'node:util'
const TARGET_PACKAGE = '@deepseek-ai/dsh'
const DEFAULT_TIMEOUT_MS = 300_000
const TERMINATION_GRACE_MS = 1_000
const FORCED_EXIT_TIMEOUT_MS = 5_000
const WORKSPACE_MANIFEST_GLOBS = [
'apps/*/package.json',
'packages/*/*/package.json',
'vendor/*/package.json',
'native/landlock-run/package.json',
'native/landlock-run/packages/*/package.json',
]
const INSTALLED_MANIFEST_GLOBS = [
'node_modules/.pnpm/*/node_modules/*/package.json',
'node_modules/.pnpm/*/node_modules/@*/*/package.json',
]
const PUBLISHED_FIELDS = [
'dependencies',
'optionalDependencies',
'peerDependencies',
'peerDependenciesMeta',
'engines',
'os',
'cpu',
'bin',
] as const
interface PackageManifest {
readonly name?: unknown
readonly version?: unknown
readonly dependencies?: Record<string, string>
readonly optionalDependencies?: Record<string, string>
readonly peerDependencies?: Record<string, string>
readonly peerDependenciesMeta?: Record<string, unknown>
readonly engines?: unknown
readonly os?: unknown
readonly cpu?: unknown
readonly bin?: unknown
}
interface RegistryVersion extends PackageManifest {
readonly name: string
readonly version: string
}
/** Package versions served by the local benchmark registry. */
export type RegistryIndex = ReadonlyMap<string, ReadonlyMap<string, RegistryVersion>>
/** Parsed command-line options for one benchmark invocation. */
export interface BenchmarkOptions {
readonly ref?: string
readonly runs: number
readonly timeoutMs: number
readonly maxMs?: number
}
/** One measured npm resolution. */
export interface BenchmarkRun {
readonly durationMs: number
readonly registryRequests: number
readonly archiveRequests: number
readonly unknownPackages: readonly string[]
}
/** Published-package fields retained in npm's package-lock layout. */
export interface NpmLockPackage {
readonly name?: string
readonly version?: string
readonly dependencies?: Readonly<Record<string, string>>
readonly optionalDependencies?: Readonly<Record<string, string>>
readonly peerDependencies?: Readonly<Record<string, string>>
readonly peerDependenciesMeta?: Readonly<Record<string, { readonly optional?: boolean }>>
}
/** The installed paths selected by npm without materializing package archives. */
export interface NpmPackageLock {
readonly lockfileVersion: number
readonly packages: Readonly<Record<string, NpmLockPackage>>
}
/** npm resolution observations together with its computed install layout. */
export interface NpmPackageLockResolution extends BenchmarkRun {
readonly packageLock: NpmPackageLock
}
/** Parse one positive-integer command-line option or use its default. */
export function parsePositiveIntegerOption(raw: string | undefined, fallback: number, name: string): number {
if (raw === undefined) return fallback
const value = Number.parseInt(raw, 10)
if (!Number.isSafeInteger(value) || value < 1 || String(value) !== raw) {
throw new Error(`${name} must be a positive integer, got ${JSON.stringify(raw)}`)
}
return value
}
/**
* Parse supported benchmark arguments.
* @param args - Command-line arguments after the script path.
* @returns Validated benchmark options.
*/
export function parseBenchmarkOptions(args: readonly string[]): BenchmarkOptions {
const normalized = args[0] === '--' ? args.slice(1) : args
const { values } = parseArgs({
args: [...normalized],
options: {
ref: { type: 'string' },
runs: { type: 'string' },
'timeout-ms': { type: 'string' },
'max-ms': { type: 'string' },
},
allowPositionals: false,
})
const maxMs = values['max-ms'] === undefined
? undefined
: parsePositiveIntegerOption(values['max-ms'], 0, '--max-ms')
return {
runs: parsePositiveIntegerOption(values.runs, 1, '--runs'),
timeoutMs: parsePositiveIntegerOption(values['timeout-ms'], DEFAULT_TIMEOUT_MS, '--timeout-ms'),
...(values.ref === undefined ? {} : { ref: values.ref }),
...(maxMs === undefined ? {} : { maxMs }),
}
}
function workspaceManifestPath(path: string): boolean {
return /^(?:apps\/[^/]+|packages\/[^/]+\/[^/]+|vendor\/[^/]+|native\/landlock-run(?:\/packages\/[^/]+)?)\/package\.json$/.test(path)
}
function workspaceManifestPaths(root: string, ref: string | undefined): string[] {
if (ref === undefined) return globSync(WORKSPACE_MANIFEST_GLOBS, { cwd: root }).sort()
return execFileSync('git', ['ls-tree', '-r', '--name-only', ref, '--', 'apps', 'packages', 'vendor', 'native'], {
cwd: root,
encoding: 'utf8',
}).split('\n').filter(workspaceManifestPath).sort()
}
function readGitFiles(root: string, ref: string, paths: readonly string[]): ReadonlyMap<string, string> {
const output = execFileSync('git', ['cat-file', '--batch'], {
cwd: root,
input: paths.map(path => `${ref}:${path}\n`).join(''),
maxBuffer: 64 * 1024 * 1024,
})
const contents = new Map<string, string>()
let offset = 0
for (const path of paths) {
const headerEnd = output.indexOf(0x0a, offset)
if (headerEnd < 0) throw new Error(`git cat-file returned no header for ${ref}:${path}`)
const header = output.subarray(offset, headerEnd).toString('utf8')
if (header.endsWith(' missing')) throw new Error(`git ref ${ref} has no ${path}`)
const size = Number.parseInt(header.split(' ')[2] ?? '', 10)
if (!Number.isSafeInteger(size) || size < 0) {
throw new Error(`git cat-file returned an invalid size for ${ref}:${path}`)
}
const contentStart = headerEnd + 1
const contentEnd = contentStart + size
if (output[contentEnd] !== 0x0a) throw new Error(`git cat-file truncated ${ref}:${path}`)
contents.set(path, output.subarray(contentStart, contentEnd).toString('utf8'))
offset = contentEnd + 1
}
return contents
}
/**
* Convert a workspace protocol range to the range published by pnpm pack.
* @param range - Dependency range from a workspace manifest.
* @param targetVersion - Current version of the referenced workspace package.
* @returns The registry-facing semver range.
*/
export function publishWorkspaceRange(range: string, targetVersion: string): string {
if (range === 'workspace:*') return targetVersion
if (range === 'workspace:^') return `^${targetVersion}`
if (range === 'workspace:~') return `~${targetVersion}`
if (range.startsWith('workspace:')) return range.slice('workspace:'.length)
return range
}
function copyPublishedManifest(
source: PackageManifest,
workspaceVersions: ReadonlyMap<string, string>,
): RegistryVersion | undefined {
if (typeof source.name !== 'string' || typeof source.version !== 'string') return undefined
const output: Record<string, unknown> = { name: source.name, version: source.version }
for (const field of PUBLISHED_FIELDS) {
const value = source[field]
if (value === undefined) continue
if (field === 'dependencies' || field === 'optionalDependencies' || field === 'peerDependencies') {
output[field] = Object.fromEntries(Object.entries(value as Record<string, string>).map(([name, range]) => {
const targetVersion = workspaceVersions.get(name)
return [name, targetVersion === undefined ? range : publishWorkspaceRange(range, targetVersion)]
}))
} else {
output[field] = structuredClone(value)
}
}
return output as unknown as RegistryVersion
}
function addManifest(index: Map<string, Map<string, RegistryVersion>>, manifest: RegistryVersion): void {
const versions = index.get(manifest.name) ?? new Map<string, RegistryVersion>()
versions.set(manifest.version, manifest)
index.set(manifest.name, versions)
}
/**
* Build registry metadata from installed external packages and workspace manifests.
* @param root - Repository root containing the pnpm virtual store.
* @param ref - Optional Git ref used instead of working-tree workspace manifests.
* @returns Package metadata served by the benchmark registry.
*/
export function buildRegistryIndex(root: string, ref?: string): RegistryIndex {
const index = new Map<string, Map<string, RegistryVersion>>()
for (const path of globSync(INSTALLED_MANIFEST_GLOBS, { cwd: root }).sort()) {
const manifest = JSON.parse(readFileSync(resolve(root, path), 'utf8')) as PackageManifest
const copied = copyPublishedManifest(manifest, new Map())
if (copied !== undefined) addManifest(index, copied)
}
const paths = workspaceManifestPaths(root, ref)
const refContents = ref === undefined ? undefined : readGitFiles(root, ref, paths)
const workspace = paths.map(path =>
JSON.parse(refContents?.get(path) ?? readFileSync(resolve(root, path), 'utf8')) as PackageManifest)
const workspaceVersions = new Map(workspace.flatMap(manifest =>
typeof manifest.name === 'string' && typeof manifest.version === 'string'
? [[manifest.name, manifest.version] as const]
: []))
for (const manifest of workspace) {
const copied = copyPublishedManifest(manifest, workspaceVersions)
if (copied !== undefined) addManifest(index, copied)
}
return index
}
function latestVersion(versions: ReadonlyMap<string, RegistryVersion>): string {
const sorted = [...versions.keys()].sort((left, right) => left.localeCompare(right, 'en', { numeric: true }))
const latest = sorted.at(-1)
if (latest === undefined) throw new Error('local registry package has no versions')
return latest
}
function listen(server: Server): Promise<number> {
return new Promise((resolveListen, reject) => {
server.once('error', reject)
server.listen(0, '127.0.0.1', () => {
server.off('error', reject)
const address = server.address()
if (address === null || typeof address === 'string') {
reject(new Error('local registry did not expose a TCP port'))
return
}
resolveListen(address.port)
})
})
}
function close(server: Server): Promise<void> {
return new Promise((resolveClose, reject) => {
server.close((error) => {
if (error === undefined) resolveClose()
else reject(error)
})
})
}
function npmExecutable(): string {
return process.platform === 'win32' ? 'npm.cmd' : 'npm'
}
function delay(ms: number): Promise<void> {
return new Promise(resolveDelay => setTimeout(resolveDelay, ms))
}
function signalProcessTree(child: ChildProcess, signal: 'SIGTERM' | 'SIGKILL'): void {
if (child.pid === undefined) {
child.kill(signal)
return
}
if (process.platform === 'win32') {
const force = signal === 'SIGKILL' ? ['/F'] : []
const result = spawnSync('taskkill', ['/PID', String(child.pid), '/T', ...force], {
stdio: 'ignore',
windowsHide: true,
})
if (result.error !== undefined) throw result.error
if (result.status !== 0 && child.exitCode === null && child.signalCode === null) child.kill(signal)
return
}
try {
process.kill(-child.pid, signal)
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ESRCH') throw error
}
}
/**
* Run one command with bounded process-tree termination after its deadline.
* @param command - Executable path or name.
* @param args - Arguments passed without shell interpolation on POSIX.
* @param options - Working directory, environment, timeout, and termination grace.
* @returns Exit facts, captured output, duration, and whether timeout handling began.
*/
export async function runCommandWithTimeout(
command: string,
args: readonly string[],
options: {
readonly cwd: string
readonly env: NodeJS.ProcessEnv
readonly timeoutMs: number
readonly terminationGraceMs?: number
},
): Promise<{ status: number | null; signal: NodeJS.Signals | null; durationMs: number; output: string; timedOut: boolean }> {
const started = performance.now()
const child = spawn(command, [...args], {
cwd: options.cwd,
detached: process.platform !== 'win32',
env: options.env,
shell: process.platform === 'win32',
stdio: ['ignore', 'pipe', 'pipe'],
})
let output = ''
child.stdout.setEncoding('utf8')
child.stderr.setEncoding('utf8')
child.stdout.on('data', (chunk) => { output += String(chunk) })
child.stderr.on('data', (chunk) => { output += String(chunk) })
const exited = new Promise<{ status: number | null; signal: NodeJS.Signals | null }>((resolveExit, reject) => {
child.once('error', reject)
child.once('close', (status, signal) => { resolveExit({ status, signal }) })
})
let timeout: NodeJS.Timeout | undefined
try {
const first = await Promise.race([
exited.then(outcome => ({ type: 'exit' as const, outcome })),
new Promise<{ type: 'timeout' }>((resolveTimeout) => {
timeout = setTimeout(() => { resolveTimeout({ type: 'timeout' }) }, options.timeoutMs)
}),
])
if (first.type === 'exit') {
return { ...first.outcome, durationMs: performance.now() - started, output, timedOut: false }
}
signalProcessTree(child, 'SIGTERM')
await delay(options.terminationGraceMs ?? TERMINATION_GRACE_MS)
signalProcessTree(child, 'SIGKILL')
const forced = await Promise.race([
exited,
delay(FORCED_EXIT_TIMEOUT_MS).then(() => undefined),
])
if (forced === undefined) throw new Error('timed-out process tree did not exit after SIGKILL')
return { ...forced, durationMs: performance.now() - started, output, timedOut: true }
} finally {
if (timeout !== undefined) clearTimeout(timeout)
}
}
function readNpmPackageLock(path: string): NpmPackageLock {
const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('npm produced an invalid package-lock.json')
}
const { lockfileVersion, packages } = parsed as Record<string, unknown>
if (!Number.isSafeInteger(lockfileVersion) || packages === null
|| typeof packages !== 'object' || Array.isArray(packages)) {
throw new Error('npm produced an invalid package-lock.json')
}
return parsed as NpmPackageLock
}
async function runNpm(
cwd: string,
registry: string,
timeoutMs: number,
): Promise<{ durationMs: number; output: string; timedOut: boolean }> {
const npmrc = join(cwd, '.npmrc')
const globalNpmrc = join(cwd, '.npmrc-global')
writeFileSync(npmrc, `registry=${registry}\n@deepseek-ai:registry=${registry}\n`)
writeFileSync(globalNpmrc, '')
const inheritedEnvironment = Object.fromEntries(Object.entries(process.env)
.filter(([name]) => !name.toLowerCase().startsWith('npm_config_')))
const result = await runCommandWithTimeout(npmExecutable(), [
'install',
'--package-lock-only',
'--ignore-scripts',
'--no-audit',
'--no-fund',
'--loglevel=error',
'--include=peer',
'--install-strategy=hoisted',
'--legacy-peer-deps=false',
`--registry=${registry}`,
], {
cwd,
env: {
...inheritedEnvironment,
npm_config_cache: join(cwd, '.npm-cache'),
npm_config_globalconfig: globalNpmrc,
npm_config_userconfig: npmrc,
npm_config_update_notifier: 'false',
},
timeoutMs,
})
if (result.timedOut) return result
if (result.status !== 0) {
throw new Error(`npm install exited ${String(result.status)} after ${result.durationMs.toFixed(0)} ms\n${result.output.trim()}`)
}
return result
}
/**
* Ask npm to compute an install layout without downloading package archives.
* @param index - Package metadata exposed through the local registry.
* @param dependencies - Root dependencies whose install layout npm computes.
* @param timeoutMs - Hard wall-clock limit for the npm child process.
* @returns The package lock plus timing and registry-request observations.
*/
export async function resolveNpmPackageLock(
index: RegistryIndex,
dependencies: Readonly<Record<string, string>>,
timeoutMs: number,
): Promise<NpmPackageLockResolution> {
let registryRequests = 0
let archiveRequests = 0
const unknownPackages = new Set<string>()
let registry = ''
const server = createServer((request, response) => {
registryRequests++
const pathname = new URL(request.url ?? '/', registry).pathname
if (pathname.startsWith('/tarballs/')) {
archiveRequests++
response.writeHead(500, { 'content-type': 'application/json' })
response.end(JSON.stringify({ error: 'package-lock-only benchmark requested an archive' }))
return
}
const name = decodeURIComponent(pathname.slice(1))
const versions = index.get(name)
if (versions === undefined) {
unknownPackages.add(name)
response.writeHead(404, { 'content-type': 'application/json' })
response.end(JSON.stringify({ error: 'not_found' }))
return
}
const materialized = Object.fromEntries([...versions].map(([version, manifest]) => [version, {
...manifest,
dist: { tarball: `${registry}tarballs/${encodeURIComponent(name)}-${version}.tgz` },
}]))
const body = JSON.stringify({
name,
'dist-tags': { latest: latestVersion(versions) },
versions: materialized,
})
response.writeHead(200, {
'content-type': 'application/json',
'content-length': Buffer.byteLength(body),
})
response.end(body)
})
const port = await listen(server)
registry = `http://127.0.0.1:${String(port)}/`
const consumer = mkdtempSync(join(tmpdir(), 'dsh-npm-resolution-'))
try {
writeFileSync(join(consumer, 'package.json'), `${JSON.stringify({
name: 'dsh-npm-resolution-benchmark',
version: '0.0.0',
private: true,
dependencies,
}, null, 2)}\n`)
const result = await runNpm(consumer, registry, timeoutMs)
if (result.timedOut) throw new Error(`npm resolution exceeded ${String(timeoutMs)} ms`)
return {
durationMs: result.durationMs,
registryRequests,
archiveRequests,
unknownPackages: [...unknownPackages].sort(),
packageLock: readNpmPackageLock(join(consumer, 'package-lock.json')),
}
} finally {
server.closeAllConnections()
await close(server)
rmSync(consumer, { recursive: true, force: true })
}
}
/**
* Resolve the CLI install graph once without downloading package archives.
* @param index - Package metadata exposed through the local registry.
* @param targetVersion - Version of `@deepseek-ai/dsh` to install.
* @param timeoutMs - Hard wall-clock limit for the npm child process.
* @returns Timing and registry-request observations.
*/
export async function benchmarkNpmResolution(
index: RegistryIndex,
targetVersion: string,
timeoutMs: number,
): Promise<BenchmarkRun> {
const result = await resolveNpmPackageLock(index, { [TARGET_PACKAGE]: targetVersion }, timeoutMs)
return {
durationMs: result.durationMs,
registryRequests: result.registryRequests,
archiveRequests: result.archiveRequests,
unknownPackages: result.unknownPackages,
}
}
async function main(): Promise<void> {
const options = parseBenchmarkOptions(process.argv.slice(2))
const root = resolve(import.meta.dirname, '..')
const started = performance.now()
const index = buildRegistryIndex(root, options.ref)
const targetVersions = index.get(TARGET_PACKAGE)
if (targetVersions === undefined) throw new Error(`local registry contains no ${TARGET_PACKAGE}`)
const targetVersion = latestVersion(targetVersions)
const npmVersion = execFileSync(npmExecutable(), ['--version'], { encoding: 'utf8' }).trim()
console.log(
`benchmark-npm-resolution: npm ${npmVersion}, ${options.ref === undefined ? 'working tree' : options.ref}, `
+ `${String(index.size)} package name(s), setup ${(performance.now() - started).toFixed(0)} ms.`,
)
const durations: number[] = []
for (let run = 1; run <= options.runs; run++) {
const result = await benchmarkNpmResolution(index, targetVersion, options.timeoutMs)
durations.push(result.durationMs)
console.log(
`benchmark-npm-resolution: run ${String(run)}/${String(options.runs)} resolved ${TARGET_PACKAGE}@${targetVersion}`
+ ` in ${(result.durationMs / 1000).toFixed(2)} s with ${String(result.registryRequests)} metadata request(s)`
+ ` and ${String(result.unknownPackages.length)} local 404 package name(s).`,
)
if (result.archiveRequests > 0) throw new Error('npm requested package archives during the metadata-only benchmark')
}
const minimum = Math.min(...durations)
const maximum = Math.max(...durations)
console.log(
`benchmark-npm-resolution: ${String(options.runs)} run(s), min ${(minimum / 1000).toFixed(2)} s, max ${(maximum / 1000).toFixed(2)} s.`,
)
if (options.maxMs !== undefined && maximum > options.maxMs) {
throw new Error(`npm resolution exceeded --max-ms=${String(options.maxMs)} (max ${maximum.toFixed(0)} ms)`)
}
}
if (import.meta.main) {
try {
await main()
} catch (error) {
console.error(`benchmark-npm-resolution: ${error instanceof Error ? error.message : String(error)}`)
process.exitCode = 1
}
}
+15 -1
View File
@@ -603,7 +603,7 @@ describe('npm release workflows', () => {
for (const file of ['release.yml', 'release-vendor.yml']) {
const workflow = loadWorkflow(`.github/workflows/${file}`)
if (!isRecord(workflow.jobs)) throw new TypeError(`${file} must define jobs`)
expect(Object.keys(workflow.jobs).sort()).toEqual(['pack'])
expect(Object.keys(workflow.jobs).sort()).toEqual(file === 'release.yml' ? ['dependencies', 'pack'] : ['pack'])
}
// publication is workflow_dispatch-only (never a PR check) and keeps the
@@ -618,6 +618,20 @@ describe('npm release workflows', () => {
expect(publish.concurrency).toMatchObject({ group: 'Release-publish' })
}
})
it('runs dependency policy and npm layout checks in the DSH release workflow', () => {
const workflow = loadWorkflow('.github/workflows/release.yml')
const dependencies = workflowJob(workflow, 'dependencies')
if (!isRecord(workflow.on) || !Array.isArray(dependencies.steps)) {
throw new TypeError('DSH release workflow must define triggers and dependency steps')
}
const commands = dependencies.steps.flatMap(step =>
isRecord(step) && typeof step.run === 'string' ? [step.run] : [])
expect(Object.keys(workflow.on).sort()).toEqual(['pull_request', 'push', 'workflow_dispatch'])
expect(commands).toContain('pnpm run verify-package-dependencies')
expect(commands).toContain('pnpm run verify-npm-install-layout')
})
})
describe('Documentation site publication', () => {
+106 -48
View File
@@ -1,21 +1,21 @@
/**
* Generate `docs/module-graph.md` from in-repo `peerDependencies`, the canonical
* runtime edges. The deterministic output groups packages by directory and
* renders both Mermaid and a dependency table; `--check` verifies freshness.
*/
/** Generate the paired shared-instance package graph from workspace peer dependencies. */
import { existsSync, readFileSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { readFileSync, writeFileSync } from 'node:fs'
import {
collectPackageGraph,
escapeMermaidLabel as escLabel,
graphNodeId as nodeId,
type PackageGraphNode,
} from './package-graph.ts'
import { gitBlobHash, storeGitBlob } from './translation-pairing-git.ts'
import { renderTranslationPairingRecord, translationPairPaths } from './translation-pairing-record.ts'
const root = resolve(import.meta.dirname, '..')
const OUT = 'docs/module-graph.md'
const SOURCE = 'docs/module-graph.md'
const PATHS = translationPairPaths(SOURCE)
type Pkg = PackageGraphNode
type Locale = 'en' | 'zh'
const GROUP_ORDER = [
'util',
@@ -46,42 +46,55 @@ function packageLink(pkg: Pkg): string {
return `[\`${pkg.short}\`](../${pkg.rel})`
}
/** Render the full docs/module-graph.md content (pure, deterministic). */
function render(pkgs: Pkg[]): string {
/**
* Render one locale of the complete deterministic package graph.
* @param pkgs - Dependency-first package nodes.
* @param locale - Output document language.
* @returns Complete generated Markdown.
*/
export function renderModuleGraph(pkgs: readonly Pkg[], locale: Locale): string {
const edges: string[] = []
for (const p of pkgs) {
for (const d of p.deps) edges.push(` ${nodeId('pkg', p.short)} --> ${nodeId('pkg', d)}`)
for (const pkg of pkgs) {
for (const dependency of pkg.deps) edges.push(` ${nodeId('pkg', pkg.short)} --> ${nodeId('pkg', dependency)}`)
}
const byShort = new Map(pkgs.map(pkg => [pkg.short, pkg]))
const groups = [...new Set(pkgs.map(pkg => pkg.group))].sort((a, b) => {
const ia = GROUP_ORDER.indexOf(a)
const ib = GROUP_ORDER.indexOf(b)
const na = ia === -1 ? Number.MAX_SAFE_INTEGER : ia
const nb = ib === -1 ? Number.MAX_SAFE_INTEGER : ib
return na - nb || a.localeCompare(b)
const groups = [...new Set(pkgs.map(pkg => pkg.group))].sort((left, right) => {
const leftIndex = GROUP_ORDER.indexOf(left)
const rightIndex = GROUP_ORDER.indexOf(right)
const normalizedLeft = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex
const normalizedRight = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex
return normalizedLeft - normalizedRight || left.localeCompare(right)
})
const groupBlocks: string[] = []
for (const group of groups) {
groupBlocks.push(` subgraph ${nodeId('group', group)}["packages/${escLabel(group)}"]`)
for (const pkg of pkgs.filter(p => p.group === group).sort((a, b) => a.short.localeCompare(b.short))) {
for (const pkg of pkgs.filter(candidate => candidate.group === group)
.sort((left, right) => left.short.localeCompare(right.short))) {
groupBlocks.push(` ${nodeId('pkg', pkg.short)}["${escLabel(pkg.short)}"]`)
}
groupBlocks.push(' end')
}
const rows = pkgs.map((p) => {
const deps = p.deps.length ? p.deps.map((d) => {
const dep = byShort.get(d)
return dep ? packageLink(dep) : `\`${d}\``
}).join(', ') : '—'
return `| ${packageLink(p)} | \`${p.group}\` | ${deps} |`
const rows = pkgs.map((pkg) => {
const dependencies = pkg.deps.length > 0
? pkg.deps.map((dependency) => {
const target = byShort.get(dependency)
return target ? packageLink(target) : `\`${dependency}\``
}).join(', ')
: '—'
return `| ${packageLink(pkg)} | \`${pkg.group}\` | ${dependencies} |`
})
const chinese = locale === 'zh'
return [
'<!-- Generated by scripts/gen-module-graph.ts — do not edit by hand.',
' Run `pnpm run gen-module-graph` to regenerate. -->',
chinese
? '<!-- 由 scripts/gen-module-graph.ts 生成——请勿手工编辑。\n 运行 `pnpm run gen-module-graph` 重新生成。 -->'
: '<!-- Generated by scripts/gen-module-graph.ts — do not edit by hand.\n Run `pnpm run gen-module-graph` to regenerate. -->',
'',
'# Module dependency graph',
chinese ? '# 共享实例依赖关系图' : '# Shared-instance dependency graph',
'',
'Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package\'s `peerDependencies` (the canonical runtime-dependency signal) and grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.',
...(chinese ? ['[English](module-graph.md) | 中文', ''] : []),
chinese
? '`@deepseek-ai/dsh-*` harness 包之间的 peer 依赖关系。peer 表示消费端需要提供共享实例,不包括普通运行时 dependency 或仅开发期关系。该图按 `packages/<group>/<pkg>` 层级分组;边 `a --> b` 表示包 `a` peer 依赖包 `b`。名称中的 `@deepseek-ai/dsh-` 前缀已移除。'
: 'Peer dependencies among the `@deepseek-ai/dsh-*` harness packages. A peer means the consumer requires a shared instance; ordinary runtime dependencies and development-only relationships are not shown. The graph is grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` has package `b` as a peer. Names omit the `@deepseek-ai/dsh-` prefix.',
'',
'```mermaid',
'flowchart TD',
@@ -89,31 +102,76 @@ function render(pkgs: Pkg[]): string {
...edges,
'```',
'',
'| Package | Group | Depends on |',
chinese ? '| 包 | 分组 | Peer 依赖 |' : '| Package | Group | Peer dependencies |',
'| --- | --- | --- |',
...rows,
'',
].join('\n')
}
const content = render(collectPackageGraph(root, GROUP_ORDER, 'gen-module-graph'))
if (process.argv.includes('--check')) {
let committed: string | null = null
try {
committed = readFileSync(resolve(root, OUT), 'utf8')
} catch {
// A missing artifact is the expected read failure. Any read failure has the
// same remedy here—regenerate—so it is reported as stale below.
committed = null
}
if (committed === content) {
console.log(`gen-module-graph: ${OUT} is up to date.`)
process.exit(0)
}
console.error(`gen-module-graph: ${OUT} is stale. Run \`pnpm run gen-module-graph\` and commit ${OUT}.`)
process.exit(1)
/**
* Compute both localized graph documents from the current workspace manifests.
* @param scanRoot - Repository root containing packages and documentation.
* @returns Repository-relative output paths and exact generated content.
*/
export function computeModuleGraphOutputs(scanRoot: string = root): ReadonlyMap<string, string> {
const packages = collectPackageGraph(scanRoot, GROUP_ORDER, 'gen-module-graph')
return new Map([
[PATHS.source, renderModuleGraph(packages, 'en')],
[PATHS.zh, renderModuleGraph(packages, 'zh')],
])
}
writeFileSync(resolve(root, OUT), content)
console.log(`gen-module-graph: wrote ${OUT}.`)
/**
* Write both graph documents and their recovery record.
* @param scanRoot - Repository root containing packages and documentation.
* @returns Repository-relative paths whose content changed.
*/
export function writeModuleGraph(scanRoot: string = root): string[] {
const outputs = computeModuleGraphOutputs(scanRoot)
const changed: string[] = []
for (const [path, content] of outputs) {
const destination = resolve(scanRoot, path)
if (existsSync(destination) && readFileSync(destination, 'utf8') === content) continue
writeFileSync(destination, content)
changed.push(path)
}
const source = Buffer.from(outputs.get(PATHS.source) ?? '')
const zh = Buffer.from(outputs.get(PATHS.zh) ?? '')
const record = renderTranslationPairingRecord(PATHS, {
sourceHash: storeGitBlob(scanRoot, source),
zhHash: storeGitBlob(scanRoot, zh),
})
const recordPath = resolve(scanRoot, PATHS.meta)
if (!existsSync(recordPath) || readFileSync(recordPath, 'utf8') !== record) {
writeFileSync(recordPath, record)
changed.push(PATHS.meta)
}
return changed.sort()
}
/** CLI entry: regenerate by default, or verify all paired outputs with `--check`. @returns Nothing. */
export function main(): void {
const outputs = computeModuleGraphOutputs(root)
const record = renderTranslationPairingRecord(PATHS, {
sourceHash: gitBlobHash(Buffer.from(outputs.get(PATHS.source) ?? '')),
zhHash: gitBlobHash(Buffer.from(outputs.get(PATHS.zh) ?? '')),
})
const expected = new Map([...outputs, [PATHS.meta, record]])
if (process.argv.includes('--check')) {
const stale = [...expected].filter(([path, content]) => (
!existsSync(resolve(root, path)) || readFileSync(resolve(root, path), 'utf8') !== content
)).map(([path]) => path)
if (stale.length === 0) {
console.log(`gen-module-graph: ${expected.size} artifact(s) are up to date.`)
return
}
console.error(`gen-module-graph: stale — ${stale.join(', ')}. Run \`pnpm run gen-module-graph\` and commit the result.`)
process.exitCode = 1
return
}
const changed = writeModuleGraph(root)
console.log(`gen-module-graph: ${expected.size} artifact(s) computed, ${String(changed.length)} written.`)
}
if (process.argv[1] !== undefined && import.meta.filename === resolve(process.argv[1])) main()
+100
View File
@@ -0,0 +1,100 @@
/** Explicit exceptions and Host packages for the published dependency policy. */
/** Packages treated as Client/Host packages without declaring `dsh.client`. */
const CLIENT_FACE_INCLUDE: readonly string[] = []
/** Packages exempted from automatic Client/Host treatment despite declaring `dsh.client`. */
const CLIENT_FACE_EXCLUDE: readonly string[] = [
'@deepseek-ai/dsh-api-session-controller',
'@deepseek-ai/dsh-api-workspace-controller',
]
/** Host-only packages whose peer relays are deliberately flattened. */
const HOST_DEPENDENCY_PACKAGES: readonly string[] = [
'@deepseek-ai/dsh-llm',
'@deepseek-ai/dsh-session',
]
/** Development-only package relationships not represented by source imports. */
const CONFIGURATION_ONLY_DEV_DEPENDENCIES = {
'@deepseek-ai/dsh-client-locale': ['@deepseek-ai/dsh-api-remotes'],
'@deepseek-ai/dsh-client-ui-conversation': [
'@deepseek-ai/dsh-api-remotes',
'@deepseek-ai/dsh-client-ui-workspace',
],
'@deepseek-ai/dsh-client-ui-model-selection': ['@deepseek-ai/dsh-client-ui-input-trigger'],
'@deepseek-ai/dsh-client-ui-sidebar': ['@deepseek-ai/dsh-client-ui-workspace'],
'@deepseek-ai/dsh-client-ui-subagent': ['@deepseek-ai/dsh-client-ui-input-trigger'],
'@deepseek-ai/dsh-client-ui-theme': ['@deepseek-ai/dsh-api-remotes'],
'@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'],
} as const satisfies Readonly<Record<string, readonly string[]>>
/**
* Runtime exports whose values remain valid when npm installs another package copy.
*/
const SAFE_HOST_DEPENDENCY_EXPORTS = {
'@deepseek-ai/dsh-api-session-controller/remote-events': ['SESSION_CONTROLLER_REMOTE_EVENTS'],
'@deepseek-ai/dsh-credentials': ['credentialKey'],
'@deepseek-ai/dsh-llm': ['MessageId', 'callConfigEquals', 'deepFreeze', 'freezeMessage'],
'@deepseek-ai/dsh-llm/brand': ['ToolCallId'],
'@deepseek-ai/dsh-session': ['isJsonValue'],
'@deepseek-ai/dsh-session/types': ['SessionId'],
'@deepseek-ai/dsh-settings': ['settingsNamespace'],
'@deepseek-ai/dsh-system-prompt': ['FIRST_PARTY_SECTION_ORDER'],
'@deepseek-ai/dsh-timeout': ['MAX_TIMER_DELAY_MS'],
'@deepseek-ai/dsh-typert-protocol': ['RemoteError', 'remoteErrorOf'],
'@deepseek-ai/dsh-util-crypto': ['randomUUID'],
'@deepseek-ai/schemastery': ['default'],
} as const satisfies HostDependencyExports
/** Runtime exports that require every consumer to resolve the provider's shared peer instance. */
const PEER_REQUIRED_HOST_EXPORTS = {
'@deepseek-ai/dsh-scope': ['carrierKeyOf', 'scopeOf', 'scopeTarget'],
'@deepseek-ai/dsh-typert-protocol': ['Remote', 'TypertRemoteService', 'remoteMethods'],
} as const satisfies HostDependencyExports
/** Exact import specifier to reviewed runtime exports. */
type HostDependencyExports = Readonly<Record<string, readonly string[]>>
/** Complete configurable input to package dependency classification. */
export interface PackageDependencyPolicy {
readonly clientFaceInclude: readonly string[]
readonly clientFaceExclude: readonly string[]
readonly hostPackages: readonly string[]
readonly configurationOnlyDevDependencies: Readonly<Record<string, readonly string[]>>
readonly safeHostDependencyExports: HostDependencyExports
readonly peerRequiredHostExports: HostDependencyExports
}
/** Repository dependency policy consumed by verification and benchmarking. */
export const PACKAGE_DEPENDENCY_POLICY: PackageDependencyPolicy = {
clientFaceInclude: CLIENT_FACE_INCLUDE,
clientFaceExclude: CLIENT_FACE_EXCLUDE,
hostPackages: HOST_DEPENDENCY_PACKAGES,
configurationOnlyDevDependencies: CONFIGURATION_ONLY_DEV_DEPENDENCIES,
safeHostDependencyExports: SAFE_HOST_DEPENDENCY_EXPORTS,
peerRequiredHostExports: PEER_REQUIRED_HOST_EXPORTS,
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
/** Whether a package manifest declares a dynamically loaded Client entry. */
export function hasClientDeclaration(dshField: unknown): boolean {
return isRecord(dshField) && Object.hasOwn(dshField, 'client')
}
/** Whether the repository policy flattens one package's non-Cordis peers. */
export function usesFlattenedPackageDependencies(
manifestPath: string,
packageName: string,
dshField: unknown,
policy: PackageDependencyPolicy = PACKAGE_DEPENDENCY_POLICY,
): boolean {
if (!manifestPath.startsWith('packages/') || manifestPath.startsWith('packages/experimental/')) return false
if (policy.hostPackages.includes(packageName)) return true
if (manifestPath.startsWith('packages/client/')) return true
const included = hasClientDeclaration(dshField) || policy.clientFaceInclude.includes(packageName)
return included && !policy.clientFaceExclude.includes(packageName)
}
+21
View File
@@ -2,6 +2,7 @@ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { renderModuleGraph } from './gen-module-graph.ts'
import { collectPackageGraph } from './package-graph.ts'
const roots: string[] = []
@@ -49,3 +50,23 @@ describe('collectPackageGraph', () => {
.toThrow('fixture: @deepseek-ai/dsh-consumer references missing in-repo peer @deepseek-ai/dsh-missing')
})
})
describe('renderModuleGraph', () => {
it('renders the same peer edge in both generated languages', () => {
const packages = [
{ short: 'provider', name: '@deepseek-ai/dsh-provider', group: 'core', rel: 'packages/core/provider', deps: [] },
{ short: 'consumer', name: '@deepseek-ai/dsh-consumer', group: 'core', rel: 'packages/core/consumer', deps: ['provider'] },
]
const english = renderModuleGraph(packages, 'en')
const chinese = renderModuleGraph(packages, 'zh')
expect(english).toContain('# Shared-instance dependency graph')
expect(chinese).toContain('# 共享实例依赖关系图')
expect(chinese).toContain('[English](module-graph.md) | 中文')
for (const output of [english, chinese]) {
expect(output).toContain('pkg_consumer --> pkg_provider')
expect(output).toContain('| [`consumer`](../packages/core/consumer) | `core` | [`provider`](../packages/core/provider) |')
}
})
})
+54 -8
View File
@@ -5,6 +5,7 @@ import { afterEach, describe, expect, it } from 'vitest'
import {
collectPackageInvariantViolations,
} from './package-invariants.ts'
import { usesFlattenedPackageDependencies } from './package-dependency-policy.ts'
const roots: string[] = []
@@ -28,7 +29,10 @@ export const apply = (ctx: { invariants: { register(name: string, install: typeo
function fixture(options: {
packageName?: string
packageDirectory?: string
source?: string
clientDeclaration?: boolean
clientExport?: boolean
invariantExport?: boolean
invariantDependency?: boolean
invariantReference?: boolean
@@ -36,19 +40,34 @@ function fixture(options: {
} = {}): string {
const root = mkdtempSync(join(tmpdir(), 'dsh-package-invariants-'))
roots.push(root)
const dir = join(root, 'packages/core/probe')
const packageDirectory = options.packageDirectory ?? 'packages/core/probe'
const dir = join(root, packageDirectory)
mkdirSync(join(dir, 'src'), { recursive: true })
const packageName = options.packageName ?? '@deepseek-ai/dsh-probe'
const exports = options.invariantExport === false ? {} : {
'./invariant': {
types: './lib/types/invariant.d.ts',
default: './lib/invariant.js',
},
...(options.clientExport === true ? {
'./client': {
types: './lib/types/client/index.d.ts',
default: './lib/client.js',
},
} : {}),
}
const dsh = options.clientDeclaration === true ? { client: {} } : undefined
const developmentOnlyInvariant = usesFlattenedPackageDependencies(
`${packageDirectory}/package.json`,
packageName,
dsh,
)
const manifest = {
name: packageName,
exports: options.invariantExport === false ? {} : {
'./invariant': {
types: './lib/types/invariant.d.ts',
default: './lib/invariant.js',
},
},
...(dsh === undefined ? {} : { dsh }),
exports,
files: ['lib/index.js', 'lib/invariant.js'],
peerDependencies: options.invariantDependency === false ? {} : {
peerDependencies: options.invariantDependency === false || developmentOnlyInvariant ? {} : {
'@deepseek-ai/dsh-invariants': 'workspace:^',
},
devDependencies: options.invariantDependency === false ? {} : {
@@ -72,6 +91,33 @@ describe('package invariant gate', () => {
expect(collectPackageInvariantViolations(fixture())).toEqual([])
})
it('accepts development-only invariants for configured Host dependencies', () => {
expect(collectPackageInvariantViolations(fixture({ packageName: '@deepseek-ai/dsh-llm' }))).toEqual([])
})
it('accepts development-only invariants for client packages', () => {
expect(collectPackageInvariantViolations(fixture({
packageName: '@deepseek-ai/dsh-client-probe',
packageDirectory: 'packages/client/probe',
}))).toEqual([])
})
it('accepts development-only invariants for packages with a dsh.client entry', () => {
expect(collectPackageInvariantViolations(fixture({ clientDeclaration: true, clientExport: true }))).toEqual([])
})
it('keeps invariant peers for packages that only export a Client API', () => {
expect(collectPackageInvariantViolations(fixture({ clientExport: true }))).toEqual([])
})
it('keeps invariant peers for experimental packages with a dsh.client entry', () => {
expect(collectPackageInvariantViolations(fixture({
packageDirectory: 'packages/experimental/probe',
clientDeclaration: true,
clientExport: true,
}))).toEqual([])
})
it('accepts an invariant reference owned by a package-local leaf project', () => {
const root = fixture({ invariantReference: false })
const dir = join(root, 'packages/core/probe')
+15 -8
View File
@@ -7,12 +7,14 @@
import { existsSync, globSync, readFileSync } from 'node:fs'
import { dirname, relative, resolve, sep } from 'node:path'
import ts from 'typescript'
import { usesFlattenedPackageDependencies } from './package-dependency-policy.ts'
/** Required explanation marker for an intentionally empty installer. */
const NO_RUNTIME_INVARIANT_MARKER = 'No runtime invariant:'
interface PackageManifest {
name?: string
dsh?: unknown
exports?: Record<string, { types?: string; default?: string } | string | undefined>
files?: string[]
peerDependencies?: Record<string, string>
@@ -96,18 +98,23 @@ function checkManifest(
addViolation(violations, owner.manifestPath, 'files must publish lib/invariant.js')
}
if (owner.packageName === '@deepseek-ai/dsh-invariants') return
if (manifest.peerDependencies?.['@deepseek-ai/dsh-invariants'] !== 'workspace:^') {
addViolation(
violations,
owner.manifestPath,
'@deepseek-ai/dsh-invariants must be a workspace:^ peerDependency',
)
const developmentOnlyInvariant = usesFlattenedPackageDependencies(
owner.manifestPath,
owner.packageName,
manifest.dsh,
)
const expectedRange = 'workspace:^'
const peerRange = manifest.peerDependencies?.['@deepseek-ai/dsh-invariants']
if (developmentOnlyInvariant ? peerRange !== undefined : peerRange !== expectedRange) {
addViolation(violations, owner.manifestPath, developmentOnlyInvariant
? '@deepseek-ai/dsh-invariants must not be a peerDependency under this package dependency policy'
: '@deepseek-ai/dsh-invariants must be a workspace:^ peerDependency')
}
if (manifest.devDependencies?.['@deepseek-ai/dsh-invariants'] !== 'workspace:^') {
if (manifest.devDependencies?.['@deepseek-ai/dsh-invariants'] !== expectedRange) {
addViolation(
violations,
owner.manifestPath,
'@deepseek-ai/dsh-invariants must also be a workspace:^ devDependency',
`@deepseek-ai/dsh-invariants must be a ${expectedRange} devDependency`,
)
}
}
+10 -1
View File
@@ -102,7 +102,7 @@ describe('gate graph validation', () => {
const ids = withPnpmEntrypoint(() => gatesForMode('hygiene').map(subject => subject.id))
expect(ids).toEqual([
'rescope-vendor', 'publint', 'constraints', 'application-entrypoints',
'rescope-vendor', 'publint', 'constraints', 'package-dependencies', 'application-entrypoints',
'dsh-package-licenses', 'package-invariants', 'built-package-invariants', 'node-next-types',
'optional-dependency-imports', 'client-packages', 'client-ui-i18n', 'cordis-config',
'runtime-closure', 'vendored-links',
@@ -141,6 +141,15 @@ describe('gate graph validation', () => {
},
)
it.each(['ci-primary', 'ci-static', 'check-all', 'hygiene'] as const)(
'keeps package dependency enforcement in %s',
(mode) => {
const ids = withPnpmEntrypoint(() => gatesForMode(mode).map(subject => subject.id))
expect(ids).toContain('package-dependencies')
},
)
it.each(['ci-primary', 'ci-static', 'check-all'] as const)(
'keeps the client dependency policy in %s',
(mode) => {
+2
View File
@@ -278,6 +278,7 @@ function ciSharedStaticGates(): Gate[] {
pnpmScript('runtime-closure', 'verify-runtime-closure', { label: 'runtime closure' }),
pnpmScript('application-entrypoints', 'verify-application-entrypoints', { label: 'application entrypoints' }),
pnpmScript('constraints', 'constraints'),
pnpmScript('package-dependencies', 'verify-package-dependencies', { label: 'package dependencies' }),
pnpmScript('dsh-package-licenses', 'verify-dsh-package-licenses', { label: 'DSH package licenses' }),
pnpmScript('package-invariants', 'verify-package-invariants', { label: 'package invariants' }),
pnpmScript('cordis-config', 'verify-cordis-config', { label: 'Cordis config' }),
@@ -667,6 +668,7 @@ function hygieneLeafGates(options: { artifactNeeds?: string[] } = {}): Gate[] {
pnpmScript('rescope-vendor', 'rescope-vendor:check', { label: 'vendor rescope' }),
pnpmScript('publint', 'publint', artifactOptions),
pnpmScript('constraints', 'constraints'),
pnpmScript('package-dependencies', 'verify-package-dependencies', { label: 'package dependencies' }),
pnpmScript('application-entrypoints', 'verify-application-entrypoints', { label: 'application entrypoints' }),
pnpmScript('dsh-package-licenses', 'verify-dsh-package-licenses', { label: 'DSH package licenses' }),
pnpmScript('package-invariants', 'verify-package-invariants', { label: 'package invariants' }),
+19 -143
View File
@@ -1,4 +1,4 @@
/** Tests for client package modes, dependency sections, and module requests. */
/** Tests for client package modes and module requests. */
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
@@ -6,6 +6,7 @@ import { dirname, join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
collectClientPackageViolations,
collectLocalSourceSpecifiers,
collectRuntimeSourcePackageUses,
collectRuntimeSourceSpecifiers,
collectSourcePackageUses,
@@ -106,6 +107,19 @@ describe('source package uses', () => {
'@deepseek-ai/dsh-b/remote',
'react',
])
expect([...collectLocalSourceSpecifiers('feature.ts', [
"import type { A } from './types.ts'",
"export { value } from './value.ts'",
"const load = () => import('./lazy.ts')",
"const legacy = require('./legacy.ts')",
"declare module './augmentation.ts' {}",
"import '@deepseek-ai/dsh-a'",
].join('\n'))].sort()).toEqual([
'./lazy.ts',
'./legacy.ts',
'./types.ts',
'./value.ts',
])
})
})
@@ -152,109 +166,6 @@ describe('package modes', () => {
})
})
describe('dependency sections', () => {
it('accepts dynamic peer plus dev relationships, static dev inputs, and private dependencies', () => {
const slots = pkg('ui-slots', { dynamic: false, staticLinked: true })
const conversation = pkg('conversation', {
inject: ['@deepseek-ai/dsh-client-feature'],
sourceUses: {
'@deepseek-ai/dsh-agent': ['packages/client/conversation/src/index.ts'],
'@deepseek-ai/dsh-client-ui-slots': ['packages/client/conversation/src/client/slots.ts'],
react: ['packages/client/conversation/src/client/view.tsx'],
},
dependencies: { immer: '^10.1.1' },
peerDependencies: {
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-agent': 'workspace:^',
'@deepseek-ai/dsh-client-feature': 'workspace:^',
},
devDependencies: {
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-agent': 'workspace:^',
'@deepseek-ai/dsh-client-feature': 'workspace:^',
'@deepseek-ai/dsh-client-ui-slots': 'workspace:^',
react: '^18.2.0',
},
})
expect(collectClientPackageViolations(facts([slots, conversation], {
platformModules: ['react', slots.name],
}))).toEqual([])
})
it('rejects internal dependencies, static peers, and mismatched peer development ranges', () => {
const slots = pkg('ui-slots', { dynamic: false, staticLinked: true })
const subject = pkg('feature', {
sourceUses: {
'@deepseek-ai/dsh-agent': ['packages/client/feature/src/index.ts'],
[slots.name]: ['packages/client/feature/src/view.tsx'],
},
dependencies: { '@deepseek-ai/dsh-agent': 'workspace:^' },
peerDependencies: { [CORDIS]: 'workspace:^', [slots.name]: 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', [slots.name]: 'workspace:*' },
})
const found = collectClientPackageViolations(facts([slots, subject]))
expect(found).toHaveLength(2)
expect(found.join('\n')).toContain('peer-installed DSH relationship')
expect(found.join('\n')).toContain('static client input')
})
it('requires every peer to have the same development range', () => {
const subject = pkg('feature', {
peerDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/cordis-plugin-loader': 'workspace:^' },
})
expect(collectClientPackageViolations(facts([subject]))).toEqual([
'packages/client/feature/package.json: peerDependencies.@deepseek-ai/cordis-plugin-loader'
+ ' is workspace:^, so devDependencies.@deepseek-ai/cordis-plugin-loader must use the same range;'
+ ' found no declaration',
])
})
it('requires statically linked third-party runtime imports in dependencies', () => {
const primitives = pkg('ui-primitives', {
dynamic: false,
staticLinked: true,
runtimeSourceUses: { shiki: ['packages/client/ui-primitives/src/highlight.ts'] },
devDependencies: { [CORDIS]: 'workspace:^', shiki: '^4.3.1' },
})
const found = collectClientPackageViolations(facts([primitives]))
expect(found).toHaveLength(1)
expect(found[0]).toContain('runtime import retained by a statically linked artifact')
expect(found[0]).toContain('declare it only in dependencies')
const valid = { ...primitives, dependencies: { shiki: '^4.3.1' }, devDependencies: { [CORDIS]: 'workspace:^' } }
expect(collectClientPackageViolations(facts([valid]))).toEqual([])
})
it('keeps the web shell runtime inputs development-only', () => {
const web = pkg('web', {
dynamic: false,
staticLinked: true,
runtimeSourceUses: {
'@deepseek-ai/cordis-plugin-loader': ['packages/client/web/src/boot.ts'],
react: ['packages/client/web/src/seed.ts'],
},
devDependencies: {
[CORDIS]: 'workspace:^',
'@deepseek-ai/cordis-plugin-loader': 'workspace:^',
react: '^18.2.0',
},
})
expect(collectClientPackageViolations(facts([web]))).toEqual([])
})
it('allows npm dependency cycles', () => {
const a = pkg('a', {
peerDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-client-b': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-client-b': 'workspace:^' },
})
const b = pkg('b', {
peerDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-client-a': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-client-a': 'workspace:^' },
})
expect(collectClientPackageViolations(facts([a, b]))).toEqual([])
})
})
describe('module requests', () => {
it('rejects runtime requests from one client feature package to another dynamic row', () => {
const ui = declaration('ui', {
@@ -378,7 +289,7 @@ describe('manifest declarations', () => {
])
})
it('fixes unambiguous dependency sections and declaration entries', () => {
it('fixes malformed declaration entries without changing dependency sections', () => {
const root = mkdtempSync(join(tmpdir(), 'client-packages-fix-'))
roots.push(root)
const subject = pkg('feature', {
@@ -426,43 +337,8 @@ describe('manifest declarations', () => {
external: ['@deepseek-ai/dsh-missing'],
inject: ['@deepseek-ai/dsh-agent'],
})
expect(fixed.dependencies).toBeUndefined()
expect(fixed.peerDependencies).toEqual({
'@deepseek-ai/cordis-plugin-loader': 'workspace:^',
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-agent': 'workspace:*',
})
expect(fixed.devDependencies).toEqual({
'@deepseek-ai/dsh-client-ui-slots': 'workspace:^',
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-agent': 'workspace:*',
'@deepseek-ai/cordis-plugin-loader': 'workspace:^',
})
})
it('fixes a statically linked runtime import into dependencies', () => {
const root = mkdtempSync(join(tmpdir(), 'client-packages-static-fix-'))
roots.push(root)
const subject = pkg('ui-primitives', {
dynamic: false,
staticLinked: true,
runtimeSourceUses: { shiki: ['packages/client/ui-primitives/src/highlight.ts'] },
devDependencies: { [CORDIS]: 'workspace:^', shiki: '^4.3.1' },
})
mkdirSync(dirname(join(root, subject.manifest)), { recursive: true })
writeFileSync(join(root, subject.manifest), JSON.stringify({
name: subject.name,
peerDependencies: subject.peerDependencies,
devDependencies: subject.devDependencies,
}))
writeFileSync(join(root, 'package.json'), JSON.stringify({ private: true }))
expect(fixClientPackageManifests(root, facts([subject]))).toEqual([subject.manifest])
const fixed = JSON.parse(readFileSync(join(root, subject.manifest), 'utf8')) as {
dependencies: Record<string, string>
devDependencies: Record<string, string>
}
expect(fixed.dependencies).toEqual({ shiki: '^4.3.1' })
expect(fixed.devDependencies).toEqual({ [CORDIS]: 'workspace:^' })
expect(fixed.dependencies).toEqual(subject.dependencies)
expect(fixed.peerDependencies).toEqual(subject.peerDependencies)
expect(fixed.devDependencies).toEqual(subject.devDependencies)
})
})
+35 -290
View File
@@ -1,6 +1,5 @@
/**
* Verify client package modes, npm dependency sections, and the synchronous
* browser module-request graph.
* Verify client package modes and the synchronous browser module-request graph.
*/
import { globSync, readFileSync, writeFileSync } from 'node:fs'
@@ -17,8 +16,6 @@ const PLATFORM_SOURCE = 'packages/client/web/src/platform.ts'
const PARSER_PRELOAD_SOURCE = 'packages/client/modules/src/index.ts'
const STATIC_PRESET_SOURCE = 'packages/client/tsdown.client.ts'
const CORDIS = '@deepseek-ai/cordis'
const DSH_PREFIX = '@deepseek-ai/dsh-'
const CLIENT_WEB = '@deepseek-ai/dsh-client-web'
/** One workspace package's browser-module declaration. */
export interface ClientDeclaration {
@@ -92,6 +89,28 @@ export function collectRuntimeSourceSpecifiers(path: string, source: string): Se
return collectSourceFileUses(sourceFile, true, 'specifier')
}
/**
* Collect relative module specifiers used to follow one source entry's local closure.
* @param path - File path used to select TypeScript's parser mode.
* @param source - Source text to inspect.
* @returns Relative imports, exports, requires, and import types.
*/
export function collectLocalSourceSpecifiers(path: string, source: string): Set<string> {
const sourceFile = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true)
return collectSourceFileUses(sourceFile, false, 'local')
}
/**
* Collect relative module specifiers retained by one production source file.
* @param path - File path used to select TypeScript's parser mode.
* @param source - Source text to inspect.
* @returns Relative imports, exports, and requires that survive compilation.
*/
export function collectRuntimeLocalSourceSpecifiers(path: string, source: string): Set<string> {
const sourceFile = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true)
return collectSourceFileUses(sourceFile, true, 'local')
}
function importCarriesRuntimeValue(node: ts.ImportDeclaration): boolean {
const clause = node.importClause
if (clause === undefined) return true
@@ -114,12 +133,17 @@ function exportCarriesRuntimeValue(node: ts.ExportDeclaration): boolean {
function collectSourceFileUses(
sourceFile: ts.SourceFile,
runtimeOnly: boolean,
key: 'package' | 'specifier',
key: 'local' | 'package' | 'specifier',
): Set<string> {
const uses = new Set<string>()
const add = (specifier: ts.Expression | undefined): void => {
if (specifier === undefined || !ts.isStringLiteral(specifier) || !isBareSpecifier(specifier.text)) return
if (specifier === undefined || !ts.isStringLiteralLike(specifier)) return
if (key === 'local') {
if (specifier.text.startsWith('.')) uses.add(specifier.text)
return
}
if (!isBareSpecifier(specifier.text)) return
uses.add(key === 'package' ? packageNameOf(specifier.text) : specifier.text)
}
const visit = (node: ts.Node): void => {
@@ -135,9 +159,10 @@ function collectSourceFileUses(
&& (node.expression.kind === ts.SyntaxKind.ImportKeyword
|| ts.isIdentifier(node.expression) && node.expression.text === 'require')) {
add(node.arguments[0])
} else if (!runtimeOnly && ts.isModuleDeclaration(node) && ts.isStringLiteral(node.name)) {
} else if (!runtimeOnly && key !== 'local' && ts.isModuleDeclaration(node) && ts.isStringLiteral(node.name)) {
add(node.name)
} else if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) {
} else if (key !== 'local'
&& (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node))) {
uses.add('react')
}
ts.forEachChild(node, visit)
@@ -170,7 +195,6 @@ export function collectClientPackageViolations(facts: ClientPackageFacts): strin
return [
...facts.malformed,
...collectModeViolations(facts),
...collectDependencyViolations(facts),
...collectModuleViolations(facts),
].sort((left, right) => left.localeCompare(right))
}
@@ -181,10 +205,8 @@ interface ManifestDocument {
changed: boolean
}
type DependencySection = 'dependencies' | 'peerDependencies' | 'devDependencies'
/**
* Repair manifest declarations whose intended result follows uniquely from the policy.
* Repair malformed or redundant `dsh.client` declaration entries.
* @param root - Absolute repository root.
* @param facts - Facts used by the verification pass.
* @returns Repository-relative manifests written by the fixer.
@@ -217,53 +239,6 @@ export function fixClientPackageManifests(root: string, facts: ClientPackageFact
) || target.changed
}
const staticInputs = new Set([
...facts.staticLinkedPackages,
...facts.platformModules.map(packageNameOf),
])
staticInputs.delete(CORDIS)
const inferredRanges = dependencyRangeCandidates(root)
for (const pkg of facts.packages) {
const target = document(pkg.manifest)
const expected = expectedSections(pkg, staticInputs)
for (const [name, rule] of expected) {
const range = preferredRange(target.manifest, name, rule.kind, inferredRanges)
if (range === undefined) continue
target.changed = rule.kind === 'dependency'
? ensureDependencyOnly(target.manifest, name, range) || target.changed
: rule.kind === 'dev'
? ensureDevOnly(target.manifest, name, range) || target.changed
: ensurePeerDev(target.manifest, name, range) || target.changed
}
if (pkg.dynamic) {
const productionNames = new Set([
...Object.keys(section(target.manifest, 'dependencies')),
...Object.keys(section(target.manifest, 'peerDependencies')),
])
for (const name of productionNames) {
if (expected.has(name)) continue
const range = preferredRange(
target.manifest,
name,
staticInputs.has(name) ? 'dev' : 'peer-dev',
inferredRanges,
)
if (range === undefined) continue
if (staticInputs.has(name)) {
target.changed = ensureDevOnly(target.manifest, name, range) || target.changed
} else if (section(target.manifest, 'dependencies')[name] !== undefined && isInternalDsh(name)) {
target.changed = ensurePeerDev(target.manifest, name, range) || target.changed
}
}
}
for (const [name, range] of Object.entries(section(target.manifest, 'peerDependencies'))) {
target.changed = setDependency(target.manifest, 'devDependencies', name, range) || target.changed
}
target.changed = deleteEmptySections(target.manifest) || target.changed
}
const changed = [...documents.values()].filter(target => target.changed).sort((left, right) =>
left.path.localeCompare(right.path))
for (const target of changed) {
@@ -295,102 +270,6 @@ function normalizeClientArray(
return true
}
function ensureDevOnly(manifest: Manifest, name: string, range: string): boolean {
let changed = deleteDependency(manifest, 'dependencies', name)
changed = deleteDependency(manifest, 'peerDependencies', name) || changed
return setDependency(manifest, 'devDependencies', name, range) || changed
}
function ensureDependencyOnly(manifest: Manifest, name: string, range: string): boolean {
let changed = deleteDependency(manifest, 'peerDependencies', name)
changed = deleteDependency(manifest, 'devDependencies', name) || changed
return setDependency(manifest, 'dependencies', name, range) || changed
}
function ensurePeerDev(manifest: Manifest, name: string, range: string): boolean {
let changed = deleteDependency(manifest, 'dependencies', name)
changed = setDependency(manifest, 'peerDependencies', name, range) || changed
return setDependency(manifest, 'devDependencies', name, range) || changed
}
function setDependency(manifest: Manifest, field: DependencySection, name: string, range: string): boolean {
const dependencies = mutableSection(manifest, field)
if (dependencies[name] === range) return false
dependencies[name] = range
return true
}
function deleteDependency(manifest: Manifest, field: DependencySection, name: string): boolean {
const dependencies = section(manifest, field)
if (dependencies[name] === undefined) return false
manifest[field] = Object.fromEntries(Object.entries(dependencies).filter(([key]) => key !== name))
return true
}
function deleteEmptySections(manifest: Manifest): boolean {
let changed = false
for (const field of ['dependencies', 'peerDependencies', 'devDependencies'] as const) {
if (manifest[field] === undefined || Object.keys(section(manifest, field)).length > 0) continue
if (field === 'dependencies') delete manifest.dependencies
else if (field === 'peerDependencies') delete manifest.peerDependencies
else delete manifest.devDependencies
changed = true
}
return changed
}
function preferredRange(
manifest: Manifest,
name: string,
kind: ExpectedRule['kind'],
inferred: ReadonlyMap<string, ReadonlySet<string>>,
): string | undefined {
const order: readonly DependencySection[] = kind === 'dependency'
? ['dependencies', 'devDependencies', 'peerDependencies']
: kind === 'dev'
? ['devDependencies', 'peerDependencies', 'dependencies']
: ['peerDependencies', 'devDependencies', 'dependencies']
for (const field of order) {
const range = section(manifest, field)[name]
if (range !== undefined) return range
}
if (isInternalDsh(name)) return 'workspace:^'
const candidates = inferred.get(name)
return candidates?.size === 1 ? [...candidates][0] : undefined
}
function dependencyRangeCandidates(root: string): Map<string, Set<string>> {
const candidates = new Map<string, Set<string>>()
const paths = globSync([
'package.json',
...MANIFEST_GLOBS,
'website/package.json',
], { cwd: root }).map(normalizePath)
for (const path of new Set(paths)) {
const manifest = JSON.parse(readFileSync(resolve(root, path), 'utf8')) as Manifest
for (const field of ['dependencies', 'peerDependencies', 'devDependencies'] as const) {
for (const [name, range] of Object.entries(section(manifest, field))) {
const ranges = candidates.get(name) ?? new Set<string>()
ranges.add(range)
candidates.set(name, ranges)
}
}
}
return candidates
}
function section(manifest: Manifest, field: DependencySection): Record<string, string> {
return manifest[field] ?? {}
}
function mutableSection(manifest: Manifest, field: DependencySection): Record<string, string> {
const value = manifest[field]
if (value !== undefined) return value
const created: Record<string, string> = {}
manifest[field] = created
return created
}
function collectModeViolations(facts: ClientPackageFacts): string[] {
const violations: string[] = []
for (const pkg of facts.packages) {
@@ -435,114 +314,6 @@ function collectModeViolations(facts: ClientPackageFacts): string[] {
return violations
}
interface ExpectedRule {
readonly kind: 'dependency' | 'dev' | 'peer-dev'
readonly origins: Set<string>
}
function collectDependencyViolations(facts: ClientPackageFacts): string[] {
const violations: string[] = []
const staticInputs = new Set([
...facts.staticLinkedPackages,
...facts.platformModules.map(packageNameOf),
])
staticInputs.delete(CORDIS)
for (const pkg of [...facts.packages].sort((left, right) => left.manifest.localeCompare(right.manifest))) {
const expected = expectedSections(pkg, staticInputs)
for (const [name, rule] of [...expected].sort(([left], [right]) => left.localeCompare(right))) {
const actual = declaredSections(pkg, name)
if (rule.kind === 'dependency') {
if (actual.length === 1 && actual[0] === 'dependencies') continue
violations.push(
pkg.manifest + ': ' + name + ' (' + describeOrigins(rule.origins) + ') is a runtime import'
+ ' retained by a statically linked artifact; declare it only in dependencies, found '
+ describeSections(actual),
)
continue
}
if (rule.kind === 'dev') {
if (actual.length === 1 && actual[0] === 'devDependencies') continue
violations.push(
pkg.manifest + ': ' + name + ' (' + describeOrigins(rule.origins) + ') is a static client input;'
+ ' declare it only in devDependencies, found ' + describeSections(actual),
)
continue
}
const peerRange = pkg.peerDependencies[name]
const devRange = pkg.devDependencies[name]
if (actual.length === 2
&& actual.includes('peerDependencies')
&& actual.includes('devDependencies')
&& peerRange === devRange) continue
violations.push(
pkg.manifest + ': ' + name + ' (' + describeOrigins(rule.origins) + ')'
+ ' is a peer-installed DSH relationship; declare it in peerDependencies and devDependencies'
+ ' with matching ranges, not dependencies; found ' + describeSections(actual)
+ describeRangeMismatch(peerRange, devRange),
)
}
for (const [name, peerRange] of Object.entries(pkg.peerDependencies).sort(([left], [right]) => left.localeCompare(right))) {
if (expected.has(name)) continue
const devRange = pkg.devDependencies[name]
if (devRange === peerRange) continue
violations.push(
pkg.manifest + ': peerDependencies.' + name + ' is ' + peerRange + ', so devDependencies.' + name
+ ' must use the same range; found ' + (devRange ?? 'no declaration'),
)
}
if (!pkg.dynamic) continue
for (const section of ['dependencies', 'peerDependencies'] as const) {
for (const name of Object.keys(pkg[section]).sort()) {
if (expected.has(name)) continue
if (staticInputs.has(name)) {
violations.push(
pkg.manifest + ': dynamic package declares static input ' + name + ' in ' + section + ';'
+ ' move it to devDependencies or delete the stale declaration',
)
} else if (section === 'dependencies' && isInternalDsh(name)) {
violations.push(
pkg.manifest + ': dynamic package declares ' + name + ' in dependencies;'
+ ' dynamic DSH relationships are peer plus dev, and static client inputs are dev-only',
)
}
}
}
}
return violations
}
function expectedSections(pkg: ClientPackage, staticInputs: ReadonlySet<string>): Map<string, ExpectedRule> {
const expected = new Map<string, ExpectedRule>([
[CORDIS, { kind: 'peer-dev', origins: new Set(['client package baseline']) }],
])
if (!pkg.dynamic) {
if (pkg.name === CLIENT_WEB) return expected
for (const [name, locations] of Object.entries(pkg.runtimeSourceUses)) {
if (name === pkg.name || name === CORDIS || isInternalDsh(name)) continue
expected.set(name, { kind: 'dependency', origins: new Set(locations) })
}
return expected
}
const add = (name: string, origin: string): void => {
if (name === pkg.name) return
const kind = staticInputs.has(name) ? 'dev' : isInternalDsh(name) ? 'peer-dev' : undefined
if (kind === undefined) return
const current = expected.get(name)
if (current !== undefined) current.origins.add(origin)
else expected.set(name, { kind, origins: new Set([origin]) })
}
for (const [name, locations] of Object.entries(pkg.sourceUses)) {
for (const location of locations) add(name, location)
}
for (const name of pkg.inject) add(name, 'dsh.client.inject')
return expected
}
interface ModuleEdge {
readonly from: string
readonly to: string
@@ -895,32 +666,6 @@ function rowPackageOf(specifier: string, rows: ReadonlySet<string>): string | un
return rows.has(stripped) ? stripped : undefined
}
function declaredSections(pkg: ClientPackage, name: string): string[] {
return (['dependencies', 'peerDependencies', 'devDependencies'] as const)
.filter(section => pkg[section][name] !== undefined)
}
function describeSections(sections: readonly string[]): string {
return sections.length === 0 ? 'no dependency declaration' : sections.join(' + ')
}
function describeRangeMismatch(peer: string | undefined, dev: string | undefined): string {
if (peer === undefined || dev === undefined || peer === dev) return ''
return ' (peer ' + peer + ', dev ' + dev + ')'
}
function describeOrigins(origins: ReadonlySet<string>): string {
const sorted = [...origins].sort()
const [first, second, ...rest] = sorted
if (first === undefined) return 'production use'
if (second === undefined) return first
return rest.length === 0 ? first + ', ' + second : first + ', ' + second + ', and ' + String(rest.length) + ' more'
}
function isInternalDsh(name: string): boolean {
return name === CORDIS || name.startsWith(DSH_PREFIX)
}
function isBareSpecifier(specifier: string): boolean {
return !specifier.startsWith('.') && !specifier.startsWith('/') && !specifier.startsWith('#')
}
@@ -956,7 +701,7 @@ async function main(): Promise<void> {
const requests = facts.declarations.reduce((total, pkg) => total + pkg.external.length, 0)
console.log(
GATE + ': ' + String(facts.packages.length) + ' client packages (' + String(dynamic) + ' dynamic, '
+ String(facts.packages.length - dynamic) + ' statically linked) satisfy dependency and module-request rules; '
+ String(facts.packages.length - dynamic) + ' statically linked) satisfy package-mode and module-request rules; '
+ String(requests) + ' explicit external request(s).',
)
}
+102
View File
@@ -0,0 +1,102 @@
import { describe, expect, it } from 'vitest'
import type { NpmPackageLock, RegistryIndex } from './benchmark-npm-resolution.ts'
import {
assertDualDshInstallLayout,
buildDualDshRegistry,
} from './verify-npm-install-layout.ts'
function validLayout(): NpmPackageLock {
return {
lockfileVersion: 3,
packages: {
'': { dependencies: { '@deepseek-ai/dsh': '0.2.0', 'dsh-previous': 'npm:@deepseek-ai/dsh@0.1.0' } },
'node_modules/@deepseek-ai/cordis': { version: '4.0.1' },
'node_modules/@deepseek-ai/dsh': {
version: '0.2.0',
dependencies: { '@deepseek-ai/dsh-child': '^0.2.0' },
peerDependencies: { '@deepseek-ai/cordis': '^4.0.1' },
},
'node_modules/@deepseek-ai/dsh-child': {
version: '0.2.0',
dependencies: { '@deepseek-ai/dsh-leaf': '^0.2.0' },
},
'node_modules/@deepseek-ai/dsh-leaf': { version: '0.2.0' },
'node_modules/dsh-previous': {
name: '@deepseek-ai/dsh',
version: '0.1.0',
dependencies: { '@deepseek-ai/dsh-child': '^0.1.0' },
peerDependencies: { '@deepseek-ai/cordis': '^4.0.1' },
},
'node_modules/dsh-previous/node_modules/@deepseek-ai/dsh-child': {
version: '0.1.0',
dependencies: { '@deepseek-ai/dsh-leaf': '^0.1.0' },
},
'node_modules/dsh-previous/node_modules/@deepseek-ai/dsh-leaf': { version: '0.1.0' },
},
}
}
describe('npm install layout verifier', () => {
it('creates two incompatible versions of every DSH package', () => {
const index: RegistryIndex = new Map([
['@deepseek-ai/dsh', new Map([['0.1.1-rc.2', {
name: '@deepseek-ai/dsh',
version: '0.1.1-rc.2',
dependencies: { '@deepseek-ai/dsh-child': '^0.1.1-rc.2' },
peerDependencies: { '@deepseek-ai/cordis': '^4.0.1' },
}]])],
['@deepseek-ai/dsh-child', new Map([['0.1.1-rc.2', {
name: '@deepseek-ai/dsh-child',
version: '0.1.1-rc.2',
}]])],
['@deepseek-ai/cordis', new Map([['4.0.1', {
name: '@deepseek-ai/cordis',
version: '4.0.1',
}]])],
])
const dual = buildDualDshRegistry(index, '0.1.1-rc.2')
expect([...dual.get('@deepseek-ai/dsh')?.keys() ?? []]).toEqual(['0.1.0', '0.2.0'])
expect(dual.get('@deepseek-ai/dsh')?.get('0.1.0')).toMatchObject({
version: '0.1.0',
dependencies: { '@deepseek-ai/dsh-child': '^0.1.0' },
peerDependencies: { '@deepseek-ai/cordis': '^4.0.1' },
})
expect(dual.get('@deepseek-ai/dsh')?.get('0.2.0')).toMatchObject({
version: '0.2.0',
dependencies: { '@deepseek-ai/dsh-child': '^0.2.0' },
})
expect(dual.get('@deepseek-ai/cordis')).toBe(index.get('@deepseek-ai/cordis'))
})
it('accepts isolated DSH releases with one shared Cordis installation', () => {
expect(assertDualDshInstallLayout(validLayout())).toEqual({
dshPackagesPerVersion: 3,
checkedDshEdges: 4,
})
})
it('rejects an internal edge that crosses release versions', () => {
const layout = validLayout()
const packages = { ...layout.packages }
Reflect.deleteProperty(packages, 'node_modules/dsh-previous/node_modules/@deepseek-ai/dsh-leaf')
expect(() => assertDualDshInstallLayout({ ...layout, packages })).toThrow(
'node_modules/dsh-previous/node_modules/@deepseek-ai/dsh-child: dependencies '
+ '@deepseek-ai/dsh-leaf resolves to node_modules/@deepseek-ai/dsh-leaf@0.2.0, expected 0.1.0',
)
})
it('rejects a second Cordis installation', () => {
const layout = validLayout()
const packages = {
...layout.packages,
'node_modules/dsh-previous/node_modules/@deepseek-ai/cordis': { version: '4.0.1' },
}
expect(() => assertDualDshInstallLayout({ ...layout, packages })).toThrow(
'expected one shared @deepseek-ai/cordis',
)
})
})
+217
View File
@@ -0,0 +1,217 @@
/** Verify npm's physical package placement for two incompatible DSH releases. */
import { readFileSync } from 'node:fs'
import { posix, resolve } from 'node:path'
import {
buildRegistryIndex,
resolveNpmPackageLock,
type NpmLockPackage,
type NpmPackageLock,
type RegistryIndex,
} from './benchmark-npm-resolution.ts'
const DSH_PACKAGE = '@deepseek-ai/dsh'
const CORDIS_PACKAGE = '@deepseek-ai/cordis'
const NESTED_DSH_ALIAS = 'dsh-previous'
const NESTED_DSH_PATH = `node_modules/${NESTED_DSH_ALIAS}`
const DEPENDENCY_FIELDS = ['dependencies', 'optionalDependencies', 'peerDependencies'] as const
const TIMEOUT_MS = 300_000
/** Synthetic incompatible versions used to expose cross-release placement errors. */
export const SYNTHETIC_DSH_VERSIONS = ['0.1.0', '0.2.0'] as const
interface MutableRegistryManifest {
name: string
version: string
dependencies?: Record<string, string>
optionalDependencies?: Record<string, string>
peerDependencies?: Record<string, string>
[key: string]: unknown
}
/** Summary of a verified two-release npm layout. */
export interface DshInstallLayoutSummary {
readonly dshPackagesPerVersion: number
readonly checkedDshEdges: number
}
function isDshPackage(name: string): boolean {
return name === DSH_PACKAGE || name.startsWith(`${DSH_PACKAGE}-`)
}
function cloneForVersion(manifest: object, version: string): MutableRegistryManifest {
const cloned = structuredClone(manifest) as MutableRegistryManifest
cloned.version = version
for (const field of DEPENDENCY_FIELDS) {
const dependencies = cloned[field]
if (dependencies === undefined) continue
for (const name of Object.keys(dependencies)) {
if (isDshPackage(name)) dependencies[name] = `^${version}`
}
}
return cloned
}
/**
* Replace the working release with two incompatible, internally consistent DSH releases.
* @param index - Registry metadata containing the working release.
* @param sourceVersion - Workspace version copied into each synthetic release.
* @returns Registry metadata containing both synthetic DSH releases and unchanged external packages.
*/
export function buildDualDshRegistry(index: RegistryIndex, sourceVersion: string): RegistryIndex {
const output = new Map(index)
let dshPackages = 0
for (const [name, versions] of index) {
if (!isDshPackage(name)) {
output.set(name, versions)
continue
}
const source = versions.get(sourceVersion)
if (source === undefined) throw new Error(`${name} has no workspace version ${sourceVersion}`)
dshPackages++
output.set(name, new Map(SYNTHETIC_DSH_VERSIONS.map(version => [
version,
cloneForVersion(source, version),
])))
}
if (dshPackages === 0) throw new Error('registry contains no DSH packages')
return output
}
function packageNameAtPath(path: string, manifest: NpmLockPackage): string | undefined {
if (manifest.name !== undefined) return manifest.name
const marker = 'node_modules/'
const markerIndex = path.lastIndexOf(marker)
if (markerIndex < 0) return undefined
const segments = path.slice(markerIndex + marker.length).split('/')
if (segments[0]?.startsWith('@')) {
return segments[1] === undefined ? undefined : `${segments[0]}/${segments[1]}`
}
return segments[0]
}
function resolvePackagePath(
packages: Readonly<Record<string, NpmLockPackage>>,
sourcePath: string,
dependency: string,
): string | undefined {
let directory = sourcePath
while (directory !== '.') {
const candidate = posix.join(directory, 'node_modules', dependency)
if (packages[candidate] !== undefined) return candidate
directory = posix.dirname(directory)
}
const rootCandidate = posix.join('node_modules', dependency)
return packages[rootCandidate] === undefined ? undefined : rootCandidate
}
function setDifference(left: ReadonlySet<string>, right: ReadonlySet<string>): string[] {
return [...left].filter(value => !right.has(value)).sort()
}
/**
* Assert that npm isolates both DSH releases while sharing the Cordis runtime.
* @param packageLock - Metadata-only package lock produced by npm.
* @returns Counts for the verified DSH packages and dependency edges.
*/
export function assertDualDshInstallLayout(packageLock: NpmPackageLock): DshInstallLayoutSummary {
const [nestedVersion, rootVersion] = SYNTHETIC_DSH_VERSIONS
const errors: string[] = []
const namesByVersion = new Map<string, Set<string>>([
[nestedVersion, new Set()],
[rootVersion, new Set()],
])
const installed = Object.entries(packageLock.packages)
let checkedDshEdges = 0
for (const [path, manifest] of installed) {
const name = packageNameAtPath(path, manifest)
if (name === undefined || !isDshPackage(name)) continue
const version = manifest.version
if (version !== nestedVersion && version !== rootVersion) {
errors.push(`${path}: expected DSH version ${nestedVersion} or ${rootVersion}, got ${String(version)}`)
continue
}
namesByVersion.get(version)?.add(name)
const expectedPath = version === rootVersion
? `node_modules/${name}`
: name === DSH_PACKAGE
? NESTED_DSH_PATH
: `${NESTED_DSH_PATH}/node_modules/${name}`
if (path !== expectedPath) {
errors.push(`${path}: expected ${name}@${version} at ${expectedPath}`)
}
for (const field of DEPENDENCY_FIELDS) {
for (const dependency of Object.keys(manifest[field] ?? {})) {
if (!isDshPackage(dependency)) continue
const targetPath = resolvePackagePath(packageLock.packages, path, dependency)
const optionalPeer = field === 'peerDependencies'
&& manifest.peerDependenciesMeta?.[dependency]?.optional === true
if (targetPath === undefined) {
if (field === 'optionalDependencies' || optionalPeer) continue
errors.push(`${path}: ${field} ${dependency} does not resolve`)
continue
}
checkedDshEdges++
const targetVersion = packageLock.packages[targetPath]?.version
if (targetVersion !== version) {
errors.push(
`${path}: ${field} ${dependency} resolves to ${targetPath}@${String(targetVersion)}, expected ${version}`,
)
}
}
}
}
const nestedNames = namesByVersion.get(nestedVersion) ?? new Set<string>()
const rootNames = namesByVersion.get(rootVersion) ?? new Set<string>()
if (!nestedNames.has(DSH_PACKAGE)) errors.push(`${NESTED_DSH_PATH}: missing ${DSH_PACKAGE}@${nestedVersion}`)
if (!rootNames.has(DSH_PACKAGE)) errors.push(`node_modules/${DSH_PACKAGE}: missing ${DSH_PACKAGE}@${rootVersion}`)
const onlyNested = setDifference(nestedNames, rootNames)
const onlyRoot = setDifference(rootNames, nestedNames)
if (onlyNested.length > 0) errors.push(`only ${nestedVersion} contains: ${onlyNested.join(', ')}`)
if (onlyRoot.length > 0) errors.push(`only ${rootVersion} contains: ${onlyRoot.join(', ')}`)
const cordisPaths = installed.flatMap(([path, manifest]) =>
packageNameAtPath(path, manifest) === CORDIS_PACKAGE ? [path] : [])
if (cordisPaths.length !== 1 || cordisPaths[0] !== `node_modules/${CORDIS_PACKAGE}`) {
errors.push(`expected one shared ${CORDIS_PACKAGE} at node_modules/${CORDIS_PACKAGE}, got ${cordisPaths.join(', ')}`)
}
if (errors.length > 0) throw new Error(`invalid npm install layout:\n${errors.map(error => ` - ${error}`).join('\n')}`)
return { dshPackagesPerVersion: rootNames.size, checkedDshEdges }
}
function workspaceVersion(root: string): string {
const manifest = JSON.parse(readFileSync(resolve(root, 'apps/cli/package.json'), 'utf8')) as { version?: unknown }
if (typeof manifest.version !== 'string') throw new Error('apps/cli/package.json has no string version')
return manifest.version
}
async function main(): Promise<void> {
const root = resolve(import.meta.dirname, '..')
const index = buildDualDshRegistry(buildRegistryIndex(root), workspaceVersion(root))
const [nestedVersion, rootVersion] = SYNTHETIC_DSH_VERSIONS
const result = await resolveNpmPackageLock(index, {
[DSH_PACKAGE]: rootVersion,
[NESTED_DSH_ALIAS]: `npm:${DSH_PACKAGE}@${nestedVersion}`,
}, TIMEOUT_MS)
if (result.archiveRequests !== 0) throw new Error(`npm requested ${String(result.archiveRequests)} package archive(s)`)
const summary = assertDualDshInstallLayout(result.packageLock)
console.log(
`verify-npm-install-layout: ${String(summary.dshPackagesPerVersion)} DSH package(s) per release and `
+ `${String(summary.checkedDshEdges)} internal edge(s) verified in ${(result.durationMs / 1000).toFixed(2)} s; `
+ `both releases share one Cordis installation; ${String(result.unknownPackages.length)} unavailable optional `
+ 'package name(s) ignored by npm.',
)
}
if (import.meta.main) {
try {
await main()
} catch (error) {
console.error(`verify-npm-install-layout: ${error instanceof Error ? error.message : String(error)}`)
process.exitCode = 1
}
}
+528
View File
@@ -0,0 +1,528 @@
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
PACKAGE_DEPENDENCY_POLICY,
type PackageDependencyPolicy,
} from './package-dependency-policy.ts'
import {
collectHostDependencyExportPolicyViolations,
collectPackageDependencyViolations,
collectRuntimeSourceExportUses,
discoverPackageDependencyScope,
fixPackageDependencies,
formatManagedRuntimeDependencies,
formatPeerRequiredRuntimeDependencies,
readPackageDependencyFacts,
repairPackageDependencyManifest,
type PackageDependencyFacts,
type PackageDependencyManifest,
type WorkspacePackageManifest,
} from './verify-package-dependencies.ts'
const CORDIS = '@deepseek-ai/cordis'
const roots: string[] = []
afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
})
function pkg(
name: string,
manifestPath: string,
manifest: Partial<PackageDependencyManifest> = {},
): WorkspacePackageManifest {
return {
name,
manifestPath,
dir: dirname(manifestPath),
manifest: { name, ...manifest },
}
}
function policy(fields: Partial<PackageDependencyPolicy> = {}): PackageDependencyPolicy {
return {
clientFaceInclude: [],
clientFaceExclude: [],
hostPackages: [],
configurationOnlyDevDependencies: {},
safeHostDependencyExports: {},
peerRequiredHostExports: {},
...fields,
}
}
function facts(manifest: PackageDependencyManifest): PackageDependencyFacts {
return {
manifestPath: 'packages/core/probe/package.json',
role: 'configured-host',
manifest,
workspaceNames: new Set([
CORDIS,
'@deepseek-ai/dsh-runtime',
'@deepseek-ai/dsh-types',
'@deepseek-ai/dsh-stale',
'@deepseek-ai/schemastery',
]),
allSourceUses: new Map([
['@deepseek-ai/dsh-runtime', ['packages/core/probe/src/index.ts']],
['@deepseek-ai/dsh-types', ['packages/core/probe/src/types.ts']],
]),
hostRuntimeSourceUses: new Map([
['@deepseek-ai/dsh-runtime', ['packages/core/probe/src/index.ts']],
]),
hostRuntimeExportUses: [{
packageName: '@deepseek-ai/dsh-runtime',
specifier: '@deepseek-ai/dsh-runtime',
exportName: 'runtimeValue',
sourcePath: 'packages/core/probe/src/index.ts',
line: 1,
column: 10,
sourceLine: "import { runtimeValue } from '@deepseek-ai/dsh-runtime'",
}],
peerRequiredHostDependencies: new Set(),
configurationOnlyDevDependencies: new Set(),
clientInject: new Set(),
}
}
describe('package dependency scope', () => {
it('keeps the measured Host relay roster explicit', () => {
expect(PACKAGE_DEPENDENCY_POLICY.clientFaceExclude).toEqual([
'@deepseek-ai/dsh-api-session-controller',
'@deepseek-ai/dsh-api-workspace-controller',
])
expect(PACKAGE_DEPENDENCY_POLICY.hostPackages).toEqual([
'@deepseek-ai/dsh-llm',
'@deepseek-ai/dsh-session',
])
expect(PACKAGE_DEPENDENCY_POLICY.configurationOnlyDevDependencies).toEqual({
'@deepseek-ai/dsh-client-locale': ['@deepseek-ai/dsh-api-remotes'],
'@deepseek-ai/dsh-client-ui-conversation': [
'@deepseek-ai/dsh-api-remotes',
'@deepseek-ai/dsh-client-ui-workspace',
],
'@deepseek-ai/dsh-client-ui-model-selection': ['@deepseek-ai/dsh-client-ui-input-trigger'],
'@deepseek-ai/dsh-client-ui-sidebar': ['@deepseek-ai/dsh-client-ui-workspace'],
'@deepseek-ai/dsh-client-ui-subagent': ['@deepseek-ai/dsh-client-ui-input-trigger'],
'@deepseek-ai/dsh-client-ui-theme': ['@deepseek-ai/dsh-api-remotes'],
'@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'],
})
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/schemastery']).toEqual(['default'])
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-session/types']).toEqual([
'SessionId',
])
expect(PACKAGE_DEPENDENCY_POLICY.safeHostDependencyExports['@deepseek-ai/dsh-typert-protocol']).toEqual([
'RemoteError', 'remoteErrorOf',
])
expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-scope']).toEqual([
'carrierKeyOf', 'scopeOf', 'scopeTarget',
])
expect(PACKAGE_DEPENDENCY_POLICY.peerRequiredHostExports['@deepseek-ai/dsh-typert-protocol']).toEqual([
'Remote', 'TypertRemoteService', 'remoteMethods',
])
})
it('discovers the Client directory, dsh.client declarations, and configured Host packages', () => {
const packages = [
pkg('@f/static', 'packages/client/static/package.json'),
pkg('@f/dynamic-client', 'packages/client/dynamic/package.json', { dsh: { client: {} } }),
pkg('@f/dual', 'packages/api/dual/package.json', { dsh: { client: {} } }),
pkg('@f/export-only', 'packages/api/export-only/package.json', { exports: { './client': './lib/client.js' } }),
pkg('@f/forced-client', 'packages/api/forced/package.json'),
pkg('@f/excluded', 'packages/api/excluded/package.json', { dsh: { client: {} } }),
pkg('@f/host', 'packages/core/host/package.json'),
]
const found = discoverPackageDependencyScope(packages, policy({
clientFaceInclude: ['@f/forced-client'],
clientFaceExclude: ['@f/excluded'],
hostPackages: ['@f/host'],
}))
expect(found.violations).toEqual([])
expect(found.selected.map(item => [item.name, item.role])).toEqual([
['@f/dual', 'client-host'],
['@f/forced-client', 'client-host'],
['@f/dynamic-client', 'client-host'],
['@f/static', 'client-only'],
['@f/host', 'configured-host'],
])
})
it('rejects stale, redundant, overlapping, and unknown configuration', () => {
const packages = [
pkg('@f/client', 'packages/client/client/package.json'),
pkg('@f/dual', 'packages/api/dual/package.json', { dsh: { client: {} } }),
pkg('@f/host', 'packages/core/host/package.json'),
]
const found = discoverPackageDependencyScope(packages, policy({
clientFaceInclude: ['@f/dual', '@f/missing', '@f/host'],
clientFaceExclude: ['@f/client', '@f/host', '@f/missing'],
hostPackages: ['@f/dual'],
}))
expect(found.violations).toEqual(expect.arrayContaining([
expect.stringContaining('clientFaceInclude redundantly names automatically discovered package @f/dual'),
expect.stringContaining('@f/host appears in both clientFaceInclude and clientFaceExclude'),
expect.stringContaining('clientFaceExclude cannot exempt packages/client package @f/client'),
expect.stringContaining('clientFaceExclude names @f/host, which declares no dsh.client entry'),
expect.stringContaining('hostPackages redundantly names Client-faced package @f/dual'),
expect.stringContaining('unknown release package @f/missing'),
]))
})
it('rejects stale, duplicate, and unbounded safe Host export entries', () => {
const consumer = pkg('@f/consumer', 'packages/core/consumer/package.json')
const provider = pkg('@f/provider', 'packages/core/provider/package.json')
const workspaceNames = new Set([CORDIS, consumer.name, provider.name])
const consumerFacts: PackageDependencyFacts = {
manifestPath: consumer.manifestPath,
role: 'configured-host',
manifest: consumer.manifest,
workspaceNames,
allSourceUses: new Map(),
hostRuntimeSourceUses: new Map([[provider.name, ['packages/core/consumer/src/index.ts']]]),
hostRuntimeExportUses: [{
packageName: provider.name,
specifier: `${provider.name}/api`,
exportName: 'safeValue',
sourcePath: 'packages/core/consumer/src/index.ts',
line: 1,
column: 10,
sourceLine: "import { safeValue } from '@f/provider/api'",
}],
peerRequiredHostDependencies: new Set(),
configurationOnlyDevDependencies: new Set(),
clientInject: new Set(),
}
expect(collectHostDependencyExportPolicyViolations(
[consumerFacts],
workspaceNames,
{
safeHostDependencyExports: {
[`${provider.name}/api`]: ['safeValue', 'safeValue', '*', 'staleValue'],
},
peerRequiredHostExports: {
[`${provider.name}/api`]: ['safeValue'],
},
},
)).toEqual(expect.arrayContaining([
expect.stringContaining('export safeValue more than once'),
expect.stringContaining('cannot classify unbounded'),
expect.stringContaining('unused @f/provider/api export staleValue'),
expect.stringContaining('appears in both Host export classifications'),
]))
})
})
describe('face-aware source classification', () => {
it('fails when a managed Host package has no Host entry', () => {
const root = mkdtempSync(join(tmpdir(), 'dsh-package-missing-host-'))
roots.push(root)
const subject = pkg('@f/host', 'packages/g/host/package.json')
expect(() => readPackageDependencyFacts(root, subject, 'configured-host', new Set([subject.name])))
.toThrow('packages/g/host/package.json: Host runtime entry packages/g/host/src/index.ts does not exist')
})
it('counts Host values as dependencies and Client values as development inputs', () => {
const root = mkdtempSync(join(tmpdir(), 'dsh-package-faces-'))
roots.push(root)
const subject = pkg('@f/dual', 'packages/g/dual/package.json', {
dsh: { client: { inject: ['@f/injected'] } },
})
const files = {
'packages/g/dual/src/index.ts': [
"import { value } from '@f/runtime'",
"import type { Shared } from '@f/types'",
"import type { Hidden } from './types.ts'",
"export { nested } from './nested.ts'",
].join('\n'),
'packages/g/dual/src/nested.ts': "export { nested } from '@f/nested'",
'packages/g/dual/src/types.ts': "import { hidden } from '@f/hidden'; export type Hidden = typeof hidden",
'packages/g/dual/src/client/index.ts': "import { browser } from '@f/browser'",
}
for (const [path, source] of Object.entries(files)) {
mkdirSync(dirname(join(root, path)), { recursive: true })
writeFileSync(join(root, path), source)
}
const found = readPackageDependencyFacts(root, subject, 'client-host', new Set([
CORDIS, '@f/runtime', '@f/types', '@f/nested', '@f/hidden', '@f/browser', '@f/injected',
]), policy({
configurationOnlyDevDependencies: { '@f/dual': ['@f/injected'] },
}))
expect([...found.hostRuntimeSourceUses.keys()].sort()).toEqual(['@f/nested', '@f/runtime'])
expect([...found.configurationOnlyDevDependencies]).toEqual(['@f/injected'])
expect(found.hostRuntimeExportUses).toEqual([
{
packageName: '@f/nested',
specifier: '@f/nested',
exportName: 'nested',
sourcePath: 'packages/g/dual/src/nested.ts',
line: 1,
column: 10,
sourceLine: "export { nested } from '@f/nested'",
},
{
packageName: '@f/runtime',
specifier: '@f/runtime',
exportName: 'value',
sourcePath: 'packages/g/dual/src/index.ts',
line: 1,
column: 10,
sourceLine: "import { value } from '@f/runtime'",
},
])
expect([...found.allSourceUses.keys()].sort()).toEqual([
'@f/browser', '@f/hidden', '@f/nested', '@f/runtime', '@f/types',
])
})
it('identifies exact runtime exports without treating type imports as values', () => {
const source = [
"import defaultValue, { value as local, type Kind } from '@f/root'",
"import * as namespace from '@f/namespace'",
"import '@f/effect'",
"import type { TypeOnly } from '@f/types'",
"export { source as renamed, type SourceType } from '@f/reexport'",
"export * from '@f/star'",
"void import('@f/dynamic')",
"void require('@f/required')",
'void defaultValue; void local; void namespace',
].join('\n')
const uses = collectRuntimeSourceExportUses('probe.ts', source)
expect(uses.map(({ specifier, exportName }) => ({ specifier, exportName }))).toEqual([
{ specifier: '@f/dynamic', exportName: '*' },
{ specifier: '@f/effect', exportName: '(side effect)' },
{ specifier: '@f/namespace', exportName: '*' },
{ specifier: '@f/reexport', exportName: 'source' },
{ specifier: '@f/required', exportName: '*' },
{ specifier: '@f/root', exportName: 'default' },
{ specifier: '@f/root', exportName: 'value' },
{ specifier: '@f/star', exportName: '*' },
])
expect(uses.find(use => use.specifier === '@f/root' && use.exportName === 'value')).toMatchObject({
line: 1,
column: 24,
sourceLine: "import defaultValue, { value as local, type Kind } from '@f/root'",
})
})
})
describe('dependency sections', () => {
it('does not leak repository configuration into captured dependency facts', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-client-locale',
dependencies: { '@deepseek-ai/dsh-runtime': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-types': 'workspace:^' },
peerDependencies: { [CORDIS]: 'workspace:^' },
}
const base = facts(manifest)
const subject: PackageDependencyFacts = {
...base,
workspaceNames: new Set([...base.workspaceNames, '@deepseek-ai/dsh-api-remotes']),
}
expect(collectPackageDependencyViolations({
facts: [subject], packages: [], policyViolations: [], workspaceNames: subject.workspaceNames,
})).toEqual([])
})
it('requires non-workspace Host runtime imports in dependencies', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
dependencies: { '@deepseek-ai/dsh-runtime': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-types': 'workspace:^', external: '^1.0.0' },
peerDependencies: { [CORDIS]: 'workspace:^' },
}
const subject: PackageDependencyFacts = {
...facts(manifest),
hostRuntimeSourceUses: new Map([
['@deepseek-ai/dsh-runtime', ['packages/core/probe/src/index.ts']],
['external', ['packages/core/probe/src/index.ts']],
]),
}
const state = {
facts: [subject], packages: [], policyViolations: [], workspaceNames: subject.workspaceNames,
}
expect(collectPackageDependencyViolations(state)).toContain(
'packages/core/probe/package.json: external (packages/core/probe/src/index.ts) '
+ 'must be dependencies-only; found devDependencies',
)
repairPackageDependencyManifest(subject)
expect(manifest.dependencies?.external).toBe('^1.0.0')
expect(manifest.devDependencies?.external).toBeUndefined()
delete manifest.dependencies?.external
expect(collectPackageDependencyViolations(state)).toContain(
'packages/core/probe/package.json: external (packages/core/probe/src/index.ts) '
+ 'must be dependencies-only; found no dependency section',
)
})
it('accepts Host dependencies, development-only inputs, and shared Cordis', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
dependencies: {
'@deepseek-ai/dsh-runtime': 'workspace:^',
'@deepseek-ai/schemastery': 'workspace:^',
external: '^1.0.0',
},
devDependencies: {
'@deepseek-ai/dsh-types': 'workspace:^',
[CORDIS]: 'workspace:^',
},
peerDependencies: { [CORDIS]: 'workspace:^' },
}
expect(collectPackageDependencyViolations({
facts: [facts(manifest)], packages: [], policyViolations: [], workspaceNames: facts(manifest).workspaceNames,
})).toEqual([])
})
it('lists managed Host runtime dependencies for fix review', () => {
const subject = facts({ name: '@deepseek-ai/dsh-probe' })
expect(formatManagedRuntimeDependencies({
facts: [subject], packages: [], policyViolations: [], workspaceNames: subject.workspaceNames,
})).toEqual([
'verify-package-dependencies: 1 managed Host runtime edge(s) remain in dependencies across 1 package(s):',
' @deepseek-ai/dsh-probe -> @deepseek-ai/dsh-runtime: @deepseek-ai/dsh-runtime#runtimeValue',
])
})
it('reports an unapproved Host runtime export without rewriting its dependency section', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
dependencies: { '@deepseek-ai/dsh-runtime': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-types': 'workspace:^' },
peerDependencies: { [CORDIS]: 'workspace:^' },
}
const subject = facts(manifest)
const safetyViolations = collectHostDependencyExportPolicyViolations(
[subject],
subject.workspaceNames,
{ safeHostDependencyExports: {}, peerRequiredHostExports: {} },
)
const state = {
facts: [subject], packages: [], policyViolations: safetyViolations, workspaceNames: subject.workspaceNames,
}
expect(safetyViolations).toEqual([
'packages/core/probe/src/index.ts:1:10: @deepseek-ai/dsh-runtime#runtimeValue is not classified as '
+ 'safe or peer-required — import { runtimeValue } from \'@deepseek-ai/dsh-runtime\'',
])
expect(fixPackageDependencies('/unused', state)).toEqual([])
expect(manifest.dependencies).toEqual({ '@deepseek-ai/dsh-runtime': 'workspace:^' })
})
it('keeps an edge as a peer when one imported export requires shared identity', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
dependencies: { '@deepseek-ai/dsh-runtime': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-types': 'workspace:^' },
peerDependencies: { [CORDIS]: 'workspace:^' },
}
const subject: PackageDependencyFacts = {
...facts(manifest),
peerRequiredHostDependencies: new Set(['@deepseek-ai/dsh-runtime']),
}
expect(collectHostDependencyExportPolicyViolations(
[subject],
subject.workspaceNames,
{
safeHostDependencyExports: {},
peerRequiredHostExports: {
'@deepseek-ai/dsh-runtime': ['runtimeValue'],
},
},
)).toEqual([])
repairPackageDependencyManifest(subject)
expect(manifest.dependencies).toBeUndefined()
expect(manifest.peerDependencies).toMatchObject({
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-runtime': 'workspace:^',
})
expect(manifest.devDependencies).toMatchObject({
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-runtime': 'workspace:^',
})
expect(formatPeerRequiredRuntimeDependencies({
facts: [subject], packages: [], policyViolations: [], workspaceNames: subject.workspaceNames,
})).toEqual([
'verify-package-dependencies: 1 Host runtime edge(s) remain in peerDependencies because their exports require shared identity across 1 package(s):',
' @deepseek-ai/dsh-probe -> @deepseek-ai/dsh-runtime: @deepseek-ai/dsh-runtime#runtimeValue',
])
})
it('reports wrong sections, workspace ranges, and stale peer metadata', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
dependencies: { '@deepseek-ai/dsh-types': 'workspace:*' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-runtime': 'workspace:^' },
peerDependencies: { [CORDIS]: 'workspace:*', '@deepseek-ai/dsh-runtime': 'workspace:^' },
peerDependenciesMeta: { '@deepseek-ai/dsh-missing': { optional: true } },
}
const state = {
facts: [facts(manifest)], packages: [], policyViolations: [], workspaceNames: facts(manifest).workspaceNames,
}
const violations = collectPackageDependencyViolations(state)
expect(violations).toEqual(expect.arrayContaining([
expect.stringContaining('@deepseek-ai/dsh-runtime'),
expect.stringContaining('@deepseek-ai/dsh-types'),
expect.stringContaining(`${CORDIS} must be matching peerDependencies + devDependencies`),
expect.stringContaining('dependencies.@deepseek-ai/dsh-types must use workspace:^'),
expect.stringContaining('peerDependenciesMeta.@deepseek-ai/dsh-missing has no matching'),
]))
})
it('repairs owned relationships without changing unrelated dependencies', () => {
const root = mkdtempSync(join(tmpdir(), 'dsh-package-dependencies-'))
roots.push(root)
const manifestPath = 'package.json'
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
dependencies: { '@deepseek-ai/schemastery': 'workspace:*', external: '^1.0.0' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-runtime': 'workspace:^' },
peerDependencies: {
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-runtime': 'workspace:^',
'@deepseek-ai/dsh-stale': 'workspace:^',
},
peerDependenciesMeta: { '@deepseek-ai/dsh-stale': { optional: true } },
}
writeFileSync(join(root, manifestPath), `${JSON.stringify(manifest, null, 2)}\n`)
const subject = { ...facts(manifest), manifestPath }
const state = { facts: [subject], packages: [], policyViolations: [], workspaceNames: subject.workspaceNames }
expect(fixPackageDependencies(root, state)).toEqual([manifestPath])
const fixed = JSON.parse(readFileSync(join(root, manifestPath), 'utf8')) as PackageDependencyManifest
expect(fixed.dependencies).toEqual({
'@deepseek-ai/schemastery': 'workspace:^',
external: '^1.0.0',
'@deepseek-ai/dsh-runtime': 'workspace:^',
})
expect(fixed.devDependencies).toEqual({
[CORDIS]: 'workspace:^',
'@deepseek-ai/dsh-types': 'workspace:^',
'@deepseek-ai/dsh-stale': 'workspace:^',
})
expect(fixed.peerDependencies).toEqual({ [CORDIS]: 'workspace:^' })
expect(fixed.peerDependenciesMeta).toBeUndefined()
})
it('repairs an in-memory manifest for benchmark simulation', () => {
const manifest: PackageDependencyManifest = {
name: '@deepseek-ai/dsh-probe',
peerDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-runtime': 'workspace:^' },
devDependencies: { [CORDIS]: 'workspace:^', '@deepseek-ai/dsh-runtime': 'workspace:^' },
}
repairPackageDependencyManifest(facts(manifest))
expect(manifest.dependencies).toEqual({ '@deepseek-ai/dsh-runtime': 'workspace:^' })
expect(manifest.peerDependencies).toEqual({ [CORDIS]: 'workspace:^' })
})
})
+734
View File
@@ -0,0 +1,734 @@
/** Verify and repair npm dependency sections from published Client and Host faces. */
import { spawnSync } from 'node:child_process'
import { existsSync, globSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, extname, join, normalize, relative, resolve, sep } from 'node:path'
import ts from 'typescript'
import { writeModuleGraph } from './gen-module-graph.ts'
import {
hasClientDeclaration,
PACKAGE_DEPENDENCY_POLICY,
type PackageDependencyPolicy,
} from './package-dependency-policy.ts'
import {
collectRuntimeLocalSourceSpecifiers,
collectSourcePackageUses,
} from './verify-client-packages.ts'
const GATE = 'verify-package-dependencies'
const CORDIS = '@deepseek-ai/cordis'
const WORKSPACE_RANGE = 'workspace:^'
const RELEASE_MANIFEST_GLOB = 'packages/!(experimental)/*/package.json'
const WORKSPACE_MANIFEST_GLOBS = [
'apps/*/package.json',
'packages/*/*/package.json',
'vendor/*/package.json',
]
type DependencySection = 'dependencies' | 'devDependencies' | 'optionalDependencies' | 'peerDependencies'
export type PackageDependencyRole = 'client-only' | 'client-host' | 'configured-host'
/** Manifest fields read and repaired by the package dependency policy. */
export interface PackageDependencyManifest {
name?: string
version?: string
exports?: unknown
dependencies?: Record<string, string>
devDependencies?: Record<string, string>
optionalDependencies?: Record<string, string>
peerDependencies?: Record<string, string>
peerDependenciesMeta?: Record<string, unknown>
dsh?: { client?: { inject?: string[] } }
}
/** One workspace package and its source location. */
export interface WorkspacePackageManifest {
readonly dir: string
readonly manifestPath: string
readonly manifest: PackageDependencyManifest
readonly name: string
}
/** Source and manifest facts for one package covered by the policy. */
export interface PackageDependencyFacts {
readonly manifestPath: string
readonly role: PackageDependencyRole
readonly manifest: PackageDependencyManifest
readonly workspaceNames: ReadonlySet<string>
readonly allSourceUses: ReadonlyMap<string, readonly string[]>
readonly hostRuntimeSourceUses: ReadonlyMap<string, readonly string[]>
readonly hostRuntimeExportUses: readonly HostRuntimeExportUse[]
readonly peerRequiredHostDependencies: ReadonlySet<string>
readonly configurationOnlyDevDependencies: ReadonlySet<string>
readonly clientInject: ReadonlySet<string>
}
/** One runtime export reached from a package's Host source closure. */
export interface HostRuntimeExportUse {
readonly packageName: string
readonly specifier: string
readonly exportName: string
readonly sourcePath: string
readonly line: number
readonly column: number
readonly sourceLine: string
}
/** Complete policy input read from the repository. */
export interface PackageDependencyState {
readonly facts: readonly PackageDependencyFacts[]
readonly packages: readonly WorkspacePackageManifest[]
readonly policyViolations: readonly string[]
readonly workspaceNames: ReadonlySet<string>
}
export interface ExpectedPackageDependency {
readonly section: 'dependencies' | 'devDependencies' | 'peer-dev'
readonly origins: readonly string[]
}
function normalizePath(path: string): string {
return path.split(sep).join('/')
}
function packageNameOf(specifier: string): string | undefined {
if (specifier.startsWith('.') || specifier.startsWith('/') || specifier.startsWith('#') || specifier.includes(':')) {
return undefined
}
const parts = specifier.split('/')
return specifier.startsWith('@') ? parts.length >= 2 ? `${parts[0]}/${parts[1]}` : undefined : parts[0]
}
/** Read package manifests used for scope discovery and workspace-name checks. */
export function readWorkspacePackageManifests(root: string): {
all: WorkspacePackageManifest[]
release: WorkspacePackageManifest[]
} {
const read = (manifestPath: string): WorkspacePackageManifest => {
const manifest = JSON.parse(readFileSync(resolve(root, manifestPath), 'utf8')) as PackageDependencyManifest
if (typeof manifest.name !== 'string') throw new Error(`${manifestPath}: missing package name`)
return {
dir: dirname(manifestPath),
manifestPath,
manifest,
name: manifest.name,
}
}
const all = globSync(WORKSPACE_MANIFEST_GLOBS, { cwd: root }).map(normalizePath).sort().map(read)
const releasePaths = new Set(globSync(RELEASE_MANIFEST_GLOB, { cwd: root }).map(normalizePath))
return { all, release: all.filter(pkg => releasePaths.has(pkg.manifestPath)) }
}
function duplicates(values: readonly string[]): string[] {
const seen = new Set<string>()
const duplicated = new Set<string>()
for (const value of values) {
if (seen.has(value)) duplicated.add(value)
seen.add(value)
}
return [...duplicated].sort()
}
/** Discover Client faces and configured Host packages, validating explicit overrides. */
export function discoverPackageDependencyScope(
packages: readonly WorkspacePackageManifest[],
policy: PackageDependencyPolicy,
): { selected: Array<WorkspacePackageManifest & { role: PackageDependencyRole }>; violations: string[] } {
const violations: string[] = []
const byName = new Map(packages.map(pkg => [pkg.name, pkg]))
const include = new Set(policy.clientFaceInclude)
const exclude = new Set(policy.clientFaceExclude)
const host = new Set(policy.hostPackages)
for (const [field, values] of [
['clientFaceInclude', policy.clientFaceInclude],
['clientFaceExclude', policy.clientFaceExclude],
['hostPackages', policy.hostPackages],
] as const) {
for (const name of duplicates(values)) violations.push(`${field} lists ${name} more than once`)
for (const name of values) {
if (!byName.has(name)) violations.push(`${field} names unknown release package ${name}`)
}
}
for (const name of include) {
if (exclude.has(name)) violations.push(`${name} appears in both clientFaceInclude and clientFaceExclude`)
const pkg = byName.get(name)
if (pkg !== undefined
&& (pkg.manifestPath.startsWith('packages/client/') || hasClientDeclaration(pkg.manifest.dsh))) {
violations.push(`clientFaceInclude redundantly names automatically discovered package ${name}`)
}
}
for (const name of exclude) {
const pkg = byName.get(name)
if (pkg !== undefined && pkg.manifestPath.startsWith('packages/client/')) {
violations.push(`clientFaceExclude cannot exempt packages/client package ${name}`)
} else if (pkg !== undefined && !hasClientDeclaration(pkg.manifest.dsh)) {
violations.push(`clientFaceExclude names ${name}, which declares no dsh.client entry`)
}
}
const selected: Array<WorkspacePackageManifest & { role: PackageDependencyRole }> = []
for (const pkg of packages) {
const clientDirectory = pkg.manifestPath.startsWith('packages/client/')
const clientHost = (hasClientDeclaration(pkg.manifest.dsh) || include.has(pkg.name)) && !exclude.has(pkg.name)
const clientOnly = clientDirectory && !clientHost
const configuredHost = host.has(pkg.name)
if (configuredHost && (clientHost || clientOnly)) {
violations.push(`hostPackages redundantly names Client-faced package ${pkg.name}`)
}
const role = clientHost ? 'client-host' : clientOnly ? 'client-only' : configuredHost ? 'configured-host' : undefined
if (role !== undefined) selected.push({ ...pkg, role })
}
return {
selected: selected.sort((left, right) => left.manifestPath.localeCompare(right.manifestPath)),
violations: [...new Set(violations)].sort(),
}
}
function addUse(target: Map<string, string[]>, name: string, path: string): void {
const paths = target.get(name) ?? []
if (!paths.includes(path)) paths.push(path)
target.set(name, paths)
}
const NAMESPACE_RUNTIME_EXPORT = '*'
const SIDE_EFFECT_RUNTIME_EXPORT = '(side effect)'
interface RuntimeSourceExportUse {
readonly specifier: string
readonly exportName: string
readonly line: number
readonly column: number
readonly sourceLine: string
}
/** Collect exact runtime exports imported or re-exported by one source file. */
export function collectRuntimeSourceExportUses(path: string, source: string): RuntimeSourceExportUse[] {
const sourceFile = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true)
const uses = new Map<string, RuntimeSourceExportUse>()
const sourceLines = source.split(/\r?\n/u)
const record = (specifier: string, exportName: string, locationNode: ts.Node): void => {
const key = `${specifier}\0${exportName}`
if (uses.has(key)) return
const position = sourceFile.getLineAndCharacterOfPosition(locationNode.getStart(sourceFile))
uses.set(key, {
specifier,
exportName,
line: position.line + 1,
column: position.character + 1,
sourceLine: sourceLines[position.line]?.trim() ?? '',
})
}
const add = (
specifierNode: ts.Expression | undefined,
exportName: string,
locationNode: ts.Node = specifierNode ?? sourceFile,
): void => {
if (specifierNode === undefined || !ts.isStringLiteralLike(specifierNode)) return
if (packageNameOf(specifierNode.text) === undefined) return
record(specifierNode.text, exportName, locationNode)
}
const visit = (node: ts.Node): void => {
if (ts.isImportDeclaration(node)) {
const clause = node.importClause
if (clause === undefined) {
add(node.moduleSpecifier, SIDE_EFFECT_RUNTIME_EXPORT)
} else if (clause.phaseModifier !== ts.SyntaxKind.TypeKeyword) {
if (clause.name !== undefined) add(node.moduleSpecifier, 'default', clause.name)
const bindings = clause.namedBindings
if (bindings !== undefined && ts.isNamespaceImport(bindings)) {
add(node.moduleSpecifier, NAMESPACE_RUNTIME_EXPORT, bindings.name)
} else if (bindings !== undefined && bindings.elements.length === 0) {
add(node.moduleSpecifier, SIDE_EFFECT_RUNTIME_EXPORT)
} else if (bindings !== undefined) {
for (const element of bindings.elements) {
const imported = element.propertyName ?? element.name
if (!element.isTypeOnly) add(node.moduleSpecifier, imported.text, imported)
}
}
}
} else if (ts.isExportDeclaration(node) && !node.isTypeOnly) {
const clause = node.exportClause
if (clause === undefined || ts.isNamespaceExport(clause)) {
add(node.moduleSpecifier, NAMESPACE_RUNTIME_EXPORT)
} else if (clause.elements.length === 0) {
add(node.moduleSpecifier, SIDE_EFFECT_RUNTIME_EXPORT)
} else {
for (const element of clause.elements) {
const imported = element.propertyName ?? element.name
if (!element.isTypeOnly) add(node.moduleSpecifier, imported.text, imported)
}
}
} else if (ts.isImportEqualsDeclaration(node)
&& !node.isTypeOnly
&& ts.isExternalModuleReference(node.moduleReference)) {
add(node.moduleReference.expression, NAMESPACE_RUNTIME_EXPORT, node.name)
} else if (ts.isCallExpression(node)
&& (node.expression.kind === ts.SyntaxKind.ImportKeyword
|| ts.isIdentifier(node.expression) && node.expression.text === 'require')) {
add(node.arguments[0], NAMESPACE_RUNTIME_EXPORT)
} else if (ts.isJsxElement(node) || ts.isJsxSelfClosingElement(node) || ts.isJsxFragment(node)) {
record('react/jsx-runtime', NAMESPACE_RUNTIME_EXPORT, node)
}
ts.forEachChild(node, visit)
}
visit(sourceFile)
return [...uses.values()].sort((left, right) =>
left.specifier.localeCompare(right.specifier)
|| left.exportName.localeCompare(right.exportName)
|| left.line - right.line
|| left.column - right.column)
}
function resolveLocal(importer: string, specifier: string): string | undefined {
const raw = resolve(dirname(importer), specifier)
const candidates = extname(raw) === ''
? [`${raw}.ts`, `${raw}.tsx`, `${raw}.mts`, `${raw}.cts`, join(raw, 'index.ts'), join(raw, 'index.tsx')]
: [raw, raw.replace(/\.js$/, '.ts'), raw.replace(/\.jsx$/, '.tsx'), raw.replace(/\.mjs$/, '.mts'), raw.replace(/\.cjs$/, '.cts')]
return candidates.find(candidate => existsSync(candidate))
}
function readHostRuntimeUses(root: string, pkg: WorkspacePackageManifest): {
packageUses: Map<string, string[]>
exportUses: HostRuntimeExportUse[]
} {
const packageUses = new Map<string, string[]>()
const exportUses = new Map<string, HostRuntimeExportUse>()
const seen = new Set<string>()
const visit = (path: string): void => {
const normalized = normalize(path)
if (seen.has(normalized)) return
seen.add(normalized)
const source = readFileSync(normalized, 'utf8')
const displayPath = normalizePath(relative(root, normalized))
for (const use of collectRuntimeSourceExportUses(normalized, source)) {
const name = packageNameOf(use.specifier)
if (name === undefined) continue
addUse(packageUses, name, displayPath)
const fact = { packageName: name, ...use, sourcePath: displayPath }
exportUses.set(`${use.specifier}\0${use.exportName}\0${displayPath}\0${String(use.line)}\0${String(use.column)}`, fact)
}
for (const specifier of collectRuntimeLocalSourceSpecifiers(normalized, source)) {
const target = resolveLocal(normalized, specifier)
if (target !== undefined) visit(target)
}
}
const entry = resolve(root, pkg.dir, 'src/index.ts')
if (!existsSync(entry)) {
throw new Error(`${pkg.manifestPath}: Host runtime entry ${normalizePath(relative(root, entry))} does not exist`)
}
visit(entry)
return {
packageUses,
exportUses: [...exportUses.values()].sort((left, right) =>
left.packageName.localeCompare(right.packageName)
|| left.specifier.localeCompare(right.specifier)
|| left.exportName.localeCompare(right.exportName)
|| left.sourcePath.localeCompare(right.sourcePath)
|| left.line - right.line
|| left.column - right.column),
}
}
function readAllSourceUses(root: string, pkg: WorkspacePackageManifest): Map<string, string[]> {
const uses = new Map<string, string[]>()
for (const sourcePath of globSync('src/**/*.{ts,tsx,mts,cts}', { cwd: resolve(root, pkg.dir) }).sort()) {
const source = readFileSync(resolve(root, pkg.dir, sourcePath), 'utf8')
const displayPath = `${pkg.dir}/${normalizePath(sourcePath)}`
for (const name of collectSourcePackageUses(sourcePath, source)) addUse(uses, name, displayPath)
}
return uses
}
/** Read source usage for one already-classified package. */
export function readPackageDependencyFacts(
root: string,
pkg: WorkspacePackageManifest,
role: PackageDependencyRole,
workspaceNames: ReadonlySet<string>,
policy: PackageDependencyPolicy = PACKAGE_DEPENDENCY_POLICY,
): PackageDependencyFacts {
const inject = pkg.manifest.dsh?.client?.inject ?? []
const hostRuntime = role === 'client-only'
? { packageUses: new Map<string, string[]>(), exportUses: [] }
: readHostRuntimeUses(root, pkg)
return {
manifestPath: pkg.manifestPath,
role,
manifest: pkg.manifest,
workspaceNames,
allSourceUses: readAllSourceUses(root, pkg),
hostRuntimeSourceUses: hostRuntime.packageUses,
hostRuntimeExportUses: hostRuntime.exportUses,
peerRequiredHostDependencies: new Set(hostRuntime.exportUses
.filter(use => policy.peerRequiredHostExports[use.specifier]?.includes(use.exportName) === true)
.map(use => use.packageName)),
configurationOnlyDevDependencies: new Set(
policy.configurationOnlyDevDependencies[pkg.manifest.name ?? ''] ?? [],
),
clientInject: new Set(inject.map(packageNameOf).filter(name => name !== undefined)),
}
}
/** Validate reviewed Host export classifications against current source facts. */
export function collectHostDependencyExportPolicyViolations(
facts: readonly PackageDependencyFacts[],
workspaceNames: ReadonlySet<string>,
policy: Pick<PackageDependencyPolicy, 'peerRequiredHostExports' | 'safeHostDependencyExports'>,
): string[] {
const violations: string[] = []
const allRuntimeUses = facts.flatMap(fact => fact.hostRuntimeExportUses)
const classifications = [
['safeHostDependencyExports', policy.safeHostDependencyExports],
['peerRequiredHostExports', policy.peerRequiredHostExports],
] as const
for (const [field, entries] of classifications) {
for (const [specifier, exportNames] of Object.entries(entries)) {
const provider = packageNameOf(specifier)
if (provider === undefined || !workspaceNames.has(provider)) {
violations.push(`${field} specifier ${specifier} is not a workspace package`)
}
if (exportNames.length === 0) {
violations.push(`${field} lists no exports for ${specifier}`)
}
for (const exportName of duplicates(exportNames)) {
violations.push(`${field} lists ${specifier} export ${exportName} more than once`)
}
for (const exportName of exportNames) {
if (exportName === '' || exportName === NAMESPACE_RUNTIME_EXPORT || exportName === SIDE_EFFECT_RUNTIME_EXPORT) {
violations.push(`${field} cannot classify unbounded ${specifier} export ${exportName}`)
continue
}
if (!allRuntimeUses.some(use => use.specifier === specifier && use.exportName === exportName)) {
violations.push(`${field} lists unused ${specifier} export ${exportName}`)
}
if (field === 'safeHostDependencyExports'
&& policy.peerRequiredHostExports[specifier]?.includes(exportName) === true) {
violations.push(`${specifier} export ${exportName} appears in both Host export classifications`)
}
}
}
}
for (const fact of facts) {
for (const use of fact.hostRuntimeExportUses) {
if (use.packageName === fact.manifest.name || use.packageName === CORDIS) continue
if (!workspaceNames.has(use.packageName)) continue
if (policy.safeHostDependencyExports[use.specifier]?.includes(use.exportName) === true) continue
if (policy.peerRequiredHostExports[use.specifier]?.includes(use.exportName) === true) continue
violations.push(
`${use.sourcePath}:${String(use.line)}:${String(use.column)}: `
+ `${use.specifier}#${use.exportName} is not classified as safe or peer-required — ${use.sourceLine}`,
)
}
}
return violations.sort()
}
/** Read every package covered by the current dependency policy. */
export function readPackageDependencyState(
root: string,
policy: PackageDependencyPolicy = PACKAGE_DEPENDENCY_POLICY,
): PackageDependencyState {
const packages = readWorkspacePackageManifests(root)
const workspaceNames = new Set(packages.all.map(pkg => pkg.name))
const discovered = discoverPackageDependencyScope(packages.release, policy)
const facts = discovered.selected.map(pkg =>
readPackageDependencyFacts(root, pkg, pkg.role, workspaceNames, policy))
const selectedNames = new Set(facts.map(fact => fact.manifest.name))
return {
facts,
packages: packages.release,
policyViolations: [
...discovered.violations,
...collectHostDependencyExportPolicyViolations(facts, workspaceNames, policy),
...Object.keys(policy.configurationOnlyDevDependencies)
.filter(name => !selectedNames.has(name))
.map(name => `configurationOnlyDevDependencies names unmanaged package ${name}`),
].sort(),
workspaceNames,
}
}
/** Derive the required npm section for each relationship owned by the policy. */
export function expectedPackageDependencies(
facts: PackageDependencyFacts,
): ReadonlyMap<string, ExpectedPackageDependency> {
const expected = new Map<string, { section: ExpectedPackageDependency['section']; origins: Set<string> }>()
const add = (name: string, sectionName: ExpectedPackageDependency['section'], origin: string): void => {
if (name === facts.manifest.name || name === CORDIS) return
const current = expected.get(name)
const section = current?.section === 'peer-dev' || sectionName === 'peer-dev'
? 'peer-dev'
: current?.section === 'dependencies' || sectionName === 'dependencies'
? 'dependencies'
: 'devDependencies'
expected.set(name, { section, origins: new Set([...(current?.origins ?? []), origin]) })
}
expected.set(CORDIS, { section: 'peer-dev', origins: new Set(['shared Cordis runtime']) })
for (const [name, paths] of facts.allSourceUses) {
if (!facts.workspaceNames.has(name)) continue
for (const path of paths) add(name, 'devDependencies', path)
}
for (const name of facts.clientInject) {
if (facts.workspaceNames.has(name)) add(name, 'devDependencies', 'dsh.client.inject')
}
for (const name of facts.configurationOnlyDevDependencies) {
if (facts.workspaceNames.has(name)) add(name, 'devDependencies', 'configured development-only relationship')
}
for (const name of Object.keys(facts.manifest.peerDependencies ?? {})) {
if (name !== CORDIS) add(name, 'devDependencies', 'existing non-Cordis peer')
}
for (const [name, paths] of facts.hostRuntimeSourceUses) {
const expectedSection = facts.workspaceNames.has(name) && facts.peerRequiredHostDependencies.has(name)
? 'peer-dev'
: 'dependencies'
for (const path of paths) add(name, expectedSection, path)
}
return new Map([...expected].map(([name, rule]) => [name, {
section: rule.section,
origins: [...rule.origins].sort(),
}]))
}
interface ManagedRuntimeEdge {
readonly consumer: string
readonly dependency: string
readonly exports: readonly string[]
}
function managedRuntimeEdges(
state: PackageDependencyState,
expectedSection: 'dependencies' | 'peer-dev',
): ManagedRuntimeEdge[] {
return state.facts.flatMap(facts => [...expectedPackageDependencies(facts)]
.filter(([name, rule]) => name !== CORDIS && rule.section === expectedSection)
.map(([dependency]) => ({
consumer: facts.manifest.name ?? facts.manifestPath,
dependency,
exports: [...new Set(facts.hostRuntimeExportUses
.filter(use => use.packageName === dependency)
.map(use => `${use.specifier}#${use.exportName}`))].sort(),
})))
.sort((left, right) =>
left.consumer.localeCompare(right.consumer) || left.dependency.localeCompare(right.dependency))
}
/** Format Host runtime edges whose reviewed exports permit ordinary dependencies. */
export function formatManagedRuntimeDependencies(state: PackageDependencyState): string[] {
const rows = managedRuntimeEdges(state, 'dependencies')
const packages = new Set(rows.map(row => row.consumer)).size
return [
`${GATE}: ${String(rows.length)} managed Host runtime edge(s) remain in dependencies across ${String(packages)} package(s):`,
...rows.map(row => ` ${row.consumer} -> ${row.dependency}: ${row.exports.join(', ')}`),
]
}
/** Format Host runtime edges retained as peers by their imported export classification. */
export function formatPeerRequiredRuntimeDependencies(state: PackageDependencyState): string[] {
const rows = managedRuntimeEdges(state, 'peer-dev')
const packages = new Set(rows.map(row => row.consumer)).size
return [
`${GATE}: ${String(rows.length)} Host runtime edge(s) remain in peerDependencies because their exports require shared identity across ${String(packages)} package(s):`,
...rows.map(row => ` ${row.consumer} -> ${row.dependency}: ${row.exports.join(', ')}`),
]
}
function section(manifest: PackageDependencyManifest, name: DependencySection): Record<string, string> {
return manifest[name] ?? {}
}
function mutableSection(manifest: PackageDependencyManifest, name: DependencySection): Record<string, string> {
manifest[name] ??= {}
return manifest[name]
}
function declaredSections(manifest: PackageDependencyManifest, name: string): DependencySection[] {
return (['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const)
.filter(sectionName => section(manifest, sectionName)[name] !== undefined)
}
function describeSections(sections: readonly DependencySection[]): string {
return sections.length === 0 ? 'no dependency section' : sections.join(' + ')
}
/** Return all manifest and policy violations in stable order. */
export function collectPackageDependencyViolations(state: PackageDependencyState): string[] {
const violations = [...state.policyViolations]
if (violations.length > 0) return [...new Set(violations)].sort()
for (const facts of state.facts) {
for (const [name, rule] of expectedPackageDependencies(facts)) {
const actual = declaredSections(facts.manifest, name)
if (rule.section === 'peer-dev') {
if (actual.length === 2
&& actual.includes('peerDependencies')
&& actual.includes('devDependencies')
&& section(facts.manifest, 'peerDependencies')[name] === WORKSPACE_RANGE
&& section(facts.manifest, 'devDependencies')[name] === WORKSPACE_RANGE
&& facts.manifest.peerDependenciesMeta?.[name] === undefined) continue
violations.push(
`${facts.manifestPath}: ${name} must be matching peerDependencies + devDependencies at ${WORKSPACE_RANGE}; found ${describeSections(actual)}`,
)
continue
}
const expectedSection = rule.section
const range = section(facts.manifest, expectedSection)[name]
if (actual.length === 1
&& actual[0] === expectedSection
&& (!facts.workspaceNames.has(name) || range === WORKSPACE_RANGE)) continue
violations.push(
`${facts.manifestPath}: ${name} (${rule.origins.join(', ')}) must be ${expectedSection}-only`
+ (facts.workspaceNames.has(name) ? ` at ${WORKSPACE_RANGE}` : '')
+ `; found ${describeSections(actual)}`,
)
}
for (const sectionName of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const) {
for (const [name, range] of Object.entries(section(facts.manifest, sectionName))) {
if (!facts.workspaceNames.has(name) || range === WORKSPACE_RANGE) continue
violations.push(`${facts.manifestPath}: ${sectionName}.${name} must use ${WORKSPACE_RANGE}, found ${range}`)
}
}
for (const name of Object.keys(facts.manifest.peerDependenciesMeta ?? {})) {
if (facts.manifest.peerDependencies?.[name] === undefined) {
violations.push(`${facts.manifestPath}: peerDependenciesMeta.${name} has no matching peerDependencies entry`)
}
}
}
return [...new Set(violations)].sort()
}
function deleteDependency(
manifest: PackageDependencyManifest,
sectionName: DependencySection,
name: string,
): void {
const dependencies = manifest[sectionName]
if (dependencies?.[name] === undefined) return
const retained = Object.fromEntries(Object.entries(dependencies).filter(([key]) => key !== name))
if (Object.keys(retained).length > 0) {
manifest[sectionName] = retained
return
}
switch (sectionName) {
case 'dependencies': delete manifest.dependencies; break
case 'devDependencies': delete manifest.devDependencies; break
case 'optionalDependencies': delete manifest.optionalDependencies; break
case 'peerDependencies': delete manifest.peerDependencies; break
}
}
function deletePeerMeta(manifest: PackageDependencyManifest, name: string): void {
if (manifest.peerDependenciesMeta?.[name] === undefined) return
const retained = Object.fromEntries(Object.entries(manifest.peerDependenciesMeta)
.filter(([key]) => key !== name))
if (Object.keys(retained).length > 0) manifest.peerDependenciesMeta = retained
else delete manifest.peerDependenciesMeta
}
function preferredRange(
facts: PackageDependencyFacts,
name: string,
target: ExpectedPackageDependency['section'],
): string | undefined {
if (facts.workspaceNames.has(name)) return WORKSPACE_RANGE
const order: readonly DependencySection[] = target === 'dependencies'
? ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']
: ['devDependencies', 'peerDependencies', 'dependencies', 'optionalDependencies']
return order.map(sectionName => section(facts.manifest, sectionName)[name]).find(value => value !== undefined)
}
/** Apply the dependency policy to one in-memory manifest. */
export function repairPackageDependencyManifest(facts: PackageDependencyFacts): void {
for (const [name, rule] of expectedPackageDependencies(facts)) {
if (rule.section === 'peer-dev') {
for (const sectionName of ['dependencies', 'optionalDependencies'] as const) {
deleteDependency(facts.manifest, sectionName, name)
}
mutableSection(facts.manifest, 'peerDependencies')[name] = WORKSPACE_RANGE
mutableSection(facts.manifest, 'devDependencies')[name] = WORKSPACE_RANGE
deletePeerMeta(facts.manifest, name)
continue
}
const range = preferredRange(facts, name, rule.section)
if (range === undefined) continue
for (const sectionName of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const) {
if (sectionName !== rule.section) deleteDependency(facts.manifest, sectionName, name)
}
mutableSection(facts.manifest, rule.section)[name] = range
deletePeerMeta(facts.manifest, name)
}
for (const name of Object.keys(facts.manifest.peerDependenciesMeta ?? {})) {
if (facts.manifest.peerDependencies?.[name] === undefined) deletePeerMeta(facts.manifest, name)
}
for (const sectionName of ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const) {
for (const name of Object.keys(section(facts.manifest, sectionName))) {
if (facts.workspaceNames.has(name)) mutableSection(facts.manifest, sectionName)[name] = WORKSPACE_RANGE
}
}
}
/** Repair every covered manifest and return repository-relative changed paths. */
export function fixPackageDependencies(root: string, state: PackageDependencyState): string[] {
if (state.policyViolations.length > 0) return []
const changed: string[] = []
for (const facts of state.facts) {
const before = `${JSON.stringify(facts.manifest, null, 2)}\n`
repairPackageDependencyManifest(facts)
const after = `${JSON.stringify(facts.manifest, null, 2)}\n`
if (after === before) continue
writeFileSync(resolve(root, facts.manifestPath), after)
changed.push(facts.manifestPath)
}
return changed.sort()
}
function refreshPnpmLockfile(root: string): void {
const result = spawnSync(
'pnpm',
['install', '--lockfile-only', '--ignore-scripts', '--no-frozen-lockfile'],
{ cwd: root, shell: process.platform === 'win32', stdio: 'inherit' },
)
if (result.error !== undefined) throw new Error(`could not refresh pnpm-lock.yaml: ${result.error.message}`)
if (result.status !== 0) throw new Error(`pnpm lockfile refresh exited with status ${String(result.status)}`)
}
function main(): void {
const root = resolve(import.meta.dirname, '..')
let state = readPackageDependencyState(root)
const fix = process.argv.includes('--fix')
if (fix) {
if (state.policyViolations.length > 0) {
console.error(`${GATE}: --fix skipped because dependency policy review failed.`)
} else {
const changed = fixPackageDependencies(root, state)
console.log(`${GATE}: fixed ${String(changed.length)} manifest(s).`)
refreshPnpmLockfile(root)
const graphChanges = writeModuleGraph(root)
console.log(
`${GATE}: refreshed pnpm-lock.yaml and wrote ${String(graphChanges.length)} module-graph artifact(s).`,
)
state = readPackageDependencyState(root)
}
}
const violations = collectPackageDependencyViolations(state)
if (violations.length > 0) {
console.error(`${GATE}: ${String(violations.length)} violation(s):`)
for (const violation of violations) console.error(` ${violation}`)
process.exitCode = 1
return
}
const roles = Object.groupBy(state.facts, fact => fact.role)
console.log(
`${GATE}: ${String(state.facts.length)} package(s) match the published dependency policy`
+ ` (${String(roles['client-only']?.length ?? 0)} Client-only,`
+ ` ${String(roles['client-host']?.length ?? 0)} Client/Host,`
+ ` ${String(roles['configured-host']?.length ?? 0)} configured Host).`,
)
if (fix) {
for (const line of formatManagedRuntimeDependencies(state)) console.log(line)
for (const line of formatPeerRequiredRuntimeDependencies(state)) console.log(line)
}
}
if (import.meta.main) main()