Merge remote-tracking branch 'origin/master' into fix/open-file-fail

This commit is contained in:
07akioni
2026-08-18 20:14:56 +08:00
74 changed files with 1092 additions and 388 deletions
@@ -198,9 +198,10 @@
toolName: subagent_fork
backgroundMode: continuable
# Production dsh does not install these optional providers. An opting-in
# Profile mounts each provider once on the host plane; copy this preset,
# then remove `disabled` from the matching tool row.
# Production dsh does not install these optional providers. Install the
# matching Bundle in this Profile and restart the Host, then copy this
# preset and remove `disabled` from the matching tool row. Host availability
# alone grants no tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
@@ -185,9 +185,10 @@
toolName: subagent_fork
backgroundMode: continuable
# Production dsh does not install these optional providers. An opting-in
# Profile mounts each provider once on the host plane; copy this preset,
# then remove `disabled` from the matching tool row.
# Production dsh does not install these optional providers. Install the
# matching Bundle in this Profile and restart the Host, then copy this
# preset and remove `disabled` from the matching tool row. Host availability
# alone grants no tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
@@ -123,7 +123,16 @@ After a clean mount-validation, ask the user to start a session on the new prese
## Native product subagents
Codex and Claude Code providers belong on the host plane but are not installed by production `dsh`. The active Profile must install and mount the selected provider before a preset can expose its ordinary delegation-tool row; never move a product provider into the preset and never add a product-specific settings field.
Codex and Claude Code providers are independent optional Profile Bundles. Install only the products a Profile needs, then restart the Profile so its Host registers those providers:
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-claude-code
```
Each Bundle owns its Host availability; the preset separately grants one Agent its ordinary delegation tool. Never move a product provider into the preset and never add a product-specific settings field. Removing one package withdraws only that provider on the next Profile start.
Copy these disabled templates from a shipped full preset and remove `disabled` only for the products the user requested:
@@ -149,7 +158,7 @@ Copy these disabled templates from a shipped full preset and remove `disabled` o
For additional named Codex or Claude Code instances, mount a separate host-plane provider row for each instance with a unique `providerName`, then add a separate preset tool row whose `provider` exactly matches that name and whose `toolName` is also unique. Keep the shipped rows for the default `codex` and `claude-code` names; do not reuse one tool row for several providers or derive either name from permission or environment settings.
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. Production `dsh` does not install or mount either optional provider: before enabling a row, the Profile must install the matching `@deepseek-ai/dsh-subagent-codex` or `@deepseek-ai/dsh-subagent-claude-code` package and mount the required provider instances on the host plane. A preset cannot provide that host dependency. `backgroundMode: one-shot` keeps omitted or `false` calls in the foreground and lets explicit `run_in_background: true` return a generic Job id. Full presets already carry `tool-jobs`, while the base host carries the job registry; retain both so `job_output`, `job_list`, `job_kill`, cancellation, and completion notices stay available. The host must also provide `codex` or `claude` on `PATH`; the preset does not install, authenticate, select a model for, or probe either product.
The two rows are independent. Leaving both disabled preserves the copied preset, enabling one exposes only that product tool, and enabling both exposes both. Production `dsh` does not install either optional provider: before enabling a row, install the matching `@deepseek-ai/dsh-subagent-codex` or `@deepseek-ai/dsh-subagent-claude-code` Bundle in the Profile and restart it. Each Bundle registers its dormant default provider and exclusively uses its pinned package-local platform CLI; additional named instances use extra host-plane rows from the same installed package. A preset cannot provide that host dependency. `backgroundMode: one-shot` keeps omitted or `false` calls in the foreground and lets explicit `run_in_background: true` return a generic Job id. Full presets already carry `tool-jobs`, while the base host carries the job registry; retain both so `job_output`, `job_list`, `job_kill`, cancellation, and completion notices stay available. Installing a Bundle or composing a preset row does not start a product, authenticate an account, select a model, probe credentials, or manage native product settings.
## What not to move into a preset
@@ -197,9 +197,10 @@
toolName: subagent_fork
backgroundMode: continuable
# Production dsh does not install these optional providers. An opting-in
# Profile mounts each provider once on the host plane; copy this preset,
# then remove `disabled` from the matching tool row.
# Production dsh does not install these optional providers. Install the
# matching Bundle in this Profile and restart the Host, then copy this
# preset and remove `disabled` from the matching tool row. Host availability
# alone grants no tool.
- id: tool-subagent-codex
name: '@deepseek-ai/dsh-tool-subagent'
disabled: true
+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 apps/cli/reference/README.md
README.md: 0be64fdfc0ad4e81d23f25a26881fa89f37565b0
README.zh.md: 649bf15df814abf4875fed794c2e76c494bfcc25
README.md: 7828f55a2e4adfd85a0018baada6945ea75aacb0
README.zh.md: e14e13731c314efd4d39913b91f2e90ba624e55c
+12
View File
@@ -42,6 +42,18 @@ dsh --profile web --patch ./extra.yml --dump-config
`dsh plugin --profile <name> <args...>` initializes the profile when missing (shipped template, or `@deepseek-ai/dsh-base` alone for other names), then forwards `<args...>` to `pnpm` with the profile directory as working directory — `add`, `remove`, `why`, `update`, and every other pnpm verb work unchanged; pnpm must be on PATH. Relative path specs (`.`, `../plugin`, and their `file:`/`link:` forms) are anchored to the invoking directory first, so `add .` from a plugin checkout installs that checkout, not the profile. After every successful run, `dsh.profile.bundles` is reconciled against the installed state: each dependency resolving to a package whose manifest declares `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }` joins the layer stack (so an `update` that gains the declaration activates it), a bundle-less dependency stays plain with a one-time warning, and a removed dependency leaves the stack.
The Codex and Claude Code subagent providers are separate optional Bundles. Add either package, both in one command, or remove either package independently:
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-claude-code
```
The successful pnpm operation changes the Profile manifest and Bundle list on disk; a running Profile keeps the Bundle set from its current start. Restart that Profile after adding, removing, or updating a Bundle. This startup boundary applies to Bundle membership, while ordinary edits to the Profile or home `cordis.patch.yml` take effect through hot reload. On the next start, each installed Bundle registers only its dormant Host provider; a copied Preset must separately enable the matching tool row for new Agents. The [Codex provider README](../../../packages/subagent/subagent-codex/README.md) and [Claude Code provider README](../../../packages/subagent/subagent-claude-code/README.md) own executable, authentication, payload, and failure details; the [base Bundle reference](../../../packages/bundle/base/README.md) owns the default dependency closure.
```sh
dsh plugin --profile tui add github:deepseek-harness/turtle-ui
dsh plugin --profile tui remove turtle-ui
+12
View File
@@ -42,6 +42,18 @@ dsh --profile web --patch ./extra.yml --dump-config
`dsh plugin --profile <name> <args...>` 在 profile 缺失时先初始化它(有随附模板的用模板,其他名称只装 `@deepseek-ai/dsh-base`),然后以 profile 目录为工作目录,把 `<args...>` 转发给 `pnpm``add``remove``why``update` 及其他所有 pnpm 子命令都照常可用;pnpm 必须在 PATH 上。相对路径 spec(`.``../plugin` 及其 `file:`/`link:` 形式)会先锚定到调用目录,因此在插件 checkout 中执行 `add .` 安装的是该 checkout,而不是 profile。每次成功运行后,系统都会根据当前安装状态更新 `dsh.profile.bundles`:如果某项依赖解析到的包在 manifest 中声明了 `"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }`,该依赖就会加入配置层栈;如果某项依赖在 `update` 后获得该声明,也会随即激活。没有组合包声明的依赖仍作为普通依赖保留,并显示一次性警告;已移除的依赖则从配置层栈中删除。
Codex 与 Claude Code subagent provider 是两个彼此独立的可选 Bundle。可以只添加一个包、在同一命令中添加两个包,或独立移除任一包:
```sh
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> add @deepseek-ai/dsh-subagent-codex @deepseek-ai/dsh-subagent-claude-code
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-codex
dsh plugin --profile <name> remove @deepseek-ai/dsh-subagent-claude-code
```
pnpm 操作成功后只会改变磁盘上的 Profile manifest 与 Bundle 列表;正在运行的 Profile 会保留本次启动时的 Bundle 集合。添加、移除或更新 Bundle 后须重启该 Profile。这个启动边界只适用于 Bundle 成员变化,Profile 或 home 中普通 `cordis.patch.yml` 的编辑通过热重载生效。下一次启动时,每个已安装 Bundle 只注册自己的休眠 Host provider;还须在复制出的 Preset 中单独启用对应工具行,新 Agent 才能看到该工具。[Codex provider README](../../../packages/subagent/subagent-codex/README.md)与 [Claude Code provider README](../../../packages/subagent/subagent-claude-code/README.md)负责可执行文件、身份验证、载荷与失败细节;[base Bundle 参考](../../../packages/bundle/base/README.md)负责默认依赖闭包。
```sh
dsh plugin --profile tui add github:deepseek-harness/turtle-ui
dsh plugin --profile tui remove turtle-ui
+15
View File
@@ -650,6 +650,21 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
}
expect(Object.keys(manifest.dependencies)).toEqual(['anchored-bundle'])
expect(manifest.dsh.profile.bundles).toContain('anchored-bundle')
const removed = await runBuiltBin(
['plugin', '--profile', 'anchor', 'remove', 'anchored-bundle'],
{ DSH_HOME: home },
checkout,
)
expect(removed.code).toBe(0)
const afterRemove = JSON.parse(
readFileSync(join(home, 'profiles', 'anchor', 'package.json'), 'utf8'),
) as {
dependencies?: Record<string, string>
dsh: { profile: { bundles: string[] } }
}
expect(Object.keys(afterRemove.dependencies ?? {})).toEqual([])
expect(afterRemove.dsh.profile.bundles).not.toContain('anchored-bundle')
} finally {
rmSync(home, { recursive: true, force: true })
rmSync(checkout, { recursive: true, force: true })
+102 -52
View File
@@ -1,15 +1,15 @@
import { randomUUID } from 'node:crypto'
import { mkdir, mkdtemp, readFile, stat, writeFile } from 'node:fs/promises'
import { mkdir, mkdtemp, readFile, stat, symlink, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
import { Context } from '@deepseek-ai/cordis'
import { boot, healProfilesModuleFallback, loadOverlayPatches } from '@deepseek-ai/dsh-app-boot'
import { boot, healProfilesModuleFallback, loadOverlayPatches, loadProfile } from '@deepseek-ai/dsh-app-boot'
import { provideCmdline } from '@deepseek-ai/dsh-cmdline'
import { SessionId } from '@deepseek-ai/dsh-session'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type { PatchOptions } from '@deepseek-ai/cordis-plugin-include'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'
import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { resolveSessionPreset, SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-agent-presets'
import { applyChildComposition, childSessionMeta } from '@deepseek-ai/dsh-subagent'
@@ -26,9 +26,10 @@ const REPO_ROOT = fileURLToPath(new URL('../../..', import.meta.url))
/** The shipped Web surface: the dsh-base and dsh-web-app bundle patches over an empty preset root. */
const BASE_PATCH = join(REPO_ROOT, 'packages/bundle/base/cordis.patch.yml')
const WEB_PATCH = join(REPO_ROOT, 'packages/bundle/web-app/cordis.patch.yml')
const CODEX_PACKAGE_DIR = join(REPO_ROOT, 'packages/subagent/subagent-codex')
const CLAUDE_CODE_PACKAGE_DIR = join(REPO_ROOT, 'packages/subagent/subagent-claude-code')
/** The installation anchor whose dependency surface the preset module fallback mirrors. */
const INSTALL_ANCHOR = join(REPO_ROOT, 'apps/cli/package.json')
const EXAMPLES_INSTALL_ANCHOR = join(REPO_ROOT, 'examples/package.json')
const MINIMAL_PROMPT = 'You are a helpful software engineer assistant.'
const MINIMAL_BASH_DESCRIPTION = `Run commands in a bash shell
* When invoking this tool, the contents of the "command" parameter does NOT need to be XML-escaped.
@@ -47,12 +48,11 @@ const MINIMAL_BASH_DESCRIPTION = `Run commands in a bash shell
async function bootWeb(
settingsFile: string,
extra: PatchOptions[] = [],
extraInstallAnchor?: string,
profilePackages: readonly string[] = [],
profileBundles?: readonly string[],
): Promise<Context> {
const storageRoot = join(dirname(settingsFile), 'storages')
const patches: PatchOptions[] = [
...loadOverlayPatches('dsh-test', BASE_PATCH),
...loadOverlayPatches('dsh-test', WEB_PATCH),
const overrides: PatchOptions[] = [
// The settings row defaults to `$DSH_HOME/settings.yaml`. Left alone it
// reads the developer's own document — and since the default preset is a
// setting, a stored `agent-presets.default` would decide this file's
@@ -115,12 +115,34 @@ async function bootWeb(
// them resolvable — the same mechanism, not a test-only shim.
const home = dirname(settingsFile)
healProfilesModuleFallback(INSTALL_ANCHOR, home)
if (extraInstallAnchor !== undefined) healProfilesModuleFallback(extraInstallAnchor, home)
const profileDir = join(home, 'profiles', 'spec')
await mkdir(profileDir, { recursive: true })
// Product Bundles are installed into the Profile, not the dsh app. Model
// pnpm's package link for only the selected products; their own production
// dependencies resolve from the linked workspace packages, while shared
// peers still resolve through the installation fallback above.
for (const packageDir of profilePackages) {
const manifest = JSON.parse(await readFile(join(packageDir, 'package.json'), 'utf8')) as { name: string }
const link = join(profileDir, 'node_modules', manifest.name)
await mkdir(dirname(link), { recursive: true })
await symlink(packageDir, link, 'junction')
}
let bundlePatches: PatchOptions[] = [
...loadOverlayPatches('dsh-test', BASE_PATCH),
...loadOverlayPatches('dsh-test', WEB_PATCH),
]
if (profileBundles !== undefined) {
await writeFile(join(profileDir, 'package.json'), JSON.stringify({
private: true,
dependencies: Object.fromEntries(profileBundles.map(name => [name, 'workspace:*'])),
dsh: { profile: { bundles: profileBundles } },
}, null, 2) + '\n')
const profile = loadProfile('dsh-test', 'spec', INSTALL_ANCHOR, home, { userLayer: false })
bundlePatches = profile.layers.flatMap(layer => layer.patches)
}
const rootConfig = join(profileDir, 'cordis.yml')
await writeFile(rootConfig, '[]\n')
return await boot('dsh-test', rootConfig, patches, (bootCtx) => {
return await boot('dsh-test', rootConfig, [...bundlePatches, ...overrides], (bootCtx) => {
provideCmdline(bootCtx, { args: [], exit: () => {} })
})
}
@@ -434,17 +456,18 @@ describe('the shipped Web composition', () => {
})
})
describe('product subagent rows in user presets', () => {
let productCtx: Context
const ids = ['products-none', 'products-codex', 'products-claude', 'products-both'] as const
describe('product Bundle and user-preset intersection', () => {
const presetIds = ['products-none', 'products-codex', 'products-claude', 'products-both'] as const
type Product = 'codex' | 'claude-code'
type PresetId = typeof presetIds[number]
beforeAll(async () => {
async function bootProducts(installed: readonly Product[]): Promise<Context> {
const root = await mkdtemp(join(tmpdir(), 'dsh-product-presets-'))
const userRoot = join(root, 'presets')
const settingsFile = join(root, 'settings.yaml')
const standard = await readFile(join(CONFIG_DIR, 'agent-presets', 'standard', 'agent.cordis.yml'), 'utf8')
await writeFile(settingsFile, '{}\n')
for (const id of ids) {
for (const id of presetIds) {
let composition = standard
if (id === 'products-codex' || id === 'products-both') {
composition = enablePresetTool(composition, 'tool-subagent-codex')
@@ -456,11 +479,15 @@ describe('product subagent rows in user presets', () => {
await mkdir(directory, { recursive: true })
await writeFile(join(directory, 'agent.cordis.yml'), composition)
}
productCtx = await bootWeb(settingsFile, [
{ insert: [
{ id: 'subagent-codex', name: '@deepseek-ai/dsh-subagent-codex' },
{ id: 'subagent-claude-code', name: '@deepseek-ai/dsh-subagent-claude-code' },
] },
const packageDir = (product: Product): string => (
product === 'codex' ? CODEX_PACKAGE_DIR : CLAUDE_CODE_PACKAGE_DIR
)
const packageName = (product: Product): string => (
product === 'codex'
? '@deepseek-ai/dsh-subagent-codex'
: '@deepseek-ai/dsh-subagent-claude-code'
)
return await bootWeb(settingsFile, [
{
id: 'agent-presets',
config: {
@@ -472,46 +499,68 @@ describe('product subagent rows in user presets', () => {
includeUserRoot: false,
},
},
], EXAMPLES_INSTALL_ANCHOR)
}, 120_000)
afterAll(async () => {
await productCtx.fiber.dispose()
})
it('composes none, either product, or both without changing the shared host registry', async () => {
const expected = new Map<string, string[]>([
['products-none', []],
['products-codex', ['subagent_codex']],
['products-claude', ['subagent_claude_code']],
['products-both', ['subagent_claude_code', 'subagent_codex']],
], installed.map(packageDir), [
'@deepseek-ai/dsh-base',
'@deepseek-ai/dsh-web-app',
...installed.map(packageName),
])
expect(productCtx.subagents.list()).toEqual(expect.arrayContaining([
'spawn', 'fork', 'codex', 'claude-code',
]))
}
for (const [id, productTools] of expected) {
const handle = await productCtx.agents.create({
sessionId: SessionId(`preset-${id}`),
setup: agentCtx => productCtx.agentPresets.mount(agentCtx, id).then(() => undefined),
})
it('composes the intersection of installed Bundles and enabled preset rows', async () => {
const enabledByPreset: Record<PresetId, Product[]> = {
'products-none': [],
'products-codex': ['codex'],
'products-claude': ['claude-code'],
'products-both': ['codex', 'claude-code'],
}
const scenarios: Array<{ installed: Product[]; presets: readonly PresetId[] }> = [
{ installed: [], presets: ['products-both'] },
{ installed: ['codex'], presets: ['products-both'] },
{ installed: ['claude-code'], presets: ['products-both'] },
{ installed: ['codex', 'claude-code'], presets: presetIds },
]
for (const { installed, presets } of scenarios) {
const productCtx = await bootProducts(installed)
const spawn = vi.spyOn(productCtx.subprocess, 'spawn')
try {
const tools = toolNames(productCtx, handle.agent)
expect(tools.filter(name => name === 'subagent_codex' || name === 'subagent_claude_code'))
.toEqual(productTools)
expect(tools).toEqual(expect.arrayContaining(['job_kill', 'job_list', 'job_output']))
for (const productTool of productTools) {
expect(toolParameterNames(productCtx, handle.agent, productTool)).toEqual([
'description', 'prompt', 'run_in_background',
])
expect(productCtx.subagents.list()
.filter(name => name === 'codex' || name === 'claude-code')
.sort())
.toEqual([...installed].sort())
for (const id of presets) {
const handle = await productCtx.agents.create({
sessionId: SessionId(`preset-${id}-${installed.join('-') || 'none'}-${randomUUID()}`),
setup: agentCtx => productCtx.agentPresets.mount(agentCtx, id).then(() => undefined),
})
try {
const productTools = enabledByPreset[id]
.filter(product => installed.includes(product))
.map(product => product === 'codex' ? 'subagent_codex' : 'subagent_claude_code')
.sort()
const tools = toolNames(productCtx, handle.agent)
expect(tools.filter(name => name === 'subagent_codex' || name === 'subagent_claude_code'))
.toEqual(productTools)
expect(tools).toEqual(expect.arrayContaining(['job_kill', 'job_list', 'job_output']))
for (const productTool of productTools) {
expect(toolParameterNames(productCtx, handle.agent, productTool)).toEqual([
'description', 'prompt', 'run_in_background',
])
}
} finally {
await handle.dispose()
}
}
expect(spawn).not.toHaveBeenCalled()
} finally {
await handle.dispose()
spawn.mockRestore()
await productCtx.fiber.dispose()
}
}
})
}, 120_000)
it('applies a product-row edit only to later sessions on the preset', async () => {
const productCtx = await bootProducts(['codex'])
const preset = await productCtx.agentPresets.resolve('products-none')
const original = await readFile(preset.path, 'utf8')
const existing = await productCtx.agents.create({
@@ -535,8 +584,9 @@ describe('product subagent rows in user presets', () => {
} finally {
await existing.dispose()
await writeFile(preset.path, original)
await productCtx.fiber.dispose()
}
})
}, 120_000)
})
describe('a switch survives the session', () => {
+5 -5
View File
@@ -17,7 +17,7 @@ const SNAPSHOT_DIR = fileURLToPath(new URL('./snapshots/skill-tool-row', import.
const UI_EXPECTED = fileURLToPath(new URL('./snapshots/skill-tool-row/ui.expected.md', import.meta.url))
const MODE = webSnapshotMode()
const SEED_ID = 'skill-tool-row-web-e2e'
const PROMPT = 'Load the snapshot-skill skill with the skill tool, then reply DONE.'
const PROMPT = 'Load the editing-cordis-compositions skill with the skill tool, then reply DONE.'
describe.skipIf(MODE === 'record')('web e2e: dedicated Skill tool row', () => {
let scaffold: WebScaffold
@@ -53,17 +53,17 @@ describe.skipIf(MODE === 'record')('web e2e: dedicated Skill tool row', () => {
it('expands the loaded skill to its exact recorded instructions', async () => {
onTestFailed(() => saveFailureShot(page, 'web-e2e-skill-tool-row'))
const call = page.locator('[data-tool="skill"]')
const row = call.getByRole('button', { name: 'Skill snapshot-skill' })
const row = call.getByRole('button', { name: 'Skill editing-cordis-compositions' })
await expect.poll(() => row.getAttribute('aria-expanded')).toBe('false')
expect(await call.getByText('snapshot-skill', { exact: true }).count()).toBe(1)
expect(await call.getByText('editing-cordis-compositions', { exact: true }).count()).toBe(1)
await row.click()
await expect.poll(() => row.getAttribute('aria-expanded')).toBe('true')
await call.getByText('Instructions', { exact: true }).waitFor()
const output = call.locator('pre')
await output.waitFor()
expect(await output.textContent()).toContain('<skill_content name="snapshot-skill">')
expect(await output.textContent()).toContain('Follow these snapshot-only instructions.')
expect(await output.textContent()).toContain('<skill_content name="editing-cordis-compositions">')
expect(await output.textContent()).toContain('Each Bundle registers its dormant default provider and exclusively uses its pinned package-local platform CLI')
expect(await output.evaluate(element => getComputedStyle(element.parentElement!).maxHeight)).toBe('260px')
const snapshot = (await captureStableAria(page, '[class*="centerCol"]', scaffold.workspaceCwd))
File diff suppressed because one or more lines are too long