diff --git a/python/sdk-runtime/README.i18n.yaml b/python/sdk-runtime/README.i18n.yaml index c9f1f119bf..5a09a8a093 100644 --- a/python/sdk-runtime/README.i18n.yaml +++ b/python/sdk-runtime/README.i18n.yaml @@ -15,5 +15,5 @@ en: 30b9d8f69da71b23 zh: 9eb984af84dc7ead /deepseek-harness-runtime-bin/build-and-distribution: - en: ce9b1a8e4a7f0a18 - zh: 43d09e469344cd87 + en: c19be7519810fca6 + zh: 958c601b02846a08 diff --git a/python/sdk-runtime/README.md b/python/sdk-runtime/README.md index c7876730b3..f88d1f92e8 100644 --- a/python/sdk-runtime/README.md +++ b/python/sdk-runtime/README.md @@ -64,6 +64,6 @@ External profile management uses `dsh plugin --profile ...`. That command Production deployment permits unused workspace patches for packages outside the runtime closure; patches for included packages must still apply successfully. This exception is confined to the deploy command; repository installation still rejects unused patches. -From the repository root, `pnpm exec tsx scripts/build-exe-for-python-sdk.ts` verifies the closure, builds packages, deploys a symlink-free tree, packages the selected target, and syncs the executable and sidecars into this module. Only the target platform’s PTY prebuilds are packaged. Package README, CHANGELOG, and HISTORY Markdown outside runtime assets is omitted. Deployment disables workspace hoisting so unrelated source packages stay out of the payload. Staging shares root workspace packages with nested consumers of the same pnpm dependency identity, preserving singleton services while retaining different peer resolutions. `scripts/build-python-release.py` stages release-shaped wheels at the root repository version and pins `deepseek-harness-sdk` to the exact runtime version. +From the repository root, `pnpm exec tsx scripts/build-exe-for-python-sdk.ts` verifies the closure, builds packages, deploys a symlink-free tree, packages the selected target, and syncs the executable and sidecars into this module. Only the target platform’s PTY prebuilds are packaged. Package README, CHANGELOG, HISTORY Markdown, source maps, and TypeScript declarations outside runtime assets are omitted. Linux packaging requires binutils `strip` and removes debugging information and symbols that are unnecessary for execution; dynamic symbols and the SEA payload are retained. Deployment disables workspace hoisting, and packaging excludes the checkout’s node_modules so dependency scans cannot add development packages to the payload. Staging shares root workspace packages with nested consumers of the same pnpm dependency identity, preserving singleton services while retaining different peer resolutions. `scripts/build-python-release.py` stages release-shaped wheels at the root repository version and pins `deepseek-harness-sdk` to the exact runtime version. The installed-wheel smoke creates a clean virtual environment outside the checkout, proves the installed distribution and executable identities, then exercises default and customized SDK profiles, external plugins, MCP, native tools, direct JSON-RPC, committed snapshots, and the real provider on trusted runs. Its Office scenario relocates the lightweight target payload, explicitly downloads the sidecar, and converts DOCX with the required platform engine: the target’s declared native engine, or WASM when no native engine is declared. See the [Python contributor workflow](../development.md) and [installed-wheel testing decision](../../.agents/notes/implemented/testing/2026-08-23-installed-python-wheel-black-box-ci.md). diff --git a/python/sdk-runtime/README.zh.md b/python/sdk-runtime/README.zh.md index 205d9f2b3a..610fb97e6a 100644 --- a/python/sdk-runtime/README.zh.md +++ b/python/sdk-runtime/README.zh.md @@ -64,6 +64,6 @@ Python bootstrap 从显式下载或选择的 sidecar 解析 Office kit,让原 生产部署允许工作区中不属于运行时闭包的补丁保持未使用;闭包内包的补丁仍必须成功应用。此例外仅用于部署命令,仓库安装仍拒绝未使用的补丁。 -在仓库根目录运行 `pnpm exec tsx scripts/build-exe-for-python-sdk.ts`,会校验闭包、构建包、部署无符号链接的文件树、打包所选目标,并把可执行程序及伴随文件同步到本模块。打包时只保留目标平台的 PTY 预构建文件,并排除运行时资源目录之外的包内 README、CHANGELOG 和 HISTORY Markdown 文档。部署会关闭工作区提升,避免无关源码包进入载荷。暂存步骤让使用相同 pnpm 依赖标识的嵌套消费者共享根目录的工作区包,保留服务单例,同时保留不同的对等依赖解析。`scripts/build-python-release.py` 按仓库根版本暂存发布形态的 wheel 包,并将 `deepseek-harness-sdk` 固定到完全相同的运行时版本。 +在仓库根目录运行 `pnpm exec tsx scripts/build-exe-for-python-sdk.ts`,会校验闭包、构建包、部署无符号链接的文件树、打包所选目标,并把可执行程序及伴随文件同步到本模块。打包时只保留目标平台的 PTY 预构建文件,并排除运行时资源目录之外的包内 README、CHANGELOG、HISTORY Markdown 文档、source map 和 TypeScript 声明。Linux 打包需要 binutils 的 `strip`,会移除调试信息和执行时不需要的符号,保留动态符号和 SEA 载荷。部署会关闭工作区提升,打包时还会排除检出目录的 node_modules,防止依赖扫描把开发包加入载荷。暂存步骤让使用相同 pnpm 依赖标识的嵌套消费者共享根目录的工作区包,保留服务单例,同时保留不同的对等依赖解析。`scripts/build-python-release.py` 按仓库根版本暂存发布形态的 wheel 包,并将 `deepseek-harness-sdk` 固定到完全相同的运行时版本。 已安装 wheel 包冒烟测试会在检出目录外创建干净的虚拟环境,验证分发物与可执行程序的来源,然后覆盖默认及自定义 SDK profile、外部插件、MCP、原生工具、直接 JSON-RPC、检入快照,以及可信运行中的真实提供方。Office 场景会迁移轻量目标载荷、显式下载 sidecar,并使用所需的平台引擎转换 DOCX:使用目标已声明的原生引擎,未声明原生引擎时使用 WASM。另见 [Python 贡献者工作流](../development.zh.md) 与 [installed-wheel 测试决策](../../.agents/notes/implemented/testing/2026-08-23-installed-python-wheel-black-box-ci.zh.md)。 diff --git a/scripts/build-exe-for-python-sdk-office.spec.ts b/scripts/build-exe-for-python-sdk-office.spec.ts index e2df0b0e0d..50865f1a9f 100644 --- a/scripts/build-exe-for-python-sdk-office.spec.ts +++ b/scripts/build-exe-for-python-sdk-office.spec.ts @@ -96,9 +96,9 @@ it('rejects an ancestor dependency outside the deployed closure', async () => { .rejects.toThrow('outside the deployed closure') }) -it('requires the declared WASM engine inside the deployed closure', async () => { +it.each([true, false])('requires a staged WASM engine when declared=%s even when an ancestor has one', async (declared) => { const { root, staging, destination, packageAt } = await fixture() - await packageAt('@deepseek-ai/libreoffice-kit', { optionalDependencies: { '@deepseek-ai/libreoffice-kit-wasm': '0.0.1' } }) + await packageAt('@deepseek-ai/libreoffice-kit', declared ? { optionalDependencies: { '@deepseek-ai/libreoffice-kit-wasm': '0.0.1' } } : {}) await packageAt('@deepseek-ai/libreoffice-kit-wasm', {}, root) await expect(copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' })) .rejects.toThrow('Office engine @deepseek-ai/libreoffice-kit-wasm required for linux/x64 is missing.') @@ -133,15 +133,6 @@ it.each(['darwin', 'win32', 'linux'])('%s requires its native package even when await expect(stat(destination)).rejects.toMatchObject({ code: 'ENOENT' }) }) -it('requires a staged Linux WASM engine even when an ancestor has one', async () => { - const { root, staging, destination, packageAt } = await fixture() - await packageAt('@deepseek-ai/libreoffice-kit') - await packageAt('@deepseek-ai/libreoffice-kit-wasm', {}, root) - await expect(copyOfficeSidecar(staging, destination, { platform: 'linux', arch: 'x64' })) - .rejects.toThrow('Office engine @deepseek-ai/libreoffice-kit-wasm required for linux/x64 is missing.') - await expect(stat(destination)).rejects.toMatchObject({ code: 'ENOENT' }) -}) - it('downloads pinned npm packages into the sidecar without resolving or executing package scripts', async () => { const { root, destination } = await fixture() const source = join(root, 'package') diff --git a/scripts/build-exe-for-python-sdk.ts b/scripts/build-exe-for-python-sdk.ts index 36f8a945a8..9978285212 100644 --- a/scripts/build-exe-for-python-sdk.ts +++ b/scripts/build-exe-for-python-sdk.ts @@ -305,7 +305,7 @@ class SingleExeBuild { for (const name of DEPLOY_ONLY_DOCS) console.log(`build-exe-for-python-sdk: [dry-run] rm -f ${join(this.staging, name)}`) } else { await Promise.all(DEPLOY_ONLY_DOCS.map(name => rm(join(this.staging, name), { force: true }))) - for await (const file of glob('node_modules/**/{README*,CHANGELOG*,HISTORY*}.md', { + for await (const file of glob('node_modules/**/{README*.md,CHANGELOG*.md,HISTORY*.md,*.map,*.d.ts,*.d.mts,*.d.cts}', { cwd: this.staging, exclude: ['node_modules/**/assets/**'], })) await rm(join(this.staging, file), { force: true }) } @@ -338,7 +338,12 @@ class SingleExeBuild { /** Add the executable entry and pkg assets to the staged manifest. */ async injectPkgConfig(): Promise { - const patch = { bin: ENTRY_BIN, pkg: { assets: ASSET_GLOBS, ignore: OFFICE_ASSET_IGNORES } } + const sourceModules = join(root, 'node_modules').replaceAll('\\', '/') + const patch = { bin: ENTRY_BIN, pkg: { assets: ASSET_GLOBS, ignore: [ + ...OFFICE_ASSET_IGNORES, + `${sourceModules}/**`, `${sourceModules}/.pnpm/**`, + '**/*.map', '**/*.d.ts', '**/*.d.mts', '**/*.d.cts', + ] } } const manifestPath = join(this.staging, 'package.json') if (this.cli.dryRun) { console.log(`build-exe-for-python-sdk: [dry-run] patch ${manifestPath} with ${JSON.stringify(patch)}`) @@ -386,6 +391,7 @@ class SingleExeBuild { if (!this.cli.dryRun && !existsSync(product)) { throw new Error(`build-exe-for-python-sdk: product ${product} is missing after the pkg run; inspect ${this.outDir}.`) } + if (target.platform === 'linux') await this.run('strip Linux executable', 'strip', ['--strip-unneeded', product]) const ripgrep = await this.copyRipgrepSidecar(target, product) const resources = join(this.outDir, `${target.platform}-${target.arch}`) if (this.cli.dryRun) { diff --git a/scripts/build-python-release.py b/scripts/build-python-release.py index 689fdce908..82c9f9833a 100644 --- a/scripts/build-python-release.py +++ b/scripts/build-python-release.py @@ -235,7 +235,7 @@ def verify_wheel( platform: tuple[str, str] | None, ) -> None: if wheel.stat().st_size > 100_000_000: - raise RuntimeError(f"{wheel} exceeds the PyPI single-file limit of 100000000 bytes") + raise RuntimeError(f"{wheel} is {wheel.stat().st_size} bytes and exceeds the PyPI single-file limit of 100000000 bytes") expected_tag = "py3-none-any" if platform is None else f"py3-none-{platform[0]}" expected_distribution = SDK_DISTRIBUTION if package == "sdk" else RUNTIME_DISTRIBUTION dist_info = f"{expected_distribution.replace('-', '_')}-{version}.dist-info" diff --git a/scripts/smoke-python-runtime.py b/scripts/smoke-python-runtime.py index a1f8735ff1..aa358eefdb 100644 --- a/scripts/smoke-python-runtime.py +++ b/scripts/smoke-python-runtime.py @@ -1361,6 +1361,8 @@ def smoke_sdk_dynamic_tools(base_url: str, executable: Path, update_snapshots: b "DSH_PERMISSION_MODE": "danger-full-access", "DSH_TELEMETRY_DISABLED": "1", "HOME": str(user_home), "USERPROFILE": str(user_home), "DSH_AGENTS_HOME": str(user_home / ".agents"), + # Pin the tool baseline independently of optional resources in the host cache. + "DSH_PRIMARY_RUNTIME": "", }, api_key="sk-keyless-smoke", base_url=base_url, request_timeout_seconds=60, ) as harness: diff --git a/scripts/snapshots/python-sdk-single-exe/dynamic-tools/tool-history.json b/scripts/snapshots/python-sdk-single-exe/dynamic-tools/tool-history.json index 7a8a75aa85..325d376c9a 100644 --- a/scripts/snapshots/python-sdk-single-exe/dynamic-tools/tool-history.json +++ b/scripts/snapshots/python-sdk-single-exe/dynamic-tools/tool-history.json @@ -56,7 +56,6 @@ "job_list", "job_output", "list_agents", - "load_workspace_dependencies", "read", "read_image", "send_message", @@ -87,7 +86,7 @@ }, { "type": "request/header", - "seq": 20, + "seq": 19, "time": 0, "data": { "header": { @@ -113,7 +112,6 @@ "job_list", "job_output", "list_agents", - "load_workspace_dependencies", "read", "read_image", "send_message", @@ -135,7 +133,7 @@ }, { "type": "developer/message", - "seq": 21, + "seq": 20, "time": 0, "data": { "turn": 1, @@ -153,13 +151,13 @@ "role": "developer", "id": "{{messageId}}" }, - "headerSeq": 20 + "headerSeq": 19 }, "surfaceOp": "append" }, { "type": "request/header", - "seq": 28, + "seq": 26, "time": 0, "data": { "header": { @@ -185,7 +183,6 @@ "job_list", "job_output", "list_agents", - "load_workspace_dependencies", "read", "read_image", "send_message", @@ -206,7 +203,7 @@ }, { "type": "developer/message", - "seq": 29, + "seq": 27, "time": 0, "data": { "turn": 1,