Browse Source

perf(infra): map each workspace package to an explicit path alias

tsconfig.base.json resolved @deepseek-ai/dsh-* through 49 candidate
globs and @deepseek-ai/dsh-*/invariant through 45, one per package
group. Resolution tries candidates in order, so a package late in the
list paid for every earlier miss — and under the dsh source launch each
miss is an ERR_MODULE_NOT_FOUND that Node decorates with a full
CommonJS resolution walk. A boot profile attributed 934.6 ms, 35% of
startup, to that decoration path across 60,942 failed resolutions. The
cost landed hardest on packages/util/*, which sits at position 44 of 49
and holds the leaf utilities nearly every plugin imports.

gen-tsconfig-paths writes one explicit alias per package into a marked
region and both group wildcards are gone; verify-tsconfig-paths reports
drift and runs in the ci-static lane. Booting the headless profile from
source drops from ~2,157 ms to ~1,055 ms with --help output unchanged.

All 1,022 dsh specifiers in repository sources resolve to the same
target as before, except seven /invariant specifiers in the lsp,
terminal, and runtime-diagnostics groups that the deleted wildcard
never listed: those reached built lib/types instead of src, against the
rule that static gates resolve through paths to src on a clean tree.
Yichen Jiang 1 month ago
parent
commit
12c161e1a7

+ 6 - 0
.agents/notes/implemented/process/2026-08-27-explicit-workspace-path-aliases.i18n.yaml

@@ -0,0 +1,6 @@
+# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
+# side as of the last confirmed-consistent state. Both languages carry equal authority;
+# after editing either side, bring the other along and re-record with:
+#   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-27-explicit-workspace-path-aliases.md
+2026-08-27-explicit-workspace-path-aliases.md: 4cd97062fa56b5b8730101e81d20b33b206ea670
+2026-08-27-explicit-workspace-path-aliases.zh.md: 003f04077837b055a50996edb49cdc25ccd8f73a

+ 53 - 0
.agents/notes/implemented/process/2026-08-27-explicit-workspace-path-aliases.md

@@ -0,0 +1,53 @@
+# Agent Note: Explicit workspace path aliases replace per-group wildcards
+
+Status: implemented
+
+English | [中文](2026-08-27-explicit-workspace-path-aliases.zh.md)
+
+## Problem
+
+`tsconfig.base.json` is the resolution facade for the whole repository: every package project extends it, both aggregates read it, and every Vitest config points `vite-tsconfig-paths` at it. Two of its aliases carried one candidate per package *group* rather than one per package — `@deepseek-ai/dsh-*` listed 49 candidate globs and `@deepseek-ai/dsh-*/invariant` listed 45.
+
+TypeScript and tsx try those candidates in order and take the first that exists, so a specifier whose package sits late in the list pays for every earlier miss. Under the `dsh` source launch each miss is an `ERR_MODULE_NOT_FOUND` that Node decorates with `decorateErrorWithCommonJSHints`, which runs a full CommonJS resolution walk per failure. A profile of a source-launch boot attributed 934.6 ms — 35% of the boot — to that decoration path alone, from 60,942 failed resolutions.
+
+The cost fell hardest on the most-imported packages. `packages/util/*` sat at position 44 of 49 and holds the leaf utilities nearly every plugin imports, so `dsh-timeout` paid roughly 9 ms per resolution against 0.05 ms for a specifier with an explicit alias.
+
+## Decision
+
+`scripts/gen-tsconfig-paths.ts` writes one explicit alias per workspace package into a marked region at the end of `paths`, and both group wildcards are deleted. `pnpm run gen-tsconfig-paths` rewrites the region; `pnpm run verify-tsconfig-paths` reports drift instead, and runs in the `ci-static` lane beside the other generated-artifact checks.
+
+The generator emits an alias only for a package whose declared name is exactly `@deepseek-ai/dsh-<directory>`, because that is the only shape a wildcard could ever have resolved: it substituted the specifier's suffix into `packages/<group>/<suffix>/src`. Packages named after something other than their directory — `@deepseek-ai/dsh-typert-protocol` at `packages/typert/protocol`, the `dsh-client-*` and `dsh-host-*` families — already carry hand-written aliases and are left alone. A specifier claimed by two package directories throws rather than picking one, because an explicit map cannot express the group-order tiebreak the wildcard used; no such collision exists today.
+
+The region is written by text surgery between marker comments rather than by re-serializing the file. `tsconfig.base.json` is JSONC and its hand-written aliases carry comments that explain non-obvious mappings; re-serializing would drop them.
+
+Four wildcards remain, each with a single candidate: `dsh-host-*/invariant`, `dsh-client-*/invariant`, `dsh-client-*/client`, and the five `dsh-host-<name>/*` subpath maps. One candidate costs one probe, so expanding them would trade file size for nothing.
+
+## Resolution differences this change makes
+
+Every `@deepseek-ai/dsh-*` specifier appearing in repository sources — 1,022 distinct — resolves to the same target as before, with seven exceptions that now resolve where they previously did not:
+
+`dsh-invariants/invariant`, `dsh-lsp/invariant`, `dsh-lsp-stdio/invariant`, `dsh-tool-lsp/invariant`, `dsh-terminal/invariant`, `dsh-terminal-bash/invariant`, and `dsh-tool-terminal/invariant`.
+
+The deleted `dsh-*/invariant` wildcard omitted the `lsp`, `terminal`, `client`, and `host` groups. The `client` and `host` omissions are deliberate and documented — those families have dedicated wildcards because their package names prefix the group directory. The `lsp` and `terminal` omissions have no such reason, and `packages/runtime-diagnostics/invariants` appeared in neither list. Those seven specifiers therefore resolved through the workspace symlink and the package's `./invariant` export to built `lib/types/*.d.ts` instead of to source, which contradicts the rule that static gates resolve workspace imports through `paths` to `src` and pass on a clean tree. Making the aliases uniform resolves them to source like every sibling.
+
+## Testing
+
+`scripts/gen-tsconfig-paths.spec.ts` pins that the collector maps a package to its own directory, skips packages carrying hand-written aliases, and returns a sorted list; that the renderer yields to a hand-written specifier and closes the region without a trailing comma; that the region writer replaces only the marked span and refuses a config without markers; and that neither group wildcard survives in the committed config.
+
+The gate's rejection path is exercised directly: deleting one generated alias makes `verify-tsconfig-paths` exit non-zero, and restoring it makes the check pass.
+
+## Alternatives considered
+
+**Reordering the wildcard's candidate globs so the hottest groups come first.** This needs no generator and no new gate, and recovers perhaps half the cost by moving `util`, `core`, `llm`, and `session` to the front. It was rejected because the win decays as packages are added, the ordering has no invariant a reader could check, and every group after the first still pays. It also leaves the worst property intact: adding a package group silently slows every boot.
+
+**Moving `paths` into a generated `tsconfig.paths.json` that the base config extends.** This keeps the generated content out of the hand-written file entirely and produces a cleaner diff. It was rejected for this change because several consumers read `tsconfig.base.json` directly rather than through a resolver that follows `extends` — six Vitest configs plus `project-reference-faces.ts`, `verify-export-jsdoc.ts`, `doc-typecheck.ts`, and `rescope-vendor.ts` — and auditing each is a larger change than the aliasing itself. The marker region achieves the same isolation with no consumer risk.
+
+**Keeping the wildcards as a fallback beneath the explicit aliases.** An explicit alias already wins over a wildcard, so correctness would be unchanged, and a package added without regenerating would keep resolving. It was rejected because the fallback is exactly what makes a stale config invisible: the repository's stance is that misconfiguration fails loud, and the `--check` gate turns a missing alias into a named failure instead of a slow boot nobody attributes.
+
+## Consequences
+
+A source-launch boot of the `headless` profile drops from a 2,157/2,182/2,153 ms baseline to 1,069/1,052/1,055 ms — about 1.1 seconds, or 51%, with the two ranges nowhere near overlapping and `--help` output byte-identical.
+
+The win is confined to the tsx source launch. Vitest resolves through `vite-tsconfig-paths`, which matches in-process and checks file existence without ever constructing a Node module error, so it never paid the decoration cost: an A/B over one package's suite measured 5,934/5,829/5,908 ms against 5,878/5,851/5,905 ms, which is noise. Repository gate scripts import few `@deepseek-ai/dsh-*` packages and likewise show no separable difference. Shipped users run built `lib/` under plain Node and were never affected.
+
+`paths` grows from 188 keys to 519, and adding a package now requires running the generator. The `--check` gate makes that a named failure rather than a silent one, and the generated region keeps the diff of such a change to a single line.

+ 53 - 0
.agents/notes/implemented/process/2026-08-27-explicit-workspace-path-aliases.zh.md

@@ -0,0 +1,53 @@
+# Agent Note: Explicit workspace path aliases replace per-group wildcards
+
+Status: implemented
+
+[English](2026-08-27-explicit-workspace-path-aliases.md) | 中文
+
+## Problem
+
+`tsconfig.base.json` 是整个仓库的解析门面:每个包的 project 都 extends 它,两个聚合配置都读它,每个 Vitest 配置都把 `vite-tsconfig-paths` 指向它。其中两条别名按包**分组**而不是按包各写一条候选——`@deepseek-ai/dsh-*` 列了 49 个候选 glob,`@deepseek-ai/dsh-*/invariant` 列了 45 个。
+
+TypeScript 与 tsx 按顺序逐个尝试这些候选、取第一个存在的,因此一个位于列表靠后位置的包,其说明符要为前面每一次未命中买单。在 `dsh` 源码启动下,每次未命中都是一个 `ERR_MODULE_NOT_FOUND`,而 Node 会用 `decorateErrorWithCommonJSHints` 装饰它——每次失败都跑一遍完整的 CommonJS 解析走查。一次源码启动的 profile 把 **934.6 ms(占启动 35%)**单独归给了这条装饰路径,来源是 60,942 次失败解析。
+
+代价最重的恰好落在被引用最多的包上:`packages/util/*` 排在 49 个候选里的第 44 位,却装着几乎每个插件都要引用的叶子工具,因此 `dsh-timeout` 每次解析约付 9 ms,而一个有显式别名的说明符只要 0.05 ms。
+
+## Decision
+
+`scripts/gen-tsconfig-paths.ts` 在 `paths` 末尾一个带标记的区域里,为每个 workspace 包写入一条显式别名,两条分组通配符随之删除。`pnpm run gen-tsconfig-paths` 重写该区域;`pnpm run verify-tsconfig-paths` 只报告漂移,并与其他生成物检查一起跑在 `ci-static` 车道上。
+
+生成器只为**声明名恰好等于 `@deepseek-ai/dsh-<目录名>`** 的包发别名,因为那是通配符唯一可能解析出的形态:它把说明符的后缀代入 `packages/<group>/<后缀>/src`。名字与目录不一致的包——`packages/typert/protocol` 上的 `@deepseek-ai/dsh-typert-protocol`、以及 `dsh-client-*` 与 `dsh-host-*` 两族——本来就有手写别名,保持不动。若某个说明符被两个包目录同时认领,生成器**抛错**而不是任选其一,因为显式映射无法表达通配符依赖的分组顺序裁决;当前不存在这种冲突。
+
+该区域用标记注释之间的**定点文本改写**生成,而不是重新序列化整个文件。`tsconfig.base.json` 是 JSONC,其手写别名带有解释非显然映射的注释,重新序列化会把它们丢掉。
+
+保留四条通配符,每条只有一个候选:`dsh-host-*/invariant`、`dsh-client-*/invariant`、`dsh-client-*/client`,以及五条 `dsh-host-<name>/*` 子路径映射。一个候选只花一次探测,展开它们只会换来文件变大而无收益。
+
+## Resolution differences this change makes
+
+仓库源码中出现的每个 `@deepseek-ai/dsh-*` 说明符——共 1,022 个互不相同——解析目标与改动前完全一致,只有七个例外:它们现在能解析,而此前不能。
+
+`dsh-invariants/invariant`、`dsh-lsp/invariant`、`dsh-lsp-stdio/invariant`、`dsh-tool-lsp/invariant`、`dsh-terminal/invariant`、`dsh-terminal-bash/invariant`、`dsh-tool-terminal/invariant`。
+
+被删除的 `dsh-*/invariant` 通配符遗漏了 `lsp`、`terminal`、`client`、`host` 四个分组。其中 `client` 与 `host` 的遗漏是**刻意且有文档的**——这两族有专用通配符,因为它们的包名以分组目录名为前缀。而 `lsp` 与 `terminal` 的遗漏没有任何这类理由,`packages/runtime-diagnostics/invariants` 则两条列表都不在。于是这七个说明符此前是通过 workspace 软链与包的 `./invariant` 导出解析到构建产物 `lib/types/*.d.ts`,而不是解析到源码——这与「静态门禁通过 `paths` 把 workspace 导入解析到 `src`、并在干净树上通过」的规则相抵触。把别名统一之后,它们与所有同类一样解析到源码。
+
+## Testing
+
+`scripts/gen-tsconfig-paths.spec.ts` 钉住:收集器把包映射到它自己的目录、跳过带手写别名的包、返回有序列表;渲染器让位于手写说明符,且区域收尾不带多余逗号;区域写入器只替换标记范围,并在缺少标记时拒绝;以及提交后的配置里两条分组通配符都不复存在。
+
+门禁的拒绝路径被直接验证过:删掉一条生成的别名会让 `verify-tsconfig-paths` 以非零码退出,恢复后检查通过。
+
+## Alternatives considered
+
+**给通配符的候选 glob 重新排序,把最热的分组放前面。** 这不需要生成器也不需要新门禁,把 `util`、`core`、`llm`、`session` 挪到前面大约能拿回一半收益。否决理由:收益随着包的增加而衰减,这个顺序没有任何读者可核验的不变量,而且第一个分组之后的每一组仍然要付钱。它还保留了最糟的性质——新增一个包分组会悄悄拖慢所有人的启动。
+
+**把 `paths` 挪进一个生成的 `tsconfig.paths.json`,由 base 配置 `extends`。** 这能把生成内容彻底移出手写文件,diff 也更干净。本次否决的理由是:有若干消费者**直接读** `tsconfig.base.json`,而不是通过会跟随 `extends` 的解析器——六个 Vitest 配置,外加 `project-reference-faces.ts`、`verify-export-jsdoc.ts`、`doc-typecheck.ts`、`rescope-vendor.ts`——逐个审计它们比这次别名改造本身还大。标记区域用零消费者风险达成了同样的隔离。
+
+**在显式别名下方保留通配符作为兜底。** 显式别名本来就优先于通配符,因此正确性不变,而且新增包忘了重新生成也仍能解析。否决理由:兜底恰恰是让陈旧配置隐形的原因——仓库的立场是「misconfiguration fails loud」,而 `--check` 门禁把缺失的别名变成一次具名失败,而不是一次没人归因的慢启动。
+
+## Consequences
+
+`headless` profile 的源码启动从 2,157 / 2,182 / 2,153 ms 的基线降到 1,069 / 1,052 / 1,055 ms——约 **1.1 秒,51%**,两个区间相距极远,且 `--help` 输出逐字节一致。
+
+**收益仅限于 tsx 源码启动这一条路径。** Vitest 走 `vite-tsconfig-paths`,它在进程内做匹配与文件存在性检查,从不构造 Node 模块错误,因此从未付过那笔装饰代价:对某个包的整套用例做 A/B,实测 5,934 / 5,829 / 5,908 ms 对 5,878 / 5,851 / 5,905 ms,属于噪声。仓库的门禁脚本只 import 少数几个 `@deepseek-ai/dsh-*` 包,同样测不出可分离的差异。发布用户在裸 Node 下跑构建好的 `lib/`,本来就不受影响。
+
+`paths` 从 188 个 key 增长到 519 个,新增包时必须运行生成器。`--check` 门禁把这件事变成一次具名失败而非静默失败,而生成区域让这类改动的 diff 只有一行。

+ 2 - 0
package.json

@@ -112,6 +112,8 @@
     "rescope-vendor": "tsx scripts/rescope-vendor.ts",
     "rescope-vendor:check": "tsx scripts/rescope-vendor.ts --check",
     "verify-client-domain-graph": "tsx scripts/verify-client-domain-graph.ts",
+    "gen-tsconfig-paths": "tsx scripts/gen-tsconfig-paths.ts",
+    "verify-tsconfig-paths": "tsx scripts/gen-tsconfig-paths.ts --check",
     "gen-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts",
     "verify-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts --check",
     "gen-cordis-api": "tsx scripts/gen-cordis-api.ts",

+ 69 - 0
scripts/gen-tsconfig-paths.spec.ts

@@ -0,0 +1,69 @@
+import { readFileSync } from 'node:fs'
+import { resolve } from 'node:path'
+import { describe, expect, it } from 'vitest'
+import { collectPackageAliases, renderAliases, writeRegion } from './gen-tsconfig-paths.ts'
+
+const root = resolve(import.meta.dirname, '..')
+
+describe('generated tsconfig package aliases', () => {
+  it('maps each package to its own source directory', () => {
+    const aliases = collectPackageAliases()
+    expect(aliases.length).toBeGreaterThan(100)
+    const session = aliases.find(alias => alias.specifier === '@deepseek-ai/dsh-session')
+    expect(session).toEqual({
+      specifier: '@deepseek-ai/dsh-session',
+      source: './packages/core/session/src',
+      hasInvariant: true,
+    })
+    // Sorted, so a package added anywhere lands in a stable spot in the diff.
+    expect([...aliases].sort((a, b) => a.specifier.localeCompare(b.specifier))).toEqual(aliases)
+    // Only packages named after their directory: the rest carry hand-written
+    // aliases, because the removed wildcards could never have resolved them.
+    expect(aliases.some(alias => alias.specifier === '@deepseek-ai/dsh-typert-protocol')).toBe(false)
+  })
+
+  it('yields to a hand-written alias and closes without a trailing comma', () => {
+    const aliases = [
+      { specifier: '@deepseek-ai/dsh-a', source: './packages/g/a/src', hasInvariant: true },
+      { specifier: '@deepseek-ai/dsh-b', source: './packages/g/b/src', hasInvariant: false },
+    ]
+    const body = renderAliases(aliases, new Set(['@deepseek-ai/dsh-a']))
+
+    // The hand-written bare alias is skipped; its /invariant sibling is not.
+    expect(body).toBe([
+      '      "@deepseek-ai/dsh-a/invariant": ["./packages/g/a/src/invariant.ts"]',
+      '      "@deepseek-ai/dsh-b": ["./packages/g/b/src"]',
+    ].join(',\n'))
+    expect(body.endsWith(',')).toBe(false)
+  })
+
+  it('replaces only the marked region', () => {
+    const text = [
+      '{ "before": 1,',
+      '      // BEGIN generated package aliases — pnpm run gen-tsconfig-paths',
+      '      "stale": ["gone"]',
+      '      // END generated package aliases',
+      '  "after": 2 }',
+    ].join('\n')
+
+    const next = writeRegion(text, '      "fresh": ["kept"]')
+
+    expect(next).toContain('{ "before": 1,')
+    expect(next).toContain('  "after": 2 }')
+    expect(next).toContain('"fresh": ["kept"]')
+    expect(next).not.toContain('stale')
+  })
+
+  it('refuses a config without the region markers', () => {
+    expect(() => writeRegion('{}', '')).toThrow('missing the generated-region markers')
+  })
+
+  it('leaves no wildcard that probes every package group', () => {
+    const config = readFileSync(resolve(root, 'tsconfig.base.json'), 'utf8')
+    // These two listed one candidate per group, so resolving a package late in
+    // the list cost a filesystem probe — and under tsx a decorated module
+    // error — for every group before it.
+    expect(config).not.toContain('"@deepseek-ai/dsh-*":')
+    expect(config).not.toContain('"@deepseek-ai/dsh-*/invariant":')
+  })
+})

