mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
Merge remote-tracking branch 'origin/master' into fix/landlock-runner-failure-classification
# Conflicts: # .agents/notes/implemented/feature/2026-07-06-sandbox.i18n.yaml # .agents/notes/implemented/feature/2026-07-06-sandbox.zh.md # docs/core-data-structures/sandbox.i18n.yaml # docs/core-data-structures/sandbox.zh.md # docs/postmortem/README.i18n.yaml # docs/postmortem/README.zh.md # examples/acp-agent/tests/acp.snapshot.ts # native/landlock-run/README.i18n.yaml # packages/sandbox/sandbox-local/README.i18n.yaml # packages/sandbox/sandbox-local/README.zh.md # packages/sandbox/sandbox/README.i18n.yaml
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
||||
import { join, relative, resolve } from 'node:path'
|
||||
import { isForbiddenPublicationFile } from './publication-payload.ts'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
// vendor/* is single-level; packages/<group>/<pkg> nests one level deeper
|
||||
@@ -14,6 +15,7 @@ const root = resolve(import.meta.dirname, '..')
|
||||
const workspaceGlobs = [
|
||||
{ dir: 'vendor', depth: 1 },
|
||||
{ dir: 'packages', depth: 2 },
|
||||
{ dir: 'apps', depth: 1 },
|
||||
] as const
|
||||
const vendoredPackages = new Set([
|
||||
'cordis',
|
||||
@@ -28,6 +30,10 @@ const vendoredPackages = new Set([
|
||||
])
|
||||
|
||||
const localArtifactDirs = new Set(['node_modules'])
|
||||
const appPackageFiles: Readonly<Record<string, readonly string[]>> = {
|
||||
'@deepseek-ai/dsh': ['lib/*.js', 'config'],
|
||||
'@deepseek-ai/dsh-frontend': ['dist'],
|
||||
}
|
||||
|
||||
/** The subset of package.json fields this constraint check cares about. */
|
||||
interface PackageManifest {
|
||||
@@ -96,7 +102,9 @@ function workspaceManifests(): WorkspaceManifest[] {
|
||||
}
|
||||
|
||||
const packageFileExtras: Readonly<Record<string, readonly string[]>> = {
|
||||
'@deepseek-ai/dsh-client-ui-theme': ['lib/styles'],
|
||||
'@deepseek-ai/dsh-helper': ['lib/assets'],
|
||||
'@deepseek-ai/dsh-pty-local': ['scripts/ensure-spawn-helper.mjs'],
|
||||
'@deepseek-ai/dsh-scripts': [
|
||||
'lib/dev/tsdown-config.js',
|
||||
'lib/local-plugin-loader-hooks.js',
|
||||
@@ -133,8 +141,6 @@ function expectedDshPackageFiles(manifest: PackageManifest): readonly string[] {
|
||||
// declarations.
|
||||
...usesEmittedTreeDefaults(manifest) ? ['lib/types/**/*.js'] : [],
|
||||
'lib/types/**/*.d.ts',
|
||||
'lib/types/**/*.d.ts.map',
|
||||
'src',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -164,7 +170,24 @@ function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
|
||||
return errors
|
||||
}
|
||||
|
||||
if (manifest.name?.startsWith('@deepseek-ai/dsh-') && manifest.name !== '@deepseek-ai/dsh-root') {
|
||||
if (manifest.name?.startsWith('@deepseek-ai/')) {
|
||||
for (const file of manifest.files ?? []) {
|
||||
if (isForbiddenPublicationFile(file)) {
|
||||
errors.push(`${label}: package.json files must not publish ${JSON.stringify(file)}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dir.startsWith('apps/') && manifest.name?.startsWith('@deepseek-ai/')) {
|
||||
const expectedFiles = appPackageFiles[manifest.name]
|
||||
if (expectedFiles === undefined) {
|
||||
errors.push(`${label}: app package has no publication files policy`)
|
||||
} else if (!sameStringList(manifest.files, expectedFiles)) {
|
||||
errors.push(`${label}: package.json files must be ${JSON.stringify(expectedFiles)}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (dir.startsWith('packages/') && manifest.name?.startsWith('@deepseek-ai/dsh-')) {
|
||||
const peer = manifest.peerDependencies?.cordis
|
||||
const dev = manifest.devDependencies?.cordis
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"docs/architecture.md": 2160,
|
||||
"docs/cordis-primer.md": 600,
|
||||
"docs/defensive-patterns.md": 550,
|
||||
"docs/testing.md": 1100,
|
||||
"docs/testing.md": 1150,
|
||||
"examples/AGENTS.md": 310,
|
||||
"packages/AGENTS.md": 675,
|
||||
"packages/README.md": 920
|
||||
|
||||
@@ -328,16 +328,17 @@ const SERVICE_ROLES: ServiceRole[] = [
|
||||
pkg: 'bash',
|
||||
title: 'Bash executor seam',
|
||||
mode: 'seam',
|
||||
implementations: ['bash-local', 'bash-sandbox'],
|
||||
consumers: ['tool-bash', 'hooks-claude', 'hooks-codex'],
|
||||
note: 'The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors replace bash-local without touching them.',
|
||||
implementations: ['bash-local', 'bash-sandbox', 'pwsh-local'],
|
||||
consumers: ['tool-bash', 'tool-pwsh', 'hooks-claude', '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.',
|
||||
},
|
||||
{
|
||||
key: 'bashEnv',
|
||||
pkg: 'tool-bash',
|
||||
pkg: 'bash-env',
|
||||
title: 'Managed bash environment registry',
|
||||
mode: 'core',
|
||||
note: 'Plugins declare effect-scoped DSH_* facts; tool-bash collects one trusted snapshot per execution and the executor rebuilds the namespace.',
|
||||
consumers: ['tool-bash', 'tool-pwsh'],
|
||||
note: 'Plugins declare effect-scoped DSH_* facts; each shell tool collects one trusted snapshot per execution and its executor rebuilds the namespace.',
|
||||
},
|
||||
{
|
||||
key: 'pty',
|
||||
|
||||
@@ -19,6 +19,8 @@ import GoalService from '@deepseek-ai/dsh-goal'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry, { type Config as ToolsConfig } from '@deepseek-ai/dsh-tools'
|
||||
import LocalBashExecutor from '@deepseek-ai/dsh-bash-local'
|
||||
import * as BashEnvPlugin from '@deepseek-ai/dsh-bash-env'
|
||||
import { PwshLocalExecutor } from '@deepseek-ai/dsh-pwsh-local'
|
||||
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
|
||||
import LocalFileSystem from '@deepseek-ai/dsh-fs-local'
|
||||
import UserInteractionService from '@deepseek-ai/dsh-user-interaction'
|
||||
@@ -36,6 +38,7 @@ import * as SkillLocal from '@deepseek-ai/dsh-skill-local'
|
||||
import LocalTaskService from '@deepseek-ai/dsh-tasks-local'
|
||||
import * as ToolAskUser from '@deepseek-ai/dsh-tool-ask-user'
|
||||
import * as ToolBash from '@deepseek-ai/dsh-tool-bash'
|
||||
import * as ToolPwsh from '@deepseek-ai/dsh-tool-pwsh'
|
||||
import * as ToolBashPersistent from '@deepseek-ai/dsh-tool-bash-persistent'
|
||||
import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
|
||||
import * as ToolFs from '@deepseek-ai/dsh-tool-fs'
|
||||
@@ -190,16 +193,35 @@ const TOOL_PACKAGES: ToolPackage[] = [
|
||||
pkg: '@deepseek-ai/dsh-tool-bash',
|
||||
dir: 'tool-bash',
|
||||
source: 'packages/bash/tool-bash/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.bash', 'ctx.tasks at call time for run_in_background'],
|
||||
requires: ['ctx.tools', 'ctx.bash', 'ctx.systemPrompt', 'ctx.bashEnv', 'ctx.tasks at call time for run_in_background'],
|
||||
writes: ['tool/call', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
await ctx.plugin(LocalSubprocessService)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
await ctx.plugin(LocalBashExecutor)
|
||||
await ctx.plugin(ToolBash)
|
||||
},
|
||||
note:
|
||||
'The bash tool is the model-facing consumer of the bash executor seam. A `run_in_background` run registers with the generic `ctx.tasks` runtime and is collected/stopped through the `task_*` tools from `@deepseek-ai/dsh-tool-tasks`; the `enableRunInBackground` config (default true) removes the parameter entirely when disabled.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-pwsh',
|
||||
dir: 'tool-pwsh',
|
||||
source: 'packages/bash/tool-pwsh/src/index.ts',
|
||||
requires: ['ctx.tools', 'ctx.bash', 'ctx.systemPrompt', 'ctx.bashEnv', 'ctx.tasks at call time for run_in_background'],
|
||||
writes: ['tool/call', 'tool/result'],
|
||||
async mount(ctx) {
|
||||
// The pwsh tool consumes the bash executor seam; the schema harvest
|
||||
// mounts the pwsh-local implementation so the inject resolves without
|
||||
// executing anything (registration never spawns a process).
|
||||
await ctx.plugin(LocalSubprocessService)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
await ctx.plugin(PwshLocalExecutor)
|
||||
await ctx.plugin(ToolPwsh)
|
||||
},
|
||||
note:
|
||||
'The pwsh tool is the PowerShell-dialect consumer of the bash executor seam for Windows compositions (a PowerShell executor such as `@deepseek-ai/dsh-pwsh-local` backs `ctx.bash`); it mirrors the bash tool call-for-call minus the sandbox surface — `run_in_background` runs register with the generic `ctx.tasks` runtime and are collected/stopped through the `task_*` tools, and the managed `DSH_*` environment comes from `@deepseek-ai/dsh-bash-env`. Each call runs in a fresh process (no persistent PTY session; ConPTY is roadmap work), with native `C:\\...` paths and `$env:NAME` variables.',
|
||||
},
|
||||
{
|
||||
pkg: '@deepseek-ai/dsh-tool-cordis',
|
||||
dir: 'tool-cordis',
|
||||
|
||||
@@ -47,7 +47,7 @@ function fixture(options: {
|
||||
default: './lib/invariant.js',
|
||||
},
|
||||
},
|
||||
files: ['lib/index.js', 'lib/invariant.js', 'src'],
|
||||
files: ['lib/index.js', 'lib/invariant.js'],
|
||||
peerDependencies: options.invariantDependency === false ? {} : {
|
||||
'@deepseek-ai/dsh-invariants': '^0.0.1',
|
||||
},
|
||||
|
||||
54
scripts/publication-payload.spec.ts
Normal file
54
scripts/publication-payload.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isForbiddenPublicationFile, validateTarballPayload } from './publication-payload.ts'
|
||||
|
||||
function validateFixtureTarball(files: readonly string[]): () => void {
|
||||
return () => {
|
||||
validateTarballPayload(files, 'fixture.tgz')
|
||||
}
|
||||
}
|
||||
|
||||
describe('publication payload policy', () => {
|
||||
it.each([
|
||||
'lib/index.js',
|
||||
'lib/types/index.d.ts',
|
||||
'lib/styles/base.css',
|
||||
])('accepts %s', (file) => {
|
||||
expect(isForbiddenPublicationFile(file)).toBe(false)
|
||||
})
|
||||
|
||||
it.each([
|
||||
'src',
|
||||
'./src',
|
||||
'src/',
|
||||
'src/index.ts',
|
||||
'./src/index.ts',
|
||||
String.raw`src\index.ts`,
|
||||
'lib/types/index.d.ts.map',
|
||||
'./lib/types/index.d.ts.map',
|
||||
])('rejects static manifest path %s', (file) => {
|
||||
expect(isForbiddenPublicationFile(file)).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects source members in packed tarballs', () => {
|
||||
expect(validateFixtureTarball([
|
||||
'package/package.json',
|
||||
'package/src/index.ts',
|
||||
])).toThrow('fixture.tgz publishes source file package/src/index.ts')
|
||||
})
|
||||
|
||||
it('rejects declaration maps in packed tarballs', () => {
|
||||
expect(validateFixtureTarball([
|
||||
'package/package.json',
|
||||
'package/lib/types/index.d.ts.map',
|
||||
])).toThrow('fixture.tgz publishes declaration map package/lib/types/index.d.ts.map')
|
||||
})
|
||||
|
||||
it('accepts a clean packed tarball', () => {
|
||||
expect(validateFixtureTarball([
|
||||
'package/package.json',
|
||||
'package/lib/index.js',
|
||||
'package/lib/types/index.d.ts',
|
||||
'package/lib/styles/base.css',
|
||||
])).not.toThrow()
|
||||
})
|
||||
})
|
||||
27
scripts/publication-payload.ts
Normal file
27
scripts/publication-payload.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/** Publication payload policy shared by static manifests and packed tarballs. */
|
||||
|
||||
/** Normalize a package manifest path or npm tarball member to its payload-relative path. */
|
||||
function payloadPath(file: string): string {
|
||||
const normalized = file.replaceAll('\\', '/').replace(/^\.\/+/, '').replace(/\/+$/, '')
|
||||
return normalized.startsWith('package/') ? normalized.slice('package/'.length) : normalized
|
||||
}
|
||||
|
||||
/** Whether a package payload path exposes source or declaration-map intermediates. */
|
||||
export function isForbiddenPublicationFile(file: string): boolean {
|
||||
const normalized = payloadPath(file)
|
||||
return normalized === 'src'
|
||||
|| normalized.startsWith('src/')
|
||||
|| normalized.endsWith('.d.ts.map')
|
||||
}
|
||||
|
||||
/** Reject source and declaration-map members in a packed npm tarball. */
|
||||
export function validateTarballPayload(files: readonly string[], context: string): void {
|
||||
for (const file of files) {
|
||||
if (!isForbiddenPublicationFile(file)) continue
|
||||
const normalized = payloadPath(file)
|
||||
if (normalized === 'src' || normalized.startsWith('src/')) {
|
||||
throw new Error(`${context} publishes source file ${file}`)
|
||||
}
|
||||
throw new Error(`${context} publishes declaration map ${file}`)
|
||||
}
|
||||
}
|
||||
1084
scripts/publish-npm-baseline.ts
Normal file
1084
scripts/publish-npm-baseline.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -597,6 +597,7 @@ function builtBinSmokeGate(needs: string[] = ['build']): Gate {
|
||||
'apps/cli/tests/built-bin.e2e.ts',
|
||||
'packages/examples/cli-demo/tests/built-bin.e2e.ts',
|
||||
'packages/examples/acp-demo/tests/built-bin.e2e.ts',
|
||||
'packages/host/directory-picker-native/tests/built-worker.e2e.ts',
|
||||
'packages/ui/jsonrpc/tests/built-scope-carrier.e2e.ts',
|
||||
// The worker-entry packages' built bundles: the only automated proof
|
||||
// that lib/index.js resolves its sibling lib/worker.cjs under plain node
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
/** Unit tests for the prompt-v4 renderer and three-section response parser. */
|
||||
/** Unit tests for the prompt-v7 content and unchanged three-section protocol. */
|
||||
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { join, resolve } from 'node:path'
|
||||
@@ -15,6 +15,20 @@ const root = resolve(import.meta.dirname, '..')
|
||||
const document = readFileSync(join(root, 'docs/i18n/translation-prompt.md'), 'utf8')
|
||||
const terminology = '| English | 中文 |\n|---|---|\n| agent | agent |'
|
||||
|
||||
const retainedExamples = [
|
||||
['### Colloquial verb → Professional verb', 'The repo pins pnpm@11.7.0 in package.json', '该仓库在 package.json 中固定使用 pnpm@11.7.0'],
|
||||
['### Run-on sentence → Natural phrasing with pause', 'Read docs/architecture.md before changing anything under packages/.', '在修改 packages/ 目录下的任何内容之前,请先阅读 docs/architecture.md。'],
|
||||
['### Stiff passive voice → Active and natural', 'a green gate means the pair was confirmed consistent at these exact contents, not that the confirmation was sound.', '门禁通过意味着这组文档在当前内容上的一致性得到了确认,不代表确认本身正确可靠。'],
|
||||
['### Invented word → Natural expression', 'A sidecar record of both blob hashes makes consistency checkable', '伴随记录保存两侧 blob hash,使一致性可检查'],
|
||||
['### Em-dash → Colon/period', 'FIXME — an issue that should block a new release.', 'FIXME:应当阻塞新版本发布的问题。'],
|
||||
['### Overly literal → Meaningful rendering', 'awkward phrasing is easier to hear without the source anchoring you', '不对照原文时,更容易察觉别扭的表达'],
|
||||
['### Terminology — do not translate what should be kept in English', 'typed service seams, and explicit extension points', '类型化的服务 seam 与显式扩展点'],
|
||||
['### Slang/jargon → Professional phrasing', 'The committed agent workflow lives in .agents/skills/dsh-translate-docs', '仓库内置的 agent 工作流见 .agents/skills/dsh-translate-docs'],
|
||||
['### "For humans" — translate the intent, not the word', 'For humans, start with the development guide', '面向开发者:请先阅读开发指南'],
|
||||
['### Code block comments — NEVER translate', '# full-screen TUI coding agent (needs DEEPSEEK_API_KEY)', 'keep exactly as-is, byte-for-byte'],
|
||||
['### Language switcher — flip direction', 'English | [中文](README.zh.md)', '[English](README.md) | 中文'],
|
||||
]
|
||||
|
||||
describe('translation prompt rendering', () => {
|
||||
it('renders both directions with every placeholder resolved', () => {
|
||||
const en = renderTranslationPrompt(document, { sourceLanguage: 'English', sourceFilename: 'guide.md', terminology })
|
||||
@@ -22,15 +36,31 @@ describe('translation prompt rendering', () => {
|
||||
expect(en).toContain(terminology)
|
||||
expect(en).not.toContain('{{')
|
||||
expect(en).toContain('plain source stays plain (必须)')
|
||||
expect(en).toContain('When the target language is English, use the "English" column without a Chinese gloss')
|
||||
expect(en).toContain('for a Chinese target, use an established Chinese rendering')
|
||||
expect(en).toContain('for an English target, use the established English technical term')
|
||||
expect(en).toContain('does an English target use established English terminology')
|
||||
expect(en).toContain('For an English target, use the established English technical term')
|
||||
expect(en).toContain('does a Chinese target use an established Chinese rendering')
|
||||
expect(en).toContain('does an English target use the established English technical term')
|
||||
expect(en).toContain('The parser removes exactly one framing escape')
|
||||
const zh = renderTranslationPrompt(document, { sourceLanguage: 'Chinese', sourceFilename: 'guide.zh.md', terminology })
|
||||
expect(zh).toContain('from Chinese to English')
|
||||
})
|
||||
|
||||
it('retains every v4 embedded example', () => {
|
||||
for (const example of retainedExamples) {
|
||||
for (const fragment of example) expect(document).toContain(fragment)
|
||||
}
|
||||
})
|
||||
|
||||
it('states the selected v7 safeguards', () => {
|
||||
const rendered = renderTranslationPrompt(document, { sourceLanguage: 'English', sourceFilename: 'guide.md', terminology })
|
||||
expect(rendered).toContain('## Priority')
|
||||
expect(rendered).toContain('### Faithfulness')
|
||||
expect(rendered).toContain('do not invent a filename or switcher')
|
||||
expect(rendered).toContain('Markdown emphasis markers do not create a word boundary')
|
||||
expect(rendered).toContain('Never invent responsibility merely to avoid a passive construction')
|
||||
expect(rendered).toContain('Never vary a terminology-table form, defined concept, or contract verb merely for stylistic variety')
|
||||
expect(rendered).toContain('Return exactly three raw XML sections')
|
||||
})
|
||||
|
||||
it('rejects a template with unknown or missing placeholders', () => {
|
||||
const alien = document.replaceAll('{{terminology}}', '{{terms_prompt}}')
|
||||
expect(() => renderTranslationPrompt(alien, { sourceLanguage: 'English', sourceFilename: 'guide.md', terminology })).toThrow(/unsupported placeholder/)
|
||||
|
||||
@@ -42,7 +42,9 @@ const NO_MODEL_EXPERIENCE_SECTION: Readonly<Record<string, string>> = {
|
||||
*/
|
||||
const SENTENCE_MODEL_EXPERIENCE: Readonly<Record<string, SentenceContract>> = {
|
||||
'packages/bash/bash': { kind: 'indirect', reason: 'The service interface delegates all model rendering to dsh-tool-bash.' },
|
||||
'packages/bash/bash-env': { kind: 'indirect', reason: 'The env service surfaces managed DSH_* facts through the shell tools (dsh-tool-bash/dsh-tool-pwsh); it registers no prompt or schema of its own.' },
|
||||
'packages/bash/bash-local': { kind: 'indirect', reason: 'The executor backend delegates model rendering to dsh-tool-bash.' },
|
||||
'packages/bash/pwsh-local': { kind: 'indirect', reason: 'The executor backend delegates model rendering to dsh-tool-pwsh.' },
|
||||
'packages/code-runtime/code-runtime': { kind: 'indirect', reason: 'The service interface delegates model rendering to Code Mode in dsh-tools.' },
|
||||
'packages/code-runtime/code-runtime-worker': { kind: 'indirect', reason: 'The worker backend delegates model rendering to Code Mode in dsh-tools.' },
|
||||
'packages/typert/registry': { kind: 'none', reason: 'Runtime type registry; consumers (cordis_inspect, wire faces, gates) own any model-visible projection of registry contents.' },
|
||||
|
||||
Reference in New Issue
Block a user