Files
deepseek-harness/scripts/package-dependency-policy.ts
T
Yichen Jiang 35953a75de fix(forge): classify the panel's Host imports under the published dependency policy
verify-package-dependencies now covers experimental Client packages, and the
forge panel's Host face imported four workspace values that no policy entry
classified. appendPluginRecord writes through the Session class's private log,
and readers recognize a deadline's TimeoutReason only by instanceof, so both
need the provider's shared instance. pluginRecordOf and generatedWorktreeName
keep no state; classifying them as peer-required keeps the panel's existing
Session and worktree peer edges.

With the exports classified, the fixer keeps worktree, Session, and timeout as
the panel's only workspace peers, moves its type-only relationships to
devDependencies and dsh-typert-protocol to dependencies, and regenerates the
lockfile and the module graph.

verify-plugin-record-callers counts a string literal that spells
appendPluginRecord as a caller, so it now admits the dependency policy file,
which names the export to classify it and never calls it.
2026-10-20 12:02:57 +08:00

116 lines
5.6 KiB
TypeScript

/** Explicit exceptions and Host packages for the published dependency policy. */
/** Packages treated as Client/Host packages without declaring `dsh.client`. */
const CLIENT_FACE_INCLUDE: readonly string[] = []
/** Packages exempted from automatic Client/Host treatment despite declaring `dsh.client`. */
const CLIENT_FACE_EXCLUDE: readonly string[] = [
'@deepseek-ai/dsh-api-session-controller',
'@deepseek-ai/dsh-api-workspace-controller',
]
/** Host-only packages whose peer relays are deliberately flattened. */
const HOST_DEPENDENCY_PACKAGES: readonly string[] = [
'@deepseek-ai/dsh-llm',
'@deepseek-ai/dsh-session',
]
/** Required Cordis services whose Host imports are type-only. */
const REQUIRED_SERVICE_PEERS = {
'@deepseek-ai/dsh-api-terminal-controller': ['@deepseek-ai/dsh-subprocess'],
} as const satisfies Readonly<Record<string, readonly string[]>>
/** Development-only package relationships not represented by source imports. */
const CONFIGURATION_ONLY_DEV_DEPENDENCIES = {
'@deepseek-ai/dsh-client-locale': ['@deepseek-ai/dsh-api-remotes'],
'@deepseek-ai/dsh-client-ui-conversation': [
'@deepseek-ai/dsh-api-remotes',
'@deepseek-ai/dsh-client-ui-workspace',
],
'@deepseek-ai/dsh-client-ui-model-selection': ['@deepseek-ai/dsh-client-ui-input-trigger'],
'@deepseek-ai/dsh-client-ui-sidebar': ['@deepseek-ai/dsh-client-ui-workspace'],
'@deepseek-ai/dsh-client-ui-subagent': ['@deepseek-ai/dsh-client-ui-input-trigger'],
'@deepseek-ai/dsh-client-ui-theme': ['@deepseek-ai/dsh-api-remotes'],
'@deepseek-ai/dsh-client-ui-tool': ['@deepseek-ai/dsh-api-remotes'],
} as const satisfies Readonly<Record<string, readonly string[]>>
/** Workspace packages whose complete runtime surface is safe across duplicate installations. */
const DUPLICATE_SAFE_PACKAGES: readonly string[] = [
'@deepseek-ai/dsh-brand',
'@deepseek-ai/dsh-lazy-require',
'@deepseek-ai/dsh-typert-protocol',
'@deepseek-ai/dsh-util-code-language',
'@deepseek-ai/dsh-util-crypto',
'@deepseek-ai/dsh-util-values',
]
/**
* Runtime exports whose values remain valid when npm installs another package copy.
* New entries are forbidden by default. Automated agents must not add an
* exception; every addition requires explicit human review and a dedicated,
* prominent heading in the pull request description.
*/
const SAFE_HOST_DEPENDENCY_EXPORTS = {
'@deepseek-ai/dsh-atomic-write': ['withFileLock'],
'@deepseek-ai/dsh-computer-use/brand': ['ComputerUseProviderName'],
'@deepseek-ai/dsh-credentials': ['credentialKey'],
'@deepseek-ai/dsh-deque': ['Deque'],
'@deepseek-ai/dsh-llm': ['callConfigEquals'],
'@deepseek-ai/dsh-session-format': ['sessionFormatLogFilename'],
'@deepseek-ai/dsh-timeout': ['MAX_TIMER_DELAY_MS'],
'@deepseek-ai/schemastery': ['default'],
} as const satisfies HostDependencyExports
/** Runtime exports that require every consumer to resolve the provider's shared peer instance. */
const PEER_REQUIRED_HOST_EXPORTS = {
// CUA checks UserQuestionError and preserves AttachmentError from image admission.
'@deepseek-ai/dsh-attachment': ['admitEncodedImages'],
'@deepseek-ai/dsh-user-questions': ['UserQuestionError'],
// `instanceof` checks need the translator's own class instance.
'@deepseek-ai/dsh-experimental-translator': ['TranslationError'],
// Stateless; a safe classification would turn the forge panel's worktree peer into a dependency.
'@deepseek-ai/dsh-experimental-worktree': ['generatedWorktreeName'],
'@deepseek-ai/dsh-scope': ['carrierKeyOf', 'createScope', 'scopeOf', 'scopeTarget'],
// appendPluginRecord writes through the Session class's private log; pluginRecordOf is stateless.
'@deepseek-ai/dsh-session': ['SESSION_FORMAT_VERSION', 'appendPluginRecord', 'pluginRecordOf'],
'@deepseek-ai/dsh-session-persistence': ['SessionPersistenceNotFoundError'],
// Readers recognize a deadline's TimeoutReason only by `instanceof` against their own copy.
'@deepseek-ai/dsh-timeout': ['deadline'],
} as const satisfies HostDependencyExports
/** Exact import specifier to reviewed runtime exports. */
type HostDependencyExports = Readonly<Record<string, readonly string[]>>
/** Complete configurable input to package dependency classification. */
export interface PackageDependencyPolicy {
readonly clientFaceInclude: readonly string[]
readonly clientFaceExclude: readonly string[]
readonly hostPackages: readonly string[]
readonly requiredServicePeers?: Readonly<Record<string, readonly string[]>>
readonly configurationOnlyDevDependencies: Readonly<Record<string, readonly string[]>>
readonly duplicateSafePackages?: readonly string[]
readonly safeHostDependencyExports: HostDependencyExports
readonly peerRequiredHostExports: HostDependencyExports
}
/** Repository dependency policy consumed by verification and benchmarking. */
export const PACKAGE_DEPENDENCY_POLICY: PackageDependencyPolicy = {
clientFaceInclude: CLIENT_FACE_INCLUDE,
clientFaceExclude: CLIENT_FACE_EXCLUDE,
hostPackages: HOST_DEPENDENCY_PACKAGES,
requiredServicePeers: REQUIRED_SERVICE_PEERS,
configurationOnlyDevDependencies: CONFIGURATION_ONLY_DEV_DEPENDENCIES,
duplicateSafePackages: DUPLICATE_SAFE_PACKAGES,
safeHostDependencyExports: SAFE_HOST_DEPENDENCY_EXPORTS,
peerRequiredHostExports: PEER_REQUIRED_HOST_EXPORTS,
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
/** Whether a package manifest declares a dynamically loaded Client entry. */
export function hasClientDeclaration(dshField: unknown): boolean {
return isRecord(dshField) && Object.hasOwn(dshField, 'client')
}