Merge branch 'codex/disable-telemetry-default' into master

Resolved conflicts:
- packages/client/ui-settings-general/README.md, README.zh.md: kept PR
  opt-in telemetry description (DSH_TELEMETRY_MODE)
- scripts/snapshots/translation-prompt-v4: kept master's newer README
  structure snapshot
- i18n.yaml pairing records: resolved per file state
- pnpm-lock.yaml: regenerated
- Modify/delete conflicts (scaffold/telemetry, sdk-follow-up-capabilities):
  kept master deletions
This commit is contained in:
Chinesezjc
2026-08-12 17:27:37 +08:00
2021 changed files with 43956 additions and 23769 deletions

View File

@@ -227,8 +227,8 @@ function createStartupFixture(): StartupFixture {
"export const inject = ['cmdlineArgs']",
'export function apply(ctx) {',
" const program = new Command().name('fixture').option('--generation <value>', 'echoed generation')",
' const values = parseCmdline(ctx, program, parsed => ({ generation: parsed.opts().generation }))',
' if (values !== undefined) ctx.provide(\'fixtureStartup\', values)',
" program.action(() => ctx.provide('fixtureStartup', { generation: program.opts().generation }))",
' parseCmdline(ctx, program)',
'}',
'',
].join('\n'))
@@ -408,16 +408,15 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
}
}, 30_000)
it('uses the Harness-home environment and managed credential through the published entry', async () => {
it('uses the launching endpoint and managed credential through the published entry', async () => {
const apiKey = 'built-home-layer-key'
const server = await startMockLlmServer({
sequence: ['success'],
apiKey,
successText: 'home environment reached the mock',
successText: 'launching endpoint reached the mock',
})
const home = mkdtempSync(join(tmpdir(), 'dsh-home-environment-'))
const project = mkdtempSync(join(tmpdir(), 'dsh-home-project-'))
writeFileSync(join(home, '.env'), `DEEPSEEK_BASE_URL=${server.baseURL}\n`)
writeFileSync(join(home, '.credentials.yaml'), `DEEPSEEK_API_KEY: ${apiKey}\n`, { mode: 0o600 })
createEnvironmentProbeProfile(home, project)
try {
@@ -427,7 +426,7 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
DSH_HOME: home,
DSH_TELEMETRY_DISABLED: '1',
DEEPSEEK_API_KEY: undefined,
DEEPSEEK_BASE_URL: undefined,
DEEPSEEK_BASE_URL: server.baseURL,
},
project,
)
@@ -435,7 +434,7 @@ describe.skipIf(!existsSync(dshBin))('dsh BUILT bin (node lib/bin.js, no tsx)',
result.code,
`${result.stderr}\nstdout:\n${result.stdout}\nmock requests: ${String(server.requests.length)}`,
).toBe(0)
expect(result.stdout).toBe('home environment reached the mock')
expect(result.stdout).toBe('launching endpoint reached the mock')
expect(result.stdout).not.toContain(apiKey)
expect(result.stderr).not.toContain(apiKey)
expect(server.requests).toHaveLength(1)

View File

@@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest'
/**
* Keyless smoke for SOURCE `dsh` execution: run `apps/cli/src/bin.ts`
* with the exact production runtime vector (`node --import tsx/esm`, the
* vector the root `dsh` script invokes after building) and assert the
* vector the root `dsh` script invokes directly) and assert the
* required-config diagnostic. The Node compatibility matrix runs this
* WHOLE file, so a Node release changing module hooks or TypeScript handling
* breaks this gate instead of every developer's `pnpm dsh`; the built-bin
@@ -17,11 +17,11 @@ const repoRoot = fileURLToPath(new URL('../../../', import.meta.url))
const dshSourceBin = 'apps/cli/src/bin.ts'
describe('dsh SOURCE launcher (node --import tsx/esm)', () => {
it('builds before launching the source CLI', async () => {
it('launches the source CLI without building', async () => {
const rootPackage = JSON.parse(await readFile(new URL('../../../package.json', import.meta.url), 'utf8')) as {
readonly scripts?: Record<string, string>
}
expect(rootPackage.scripts?.dsh).toBe('pnpm run build && node --import tsx/esm apps/cli/src/bin.ts')
expect(rootPackage.scripts?.dsh).toBe('node --import tsx/esm apps/cli/src/bin.ts')
})
it('boots the source entry and requires a profile', async () => {

View File

@@ -14,7 +14,7 @@ import { settingsNamespace } from '@deepseek-ai/dsh-settings'
import { resolveSessionPreset, SETTINGS_NAMESPACE } from '@deepseek-ai/dsh-agent-presets'
import { applyChildComposition, childSessionMeta } from '@deepseek-ai/dsh-subagent'
import { CallId } from '@deepseek-ai/dsh-llm'
import type { BasicCompactService } from '@deepseek-ai/dsh-compact-basic'
import type {} from '@deepseek-ai/dsh-compact-basic'
import type {} from '@deepseek-ai/dsh-skill'
import type {} from '@deepseek-ai/dsh-tools'
// Type-only: resolves `ctx.get('sessionProjections')` and `ctx.get('tokenMeter')`.
@@ -79,18 +79,28 @@ async function bootWeb(settingsFile: string, extra: PatchOptions[] = []): Promis
{ id: 'skill-badge', disabled: false },
{ id: 'modules', disabled: true },
{ id: 'connection', disabled: true },
// The always-on reload chain waits for the browser roster and bound port
// disabled above.
{ id: 'client-hmr', disabled: true },
// The shipped `-auto` chooser resolves its interaction from a running
// host and so waits for the webserver disabled above; the browse variant
// supplies `directoryPicker` without one.
{ id: 'directory-picker', disabled: true },
{ insert: [{ id: 'directory-picker-browse', name: '@deepseek-ai/dsh-host-directory-picker-browse' }] },
{ insert: [
{ id: 'directory-picker-browse', name: '@deepseek-ai/dsh-host-directory-picker-browse' },
{ id: 'ui-directory-picker', name: '@deepseek-ai/dsh-client-ui-directory-picker' },
] },
// The roster AppCLIEntry would patch in; only the shipped root, so a
// developer's own `~/.dsh/.preset` cannot change this test's outcome.
// `default` here is the COMPOSITION default — the base layer the settings
// document overrides.
{
id: 'agent-presets',
config: { default: 'standard', roots: [{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' }] },
config: {
default: 'standard',
roots: [{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' }],
includeUserRoot: false,
},
},
...extra,
]
@@ -213,16 +223,8 @@ describe('the shipped Web composition', () => {
expect(assembly.tools.find(tool => tool.name === 'bash')?.description).toBe(MINIMAL_BASH_DESCRIPTION)
expect(JSON.stringify(assembly.tools.find(tool => tool.name === 'str_replace_editor')?.parameters))
.toContain('Absolute path')
const compact = ctx.agentPresets.serviceFor(handle.agent, 'compact')
expect(compact).toBeDefined()
expect((compact as BasicCompactService).config).toMatchObject({
thresholdRatio: 0.8,
retainTokens: 20480,
summarizationProvider: '',
summarizationModel: '',
maxTokens: 8192,
compactionRetries: 1,
})
expect(ctx.agentPresets.serviceFor(handle.agent, 'compact')).toBeUndefined()
expect(handle.agent.ctx.get('compact')).toBeUndefined()
} finally {
await handle.dispose()
}
@@ -444,6 +446,7 @@ describe('product subagent rows in user presets', () => {
{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' },
{ path: userRoot, trust: 'user' },
],
includeUserRoot: false,
},
}])
}, 120_000)
@@ -626,6 +629,66 @@ describe('a delegated child', () => {
})
})
describe('a launcher that configures no writable root', () => {
// The claim this default exists for, asserted through the real shipped
// bundles rather than a hand-built context: `apps/cli` patches in only the
// system root, and a person's own presets are found anyway because the
// roster derives `<dshHome>/.agent-presets` itself. `$DSH_HOME` is pointed
// at a temp home BEFORE boot — the derived root is resolved when the plugin
// is constructed, and an unpinned run would read the developer's own.
let derivedCtx: Context
let previousHome: string | undefined
beforeAll(async () => {
const home = await mkdtemp(join(tmpdir(), 'dsh-preset-derived-'))
previousHome = process.env.DSH_HOME
process.env.DSH_HOME = home
await mkdir(join(home, '.agent-presets', 'derived-mine'), { recursive: true })
await writeFile(
join(home, '.agent-presets', 'derived-mine', 'agent.cordis.yml'),
'- id: tool-todo\n name: \'@deepseek-ai/dsh-tool-todo\'\n config:\n allowParallelInProgress: true\n',
)
const settingsFile = join(await mkdtemp(join(tmpdir(), 'dsh-preset-derived-settings-')), 'settings.yaml')
await writeFile(settingsFile, '{}\n')
// Only the shipped root, exactly what `composeProfile` supplies; the
// writable one is the roster's own default rather than this patch's job.
derivedCtx = await bootWeb(settingsFile, [{
id: 'agent-presets',
config: {
default: 'standard',
roots: [{ path: join(CONFIG_DIR, 'agent-presets'), trust: 'system' }],
includeUserRoot: true,
},
}])
}, 120_000)
afterAll(async () => {
if (previousHome === undefined) delete process.env.DSH_HOME
else process.env.DSH_HOME = previousHome
await derivedCtx.fiber.dispose()
})
it('discovers and mounts a preset the person authored under the harness home', async () => {
const listed = await derivedCtx.agentPresets.list()
const mine = listed.find(preset => preset.id === 'derived-mine')
expect(mine).toMatchObject({ trust: 'user' })
// Omitted rather than undefined: a healthy row carries no `broken` key.
expect(mine?.broken).toBeUndefined()
expect(derivedCtx.agentPresets.authorable).toBe(true)
const handle = await derivedCtx.agents.create({
sessionId: SessionId('preset-derived-root'),
setup: agentCtx => derivedCtx.agentPresets.mount(agentCtx, 'derived-mine').then(() => undefined),
})
try {
expect(toolNames(derivedCtx, handle.agent)).toContain('todo_write')
} finally {
await handle.dispose()
}
})
})
describe('authoring a preset on the shipped composition', () => {
let authorCtx: Context
let userRoot: string
@@ -644,6 +707,7 @@ describe('authoring a preset on the shipped composition', () => {
// nothing is the normal first-run state.
{ path: userRoot, trust: 'user' },
],
includeUserRoot: false,
},
}])
})

View File

@@ -1,73 +1,38 @@
/**
* The shipped shell composition: the base bundle gates both shell stacks by
* platform on its own rows (`disabled: !!js process.platform`), so exactly
* one shell stack mounts per host and no separate platform layer exists —
* the launcher applies nothing beyond the bundle layers. The spec composes
* the REAL shipped bundle layers (dsh-base + dsh-web-app resolved from the
* app installation anchor) through the boot's patch algorithm and pins the
* effective per-platform roster, the preset-level gates that keep tool-bash
* out of win32 sessions and tool-pwsh out of POSIX sessions, and the
* cold-start resolution closure for the pwsh rows' bare plugin names.
*/
import { afterEach, describe, expect, it } from 'vitest'
import { mkdtempSync, writeFileSync, rmSync, mkdirSync, readFileSync } from 'node:fs'
import { mkdtempSync, rmSync, readFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import type { ProfileLayer } from '@deepseek-ai/dsh-app-boot'
import yaml from 'js-yaml'
import { entryListSchema } from '@deepseek-ai/cordis-plugin-include'
import { evaluate } from '@deepseek-ai/cordis-plugin-loader'
import { composeEntries, initProfile, loadProfile, PROFILES_DIR } from '@deepseek-ai/dsh-app-boot'
import {
BASE_BUNDLE,
resolveWindowsShellLayer,
WINDOWS_SHELL_PATCH_FILENAME,
} from '../src/windows-shell.ts'
const WINDOWS_PATCH = `- id: bash-sandbox
disabled: true
- insert:
- id: pwsh-sandbox
name: '@deepseek-ai/dsh-pwsh-sandbox'
`
/** One fake bundle layer rooted in a temp directory. */
function fakeLayer(packageName: string, dir: string): ProfileLayer {
return { packageName, packageDir: dir, patchPath: join(dir, 'cordis.patch.yml'), patches: [] }
}
/** A base bundle layer whose package carries the Windows shell patch. */
function baseLayerWithPatch(dir: string): ProfileLayer {
writeFileSync(join(dir, WINDOWS_SHELL_PATCH_FILENAME), WINDOWS_PATCH)
return fakeLayer(BASE_BUNDLE, dir)
}
describe('resolveWindowsShellLayer', () => {
let base: string
afterEach(() => { if (base !== undefined) rmSync(base, { recursive: true, force: true }) })
const tempBase = (): string => {
base = mkdtempSync(join(tmpdir(), 'dsh-windows-shell-'))
return base
/**
* The effective disabled state of one row on one platform: a `!!js` expression
* evaluates with a platform-scoped `process` so both outcomes pin on any host.
*/
function disabledOn(row: { disabled?: unknown }, platform: 'win32' | 'linux'): boolean {
const value = row.disabled
if (value !== null && typeof value === 'object' && '__jsExpr' in value) {
return Boolean(evaluate({ process: { platform } }, (value as { __jsExpr: string }).__jsExpr))
}
return value === true
}
it('never applies on POSIX hosts', () => {
expect(resolveWindowsShellLayer('linux', [baseLayerWithPatch(tempBase())], 'dsh')).toBeUndefined()
expect(resolveWindowsShellLayer('darwin', [baseLayerWithPatch(tempBase())], 'dsh')).toBeUndefined()
})
it('defaults Windows hosts to the pwsh platform layer', () => {
const layer = resolveWindowsShellLayer('win32', [baseLayerWithPatch(tempBase())], 'dsh')
expect(layer).toBeDefined()
expect(layer?.label.endsWith(WINDOWS_SHELL_PATCH_FILENAME)).toBe(true)
expect(layer?.patches).toEqual([
{ id: 'bash-sandbox', disabled: true },
{ insert: [{ id: 'pwsh-sandbox', name: '@deepseek-ai/dsh-pwsh-sandbox' }] },
])
})
it('skips custom profiles without a base bundle', () => {
const other = fakeLayer('@deepseek-ai/dsh-custom', tempBase())
expect(resolveWindowsShellLayer('win32', [other], 'dsh')).toBeUndefined()
})
it('fails loud when the base bundle ships no Windows shell patch', () => {
const base = tempBase()
mkdirSync(base, { recursive: true })
// The overlay loader owns the fail-loud contract: the caller named this
// file, so its absence is a misconfiguration, not "no overlay".
expect(() => resolveWindowsShellLayer('win32', [fakeLayer(BASE_BUNDLE, base)], 'dsh'))
.toThrow(/dsh: failed to read overlay .*windows\.cordis\.patch\.yml/)
})
})
describe('the shipped Windows composition (real bundle layers)', () => {
describe('the shipped shell composition (real bundle layers)', () => {
let home: string
afterEach(() => { if (home !== undefined) rmSync(home, { recursive: true, force: true }) })
// The app installation anchor, mirroring profile-boot.ts: the bundle layers
@@ -75,65 +40,98 @@ describe('the shipped Windows composition (real bundle layers)', () => {
// suite composes the shipped patch files, not test fixtures.
const anchor = fileURLToPath(new URL('../package.json', import.meta.url))
it('composes the win32 confined roster through the real patch layers', () => {
it('composes the confined pwsh roster on win32 and the bash roster on POSIX from the same rows', () => {
home = mkdtempSync(join(tmpdir(), 'dsh-windows-home-'))
initProfile(join(home, PROFILES_DIR, 'web'), ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app'])
const profile = loadProfile('dsh', 'web', anchor, home)
const warnings: string[] = []
const win32 = resolveWindowsShellLayer('win32', profile.layers, 'dsh')
expect(win32).toBeDefined()
const rows = composeEntries(
[...profile.layers.map(layer => layer.patches), win32!.patches],
profile.layers.map(layer => layer.patches),
message => warnings.push(message),
)
const byId = new Map(rows.map(row => [row.id, row]))
// Only the POSIX bash stack leaves the roster: the permission surface
// (sandbox/sandbox-policy/fs-sandbox, permission, approval) stays enabled
// exactly as on POSIX — the confined pwsh executor is what changes.
for (const id of ['bash-sandbox', 'tool-bash']) {
expect(byId.get(id)?.disabled, `row ${id}`).toBe(true)
// One shared patch set, two rosters: the shell stacks gate themselves.
for (const id of ['bash-sandbox', 'pwsh-sandbox', 'tool-bash', 'tool-pwsh']) {
expect(byId.has(id), `row ${id}`).toBe(true)
}
expect(disabledOn(byId.get('bash-sandbox')!, 'win32'), 'bash-sandbox on win32').toBe(true)
expect(disabledOn(byId.get('bash-sandbox')!, 'linux'), 'bash-sandbox on linux').toBe(false)
expect(disabledOn(byId.get('pwsh-sandbox')!, 'win32'), 'pwsh-sandbox on win32').toBe(false)
expect(disabledOn(byId.get('pwsh-sandbox')!, 'linux'), 'pwsh-sandbox on linux').toBe(true)
// Host shell-tool rows are disabled on every platform; sessions mount
// their own rows instead.
expect(byId.get('tool-bash')?.disabled).toBe(true)
expect(byId.get('tool-pwsh')?.disabled).toBe(true)
// The permission surface never moves: the sandbox/policy rows, the
// permission switcher, fs-sandbox, and the approval service stay enabled
// exactly as on POSIX — the confined pwsh executor is what changes.
for (const id of ['permission', 'ui-permission', 'sandbox', 'sandbox-policy', 'fs-sandbox', 'approval']) {
expect(byId.get(id)?.disabled, `row ${id}`).not.toBe(true)
}
for (const id of ['pwsh-sandbox', 'tool-pwsh']) {
expect(byId.has(id), `inserted row ${id}`).toBe(true)
}
// The launcher's cold-start module fallback BFS-links the apps/cli
// dependency closure into the profile's node_modules (the pwsh-local
// precedent), so every inserted bare plugin must resolve from there.
// dependency closure into the profile's node_modules, so every bare
// plugin name in the base patch must resolve from there.
const cliManifest = JSON.parse(readFileSync(anchor, 'utf8')) as { dependencies?: Record<string, string> }
for (const name of ['@deepseek-ai/dsh-pwsh-sandbox', '@deepseek-ai/dsh-tool-pwsh']) {
expect(cliManifest.dependencies?.[name], `cold-start closure must reach ${name}`).toBeDefined()
}
// The patch touches only base-owned rows plus inserts, so the full web
// profile composes without any no-match warning.
expect(warnings).toEqual([])
})
it('leaves POSIX untouched and base-only profiles compose without warnings', () => {
it('base-only profiles carry both stacks with the same platform gating', () => {
home = mkdtempSync(join(tmpdir(), 'dsh-windows-home-'))
initProfile(join(home, PROFILES_DIR, 'web'), ['@deepseek-ai/dsh-base', '@deepseek-ai/dsh-web-app'])
const profile = loadProfile('dsh', 'web', anchor, home)
// POSIX: no platform layer, the bash stack stays enabled.
const posixRows = composeEntries(profile.layers.map(layer => layer.patches))
const posixById = new Map(posixRows.map(row => [row.id, row]))
expect(posixById.get('bash-sandbox')?.disabled).not.toBe(true)
expect(posixById.has('pwsh-local')).toBe(false)
expect(posixById.has('pwsh-sandbox')).toBe(false)
// A base-only custom profile (the DEFAULT_PROFILE_BUNDLES template): the
// patch touches only base-owned rows (bash-sandbox/tool-bash) plus its
// inserts, so the composition produces no no-match warning.
initProfile(join(home, PROFILES_DIR, 'base-only'), ['@deepseek-ai/dsh-base'])
const baseOnly = loadProfile('dsh', 'base-only', anchor, home)
const baseWarnings: string[] = []
const win32 = resolveWindowsShellLayer('win32', baseOnly.layers, 'dsh')
expect(win32).toBeDefined()
composeEntries(
[...baseOnly.layers.map(layer => layer.patches), win32!.patches],
message => baseWarnings.push(message),
const profile = loadProfile('dsh', 'base-only', anchor, home)
const warnings: string[] = []
const rows = composeEntries(
profile.layers.map(layer => layer.patches),
message => warnings.push(message),
)
expect(baseWarnings).toEqual([])
const byId = new Map(rows.map(row => [row.id, row]))
for (const id of ['bash-sandbox', 'tool-bash', 'pwsh-sandbox', 'tool-pwsh']) {
expect(byId.has(id), `row ${id}`).toBe(true)
}
// No web overlay: the tool rows keep their own gating too.
expect(disabledOn(byId.get('tool-bash')!, 'win32'), 'tool-bash on win32').toBe(true)
expect(disabledOn(byId.get('tool-bash')!, 'linux'), 'tool-bash on linux').toBe(false)
expect(disabledOn(byId.get('tool-pwsh')!, 'win32'), 'tool-pwsh on win32').toBe(false)
expect(disabledOn(byId.get('tool-pwsh')!, 'linux'), 'tool-pwsh on linux').toBe(true)
expect(warnings).toEqual([])
})
})
describe('shipped agent presets gate both shell tools by platform', () => {
const presetRoot = resolve(fileURLToPath(new URL('../package.json', import.meta.url)), '..', 'config', 'agent-presets')
it.each(['standard', 'code', 'cordis'])('preset %s gates its shell tool rows by platform', (preset) => {
const entries: unknown = yaml.load(
readFileSync(join(presetRoot, preset, 'agent.cordis.yml'), 'utf8'),
{ schema: entryListSchema },
)
if (!Array.isArray(entries)) throw new TypeError(`preset ${preset} must parse to an entry array`)
for (const [id, win32] of [['tool-bash', true], ['tool-pwsh', false]] as const) {
const row = entries.find((entry): entry is Record<string, unknown> => (
typeof entry === 'object' && entry !== null && (entry as Record<string, unknown>).id === id
))
if (row === undefined) throw new TypeError(`preset ${preset} must mount ${id}`)
expect(row.disabled).toMatchObject({ __jsExpr: expect.any(String) as string })
// A platform-scoped context pins both outcomes on every host.
const expression = (row.disabled as { __jsExpr: string }).__jsExpr
expect(Boolean(evaluate({ process: { platform: 'win32' } }, expression)), `${id} on win32`).toBe(win32)
expect(Boolean(evaluate({ process: { platform: 'linux' } }, expression)), `${id} on linux`).toBe(!win32)
}
})
it('minimal mounts no shell tool row at all (its shell is the PTY stack)', () => {
const entries: unknown = yaml.load(
readFileSync(join(presetRoot, 'minimal', 'agent.cordis.yml'), 'utf8'),
{ schema: entryListSchema },
)
if (!Array.isArray(entries)) throw new TypeError('minimal preset must parse to an entry array')
for (const id of ['tool-bash', 'tool-pwsh']) {
expect(entries.some(entry => (
typeof entry === 'object' && entry !== null && (entry as Record<string, unknown>).id === id
)), `${id} must be absent from minimal`).toBe(false)
}
})
})