fix(experimental): retain published names and centralize identity policy

This commit is contained in:
Tianyi Cui committed 2026-10-06 16:42:25 +08:00
1 parent 5990ccbb4a
commit 582c9ab7b7
186 files changed
+1559 -1350

No files matched your search

+3 -2
View File
@@ -3,6 +3,7 @@
import { existsSync, readFileSync, realpathSync } from 'node:fs'
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path'
import { fileURLToPath } from 'node:url'
import { hasExperimentalPackageReference, isExperimentalPackageName } from './experimental-package-policy.ts'
/** Package identities are cached only within one build. */
export class BundleInputIsolation {
@@ -35,7 +36,7 @@ export class BundleInputIsolation {
}
private checkInput(id: string, allowMissing: boolean): void {
if (/(?:^|[/:\u0000])@deepseek-ai\/dsh-experimental-[^/?#]+/.test(id)) {
if (hasExperimentalPackageReference(id)) {
throw new Error(`${this.label}: experimental input ${id}`)
}
const file = physicalBundleInput(id)
@@ -54,7 +55,7 @@ export class BundleInputIsolation {
const canonical = resolve(realpathSync(existing), relative(existing, file))
if (canonical !== file) this.checkInput(canonical, allowMissing)
const name = this.packageName(dirname(canonical))
if (name?.startsWith('@deepseek-ai/dsh-experimental-')) {
if (name !== undefined && isExperimentalPackageName(name)) {
throw new Error(`${this.label}: ${id} belongs to experimental package ${name}`)
}
}
+34 -1
View File
@@ -7,6 +7,7 @@ import { describe, expect, it, onTestFinished } from 'vitest'
import {
isPublicExperimentalPackageDirectory,
PRIVATE_EXPERIMENTAL_PACKAGE_DIRECTORIES,
EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS,
} from './experimental-package-policy.ts'
import {
checkDshFamilyVersion,
@@ -14,6 +15,7 @@ import {
checkWorkspaceProtocol,
checkExperimentalDependencyIsolation,
checkExperimentalManifest,
checkExperimentalNameExceptions,
expectedDshPackageFiles,
readWorkspaceManifests,
type WorkspaceManifest,
@@ -120,10 +122,41 @@ describe('experimental workspace constraints', () => {
...experimental,
manifest: { ...experimental.manifest, name: '@deepseek-ai/dsh-prototype' },
})).toEqual([
'@deepseek-ai/dsh-prototype: experimental package name must start with "@deepseek-ai/dsh-experimental-"',
'@deepseek-ai/dsh-prototype: experimental package name must start with "@deepseek-ai/dsh-experimental-" or match its declared directory exception',
])
})
it.each(Object.entries(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))('retains %s only at its declared directory', (dir, name) => {
expect(checkExperimentalManifest({ dir, manifest: { name, publishConfig: { access: 'public' } } })).toEqual([])
expect(checkExperimentalManifest({ ...experimental, manifest: { ...experimental.manifest, name } }))
.toEqual([expect.stringContaining('declared directory exception')])
})
it('rejects a missing manifest name even when the directory has no exception', () => {
expect(checkExperimentalManifest({ ...experimental, manifest: { publishConfig: { access: 'public' } } }))
.toEqual([expect.stringContaining('experimental package name must start')])
})
it('rejects stale, moved and duplicated retained-name declarations', () => {
const manifests = Object.entries(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS).map(([dir, name]) => ({ dir, manifest: { name } }))
expect(checkExperimentalNameExceptions(manifests)).toEqual([])
const [first, ...rest] = manifests
for (const invalid of [
rest,
[{ ...first!, manifest: { name: '@deepseek-ai/dsh-experimental-replacement' } }, ...rest],
[{ ...first!, dir: 'packages/core/replacement' }, ...rest],
[...manifests, { ...first!, dir: 'packages/experimental/duplicate' }],
]) expect(checkExperimentalNameExceptions(invalid)).toEqual([expect.stringContaining(first!.manifest.name)])
})
it.each(Object.values(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))('rejects retained runtime names and aliases: %s', (name) => {
for (const dependencies of [{ [name]: 'workspace:*' }, { alias: `npm:${name}@1.0.0` }, { alias: `workspace:${name}@*` }]) {
expect(checkExperimentalDependencyIsolation([{
dir: 'apps/cli', manifest: { name: '@deepseek-ai/dsh', dependencies },
}], [])).toEqual([expect.stringContaining('must not reference an experimental package')])
}
})
it('requires public metadata for unlisted experimental packages', () => {
expect(checkExperimentalManifest(experimental)).toEqual([])
expect(checkExperimentalManifest({
+30 -8
View File
@@ -11,6 +11,10 @@ import { pathToFileURL } from 'node:url'
import { load as loadYaml } from 'js-yaml'
import {
isPublicExperimentalPackageDirectory,
isExperimentalPackageName,
hasExperimentalPackageReference,
EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS,
EXPERIMENTAL_PACKAGE_NAME_PREFIX,
PRIVATE_EXPERIMENTAL_PACKAGE_DIRECTORIES,
} from './experimental-package-policy.ts'
import { hasTypertRemoteNavigation, isForbiddenPublicationFile } from './publication-payload.ts'
@@ -53,8 +57,6 @@ const publicationSourceAllowlist: Readonly<Record<string, readonly string[]>> =
const publishedRepositoryUrl = 'git+https://github.com/deepseek-ai/deepseek-harness.git'
/** Packages that participate in the experimental policy. */
const experimentalPackageDirectory = /^packages\/experimental\/[^/]+$/
/** npm namespace reserved for experimental packages. */
const experimentalPackageNamePrefix = '@deepseek-ai/dsh-experimental-'
/** Ordinary directories whose packages this repository publishes: one release member each. */
const standardReleaseMemberDirectory = /^(?:packages\/(?!experimental\/)[^/]+\/[^/]+|apps\/(?!desktop(?:-host)?$)[^/]+|vendor\/[^/]+)$/
/** Installable application assembled by electron-builder rather than published to npm. */
@@ -168,7 +170,7 @@ export function readWorkspaceManifests(repositoryRoot: string): WorkspaceManifes
}
const packageFileExtras: Readonly<Record<string, readonly string[]>> = {
'@deepseek-ai/dsh-experimental-webhook-github': ['examples/github-review/cordis.yml', 'examples/github-review/github-ready-review-rule.mjs'],
'@deepseek-ai/dsh-webhook-github': ['examples/github-review/cordis.yml', 'examples/github-review/github-ready-review-rule.mjs'],
// Owned Worker bundles import this public bootstrap before their business entry.
'@deepseek-ai/dsh-app-boot': ['lib/worker/profile-resolution-bootstrap.js'],
// Statically linked client libraries keep their stylesheets next to the emitted
@@ -203,7 +205,7 @@ const packageFileExtras: Readonly<Record<string, readonly string[]>> = {
// sandbox-local resolves it through the package's ./runner export. tsdown
// also shares its generated FFI code through a hashed runtime chunk.
'@deepseek-ai/dsh-sandbox-windows-acl': ['lib/runner.js', 'lib/types-*.js', 'assets'],
'@deepseek-ai/dsh-experimental-skill-badge': ['assets'],
'@deepseek-ai/dsh-skill-badge': ['assets'],
'@deepseek-ai/dsh-skill-office': ['assets'],
'@deepseek-ai/dsh-subprocess': ['lib/control.js'],
// SSH launches a private helper and shares wire definitions and TLS setup
@@ -343,8 +345,9 @@ export function checkExperimentalManifest(
if (!experimentalPackageDirectory.test(dir)) return []
const label = manifest.name ?? dir
const errors: string[] = []
if (manifest.name?.startsWith(experimentalPackageNamePrefix) !== true) {
errors.push(`${label}: experimental package name must start with ${JSON.stringify(experimentalPackageNamePrefix)}`)
if (manifest.name === undefined || !manifest.name.startsWith(EXPERIMENTAL_PACKAGE_NAME_PREFIX)
&& manifest.name !== EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS[dir]) {
errors.push(`${label}: experimental package name must start with ${JSON.stringify(EXPERIMENTAL_PACKAGE_NAME_PREFIX)} or match its declared directory exception`)
}
if (isPublicExperimentalPackageDirectory(dir, privateDirectories)) {
if (manifest.private === true) errors.push(`${label}: public experimental package must not set "private": true`)
@@ -358,6 +361,22 @@ export function checkExperimentalManifest(
return errors
}
/**
* Reject missing, renamed, moved, or duplicated retained-name exceptions.
* @param manifests - every workspace manifest, with repository-relative directories.
* @returns Violations of the exact directory/name ownership declarations.
*/
export function checkExperimentalNameExceptions(manifests: readonly WorkspaceManifest[]): string[] {
const errors: string[] = []
for (const [dir, name] of Object.entries(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS)) {
const owners = manifests.filter(entry => entry.manifest.name === name)
if (owners.length !== 1 || owners[0]?.dir !== dir) {
errors.push(`${dir}: retained experimental name ${name} must have exactly one workspace owner at this directory`)
}
}
return errors
}
function isReleaseMemberDirectory(dir: string): boolean {
return standardReleaseMemberDirectory.test(dir) || isPublicExperimentalPackageDirectory(dir)
}
@@ -560,8 +579,10 @@ export function checkExperimentalDependencyIsolation(
if (!standardReleaseMemberDirectory.test(dir) && dir !== 'python/sdk-runtime') continue
const offered = manifest.name === '@deepseek-ai/dsh' ? new Set(optionalBundles) : new Set<string>()
for (const section of runtimeDependencySections) {
for (const name of Object.keys(manifest[section] ?? {})) {
if (!experimentalNames.has(name)) continue
for (const [name, range] of Object.entries(manifest[section] ?? {})) {
const experimental = experimentalNames.has(name) || isExperimentalPackageName(name)
|| /^(?:npm:|workspace:)/.test(range) && hasExperimentalPackageReference(range)
if (!experimental) continue
if (section === 'dependencies' && offered.has(name)) continue
errors.push(`${manifest.name ?? dir}: ${section}.${name} must not reference an experimental package`)
}
@@ -608,6 +629,7 @@ export function main(): void {
...checkRepositoryVersion(),
...workspaceManifests().flatMap(checkWorkspaceManifest),
...checkWorkspaceProtocol(manifests),
...checkExperimentalNameExceptions(manifests),
...checkExperimentalDependencyIsolation(manifests),
...checkHierarchyShape(),
...collectProjectReferenceFaceViolations(root),
+9
View File
@@ -269,6 +269,15 @@ describe('client bundle experimental input isolation', () => {
expect(existsSync(join(owner, 'lib/index.js'))).toBe(false)
})
it.each(['static', 'dynamic'] as const)('keeps retained-name experimental %s inputs outside ordinary artifacts', async (kind) => {
const { root, owner, entry, prototype } = fixture()
const name = '@deepseek-ai/dsh-tool-terminal'
writeFileSync(join(root, 'prototype/package.json'), JSON.stringify({ name, type: 'module' }))
writeFileSync(entry, `export { marker } from ${JSON.stringify(importPath(entry, prototype))}\n`)
await expect(bundle(owner, config(kind))).rejects.toThrow(/client bundle isolation.*experimental/)
await expect(bundle(owner, config(kind, name))).resolves.toContain('experimental sentinel')
})
it('proves the static library otherwise hides the experimental input in its emitted JavaScript', async () => {
const { owner, entry, prototype } = fixture()
writeFileSync(entry, `export { marker } from ${JSON.stringify(importPath(entry, prototype))}\n`)
@@ -0,0 +1,38 @@
/** Retained package names keep experimental isolation and persisted record namespaces. */
import { describe, expect, it } from 'vitest'
import {
EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS,
experimentalPackageRecordNamespace,
hasExperimentalPackageReference,
isExperimentalPackageName,
} from './experimental-package-policy.ts'
const names = Object.values(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS)
describe('experimental package identities', () => {
it.each([...names, '@deepseek-ai/dsh-experimental-terminal-bundle'])('recognizes %s independently of installation layout', (name) => {
expect(isExperimentalPackageName(name)).toBe(true)
for (const reference of [name, `${name}/client`, `${name}@1.2.3`, `npm:${name}@1.2.3`, `workspace:${name}@*`,
`\0virtual:${name}/client?raw`, `/node_modules/${name}/lib/index.js`, `C:\\node_modules\\${name}\\lib\\index.js`, `C:\\node_modules\\${name.replaceAll('/', '\\')}\\lib\\index.js`]) {
expect(hasExperimentalPackageReference(reference), reference).toBe(true)
}
})
it.each(names)('does not extend the retained-name exception %s to another package', (name) => {
for (const reference of [`${name}-unrelated`, `other-${name}`, name.replace('@deepseek-ai/', '@other/')]) {
expect(isExperimentalPackageName(reference)).toBe(false)
expect(hasExperimentalPackageReference(reference)).toBe(false)
expect(experimentalPackageRecordNamespace(reference)).toBeUndefined()
}
})
it('keeps new experimental packages under their prefixed namespaces', () => {
expect(experimentalPackageRecordNamespace('@deepseek-ai/dsh-experimental-bridge')).toBe('bridge')
expect(hasExperimentalPackageReference('@deepseek-ai/dsh-core')).toBe(false)
})
it.each(Object.entries(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))('keeps %s record names independent of the retained npm name', (directory, name) => {
expect(experimentalPackageRecordNamespace(name)).toBe(directory.split('/').at(-1))
})
})
+49
View File
@@ -1,3 +1,52 @@
/** Experimental package identities, publication policy, and retained npm names. */
/** Existing npm names retained by capabilities now housed under experimental/. */
export const EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS: Readonly<Record<string, string>> = {
'packages/experimental/session-title-all-prompts-llm': '@deepseek-ai/dsh-session-title-all-prompts-llm',
'packages/experimental/tool-terminal': '@deepseek-ai/dsh-tool-terminal',
'packages/experimental/tool-ralph': '@deepseek-ai/dsh-tool-ralph',
'packages/experimental/skill-badge': '@deepseek-ai/dsh-skill-badge',
'packages/experimental/hook-protocol': '@deepseek-ai/dsh-hook-protocol',
'packages/experimental/hooks-claude-code': '@deepseek-ai/dsh-hooks-claude-code',
'packages/experimental/hooks-codex': '@deepseek-ai/dsh-hooks-codex',
'packages/experimental/webhook': '@deepseek-ai/dsh-webhook',
'packages/experimental/webhook-github': '@deepseek-ai/dsh-webhook-github',
} satisfies Record<`packages/experimental/${string}`, `@deepseek-ai/dsh-${string}`>
/** npm namespace for experimental packages without retained-name exceptions. */
export const EXPERIMENTAL_PACKAGE_NAME_PREFIX = '@deepseek-ai/dsh-experimental-'
const retainedNames = new Set(Object.values(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))
/**
* Identify an experimental npm package independently of its installed directory.
* @param name - complete npm package name, without a version or subpath.
* @returns Whether the name uses the experimental prefix or a retained name.
*/
export function isExperimentalPackageName(name: string): boolean {
return name.startsWith(EXPERIMENTAL_PACKAGE_NAME_PREFIX) || retainedNames.has(name)
}
/**
* Find experimental identities in module ids, paths, subpaths, or npm alias specifications.
* @param reference - package reference or bundler/runtime module identifier.
* @returns Whether a delimited package name identifies an experimental capability.
*/
export function hasExperimentalPackageReference(reference: string): boolean {
const normalized = reference.replaceAll('\\', '/')
return [...normalized.matchAll(/(?:^|[/:\u0000])(@deepseek-ai\/[^/@?#:\s]+)(?=$|[/@?#:])/g)]
.some(([, name]) => name !== undefined && isExperimentalPackageName(name))
}
/**
* Derive the package-owned plugin record namespace without changing persisted names.
* @param name - complete experimental npm package name.
* @returns Capability suffix, or undefined for a non-experimental name.
*/
export function experimentalPackageRecordNamespace(name: string): string | undefined {
if (!isExperimentalPackageName(name)) return undefined
return name.slice(name.startsWith(EXPERIMENTAL_PACKAGE_NAME_PREFIX) ? EXPERIMENTAL_PACKAGE_NAME_PREFIX.length : '@deepseek-ai/dsh-'.length)
}
/** Experimental packages excluded from public releases and npm baselines. */
export const PRIVATE_EXPERIMENTAL_PACKAGE_DIRECTORIES: readonly string[] = []
+12 -12
View File
@@ -376,7 +376,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Durable session persistence seam',
mode: 'seam',
implementations: ['session-persistence-jsonl'],
consumers: ['agent-loop', 'tool-bash', 'experimental-hooks-claude-code', 'experimental-hooks-codex', 'session-query', 'session-query-sqlite', 'message-feedback'],
consumers: ['agent-loop', 'tool-bash', 'hooks-claude-code', 'hooks-codex', 'session-query', 'session-query-sqlite', 'message-feedback'],
note: 'The JSONL backend persists the SessionEvent vocabulary as one artifact per Session.',
},
{
@@ -531,7 +531,7 @@ const SERVICE_ROLES: ServiceRole[] = [
pkg: 'session-title',
title: 'Log-backed session titles',
mode: 'seam',
implementations: ['session-title-first-prompt-llm', 'experimental-session-title-all-prompts-llm'],
implementations: ['session-title-first-prompt-llm', 'session-title-all-prompts-llm'],
note: 'Owns the deterministic fallback, latest-title fold, and sole optional asynchronous provider registration.',
},
{
@@ -539,7 +539,7 @@ const SERVICE_ROLES: ServiceRole[] = [
pkg: 'system-prompt',
title: 'System prompt assembly registry',
mode: 'core',
consumers: ['agent-loop', 'tools', 'tool-fs', 'experimental-tool-terminal', 'tool-web'],
consumers: ['agent-loop', 'tools', 'tool-fs', 'tool-terminal', 'tool-web'],
note: 'Collects prompt sections and model-facing tool schemas for each step.',
},
{
@@ -547,7 +547,7 @@ const SERVICE_ROLES: ServiceRole[] = [
pkg: 'tools',
title: 'Tool registry and guarded execution pipeline',
mode: 'core',
consumers: ['agent-loop', 'tool-ask-user', 'tool-bash', 'tool-cordis', 'tool-fs', 'experimental-tool-terminal', 'tool-skill', 'tool-subagent', 'tool-todo', 'tool-web'],
consumers: ['agent-loop', 'tool-ask-user', 'tool-bash', 'tool-cordis', 'tool-fs', 'tool-terminal', 'tool-skill', 'tool-subagent', 'tool-todo', 'tool-web'],
note: 'Registers capabilities, owns PTC mode transport, and routes calls through pre-policy, monotonic guards, around dispatch, post-policy, and final-result observation.',
},
{
@@ -600,7 +600,7 @@ const SERVICE_ROLES: ServiceRole[] = [
pkg: 'skill',
title: 'Skill provider registry',
mode: 'seam',
implementations: ['sandbox-windows-acl', 'experimental-skill-badge', 'skill-filesystem', 'skill-office'],
implementations: ['sandbox-windows-acl', 'skill-badge', 'skill-filesystem', 'skill-office'],
consumers: ['tool-skill'],
note: 'Merges provider skill catalogs; tool-skill renders the session-prefix catalog and loads complete skill bodies.',
},
@@ -665,7 +665,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Bash executor seam',
mode: 'seam',
implementations: ['bash-local', 'bash-sandbox', 'pwsh-local'],
consumers: ['tool-bash', 'tool-pwsh', 'experimental-hooks-claude-code', 'experimental-hooks-codex'],
consumers: ['tool-bash', 'tool-pwsh', 'hooks-claude-code', 'hooks-codex'],
note: 'The model-facing shell tools and hook bridges consume this seam; sandboxed, remote, or PowerShell executors replace bash-local without touching them.',
},
{
@@ -682,7 +682,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Persistent PTY session registry',
mode: 'seam',
implementations: ['terminal-bash'],
consumers: ['experimental-tool-terminal'],
consumers: ['tool-terminal'],
note: 'The registry owns exact-Agent session identity and cleanup; backends own terminal mechanics, while tool-terminal exposes the owner-scoped model tools.',
},
{
@@ -754,7 +754,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Subagent provider and continuation service',
mode: 'seam',
implementations: ['subagent-spawn-in-process', 'subagent-fork-in-process', 'subagent-acp', 'subagent-codex', 'subagent-claude-code', 'subagent-dsh-sdk'],
consumers: ['tool-subagent', 'tool-subagent-control', 'experimental-tool-ralph'],
consumers: ['tool-subagent', 'tool-subagent-control', 'tool-ralph'],
note: 'Providers prepare local children or execute external tasks; one activation manager owns both lifecycles. The delegation tool selects a backend, control tools deliver local follow-ups, and workflows await activation results.',
},
{
@@ -794,7 +794,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Background job registry',
mode: 'seam',
implementations: ['jobs-local'],
consumers: ['tool-bash', 'tool-pwsh', 'experimental-tool-terminal', 'tool-jobs', 'api-job-controller'],
consumers: ['tool-bash', 'tool-pwsh', 'tool-terminal', 'tool-jobs', 'api-job-controller'],
note: 'Producers (background bash/pwsh and PTY sends) register running work; record-declaring jobs additionally stream raw output for non-consuming observers; tool-jobs is the model-facing controller that reads, lists, and kills it; jobs-local is the process-local registry.',
},
{
@@ -846,15 +846,15 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Workflow script engine',
mode: 'seam',
implementations: ['workflow-ptc'],
consumers: ['tool-workflow', 'experimental-tool-ralph'],
consumers: ['tool-workflow', 'tool-ralph'],
note: 'One engine per context, as in bash, with no named-provider registry; the general workflow and fixed Ralph consumers start runs whose agent() calls fan out through ctx.subagents.',
},
{
key: 'webhookRuntime',
pkg: 'experimental-webhook',
pkg: 'webhook',
title: 'Webhook rule runtime',
mode: 'core',
consumers: ['experimental-webhook-github'],
consumers: ['webhook-github'],
note: 'Provider adapters dispatch authenticated deliveries; trusted plugins register independent process-local rules, and the runtime turns non-null results into ordinary Workspace-backed Sessions without delivery or completion state.',
},
{
+4 -4
View File
@@ -54,7 +54,7 @@ import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
import * as ToolFsSearch from '@deepseek-ai/dsh-tool-fs-search'
import * as ToolStrReplaceEditor from '@deepseek-ai/dsh-tool-str-replace-editor'
import TerminalSessionService from '@deepseek-ai/dsh-terminal'
import * as ToolPty from '@deepseek-ai/dsh-experimental-tool-terminal'
import * as ToolPty from '@deepseek-ai/dsh-tool-terminal'
import * as ToolGoal from '@deepseek-ai/dsh-tool-goal'
import * as ToolSchedule from '@deepseek-ai/dsh-tool-schedule'
import type ScheduleService from '@deepseek-ai/dsh-schedule'
@@ -77,7 +77,7 @@ import { registerListSubagentModels } from '../packages/subagent/tool-subagent/s
import * as ToolWeb from '@deepseek-ai/dsh-tool-web'
import WorkflowEngine from '@deepseek-ai/dsh-workflow'
import type { WorkflowRun, WorkflowStartRequest } from '@deepseek-ai/dsh-workflow'
import * as ToolRalph from '@deepseek-ai/dsh-experimental-tool-ralph'
import * as ToolRalph from '@deepseek-ai/dsh-tool-ralph'
import * as ToolWorkflow from '@deepseek-ai/dsh-tool-workflow'
import * as ToolWorkspaceDependencies from '@deepseek-ai/dsh-tool-workspace-dependencies'
import { githubSlug } from './verify-md-links.ts'
@@ -400,7 +400,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
'glob and grep are unconditional discovery tools that spawn the packaged ripgrep binary (`@vscode/ripgrep`) through ctx.subprocess as ordinary foreground calls (never background jobs) — no host `rg` install and no shell layer. The catalog uses `sampleOverCapGlobResults: true`; deployments must choose that behavior explicitly. Capped results save the complete formatted list through the optional ctx.spillStore backend; returned locators are follow-up-readable/searchable when the backend exposes local paths in co-located deployments.',
},
{
pkg: '@deepseek-ai/dsh-experimental-tool-terminal',
pkg: '@deepseek-ai/dsh-tool-terminal',
dir: 'tool-terminal',
source: 'packages/experimental/tool-terminal/src/index.ts',
requires: ['ctx.workingDirectory', 'ctx.tools', 'ctx.terminals', 'ctx.systemPrompt', 'ctx.jobs at call time for run_in_background'],
@@ -460,7 +460,7 @@ const TOOL_PACKAGES: ToolPackage[] = [
'The lsp tool keeps provider selection and language-server subprocesses behind ctx.lsp, so its model-visible schema stays stable across providers. Requires a registered provider (e.g. `@deepseek-ai/dsh-lsp-stdio`) at runtime; without one, a query returns the structured `LSP_UNAVAILABLE` error rather than changing the schema.',
},
{
pkg: '@deepseek-ai/dsh-experimental-tool-ralph',
pkg: '@deepseek-ai/dsh-tool-ralph',
dir: 'tool-ralph',
source: 'packages/experimental/tool-ralph/src/index.ts',
requires: ['ctx.tools', 'ctx.workflowEngine', 'ctx.subagents', 'ctx.systemPrompt', 'a calling Agent (exec.agent parents every fresh round)'],
+16 -1
View File
@@ -4,6 +4,7 @@ import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { dirname, join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS } from './experimental-package-policy.ts'
import { collectPluginRecords } from './plugin-record-catalog.ts'
import { persistenceCatalogArtifacts, render, renderPluginRecordCatalog } from './gen-persistence-catalog.ts'
import { extractPersistenceSchema } from './persistence-schema.ts'
@@ -43,6 +44,20 @@ function augmentation(members: string, heritage = ''): string {
const ENTRY = " /** Retains a bridge entry. */\n 'plugin:bridge/entry': SavedEntry"
describe('plugin record declarations', () => {
it.each(Object.entries(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))('retains the persisted namespace for %s', (directory, owner) => {
const namespace = directory.split('/').at(-1)!
const source = `${directory}/src/records.ts`
const root = fixture({
[`${directory}/package.json`]: JSON.stringify({ name: owner }),
[source]: augmentation(`/** Retains the capability event. */\n'plugin:${namespace}/entry': SavedEntry`),
})
expect(collectPluginRecords(root)).toEqual([{
name: `plugin:${namespace}/entry`, owner, payload: 'SavedEntry', doc: 'Retains the capability event.', source,
}])
put(root, source, augmentation("/** Another package event. */\n'plugin:unrelated/entry': SavedEntry"))
expect(() => collectPluginRecords(root)).toThrow(`must use 'plugin:${namespace}/<name>'`)
})
it('accepts an empty inventory only when the owning empty map exists', () => {
expect(collectPluginRecords(fixture())).toEqual([])
expect(() => collectPluginRecords(fixture({ [OWNER]: 'export {}\n' })))
@@ -99,7 +114,7 @@ describe('plugin record declarations', () => {
'packages/core/bridge/package.json': '{"name":"@deepseek-ai/dsh-experimental-bridge"}',
'packages/core/bridge/src/records.ts': augmentation(ENTRY),
})
expect(() => collectPluginRecords(root)).toThrow('must belong to an experimental')
expect(() => collectPluginRecords(root)).toThrow('must belong to a package declared experimental')
})
it('requires the types module augmentation and verifiable package ownership', () => {
+5 -4
View File
@@ -3,11 +3,11 @@
import { globSync, readFileSync } from 'node:fs'
import { resolve, sep } from 'node:path'
import ts from 'typescript'
import { experimentalPackageRecordNamespace } from './experimental-package-policy.ts'
import { parseJsDoc, pointer, rawJsDoc, reportViolations } from './jsdoc.ts'
const SESSION_PACKAGE = '@deepseek-ai/dsh-session'
const SESSION_TYPES_MODULE = `${SESSION_PACKAGE}/types`
const PACKAGE_PREFIX = '@deepseek-ai/dsh-experimental-'
const RECORD_NAME = /^plugin:[a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)+$/u
const printer = ts.createPrinter({ removeComments: true })
@@ -94,11 +94,12 @@ export function collectPluginRecords(scanRoot: string): PluginRecordEntry[] {
violations.push(`${where} must augment declare module '${SESSION_TYPES_MODULE}'.`)
return
}
if (!file.startsWith('packages/experimental/') || owner === undefined || !owner.startsWith(PACKAGE_PREFIX)) {
violations.push(`${where} must belong to an experimental ${PACKAGE_PREFIX}<name> package.`)
const namespace = owner === undefined ? undefined : experimentalPackageRecordNamespace(owner)
if (!file.startsWith('packages/experimental/') || owner === undefined || namespace === undefined) {
violations.push(`${where} must belong to a package declared experimental by the package naming policy.`)
return
}
const prefix = `plugin:${owner.slice(PACKAGE_PREFIX.length)}/`
const prefix = `plugin:${namespace}/`
for (const member of node.members) {
const location = pointer(file, source, member)
if (!ts.isPropertySignature(member) || member.type === undefined || member.questionToken !== undefined
+9 -9
View File
@@ -64,25 +64,25 @@ describe('release families', () => {
'@deepseek-ai/dsh-experimental-computer-use-cua-driver-mcp',
'@deepseek-ai/dsh-experimental-computer-use-cua-driver-native',
'@deepseek-ai/dsh-experimental-cot-translation-bundle',
'@deepseek-ai/dsh-experimental-hook-protocol',
'@deepseek-ai/dsh-experimental-hooks-claude-code',
'@deepseek-ai/dsh-experimental-hooks-codex',
'@deepseek-ai/dsh-hook-protocol',
'@deepseek-ai/dsh-hooks-claude-code',
'@deepseek-ai/dsh-hooks-codex',
'@deepseek-ai/dsh-experimental-inspector-profile',
'@deepseek-ai/dsh-experimental-inspector',
'@deepseek-ai/dsh-experimental-ptc-runtime-python',
'@deepseek-ai/dsh-experimental-session-inspector',
'@deepseek-ai/dsh-experimental-session-title-all-prompts-llm',
'@deepseek-ai/dsh-experimental-skill-badge',
'@deepseek-ai/dsh-session-title-all-prompts-llm',
'@deepseek-ai/dsh-skill-badge',
'@deepseek-ai/dsh-experimental-speech-to-text-sensevoice',
'@deepseek-ai/dsh-experimental-speech-to-text',
'@deepseek-ai/dsh-experimental-tool-agent-team',
'@deepseek-ai/dsh-experimental-tool-ralph',
'@deepseek-ai/dsh-experimental-tool-terminal',
'@deepseek-ai/dsh-tool-ralph',
'@deepseek-ai/dsh-tool-terminal',
'@deepseek-ai/dsh-experimental-tool-worktree',
'@deepseek-ai/dsh-experimental-translator',
'@deepseek-ai/dsh-experimental-voice-input-bundle',
'@deepseek-ai/dsh-experimental-webhook-github',
'@deepseek-ai/dsh-experimental-webhook',
'@deepseek-ai/dsh-webhook-github',
'@deepseek-ai/dsh-webhook',
'@deepseek-ai/dsh-experimental-webworker-packer',
'@deepseek-ai/dsh-experimental-webworker-runtime',
'@deepseek-ai/dsh-experimental-worktree',
@@ -4,6 +4,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 { EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS } from '../experimental-package-policy.ts'
import { verifyInstalledProductIsolation } from './installed-product-isolation.ts'
const roots: string[] = []
@@ -26,7 +27,7 @@ afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true })
})
describe('installed default-product isolation', () => {
describe.each([experimental, ...Object.values(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS)])('installed default-product isolation: %s', (experimental) => {
it('ignores development dependencies and unrelated installed experimental packages', () => {
const root = fixture()
const entry = writePackage(root, '@deepseek-ai/dsh', {
@@ -3,6 +3,7 @@
import { existsSync, readFileSync, realpathSync } from 'node:fs'
import { createRequire } from 'node:module'
import { join } from 'node:path'
import { hasExperimentalPackageReference } from '../experimental-package-policy.ts'
import { OPTIONAL_BUNDLES } from '../../packages/boot/app-boot/src/profile.ts'
interface InstalledManifest {
@@ -56,7 +57,7 @@ export function verifyInstalledProductIsolation(directory: string, optionalBundl
}
function rejectExperimental(name: string, chain: readonly string[]): void {
if (name.startsWith('@deepseek-ai/dsh-experimental-')) {
if (hasExperimentalPackageReference(name)) {
throw new Error(`default product includes an experimental package: ${chain.join(' -> ')}`)
}
}
@@ -5,6 +5,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { afterEach, describe, expect, it } from 'vitest'
import { EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS } from './experimental-package-policy.ts'
import { verifyDefaultProductIsolation } from './verify-default-product-isolation.ts'
import { writeFixtureFile as write } from './fixture-file.ts'
@@ -47,6 +48,32 @@ afterEach(() => {
})
describe('default product isolation', () => {
describe.each(Object.entries(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))('retained experimental identity %s', (directory, name) => {
it.each(['direct dependency', 'transitive dependency', 'npm alias', 'value import', 'subpath import', 'profile row', 'preset row'])(
'rejects default product use through a %s', (route) => {
const root = fixture()
write(root, `${directory}/package.json`, { name })
if (route === 'direct dependency') manifest(root, 'apps/cli/package.json', { dependencies: { [name]: 'workspace:*' } })
if (route === 'transitive dependency') manifest(root, 'packages/core/core/package.json', { dependencies: { [name]: 'workspace:*' } })
if (route === 'npm alias') manifest(root, 'packages/core/core/package.json', { dependencies: { alias: `npm:${name}@1.0.0` } })
if (route === 'value import') write(root, 'apps/cli/src/bin.ts', `import '${name}'\n`)
if (route === 'subpath import') write(root, 'apps/cli/src/bin.ts', `import '${name}/feature'\n`)
if (route === 'profile row') write(root, patch, [{ insert: [{ name }] }])
if (route === 'preset row') write(root, preset, [{ insert: [{ name: '@deepseek-ai/dsh-agent-preset', config: { id: 'standard', plugins: [{ name }] } }] }])
expect(verifyDefaultProductIsolation(root).failures).toEqual(expect.arrayContaining([
expect.stringMatching(new RegExp(`${name}.*default product must not include experimental packages`)),
]))
},
)
it('allows a type-only reference without promoting it to runtime use', () => {
const root = fixture()
write(root, `${directory}/package.json`, { name })
write(root, 'apps/cli/src/bin.ts', `import type { Options } from '${name}'\nexport type { Options }\n`)
expect(verifyDefaultProductIsolation(root).failures).toEqual([])
})
})
it.each(['@deepseek-ai/libreoffice-kit'])(
'accepts independently published %s but rejects unknown workspace packages', (name) => {
const root = fixture()
+3 -3
View File
@@ -20,7 +20,7 @@ import {
collectRuntimeSourceSpecifiers,
} from './verify-client-packages.ts'
const EXPERIMENTAL_PREFIX = '@deepseek-ai/dsh-experimental-'
import { isExperimentalPackageName } from './experimental-package-policy.ts'
// The independently published entry package owns platform-engine dependencies.
const EXTERNAL_KIT_PACKAGES = new Set(['@deepseek-ai/libreoffice-kit'])
const PROFILE_SOURCE = 'packages/boot/app-boot/src/profile.ts'
@@ -107,7 +107,7 @@ export function verifyDefaultProductIsolation(root: string): ProductIsolationRes
const sources = new Set<string>()
const configs = new Set<string>()
let webPluginCount = 0
const isExperimental = (pkg: Package): boolean => pkg.manifest.name.startsWith(EXPERIMENTAL_PREFIX)
const isExperimental = (pkg: Package): boolean => isExperimentalPackageName(pkg.manifest.name)
|| display(pkg.directory).startsWith('packages/experimental/')
const add = (pkg: Package, origin: string): void => {
if (isExperimental(pkg)) {
@@ -136,7 +136,7 @@ export function verifyDefaultProductIsolation(root: string): ProductIsolationRes
return
}
const packageName = barePackageName(name)
if (packageName.startsWith(EXPERIMENTAL_PREFIX)) {
if (isExperimentalPackageName(packageName)) {
failures.push(`${origin} -> ${name}: default product must not include experimental packages`)
return
}
@@ -170,7 +170,7 @@ const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
'packages/host/directory-picker-browse': { kind: 'none', reason: 'The GUI-host picking backend registers nothing model-facing.' },
'packages/host/directory-picker-native': { kind: 'none', reason: 'The GUI-host picking backend registers nothing model-facing.' },
'packages/host/webserver': { kind: 'none', reason: 'The HTTP carrier bridges browser and API handler and registers nothing model-facing.' },
'packages/experimental/webhook-github': { kind: 'indirect', reason: 'The adapter delegates model-visible text to matching rules and dsh-experimental-webhook.' },
'packages/experimental/webhook-github': { kind: 'indirect', reason: 'The adapter delegates model-visible text to matching rules and dsh-webhook.' },
'packages/host/frontend-static': { kind: 'none', reason: 'The SPA dist server answers browser asset requests and registers nothing model-facing.' },
'packages/host/plugin-inventory': { kind: 'none', reason: 'Host-side read-only Loader projection; registers nothing model-facing.' },
'packages/host/open-in-app': { kind: 'none', reason: 'Host routes opening desktop applications for a human; registers nothing model-facing.' },
+16 -2
View File
@@ -8,6 +8,7 @@ import { pathToFileURL } from 'node:url'
import { describe, expect, it, onTestFinished, vi } from 'vitest'
import { browserDependencyAnalysis, productWebBundleIsolation } from '../apps/web/product-isolation.ts'
import { WebProductBundleIsolation } from './web-product-bundle-isolation.ts'
import { EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS } from './experimental-package-policy.ts'
import { BundleInputIsolation } from './bundle-input-isolation.ts'
const filesystem = vi.hoisted(() => ({ missingRoot: false }))
@@ -120,11 +121,11 @@ describe('default Web bundle input isolation', () => {
.rejects.toThrow(/Web product isolation: experimental input/)
})
it('checks npm alias package identity independently of its directory name', async () => {
it.each(['@deepseek-ai/dsh-experimental-aliased', ...Object.values(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS)])('checks npm alias package identity %s independently of its directory name', async (name) => {
const test = fixture()
test.write('apps/web/src/main.js', 'import "ordinary-name"')
test.write('apps/web/node_modules/ordinary-name/package.json',
'{"name":"@deepseek-ai/dsh-experimental-aliased","type":"module","main":"index.js"}')
JSON.stringify({ name, type: 'module', main: 'index.js' }))
test.write('apps/web/node_modules/ordinary-name/index.js', 'globalThis.aliased = true')
await expect(test.run()).rejects.toThrow(/belongs to experimental package/)
})
@@ -221,6 +222,19 @@ describe('default Web bundle input isolation', () => {
})
describe('bundler input ownership', () => {
it.each(Object.values(EXPERIMENTAL_PACKAGE_NAME_EXCEPTIONS))('rejects retained identity %s in unresolved inputs and source-map ownership', (name) => {
const test = fixture()
const ownership = new BundleInputIsolation(test.root, 'fixture')
for (const reference of [name, `${name}/client`, `\u0000virtual:${name}/client?raw`]) {
expect(() => { ownership.assertInput(reference) }).toThrow(/experimental input/)
}
const absentWindowsSource = `C:\\omitted\\node_modules\\${name.replaceAll('/', '\\')}\\src\\index.ts`
expect(() => { ownership.assertSourceMapInput(absentWindowsSource) }).toThrow(/experimental input/)
test.write('apps/web/node_modules/upstream/package.json', JSON.stringify({ name }))
expect(() => { ownership.assertSourceMapInput(join(test.web, 'node_modules/upstream/src/omitted.js')) })
.toThrow(/belongs to experimental package/)
})
it('refuses source-map paths whose filesystem root is unavailable', () => {
const test = fixture()
const ownership = new BundleInputIsolation(test.root, 'fixture')