The branch label left the Session header, and finding which Sessions work in their own worktrees, or which of their change requests need attention, meant opening each Session's tab. - A Session row shows a branch glyph while the Session works in the worktree the panel created for it, and the state glyph of its most urgent change request with a dot for its checks. The row's hover card names the worktree branch and its base, then up to three change requests with their state and checks. - The forgeWorktree projection follows the Session's working directory; its client view is the created worktree while the Session works in it. The forgeChangeRequests projection gains a client view with the bindings, so the Session summary carries both without loading a Session. - The forgePanel.changeRequestStates stream serves the states of the change requests every listed Session binds from one Host run. It polls each until it is merged or closed and stores every change in the forge_panel_change_requests storage domain, so a restart shows the last known states before the next poll. - A new Agent Note records the decision; the READMEs, the forge subsystem page, and the catalogs follow.
121 lines
5.9 KiB
TypeScript
121 lines
5.9 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 forge and worktree peers into dependencies.
|
|
'@deepseek-ai/dsh-experimental-forge': ['changeRequestKey'],
|
|
'@deepseek-ai/dsh-experimental-worktree': ['generatedWorktreeName'],
|
|
// The forge panel matches ctx.fs failures with `instanceof FsError`.
|
|
'@deepseek-ai/dsh-fs': ['FsError'],
|
|
'@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'],
|
|
// Stateless spec helpers; the forge panel opens its domain through the Host's own storage service.
|
|
'@deepseek-ai/dsh-storage-domain': ['defineDomain', 'domainTable'],
|
|
// 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')
|
|
}
|