mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Merge remote-tracking branch 'origin/master' into feat/pwsh-persistent-pty
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
/** Experimental-package publication and dependency constraints. */
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
checkExperimentalDependencyIsolation,
|
||||
checkExperimentalManifest,
|
||||
type WorkspaceManifest,
|
||||
} from './check-workspace-constraints.ts'
|
||||
|
||||
const experimental: WorkspaceManifest = {
|
||||
dir: 'packages/experimental/prototype',
|
||||
manifest: { name: '@deepseek-ai/dsh-prototype', private: true },
|
||||
}
|
||||
|
||||
describe('experimental workspace constraints', () => {
|
||||
it('requires private manifests without publication metadata', () => {
|
||||
expect(checkExperimentalManifest(experimental)).toEqual([])
|
||||
expect(checkExperimentalManifest({
|
||||
...experimental,
|
||||
manifest: { ...experimental.manifest, private: false, publishConfig: { access: 'public' } },
|
||||
})).toEqual([
|
||||
'@deepseek-ai/dsh-prototype: experimental package must set "private": true',
|
||||
'@deepseek-ai/dsh-prototype: experimental package must omit publishConfig',
|
||||
])
|
||||
})
|
||||
|
||||
it.each(['dependencies', 'optionalDependencies', 'peerDependencies'] as const)(
|
||||
'rejects release %s on an experimental package',
|
||||
(section) => {
|
||||
expect(checkExperimentalDependencyIsolation([experimental, {
|
||||
dir: 'packages/core/consumer',
|
||||
manifest: {
|
||||
name: '@deepseek-ai/dsh-consumer',
|
||||
[section]: { '@deepseek-ai/dsh-prototype': 'workspace:^' },
|
||||
},
|
||||
}])).toEqual([
|
||||
`@deepseek-ai/dsh-consumer: ${section}.@deepseek-ai/dsh-prototype must not reference an experimental package`,
|
||||
])
|
||||
},
|
||||
)
|
||||
|
||||
it('allows development and experimental consumers but rejects the Python release runtime', () => {
|
||||
const manifests: WorkspaceManifest[] = [experimental, {
|
||||
dir: 'packages/core/test-only',
|
||||
manifest: {
|
||||
name: '@deepseek-ai/dsh-test-only',
|
||||
devDependencies: { '@deepseek-ai/dsh-prototype': 'workspace:^' },
|
||||
},
|
||||
}, {
|
||||
dir: 'packages/experimental/consumer',
|
||||
manifest: {
|
||||
name: '@deepseek-ai/dsh-experimental-consumer',
|
||||
dependencies: { '@deepseek-ai/dsh-prototype': 'workspace:^' },
|
||||
},
|
||||
}, {
|
||||
dir: 'python/sdk-runtime',
|
||||
manifest: {
|
||||
name: '@deepseek-ai/dsh-python-runtime',
|
||||
dependencies: { '@deepseek-ai/dsh-prototype': 'workspace:^' },
|
||||
},
|
||||
}]
|
||||
|
||||
expect(checkExperimentalDependencyIsolation(manifests)).toEqual([
|
||||
'@deepseek-ai/dsh-python-runtime: dependencies.@deepseek-ai/dsh-prototype must not reference an experimental package',
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
||||
import { join, relative, resolve } from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { hasTypertRemoteNavigation, isForbiddenPublicationFile } from './publication-payload.ts'
|
||||
import { collectProjectReferenceFaceViolations } from './project-reference-faces.ts'
|
||||
|
||||
@@ -47,8 +48,10 @@ const repositoryUrl = 'git+https://github.com/deepseek-harness/deepseek-harness.
|
||||
* their trusted publishing against the repository that runs the workflow.
|
||||
*/
|
||||
const publishedRepositoryUrl = 'git+https://github.com/deepseek-ai/deepseek-harness.git'
|
||||
/** Private packages that participate in workspace checks but not releases. */
|
||||
const experimentalPackageDirectory = /^packages\/experimental\/[^/]+$/
|
||||
/** Directories whose packages this repository publishes: one release member each. */
|
||||
const releaseMemberDirectory = /^(?:packages\/[^/]+\/[^/]+|apps\/[^/]+|vendor\/[^/]+)$/
|
||||
const releaseMemberDirectory = /^(?:packages\/(?!experimental\/)[^/]+\/[^/]+|apps\/[^/]+|vendor\/[^/]+)$/
|
||||
|
||||
const localArtifactDirs = new Set(['node_modules'])
|
||||
const appPackageFiles: Readonly<Record<string, readonly string[]>> = {
|
||||
@@ -59,7 +62,7 @@ const appPackageFiles: Readonly<Record<string, readonly string[]>> = {
|
||||
}
|
||||
|
||||
/** The subset of package.json fields this constraint check cares about. */
|
||||
interface PackageManifest {
|
||||
export interface PackageManifest {
|
||||
name?: string
|
||||
version?: string
|
||||
private?: boolean
|
||||
@@ -87,7 +90,7 @@ interface PackageManifest {
|
||||
}
|
||||
|
||||
/** One workspace manifest and its repo-relative path. */
|
||||
interface WorkspaceManifest {
|
||||
export interface WorkspaceManifest {
|
||||
dir: string
|
||||
manifest: PackageManifest
|
||||
}
|
||||
@@ -228,8 +231,18 @@ function usesEmittedTreeDefaults(manifest: PackageManifest): boolean {
|
||||
exportDefault(manifest, subpath)?.startsWith('./lib/types/') === true)
|
||||
}
|
||||
|
||||
function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
|
||||
/** Experimental manifest requirements enforced independently from release metadata. */
|
||||
export function checkExperimentalManifest({ dir, manifest }: WorkspaceManifest): string[] {
|
||||
if (!experimentalPackageDirectory.test(dir)) return []
|
||||
const label = manifest.name ?? dir
|
||||
const errors: string[] = []
|
||||
if (manifest.private !== true) errors.push(`${label}: experimental package must set "private": true`)
|
||||
if (manifest.publishConfig !== undefined) errors.push(`${label}: experimental package must omit publishConfig`)
|
||||
return errors
|
||||
}
|
||||
|
||||
function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
|
||||
const errors = checkExperimentalManifest({ dir, manifest })
|
||||
const label = manifest.name ?? dir
|
||||
const isLandlockPackageDir = dir.startsWith('native/landlock-run/packages/')
|
||||
const isPublicLandlockPackage = isLandlockPackageDir
|
||||
@@ -271,7 +284,7 @@ function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
|
||||
|| manifest.repository.directory !== dir) {
|
||||
errors.push(`${label}: release member repository must use ${publishedRepositoryUrl} with directory ${dir}`)
|
||||
}
|
||||
} else if (manifest.private !== true) {
|
||||
} else if (!experimentalPackageDirectory.test(dir) && manifest.private !== true) {
|
||||
errors.push(`${label}: package.json must set "private": true`)
|
||||
}
|
||||
|
||||
@@ -390,6 +403,31 @@ function checkRepositoryVersion(): string[] {
|
||||
|
||||
/** Dependency sections whose ranges reach a published tarball or a local install. */
|
||||
const dependencySections = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'] as const
|
||||
/** Dependency sections present in an installed runtime. */
|
||||
const runtimeDependencySections = ['dependencies', 'optionalDependencies', 'peerDependencies'] as const
|
||||
|
||||
/**
|
||||
* Prevent an official runtime from requiring a package its release omits.
|
||||
* @param manifests - release, private experimental, and deployment-root manifests.
|
||||
* @returns One error for each forbidden runtime dependency.
|
||||
*/
|
||||
export function checkExperimentalDependencyIsolation(manifests: readonly WorkspaceManifest[]): string[] {
|
||||
const experimentalNames = new Set(manifests
|
||||
.filter(entry => experimentalPackageDirectory.test(entry.dir))
|
||||
.map(entry => entry.manifest.name)
|
||||
.filter(name => name !== undefined))
|
||||
const errors: string[] = []
|
||||
for (const { dir, manifest } of manifests) {
|
||||
if (!releaseMemberDirectory.test(dir) && dir !== 'python/sdk-runtime') continue
|
||||
for (const section of runtimeDependencySections) {
|
||||
for (const name of Object.keys(manifest[section] ?? {})) {
|
||||
if (!experimentalNames.has(name)) continue
|
||||
errors.push(`${manifest.name ?? dir}: ${section}.${name} must not reference an experimental package`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
/**
|
||||
* Require the `workspace:` protocol for every reference to a workspace member.
|
||||
@@ -415,15 +453,25 @@ function checkWorkspaceProtocol(manifests: readonly WorkspaceManifest[]): string
|
||||
return errors
|
||||
}
|
||||
|
||||
const manifests = workspaceManifests()
|
||||
const errors = [
|
||||
...checkRepositoryVersion(),
|
||||
...manifests.flatMap(checkWorkspace),
|
||||
...checkWorkspaceProtocol(manifests),
|
||||
...checkHierarchyShape(),
|
||||
...collectProjectReferenceFaceViolations(root),
|
||||
]
|
||||
if (errors.length > 0) {
|
||||
console.error(errors.join('\n'))
|
||||
process.exitCode = 1
|
||||
/** Run the repository constraint gate. */
|
||||
export function main(): void {
|
||||
const manifests = workspaceManifests()
|
||||
const dependencyManifests = [
|
||||
...manifests,
|
||||
{ dir: 'python/sdk-runtime', manifest: readJson(join(root, 'python/sdk-runtime/package.json')) },
|
||||
]
|
||||
const errors = [
|
||||
...checkRepositoryVersion(),
|
||||
...manifests.flatMap(checkWorkspace),
|
||||
...checkWorkspaceProtocol(manifests),
|
||||
...checkExperimentalDependencyIsolation(dependencyManifests),
|
||||
...checkHierarchyShape(),
|
||||
...collectProjectReferenceFaceViolations(root),
|
||||
]
|
||||
if (errors.length > 0) {
|
||||
console.error(errors.join('\n'))
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) main()
|
||||
|
||||
@@ -97,6 +97,7 @@ export const SERVICE_PAGE: Record<string, string> = {
|
||||
systemPrompt: 'system-prompt.md',
|
||||
jobs: 'jobs.md',
|
||||
sessionTelemetry: 'session-telemetry.md',
|
||||
teams: 'team.md',
|
||||
tokenMeter: 'token-meter.md',
|
||||
toolResultPruner: 'compaction.md',
|
||||
tools: 'tools.md',
|
||||
@@ -423,6 +424,18 @@ export const LINK_MAP: Readonly<Record<string, string>> = {
|
||||
JobSnapshot: 'jobs.md',
|
||||
JobStart: 'jobs.md',
|
||||
JobsChangedListener: 'jobs.md',
|
||||
CreateTeamTaskRequest: 'team.md',
|
||||
SendTeamMessageRequest: 'team.md',
|
||||
SendTeamMessageResult: 'team.md',
|
||||
SpawnTeammateRequest: 'team.md',
|
||||
SpawnTeammateResult: 'team.md',
|
||||
TeamId: 'team.md',
|
||||
TeamMemberView: 'team.md',
|
||||
TeamMembership: 'team.md',
|
||||
TeamTaskId: 'team.md',
|
||||
TeamTaskView: 'team.md',
|
||||
TeamWaitResult: 'team.md',
|
||||
UpdateTeamTaskRequest: 'team.md',
|
||||
TokenMeasurement: 'token-meter.md',
|
||||
CodeDispatchLog: 'tools.md',
|
||||
PostToolDecision: 'tools.md',
|
||||
|
||||
@@ -67,6 +67,7 @@ const GROUP_ORDER = [
|
||||
'core',
|
||||
'typert',
|
||||
'goal',
|
||||
'experimental',
|
||||
'process',
|
||||
'bash',
|
||||
'pty',
|
||||
@@ -469,6 +470,14 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
consumers: ['tool-subagent', 'tool-subagent-control', 'tool-ralph'],
|
||||
note: 'Providers implement transports; the service also owns optional Activation-based continuation orchestration, tool-subagent selects one-shot or continuable delegation, tool-subagent-control delivers follow-ups, and tool-ralph requires one fresh structured-output route.',
|
||||
},
|
||||
{
|
||||
key: 'teams',
|
||||
pkg: 'team',
|
||||
title: 'Agent Teams coordination domain',
|
||||
mode: 'core',
|
||||
consumers: ['tool-team'],
|
||||
note: 'Owns the implicit-root roster, durable peer mailbox, shared task DAG, and continuable-child lifecycle; tool-team contributes the scoped model policy and controls.',
|
||||
},
|
||||
{
|
||||
key: 'jobs',
|
||||
pkg: 'jobs',
|
||||
|
||||
@@ -52,6 +52,11 @@ const LINK_MAP: Record<string, string> = {
|
||||
SessionTitleModelProvenance: 'session-title.md',
|
||||
SessionTitleProviderId: 'session-title.md',
|
||||
SessionTitleSource: 'session-title.md',
|
||||
TeamId: 'team.md',
|
||||
TeamMemberSnapshot: 'team.md',
|
||||
TeamMessageId: 'team.md',
|
||||
TeamMessageSnapshot: 'team.md',
|
||||
TeamTaskSnapshot: 'team.md',
|
||||
}
|
||||
|
||||
/** One log event, extracted from a `SessionEventMap` declaration. */
|
||||
|
||||
@@ -58,6 +58,8 @@ import * as ToolLsp from '@deepseek-ai/dsh-tool-lsp'
|
||||
import * as ToolSkill from '@deepseek-ai/dsh-tool-skill'
|
||||
import * as ToolSessionQuery from '@deepseek-ai/dsh-tool-session-query'
|
||||
import * as ToolTasks from '@deepseek-ai/dsh-tool-jobs'
|
||||
import type TeamService from '@deepseek-ai/dsh-team'
|
||||
import * as ToolTeam from '@deepseek-ai/dsh-tool-team'
|
||||
import * as ToolTodo from '@deepseek-ai/dsh-tool-todo'
|
||||
import * as ToolSubagent from '@deepseek-ai/dsh-tool-subagent'
|
||||
import * as ToolWeb from '@deepseek-ai/dsh-tool-web'
|
||||
@@ -521,6 +523,44 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
note:
|
||||
'The kind-agnostic background-job controller: background bash commands, PTY sends, and subagents are read, listed, and killed through the same three tools. Loading the plugin attaches the controller that arms producers\' `ctx.jobs.start()`.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-team',
|
||||
dir: 'tool-team',
|
||||
source: 'packages/experimental/tool-team/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.systemPrompt', 'ctx.teams', 'an exact live Team member Agent'],
|
||||
writes: ['tool/call', 'team/member', 'team/message/queued', 'team/message/delivered', 'team/task', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(SessionStore)
|
||||
const session = ctx.sessions.create(SessionId('tool-catalog-team-lead'))
|
||||
let agent!: Agent
|
||||
const membership = {
|
||||
get root() { return agent },
|
||||
id: session.id,
|
||||
role: 'lead' as const,
|
||||
name: 'lead',
|
||||
}
|
||||
ctx.provide('teams', {
|
||||
tryMembership: (candidate: Agent) => candidate === agent ? membership : undefined,
|
||||
membership: () => membership,
|
||||
} as unknown as TeamService)
|
||||
await ctx.plugin(Object.assign((inner: Context) => {
|
||||
agent = {
|
||||
id: session.id,
|
||||
session,
|
||||
options: {},
|
||||
status: 'idle',
|
||||
} as unknown as Agent
|
||||
Object.assign(agent, { ctx: createScope(inner, agent).ctx })
|
||||
inner.agents.register(agent)
|
||||
}, { inject: ['tools', 'systemPrompt', 'agents', 'teams'] }))
|
||||
await ctx.plugin(ToolTeam)
|
||||
catalogChildScopes.set(ctx, agent)
|
||||
},
|
||||
scope: ctx => catalogChildScopes.get(ctx) as Agent,
|
||||
note:
|
||||
'All ten tools are scoped to implicit Team Leads and durable teammates. The shipped dsh-base bundle keeps the package disabled; the documented Agent Teams profile patch enables it while disabling the legacy continuable-child control names.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-todo',
|
||||
dir: 'tool-todo',
|
||||
|
||||
@@ -24,7 +24,7 @@ const DEFAULT_REGISTRY = 'https://registry.npm.harnessment.com'
|
||||
const DEFAULT_OUTPUT_DIRECTORY = '.artifacts/npm-baseline'
|
||||
const PACKAGE_PATTERNS = [
|
||||
'vendor/*/package.json',
|
||||
'packages/*/*/package.json',
|
||||
'packages/!(experimental)/*/package.json',
|
||||
'apps/*/package.json',
|
||||
] as const
|
||||
const DEPENDENCY_SECTIONS = [
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/** Release family discovery, publish order, tag naming, and the bump judgements. */
|
||||
|
||||
import { resolve } from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { releaseFamily, type ReleaseMember } from './families.ts'
|
||||
import { compareVersions, nextVendorVersion, reachesPayload } from './bump.ts'
|
||||
@@ -16,6 +17,13 @@ function member(directory: string, name: string, manifest: Record<string, unknow
|
||||
}
|
||||
|
||||
describe('release families', () => {
|
||||
it('excludes private experimental packages from the dsh release', () => {
|
||||
const members = releaseFamily('dsh').members(resolve(import.meta.dirname, '../..'))
|
||||
|
||||
expect(members.some(member => member.directory.startsWith('packages/experimental/'))).toBe(false)
|
||||
expect(members.map(member => member.name)).not.toContain('@deepseek-ai/dsh-team')
|
||||
})
|
||||
|
||||
it('names one tag for the whole dsh family and one per vendored package', () => {
|
||||
const dsh = releaseFamily('dsh')
|
||||
const vendor = releaseFamily('vendor')
|
||||
|
||||
@@ -305,10 +305,10 @@ export abstract class ReleaseFamily {
|
||||
abstract readonly installedEntry: InstalledEntry | undefined
|
||||
}
|
||||
|
||||
/** `packages/*` and `apps/*`: one shared version across the whole family. */
|
||||
/** Release packages and apps: one shared version across the whole family. */
|
||||
class DshFamily extends ReleaseFamily {
|
||||
readonly id = 'dsh'
|
||||
readonly patterns = ['packages/*/*/package.json', 'apps/*/package.json'] as const
|
||||
readonly patterns = ['packages/!(experimental)/*/package.json', 'apps/*/package.json'] as const
|
||||
readonly tagPrefix = 'dsh-v'
|
||||
|
||||
/**
|
||||
|
||||
@@ -301,6 +301,26 @@
|
||||
"symbol": "ScheduleView",
|
||||
"source": "packages/schedule/schedule/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/team.md",
|
||||
"symbol": "TeamMemberSnapshot",
|
||||
"source": "packages/experimental/team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/team.md",
|
||||
"symbol": "TeamMessageSnapshot",
|
||||
"source": "packages/experimental/team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/team.md",
|
||||
"symbol": "TeamMessageSource",
|
||||
"source": "packages/experimental/team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/team.md",
|
||||
"symbol": "TeamTaskSnapshot",
|
||||
"source": "packages/experimental/team/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/commands.md",
|
||||
"symbol": "CommandInputDescriptor",
|
||||
|
||||
Reference in New Issue
Block a user