+ 171 - 0
scripts/gen-tsconfig-paths.ts

@@ -0,0 +1,171 @@
+/**
+ * Expand the workspace path aliases that a wildcard would otherwise resolve by
+ * probing every package group in turn.
+ *
+ * `tsconfig.base.json` is the resolution facade for the whole repository, and
+ * two of its aliases used one key per *group* rather than per package:
+ * `@deepseek-ai/dsh-*` listed 49 candidate globs and `@deepseek-ai/dsh-*\/invariant`
+ * listed 45. TypeScript and tsx try those candidates in order, so a specifier
+ * whose package sits late in the list pays for every earlier miss. Under tsx's
+ * ESM hook each miss is an `ERR_MODULE_NOT_FOUND` that Node decorates with a
+ * full CommonJS resolution walk, which dominated source-launch boot.
+ *
+ * This generator writes one explicit entry per package into a marked region of
+ * `tsconfig.base.json`, leaving every hand-written alias and comment outside
+ * that region untouched. `--check` reports drift instead of writing, so a new
+ * package that needs an alias fails a gate rather than silently resolving
+ * through a fallback that no longer exists.
+ *
+ * @module scripts/gen-tsconfig-paths
+ */
+
+import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs'
+import { join } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const ROOT = fileURLToPath(new URL('..', import.meta.url))
+const CONFIG = join(ROOT, 'tsconfig.base.json')
+const BEGIN = '      // BEGIN generated package aliases — pnpm run gen-tsconfig-paths'
+const END = '      // END generated package aliases'
+
+/** Package-name prefix the expanded aliases cover. */
+const PREFIX = '@deepseek-ai/dsh-'
+
+/** One workspace package the generated region maps. */
+interface PackageAlias {
+  /** Bare specifier, e.g. `@deepseek-ai/dsh-session`. */
+  readonly specifier: string
+  /** Repository-relative source directory, e.g. `./packages/session/session/src`. */
+  readonly source: string
+  /** Whether the package carries `src/invariant.ts`, which earns a second alias. */
+  readonly hasInvariant: boolean
+}
+
+/**
+ * Read a workspace manifest's declared name.
+ * @param manifest - absolute path to a `package.json`.
+ * @returns The declared name, or undefined when the file is absent or nameless.
+ */
+function packageName(manifest: string): string | undefined {
+  let parsed: unknown
+  try {
+    parsed = JSON.parse(readFileSync(manifest, 'utf8'))
+  } catch (_absentOrUnreadableManifest) {
+    return undefined
+  }
+  if (typeof parsed !== 'object' || parsed === null) return undefined
+  const name: unknown = (parsed as { name?: unknown }).name
+  return typeof name === 'string' ? name : undefined
+}
+
+/**
+ * Collect every package the removed wildcards could resolve.
+ *
+ * A wildcard substituted the specifier's suffix into `packages/<group>/<suffix>/src`,
+ * so it only ever resolved a package whose declared name is exactly
+ * `@deepseek-ai/dsh-<directory>`. Packages named after something other than
+ * their directory already carry a hand-written alias and are skipped here.
+ *
+ * @returns Aliases sorted by specifier.
+ * @throws When two package directories claim one specifier, which the removed
+ * wildcards resolved by group order and an explicit map cannot express.
+ */
+export function collectPackageAliases(): PackageAlias[] {
+  const packages = join(ROOT, 'packages')
+  const bySpecifier = new Map<string, PackageAlias & { directory: string }>()
+  for (const group of readdirSync(packages).sort()) {
+    const groupDir = join(packages, group)
+    if (!statSync(groupDir).isDirectory()) continue
+    for (const directory of readdirSync(groupDir).sort()) {
+      const packageDir = join(groupDir, directory)
+      const name = packageName(join(packageDir, 'package.json'))
+      if (name === undefined || name !== `${PREFIX}${directory}`) continue
+      if (!existsSync(join(packageDir, 'src'))) continue
+      const previous = bySpecifier.get(name)
+      if (previous !== undefined) {
+        throw new Error(
+          `gen-tsconfig-paths: ${name} is claimed by packages/${previous.directory} and packages/${group}/${directory}; `
+          + 'an explicit alias cannot express the group-order tiebreak the wildcard used.',
+        )
+      }
+      bySpecifier.set(name, {
+        specifier: name,
+        source: `./packages/${group}/${directory}/src`,
+        hasInvariant: existsSync(join(packageDir, 'src', 'invariant.ts')),
+        directory: `${group}/${directory}`,
+      })
+    }
+  }
+  return [...bySpecifier.values()]
+    .map(({ specifier, source, hasInvariant }) => ({ specifier, source, hasInvariant }))
+    .sort((left, right) => left.specifier.localeCompare(right.specifier))
+}
+
+/**
+ * Render the generated region's alias lines.
+ * @param aliases - packages to map, in emission order.
+ * @param handWritten - specifiers already mapped outside the region; a duplicate key would shadow one silently.
+ * @returns The region body, one JSON member per line.
+ */
+export function renderAliases(aliases: readonly PackageAlias[], handWritten: ReadonlySet<string>): string {
+  const lines: string[] = []
+  for (const alias of aliases) {
+    if (!handWritten.has(alias.specifier)) {
+      lines.push(`      ${JSON.stringify(alias.specifier)}: [${JSON.stringify(alias.source)}]`)
+    }
+    const invariant = `${alias.specifier}/invariant`
+    if (alias.hasInvariant && !handWritten.has(invariant)) {
+      lines.push(`      ${JSON.stringify(invariant)}: [${JSON.stringify(`${alias.source}/invariant.ts`)}]`)
+    }
+  }
+  // The region closes `paths`, so the last member carries no trailing comma.
+  return lines.join(',\n')
+}
+
+/**
+ * Replace the generated region of a config's text.
+ * @param text - current `tsconfig.base.json` contents.
+ * @param body - rendered alias lines.
+ * @returns The updated contents.
+ * @throws When the markers are missing or out of order.
+ */
+export function writeRegion(text: string, body: string): string {
+  const begin = text.indexOf(BEGIN)
+  const end = text.indexOf(END)
+  if (begin < 0 || end < begin) {
+    throw new Error(`gen-tsconfig-paths: ${CONFIG} is missing the generated-region markers.`)
+  }
+  return `${text.slice(0, begin)}${BEGIN}\n${body}\n${END}${text.slice(end + END.length)}`
+}
+
+/**
+ * Parse the config's `paths` keys, ignoring the generated region.
+ * @param text - current `tsconfig.base.json` contents.
+ * @returns Specifiers mapped by hand.
+ */
+function handWrittenSpecifiers(text: string): Set<string> {
+  const begin = text.indexOf(BEGIN)
+  const end = text.indexOf(END)
+  const outside = begin < 0 || end < begin ? text : text.slice(0, begin) + text.slice(end)
+  const keys = new Set<string>()
+  for (const match of outside.matchAll(/^\s*"(@deepseek-ai\/[^"]+)":/gm)) {
+    const key = match[1]
+    if (key !== undefined) keys.add(key)
+  }
+  return keys
+}
+
+if (import.meta.url === `file://${process.argv[1] ?? ''}`) {
+  const check = process.argv.includes('--check')
+  const current = readFileSync(CONFIG, 'utf8')
+  const next = writeRegion(current, renderAliases(collectPackageAliases(), handWrittenSpecifiers(current)))
+  if (current === next) {
+    console.log('gen-tsconfig-paths: tsconfig.base.json package aliases are current.')
+  } else if (check) {
+    console.error('gen-tsconfig-paths: tsconfig.base.json is stale; run `pnpm run gen-tsconfig-paths`.')
+    process.exitCode = 1
+  } else {
+    writeFileSync(CONFIG, next)
+    console.log('gen-tsconfig-paths: rewrote tsconfig.base.json package aliases.')
+  }
+}

+ 1 - 0
scripts/run-gates.ts

@@ -720,6 +720,7 @@ function docSyncLeafGates(options: {
     pnpmScript('doc-refs', 'verify-doc-refs', { label: 'doc refs', quick: true }),
     pnpmScript('subsystem-pages', 'verify-subsystem-pages', { label: 'subsystem pages' }),
     pnpmScript('package-paths', 'verify-package-paths', { label: 'package paths' }),
+    pnpmScript('tsconfig-paths', 'verify-tsconfig-paths', { label: 'tsconfig paths' }),
     pnpmScript('config-source-ownership', 'verify-config-source-ownership', { label: 'config source ownership' }),
     pnpmScript('package-readme-model-experience', 'verify-package-readme-model-experience', { label: 'package README model experience', quick: true }),
     pnpmScript('agent-note-classification', 'verify-agent-note-classification', { label: 'agent note classification', quick: true }),

+ 335 - 104
tsconfig.base.json

@@ -126,53 +126,6 @@
       "@deepseek-ai/dsh-experimental-webworker-runtime/invariant": ["./packages/experimental/webworker-runtime/src/invariant.ts"],
       "@deepseek-ai/dsh-experimental-webworker-packer/invariant": ["./packages/experimental/webworker-packer/src/invariant.ts"],
       "@deepseek-ai/dsh-util-crypto/invariant": ["./packages/util/crypto/src/invariant.ts"],
-      "@deepseek-ai/dsh-*/invariant": [
-        "./packages/core/*/src/invariant.ts",
-        "./packages/prompt/*/src/invariant.ts",
-        "./packages/llm/*/src/invariant.ts",
-        "./packages/shell/*/src/invariant.ts",
-        "./packages/subprocess/*/src/invariant.ts",
-        "./packages/e2b/*/src/invariant.ts",
-        "./packages/code-runtime/*/src/invariant.ts",
-        "./packages/fs/*/src/invariant.ts",
-        "./packages/skill/*/src/invariant.ts",
-        "./packages/compaction/*/src/invariant.ts",
-        "./packages/context/*/src/invariant.ts",
-        "./packages/goal/*/src/invariant.ts",
-        "./packages/feedback/*/src/invariant.ts",
-        "./packages/schedule/*/src/invariant.ts",
-        "./packages/guard/*/src/invariant.ts",
-        "./packages/plan/*/src/invariant.ts",
-        "./packages/preset/*/src/invariant.ts",
-        "./packages/subagent/*/src/invariant.ts",
-        "./packages/jobs/*/src/invariant.ts",
-        "./packages/experimental/*/src/invariant.ts",
-        "./packages/workflow/*/src/invariant.ts",
-        "./packages/webhook/*/src/invariant.ts",
-        "./packages/web/*/src/invariant.ts",
-        "./packages/attachment/*/src/invariant.ts",
-        "./packages/spill/*/src/invariant.ts",
-        "./packages/todo/*/src/invariant.ts",
-        "./packages/bundle/*/src/invariant.ts",
-        "./packages/extensions/*/src/invariant.ts",
-        "./packages/sandbox/*/src/invariant.ts",
-        "./packages/hooks/*/src/invariant.ts",
-        "./packages/session/*/src/invariant.ts",
-        "./packages/session-query/*/src/invariant.ts",
-        "./packages/settings/*/src/invariant.ts",
-        "./packages/credentials/*/src/invariant.ts",
-        "./packages/acp/*/src/invariant.ts",
-        "./packages/storage/*/src/invariant.ts",
-        "./packages/workspace/*/src/invariant.ts",
-        "./packages/sdk/*/src/invariant.ts",
-        "./packages/interaction/*/src/invariant.ts",
-        "./packages/boot/*/src/invariant.ts",
-        "./packages/examples/*/src/invariant.ts",
-        "./packages/util/*/src/invariant.ts",
-        "./packages/mcp/*/src/invariant.ts",
-        "./packages/identity/*/src/invariant.ts",
-        "./packages/test-support/*/src/invariant.ts"
-      ],
       // host/client package names prefix the group dir (dsh-client-<dir>), so
       // the generic dsh-*/invariant list above cannot map them; strip the
       // group prefix with a dedicated wildcard per group instead.
@@ -181,12 +134,6 @@
       "@deepseek-ai/dsh-headless/startup": ["./packages/bundle/headless/src/startup.ts"],
       "@deepseek-ai/dsh-web-app/startup": ["./packages/bundle/web-app/src/startup.ts"],
       "@deepseek-ai/dsh-client-*/client": ["./packages/client/*/src/client"],
-       // One wildcard maps every @deepseek-ai/dsh-<name> to its source. Package
-      // dir names are unique across groups, so first-on-disk-wins resolution is
-      // unambiguous; adding a package under an existing group needs no edit
-      // here. The aggregates' project references (tsconfig.host.json /
-      // tsconfig.client.json) stay explicit — TS project references have no
-      // wildcard form.
       "@deepseek-ai/dsh-host-apiproxy": ["./packages/host/apiproxy/src"],
       "@deepseek-ai/dsh-host-directory-picker": ["./packages/host/directory-picker/src"],
       "@deepseek-ai/dsh-host-directory-picker/*": ["./packages/host/directory-picker/src/*"],
@@ -272,57 +219,341 @@
       "@deepseek-ai/dsh-experimental-webworker-runtime": ["./packages/experimental/webworker-runtime/src"],
       "@deepseek-ai/dsh-experimental-webworker-packer": ["./packages/experimental/webworker-packer/src"],
       "@deepseek-ai/dsh-util-crypto": ["./packages/util/crypto/src"],
-      "@deepseek-ai/dsh-*": [
-        "./packages/core/*/src",
-        "./packages/prompt/*/src",
-        "./packages/llm/*/src",
-        "./packages/shell/*/src",
-        "./packages/terminal/*/src",
-        "./packages/subprocess/*/src",
-        "./packages/e2b/*/src",
-        "./packages/code-runtime/*/src",
-        "./packages/fs/*/src",
-        "./packages/lsp/*/src",
-        "./packages/skill/*/src",
-        "./packages/compaction/*/src",
-        "./packages/context/*/src",
-        "./packages/goal/*/src",
-        "./packages/feedback/*/src",
-        "./packages/schedule/*/src",
-        "./packages/guard/*/src",
-        "./packages/plan/*/src",
-        "./packages/preset/*/src",
-        "./packages/subagent/*/src",
-        "./packages/jobs/*/src",
-        "./packages/experimental/*/src",
-        "./packages/workflow/*/src",
-        "./packages/webhook/*/src",
-        "./packages/web/*/src",
-        "./packages/attachment/*/src",
-        "./packages/spill/*/src",
-        "./packages/todo/*/src",
-        "./packages/bundle/*/src",
-        "./packages/extensions/*/src",
-        "./packages/sandbox/*/src",
-        "./packages/hooks/*/src",
-        "./packages/session/*/src",
-        "./packages/session-query/*/src",
-        "./packages/settings/*/src",
-        "./packages/credentials/*/src",
-        "./packages/acp/*/src",
-        "./packages/storage/*/src",
-        "./packages/workspace/*/src",
-        "./packages/sdk/*/src",
-        "./packages/interaction/*/src",
-        "./packages/boot/*/src",
-        "./packages/examples/*/src",
-        "./packages/util/*/src",
-        "./packages/mcp/*/src",
-        "./packages/identity/*/src",
-        "./packages/test-support/*/src",
-        "./packages/host/*/src",
-        "./packages/client/*/src"
-      ]
+      // BEGIN generated package aliases — pnpm run gen-tsconfig-paths
+      "@deepseek-ai/dsh-acp": ["./packages/acp/acp/src"],
+      "@deepseek-ai/dsh-acp/invariant": ["./packages/acp/acp/src/invariant.ts"],
+      "@deepseek-ai/dsh-acp-app": ["./packages/bundle/acp-app/src"],
+      "@deepseek-ai/dsh-acp-app/invariant": ["./packages/bundle/acp-app/src/invariant.ts"],
+      "@deepseek-ai/dsh-agent": ["./packages/core/agent/src"],
+      "@deepseek-ai/dsh-agent-default-model": ["./packages/core/agent-default-model/src"],
+      "@deepseek-ai/dsh-agent-default-model/invariant": ["./packages/core/agent-default-model/src/invariant.ts"],
+      "@deepseek-ai/dsh-agent-instructions": ["./packages/context/agent-instructions/src"],
+      "@deepseek-ai/dsh-agent-instructions/invariant": ["./packages/context/agent-instructions/src/invariant.ts"],
+      "@deepseek-ai/dsh-agent-loop": ["./packages/core/agent-loop/src"],
+      "@deepseek-ai/dsh-agent-loop-testkit": ["./packages/test-support/agent-loop-testkit/src"],
+      "@deepseek-ai/dsh-agent-loop-testkit/invariant": ["./packages/test-support/agent-loop-testkit/src/invariant.ts"],
+      "@deepseek-ai/dsh-agent-presets": ["./packages/preset/agent-presets/src"],
+      "@deepseek-ai/dsh-agent-presets/invariant": ["./packages/preset/agent-presets/src/invariant.ts"],
+      "@deepseek-ai/dsh-agent-spine-demo": ["./packages/examples/agent-spine-demo/src"],
+      "@deepseek-ai/dsh-agent-spine-demo/invariant": ["./packages/examples/agent-spine-demo/src/invariant.ts"],
+      "@deepseek-ai/dsh-agent-tool-presentation": ["./packages/core/agent-tool-presentation/src"],
+      "@deepseek-ai/dsh-agent-tool-presentation/invariant": ["./packages/core/agent-tool-presentation/src/invariant.ts"],
+      "@deepseek-ai/dsh-anonymous-user-id": ["./packages/identity/anonymous-user-id/src"],
+      "@deepseek-ai/dsh-anonymous-user-id/invariant": ["./packages/identity/anonymous-user-id/src/invariant.ts"],
+      "@deepseek-ai/dsh-app-boot": ["./packages/boot/app-boot/src"],
+      "@deepseek-ai/dsh-app-boot/invariant": ["./packages/boot/app-boot/src/invariant.ts"],
+      "@deepseek-ai/dsh-atomic-write": ["./packages/util/atomic-write/src"],
+      "@deepseek-ai/dsh-atomic-write/invariant": ["./packages/util/atomic-write/src/invariant.ts"],
+      "@deepseek-ai/dsh-attachment": ["./packages/attachment/attachment/src"],
+      "@deepseek-ai/dsh-attachment/invariant": ["./packages/attachment/attachment/src/invariant.ts"],
+      "@deepseek-ai/dsh-attachment-local": ["./packages/attachment/attachment-local/src"],
+      "@deepseek-ai/dsh-attachment-local/invariant": ["./packages/attachment/attachment-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-authorization": ["./packages/credentials/authorization/src"],
+      "@deepseek-ai/dsh-authorization/invariant": ["./packages/credentials/authorization/src/invariant.ts"],
+      "@deepseek-ai/dsh-base": ["./packages/bundle/base/src"],
+      "@deepseek-ai/dsh-base/invariant": ["./packages/bundle/base/src/invariant.ts"],
+      "@deepseek-ai/dsh-bash-local": ["./packages/shell/bash-local/src"],
+      "@deepseek-ai/dsh-bash-local/invariant": ["./packages/shell/bash-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-bash-sandbox": ["./packages/shell/bash-sandbox/src"],
+      "@deepseek-ai/dsh-bash-sandbox/invariant": ["./packages/shell/bash-sandbox/src/invariant.ts"],
+      "@deepseek-ai/dsh-brand": ["./packages/util/brand/src"],
+      "@deepseek-ai/dsh-brand/invariant": ["./packages/util/brand/src/invariant.ts"],
+      "@deepseek-ai/dsh-cmdline": ["./packages/boot/cmdline/src"],
+      "@deepseek-ai/dsh-cmdline/invariant": ["./packages/boot/cmdline/src/invariant.ts"],
+      "@deepseek-ai/dsh-code-runtime": ["./packages/code-runtime/code-runtime/src"],
+      "@deepseek-ai/dsh-code-runtime/invariant": ["./packages/code-runtime/code-runtime/src/invariant.ts"],
+      "@deepseek-ai/dsh-code-runtime-python": ["./packages/code-runtime/code-runtime-python/src"],
+      "@deepseek-ai/dsh-code-runtime-python/invariant": ["./packages/code-runtime/code-runtime-python/src/invariant.ts"],
+      "@deepseek-ai/dsh-code-runtime-worker-thread": ["./packages/code-runtime/code-runtime-worker-thread/src"],
+      "@deepseek-ai/dsh-code-runtime-worker-thread/invariant": ["./packages/code-runtime/code-runtime-worker-thread/src/invariant.ts"],
+      "@deepseek-ai/dsh-command-compact": ["./packages/compaction/command-compact/src"],
+      "@deepseek-ai/dsh-command-compact/invariant": ["./packages/compaction/command-compact/src/invariant.ts"],
+      "@deepseek-ai/dsh-command-feedback": ["./packages/feedback/command-feedback/src"],
+      "@deepseek-ai/dsh-command-feedback/invariant": ["./packages/feedback/command-feedback/src/invariant.ts"],
+      "@deepseek-ai/dsh-command-goal": ["./packages/goal/command-goal/src"],
+      "@deepseek-ai/dsh-command-goal/invariant": ["./packages/goal/command-goal/src/invariant.ts"],
+      "@deepseek-ai/dsh-commands": ["./packages/interaction/commands/src"],
+      "@deepseek-ai/dsh-commands/invariant": ["./packages/interaction/commands/src/invariant.ts"],
+      "@deepseek-ai/dsh-compaction": ["./packages/compaction/compaction/src"],
+      "@deepseek-ai/dsh-compaction/invariant": ["./packages/compaction/compaction/src/invariant.ts"],
+      "@deepseek-ai/dsh-compaction-basic": ["./packages/compaction/compaction-basic/src"],
+      "@deepseek-ai/dsh-compaction-basic/invariant": ["./packages/compaction/compaction-basic/src/invariant.ts"],
+      "@deepseek-ai/dsh-compaction-tool-result-pruner": ["./packages/compaction/compaction-tool-result-pruner/src"],
+      "@deepseek-ai/dsh-compaction-tool-result-pruner/invariant": ["./packages/compaction/compaction-tool-result-pruner/src/invariant.ts"],
+      "@deepseek-ai/dsh-cordis-host-runner/invariant": ["./packages/extensions/cordis-host-runner/src/invariant.ts"],
+      "@deepseek-ai/dsh-credentials": ["./packages/credentials/credentials/src"],
+      "@deepseek-ai/dsh-credentials/invariant": ["./packages/credentials/credentials/src/invariant.ts"],
+      "@deepseek-ai/dsh-credentials-local": ["./packages/credentials/credentials-local/src"],
+      "@deepseek-ai/dsh-credentials-local/invariant": ["./packages/credentials/credentials-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-deepseek-llm-api-extensions": ["./packages/llm/deepseek-llm-api-extensions/src"],
+      "@deepseek-ai/dsh-deepseek-llm-api-extensions/invariant": ["./packages/llm/deepseek-llm-api-extensions/src/invariant.ts"],
+      "@deepseek-ai/dsh-e2b": ["./packages/e2b/e2b/src"],
+      "@deepseek-ai/dsh-e2b/invariant": ["./packages/e2b/e2b/src/invariant.ts"],
+      "@deepseek-ai/dsh-file-reference": ["./packages/context/file-reference/src"],
+      "@deepseek-ai/dsh-file-reference/invariant": ["./packages/context/file-reference/src/invariant.ts"],
+      "@deepseek-ai/dsh-file-reference-local": ["./packages/context/file-reference-local/src"],
+      "@deepseek-ai/dsh-file-reference-local/invariant": ["./packages/context/file-reference-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-fs": ["./packages/fs/fs/src"],
+      "@deepseek-ai/dsh-fs/invariant": ["./packages/fs/fs/src/invariant.ts"],
+      "@deepseek-ai/dsh-fs-e2b": ["./packages/e2b/fs-e2b/src"],
+      "@deepseek-ai/dsh-fs-e2b/invariant": ["./packages/e2b/fs-e2b/src/invariant.ts"],
+      "@deepseek-ai/dsh-fs-local": ["./packages/fs/fs-local/src"],
+      "@deepseek-ai/dsh-fs-local/invariant": ["./packages/fs/fs-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-fs-observation-policy": ["./packages/fs/fs-observation-policy/src"],
+      "@deepseek-ai/dsh-fs-observation-policy/invariant": ["./packages/fs/fs-observation-policy/src/invariant.ts"],
+      "@deepseek-ai/dsh-fs-sandbox": ["./packages/fs/fs-sandbox/src"],
+      "@deepseek-ai/dsh-fs-sandbox/invariant": ["./packages/fs/fs-sandbox/src/invariant.ts"],
+      "@deepseek-ai/dsh-goal": ["./packages/goal/goal/src"],
+      "@deepseek-ai/dsh-goal/invariant": ["./packages/goal/goal/src/invariant.ts"],
+      "@deepseek-ai/dsh-goal-round-driver": ["./packages/goal/goal-round-driver/src"],
+      "@deepseek-ai/dsh-goal-round-driver/invariant": ["./packages/goal/goal-round-driver/src/invariant.ts"],
+      "@deepseek-ai/dsh-headless": ["./packages/bundle/headless/src"],
+      "@deepseek-ai/dsh-headless/invariant": ["./packages/bundle/headless/src/invariant.ts"],
+      "@deepseek-ai/dsh-home-paths": ["./packages/util/home-paths/src"],
+      "@deepseek-ai/dsh-home-paths/invariant": ["./packages/util/home-paths/src/invariant.ts"],
+      "@deepseek-ai/dsh-hook-protocol": ["./packages/hooks/hook-protocol/src"],
+      "@deepseek-ai/dsh-hook-protocol/invariant": ["./packages/hooks/hook-protocol/src/invariant.ts"],
+      "@deepseek-ai/dsh-hooks-claude-code": ["./packages/hooks/hooks-claude-code/src"],
+      "@deepseek-ai/dsh-hooks-claude-code/invariant": ["./packages/hooks/hooks-claude-code/src/invariant.ts"],
+      "@deepseek-ai/dsh-hooks-codex": ["./packages/hooks/hooks-codex/src"],
+      "@deepseek-ai/dsh-hooks-codex/invariant": ["./packages/hooks/hooks-codex/src/invariant.ts"],
+      "@deepseek-ai/dsh-invariants/invariant": ["./packages/runtime-diagnostics/invariants/src/invariant.ts"],
+      "@deepseek-ai/dsh-jobs": ["./packages/jobs/jobs/src"],
+      "@deepseek-ai/dsh-jobs/invariant": ["./packages/jobs/jobs/src/invariant.ts"],
+      "@deepseek-ai/dsh-jobs-local": ["./packages/jobs/jobs-local/src"],
+      "@deepseek-ai/dsh-jobs-local/invariant": ["./packages/jobs/jobs-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-launch-environment": ["./packages/util/launch-environment/src"],
+      "@deepseek-ai/dsh-launch-environment/invariant": ["./packages/util/launch-environment/src/invariant.ts"],
+      "@deepseek-ai/dsh-llm": ["./packages/llm/llm/src"],
+      "@deepseek-ai/dsh-llm/invariant": ["./packages/llm/llm/src/invariant.ts"],
+      "@deepseek-ai/dsh-llm-deepseek": ["./packages/llm/llm-deepseek/src"],
+      "@deepseek-ai/dsh-llm-deepseek/invariant": ["./packages/llm/llm-deepseek/src/invariant.ts"],
+      "@deepseek-ai/dsh-llm-mock-server": ["./packages/test-support/llm-mock-server/src"],
+      "@deepseek-ai/dsh-llm-mock-server/invariant": ["./packages/test-support/llm-mock-server/src/invariant.ts"],
+      "@deepseek-ai/dsh-llm-pi-ai": ["./packages/llm/llm-pi-ai/src"],
+      "@deepseek-ai/dsh-llm-pi-ai/invariant": ["./packages/llm/llm-pi-ai/src/invariant.ts"],
+      "@deepseek-ai/dsh-llm-replay": ["./packages/test-support/llm-replay/src"],
+      "@deepseek-ai/dsh-llm-replay/invariant": ["./packages/test-support/llm-replay/src/invariant.ts"],
+      "@deepseek-ai/dsh-llm-retry": ["./packages/llm/llm-retry/src"],
+      "@deepseek-ai/dsh-llm-retry/invariant": ["./packages/llm/llm-retry/src/invariant.ts"],
+      "@deepseek-ai/dsh-loader-smoke": ["./packages/test-support/loader-smoke/src"],
+      "@deepseek-ai/dsh-loader-smoke/invariant": ["./packages/test-support/loader-smoke/src/invariant.ts"],
+      "@deepseek-ai/dsh-lsp": ["./packages/lsp/lsp/src"],
+      "@deepseek-ai/dsh-lsp/invariant": ["./packages/lsp/lsp/src/invariant.ts"],
+      "@deepseek-ai/dsh-lsp-stdio": ["./packages/lsp/lsp-stdio/src"],
+      "@deepseek-ai/dsh-lsp-stdio/invariant": ["./packages/lsp/lsp-stdio/src/invariant.ts"],
+      "@deepseek-ai/dsh-mcp-client": ["./packages/mcp/mcp-client/src"],
+      "@deepseek-ai/dsh-mcp-client/invariant": ["./packages/mcp/mcp-client/src/invariant.ts"],
+      "@deepseek-ai/dsh-message-feedback": ["./packages/feedback/message-feedback/src"],
+      "@deepseek-ai/dsh-message-feedback/invariant": ["./packages/feedback/message-feedback/src/invariant.ts"],
+      "@deepseek-ai/dsh-native-command": ["./packages/util/native-command/src"],
+      "@deepseek-ai/dsh-native-command/invariant": ["./packages/util/native-command/src/invariant.ts"],
+      "@deepseek-ai/dsh-output-retention": ["./packages/util/output-retention/src"],
+      "@deepseek-ai/dsh-output-retention/invariant": ["./packages/util/output-retention/src/invariant.ts"],
+      "@deepseek-ai/dsh-permission-presets": ["./packages/interaction/permission-presets/src"],
+      "@deepseek-ai/dsh-permission-presets/invariant": ["./packages/interaction/permission-presets/src/invariant.ts"],
+      "@deepseek-ai/dsh-persona": ["./packages/preset/persona/src"],
+      "@deepseek-ai/dsh-persona/invariant": ["./packages/preset/persona/src/invariant.ts"],
+      "@deepseek-ai/dsh-plan-mode": ["./packages/plan/plan-mode/src"],
+      "@deepseek-ai/dsh-plan-mode/invariant": ["./packages/plan/plan-mode/src/invariant.ts"],
+      "@deepseek-ai/dsh-plugin-package-inventory-deepseek": ["./packages/llm/plugin-package-inventory-deepseek/src"],
+      "@deepseek-ai/dsh-plugin-package-inventory-deepseek/invariant": ["./packages/llm/plugin-package-inventory-deepseek/src/invariant.ts"],
+      "@deepseek-ai/dsh-pwsh-local/invariant": ["./packages/shell/pwsh-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-pwsh-sandbox": ["./packages/shell/pwsh-sandbox/src"],
+      "@deepseek-ai/dsh-pwsh-sandbox/invariant": ["./packages/shell/pwsh-sandbox/src/invariant.ts"],
+      "@deepseek-ai/dsh-repeat-tool-reminder": ["./packages/guard/repeat-tool-reminder/src"],
+      "@deepseek-ai/dsh-repeat-tool-reminder/invariant": ["./packages/guard/repeat-tool-reminder/src/invariant.ts"],
+      "@deepseek-ai/dsh-sandbox": ["./packages/sandbox/sandbox/src"],
+      "@deepseek-ai/dsh-sandbox/invariant": ["./packages/sandbox/sandbox/src/invariant.ts"],
+      "@deepseek-ai/dsh-sandbox-local": ["./packages/sandbox/sandbox-local/src"],
+      "@deepseek-ai/dsh-sandbox-local/invariant": ["./packages/sandbox/sandbox-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-sandbox-policy": ["./packages/sandbox/sandbox-policy/src"],
+      "@deepseek-ai/dsh-sandbox-policy/invariant": ["./packages/sandbox/sandbox-policy/src/invariant.ts"],
+      "@deepseek-ai/dsh-sandbox-windows-acl": ["./packages/sandbox/sandbox-windows-acl/src"],
+      "@deepseek-ai/dsh-sandbox-windows-acl/invariant": ["./packages/sandbox/sandbox-windows-acl/src/invariant.ts"],
+      "@deepseek-ai/dsh-schedule": ["./packages/schedule/schedule/src"],
+      "@deepseek-ai/dsh-schedule/invariant": ["./packages/schedule/schedule/src/invariant.ts"],
+      "@deepseek-ai/dsh-scope": ["./packages/core/scope/src"],
+      "@deepseek-ai/dsh-sdk-app": ["./packages/bundle/sdk-app/src"],
+      "@deepseek-ai/dsh-sdk-app/invariant": ["./packages/bundle/sdk-app/src/invariant.ts"],
+      "@deepseek-ai/dsh-sdk-minimal": ["./packages/bundle/sdk-minimal/src"],
+      "@deepseek-ai/dsh-sdk-minimal/invariant": ["./packages/bundle/sdk-minimal/src/invariant.ts"],
+      "@deepseek-ai/dsh-session": ["./packages/core/session/src"],
+      "@deepseek-ai/dsh-session-checkpoint-policy": ["./packages/session/session-checkpoint-policy/src"],
+      "@deepseek-ai/dsh-session-checkpoint-policy/invariant": ["./packages/session/session-checkpoint-policy/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-log-deepseek": ["./packages/session/session-log-deepseek/src"],
+      "@deepseek-ai/dsh-session-log-deepseek/invariant": ["./packages/session/session-log-deepseek/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-log-export": ["./packages/session-query/session-log-export/src"],
+      "@deepseek-ai/dsh-session-log-export/invariant": ["./packages/session-query/session-log-export/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-persistence": ["./packages/session/session-persistence/src"],
+      "@deepseek-ai/dsh-session-persistence/invariant": ["./packages/session/session-persistence/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-persistence-jsonl": ["./packages/session/session-persistence-jsonl/src"],
+      "@deepseek-ai/dsh-session-persistence-jsonl/invariant": ["./packages/session/session-persistence-jsonl/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-persistence-sqlite": ["./packages/session/session-persistence-sqlite/src"],
+      "@deepseek-ai/dsh-session-persistence-sqlite/invariant": ["./packages/session/session-persistence-sqlite/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-projection": ["./packages/session/session-projection/src"],
+      "@deepseek-ai/dsh-session-projection/invariant": ["./packages/session/session-projection/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-projection-cache": ["./packages/session/session-projection-cache/src"],
+      "@deepseek-ai/dsh-session-projection-cache/invariant": ["./packages/session/session-projection-cache/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-query": ["./packages/session-query/session-query/src"],
+      "@deepseek-ai/dsh-session-query/invariant": ["./packages/session-query/session-query/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-query-sqlite": ["./packages/session-query/session-query-sqlite/src"],
+      "@deepseek-ai/dsh-session-query-sqlite/invariant": ["./packages/session-query/session-query-sqlite/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-reference": ["./packages/context/session-reference/src"],
+      "@deepseek-ai/dsh-session-reference/invariant": ["./packages/context/session-reference/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-snapshot": ["./packages/test-support/session-snapshot/src"],
+      "@deepseek-ai/dsh-session-snapshot/invariant": ["./packages/test-support/session-snapshot/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-stats": ["./packages/session/session-stats/src"],
+      "@deepseek-ai/dsh-session-stats/invariant": ["./packages/session/session-stats/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-telemetry": ["./packages/session/session-telemetry/src"],
+      "@deepseek-ai/dsh-session-telemetry/invariant": ["./packages/session/session-telemetry/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-telemetry-otel": ["./packages/session/session-telemetry-otel/src"],
+      "@deepseek-ai/dsh-session-telemetry-otel/invariant": ["./packages/session/session-telemetry-otel/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-title": ["./packages/session/session-title/src"],
+      "@deepseek-ai/dsh-session-title/invariant": ["./packages/session/session-title/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-title-all-prompts-llm": ["./packages/session/session-title-all-prompts-llm/src"],
+      "@deepseek-ai/dsh-session-title-all-prompts-llm/invariant": ["./packages/session/session-title-all-prompts-llm/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-title-first-prompt-llm": ["./packages/session/session-title-first-prompt-llm/src"],
+      "@deepseek-ai/dsh-session-title-first-prompt-llm/invariant": ["./packages/session/session-title-first-prompt-llm/src/invariant.ts"],
+      "@deepseek-ai/dsh-session-title-llm": ["./packages/session/session-title-llm/src"],
+      "@deepseek-ai/dsh-session-title-llm/invariant": ["./packages/session/session-title-llm/src/invariant.ts"],
+      "@deepseek-ai/dsh-settings": ["./packages/settings/settings/src"],
+      "@deepseek-ai/dsh-settings/invariant": ["./packages/settings/settings/src/invariant.ts"],
+      "@deepseek-ai/dsh-settings-file": ["./packages/settings/settings-file/src"],
+      "@deepseek-ai/dsh-settings-file/invariant": ["./packages/settings/settings-file/src/invariant.ts"],
+      "@deepseek-ai/dsh-shell": ["./packages/shell/shell/src"],
+      "@deepseek-ai/dsh-shell/invariant": ["./packages/shell/shell/src/invariant.ts"],
+      "@deepseek-ai/dsh-shell-env/invariant": ["./packages/shell/shell-env/src/invariant.ts"],
+      "@deepseek-ai/dsh-skill": ["./packages/skill/skill/src"],
+      "@deepseek-ai/dsh-skill/invariant": ["./packages/skill/skill/src/invariant.ts"],
+      "@deepseek-ai/dsh-skill-badge": ["./packages/skill/skill-badge/src"],
+      "@deepseek-ai/dsh-skill-badge/invariant": ["./packages/skill/skill-badge/src/invariant.ts"],
+      "@deepseek-ai/dsh-skill-filesystem": ["./packages/skill/skill-filesystem/src"],
+      "@deepseek-ai/dsh-skill-filesystem/invariant": ["./packages/skill/skill-filesystem/src/invariant.ts"],
+      "@deepseek-ai/dsh-spill": ["./packages/spill/spill/src"],
+      "@deepseek-ai/dsh-spill/invariant": ["./packages/spill/spill/src/invariant.ts"],
+      "@deepseek-ai/dsh-spill-local": ["./packages/spill/spill-local/src"],
+      "@deepseek-ai/dsh-spill-local/invariant": ["./packages/spill/spill-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-spill-policy": ["./packages/spill/spill-policy/src"],
+      "@deepseek-ai/dsh-spill-policy/invariant": ["./packages/spill/spill-policy/src/invariant.ts"],
+      "@deepseek-ai/dsh-storage": ["./packages/storage/storage/src"],
+      "@deepseek-ai/dsh-storage/invariant": ["./packages/storage/storage/src/invariant.ts"],
+      "@deepseek-ai/dsh-storage-domain": ["./packages/storage/storage-domain/src"],
+      "@deepseek-ai/dsh-storage-domain/invariant": ["./packages/storage/storage-domain/src/invariant.ts"],
+      "@deepseek-ai/dsh-storage-json": ["./packages/storage/storage-json/src"],
+      "@deepseek-ai/dsh-storage-json/invariant": ["./packages/storage/storage-json/src/invariant.ts"],
+      "@deepseek-ai/dsh-storage-sqlite": ["./packages/storage/storage-sqlite/src"],
+      "@deepseek-ai/dsh-storage-sqlite/invariant": ["./packages/storage/storage-sqlite/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent": ["./packages/subagent/subagent/src"],
+      "@deepseek-ai/dsh-subagent/invariant": ["./packages/subagent/subagent/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-acp": ["./packages/subagent/subagent-acp/src"],
+      "@deepseek-ai/dsh-subagent-acp/invariant": ["./packages/subagent/subagent-acp/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-claude-code": ["./packages/subagent/subagent-claude-code/src"],
+      "@deepseek-ai/dsh-subagent-claude-code/invariant": ["./packages/subagent/subagent-claude-code/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-codex": ["./packages/subagent/subagent-codex/src"],
+      "@deepseek-ai/dsh-subagent-codex/invariant": ["./packages/subagent/subagent-codex/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-dsh-sdk": ["./packages/subagent/subagent-dsh-sdk/src"],
+      "@deepseek-ai/dsh-subagent-dsh-sdk/invariant": ["./packages/subagent/subagent-dsh-sdk/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-fork-in-process": ["./packages/subagent/subagent-fork-in-process/src"],
+      "@deepseek-ai/dsh-subagent-fork-in-process/invariant": ["./packages/subagent/subagent-fork-in-process/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-in-process-driver": ["./packages/subagent/subagent-in-process-driver/src"],
+      "@deepseek-ai/dsh-subagent-in-process-driver/invariant": ["./packages/subagent/subagent-in-process-driver/src/invariant.ts"],
+      "@deepseek-ai/dsh-subagent-spawn-in-process": ["./packages/subagent/subagent-spawn-in-process/src"],
+      "@deepseek-ai/dsh-subagent-spawn-in-process/invariant": ["./packages/subagent/subagent-spawn-in-process/src/invariant.ts"],
+      "@deepseek-ai/dsh-subprocess": ["./packages/subprocess/subprocess/src"],
+      "@deepseek-ai/dsh-subprocess/invariant": ["./packages/subprocess/subprocess/src/invariant.ts"],
+      "@deepseek-ai/dsh-subprocess-e2b": ["./packages/e2b/subprocess-e2b/src"],
+      "@deepseek-ai/dsh-subprocess-e2b/invariant": ["./packages/e2b/subprocess-e2b/src/invariant.ts"],
+      "@deepseek-ai/dsh-subprocess-local": ["./packages/subprocess/subprocess-local/src"],
+      "@deepseek-ai/dsh-subprocess-local/invariant": ["./packages/subprocess/subprocess-local/src/invariant.ts"],
+      "@deepseek-ai/dsh-system-prompt": ["./packages/core/system-prompt/src"],
+      "@deepseek-ai/dsh-system-prompt/invariant": ["./packages/core/system-prompt/src/invariant.ts"],
+      "@deepseek-ai/dsh-terminal": ["./packages/terminal/terminal/src"],
+      "@deepseek-ai/dsh-terminal/invariant": ["./packages/terminal/terminal/src/invariant.ts"],
+      "@deepseek-ai/dsh-terminal-bash": ["./packages/terminal/terminal-bash/src"],
+      "@deepseek-ai/dsh-terminal-bash/invariant": ["./packages/terminal/terminal-bash/src/invariant.ts"],
+      "@deepseek-ai/dsh-time-context": ["./packages/context/time-context/src"],
+      "@deepseek-ai/dsh-time-context/invariant": ["./packages/context/time-context/src/invariant.ts"],
+      "@deepseek-ai/dsh-timeout": ["./packages/util/timeout/src"],
+      "@deepseek-ai/dsh-timeout/invariant": ["./packages/util/timeout/src/invariant.ts"],
+      "@deepseek-ai/dsh-tmux-context": ["./packages/context/tmux-context/src"],
+      "@deepseek-ai/dsh-tmux-context/invariant": ["./packages/context/tmux-context/src/invariant.ts"],
+      "@deepseek-ai/dsh-token-meter": ["./packages/llm/token-meter/src"],
+      "@deepseek-ai/dsh-token-meter/invariant": ["./packages/llm/token-meter/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-ask-user": ["./packages/interaction/tool-ask-user/src"],
+      "@deepseek-ai/dsh-tool-ask-user/invariant": ["./packages/interaction/tool-ask-user/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-bash": ["./packages/shell/tool-bash/src"],
+      "@deepseek-ai/dsh-tool-bash/invariant": ["./packages/shell/tool-bash/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-bash-persistent": ["./packages/shell/tool-bash-persistent/src"],
+      "@deepseek-ai/dsh-tool-bash-persistent/invariant": ["./packages/shell/tool-bash-persistent/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-cordis": ["./packages/extensions/tool-cordis/src"],
+      "@deepseek-ai/dsh-tool-cordis/invariant": ["./packages/extensions/tool-cordis/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-fs": ["./packages/fs/tool-fs/src"],
+      "@deepseek-ai/dsh-tool-fs/invariant": ["./packages/fs/tool-fs/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-fs-search": ["./packages/fs/tool-fs-search/src"],
+      "@deepseek-ai/dsh-tool-fs-search/invariant": ["./packages/fs/tool-fs-search/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-goal": ["./packages/goal/tool-goal/src"],
+      "@deepseek-ai/dsh-tool-goal/invariant": ["./packages/goal/tool-goal/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-jobs": ["./packages/jobs/tool-jobs/src"],
+      "@deepseek-ai/dsh-tool-jobs/invariant": ["./packages/jobs/tool-jobs/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-lsp": ["./packages/lsp/tool-lsp/src"],
+      "@deepseek-ai/dsh-tool-lsp/invariant": ["./packages/lsp/tool-lsp/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-pwsh/invariant": ["./packages/shell/tool-pwsh/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-pwsh-persistent": ["./packages/shell/tool-pwsh-persistent/src"],
+      "@deepseek-ai/dsh-tool-pwsh-persistent/invariant": ["./packages/shell/tool-pwsh-persistent/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-ralph": ["./packages/workflow/tool-ralph/src"],
+      "@deepseek-ai/dsh-tool-ralph/invariant": ["./packages/workflow/tool-ralph/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-session-query": ["./packages/session-query/tool-session-query/src"],
+      "@deepseek-ai/dsh-tool-session-query/invariant": ["./packages/session-query/tool-session-query/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-skill": ["./packages/skill/tool-skill/src"],
+      "@deepseek-ai/dsh-tool-skill/invariant": ["./packages/skill/tool-skill/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-str-replace-editor": ["./packages/fs/tool-str-replace-editor/src"],
+      "@deepseek-ai/dsh-tool-str-replace-editor/invariant": ["./packages/fs/tool-str-replace-editor/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-subagent": ["./packages/subagent/tool-subagent/src"],
+      "@deepseek-ai/dsh-tool-subagent/invariant": ["./packages/subagent/tool-subagent/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-subagent-control": ["./packages/subagent/tool-subagent-control/src"],
+      "@deepseek-ai/dsh-tool-subagent-control/invariant": ["./packages/subagent/tool-subagent-control/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-subagent-report": ["./packages/subagent/tool-subagent-report/src"],
+      "@deepseek-ai/dsh-tool-subagent-report/invariant": ["./packages/subagent/tool-subagent-report/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-terminal": ["./packages/terminal/tool-terminal/src"],
+      "@deepseek-ai/dsh-tool-terminal/invariant": ["./packages/terminal/tool-terminal/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-todo": ["./packages/todo/tool-todo/src"],
+      "@deepseek-ai/dsh-tool-todo/invariant": ["./packages/todo/tool-todo/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-web": ["./packages/web/tool-web/src"],
+      "@deepseek-ai/dsh-tool-web/invariant": ["./packages/web/tool-web/src/invariant.ts"],
+      "@deepseek-ai/dsh-tool-workflow": ["./packages/workflow/tool-workflow/src"],
+      "@deepseek-ai/dsh-tool-workflow/invariant": ["./packages/workflow/tool-workflow/src/invariant.ts"],
+      "@deepseek-ai/dsh-tools": ["./packages/core/tools/src"],
+      "@deepseek-ai/dsh-tools/invariant": ["./packages/core/tools/src/invariant.ts"],
+      "@deepseek-ai/dsh-user-approval": ["./packages/interaction/user-approval/src"],
+      "@deepseek-ai/dsh-user-approval/invariant": ["./packages/interaction/user-approval/src/invariant.ts"],
+      "@deepseek-ai/dsh-user-questions": ["./packages/interaction/user-questions/src"],
+      "@deepseek-ai/dsh-user-questions/invariant": ["./packages/interaction/user-questions/src/invariant.ts"],
+      "@deepseek-ai/dsh-web": ["./packages/web/web/src"],
+      "@deepseek-ai/dsh-web/invariant": ["./packages/web/web/src/invariant.ts"],
+      "@deepseek-ai/dsh-web-app": ["./packages/bundle/web-app/src"],
+      "@deepseek-ai/dsh-web-app/invariant": ["./packages/bundle/web-app/src/invariant.ts"],
+      "@deepseek-ai/dsh-web-fetch-http": ["./packages/web/web-fetch-http/src"],
+      "@deepseek-ai/dsh-web-fetch-http/invariant": ["./packages/web/web-fetch-http/src/invariant.ts"],
+      "@deepseek-ai/dsh-web-search-deepseek": ["./packages/web/web-search-deepseek/src"],
+      "@deepseek-ai/dsh-web-search-deepseek/invariant": ["./packages/web/web-search-deepseek/src/invariant.ts"],
+      "@deepseek-ai/dsh-web-search-exa": ["./packages/web/web-search-exa/src"],
+      "@deepseek-ai/dsh-web-search-exa/invariant": ["./packages/web/web-search-exa/src/invariant.ts"],
+      "@deepseek-ai/dsh-web-search-perplexity": ["./packages/web/web-search-perplexity/src"],
+      "@deepseek-ai/dsh-web-search-perplexity/invariant": ["./packages/web/web-search-perplexity/src/invariant.ts"],
+      "@deepseek-ai/dsh-webhook": ["./packages/webhook/webhook/src"],
+      "@deepseek-ai/dsh-webhook/invariant": ["./packages/webhook/webhook/src/invariant.ts"],
+      "@deepseek-ai/dsh-webhook-github": ["./packages/webhook/webhook-github/src"],
+      "@deepseek-ai/dsh-webhook-github/invariant": ["./packages/webhook/webhook-github/src/invariant.ts"],
+      "@deepseek-ai/dsh-win32-process": ["./packages/subprocess/win32-process/src"],
+      "@deepseek-ai/dsh-win32-process/invariant": ["./packages/subprocess/win32-process/src/invariant.ts"],
+      "@deepseek-ai/dsh-workflow": ["./packages/workflow/workflow/src"],
+      "@deepseek-ai/dsh-workflow/invariant": ["./packages/workflow/workflow/src/invariant.ts"],
+      "@deepseek-ai/dsh-workflow-worker-thread": ["./packages/workflow/workflow-worker-thread/src"],
+      "@deepseek-ai/dsh-workflow-worker-thread/invariant": ["./packages/workflow/workflow-worker-thread/src/invariant.ts"],
+      "@deepseek-ai/dsh-workspace": ["./packages/workspace/workspace/src"],
+      "@deepseek-ai/dsh-workspace/invariant": ["./packages/workspace/workspace/src/invariant.ts"]
+      // END generated package aliases
     }
   }
 }