diff --git a/apps/cli/config/examples/ptc.yml b/apps/cli/config/examples/ptc.yml new file mode 100644 index 0000000000..d0c2cc888a --- /dev/null +++ b/apps/cli/config/examples/ptc.yml @@ -0,0 +1,5 @@ +- id: tool-executor-native + disabled: true + +- id: tool-executor-ptc + disabled: false diff --git a/apps/cli/reference/README.i18n.yaml b/apps/cli/reference/README.i18n.yaml index 3144f47b3a..187828977b 100644 --- a/apps/cli/reference/README.i18n.yaml +++ b/apps/cli/reference/README.i18n.yaml @@ -21,8 +21,8 @@ en: 8d2cfff9bab7327f zh: 21515ab6e6798504 /dsh-cli-behavior-reference/web-profile: - en: 850434e8ca162221 - zh: 4893613f97a21eb8 + en: 179aa84fb1f628c0 + zh: 01da19b1a5d6aaf3 /dsh-cli-behavior-reference/shared-deployment-behavior: en: 6a7abc9339e8f37f zh: 133adb834c8e195d diff --git a/apps/cli/reference/README.md b/apps/cli/reference/README.md index 58ffe96eb8..7ab66654e3 100644 --- a/apps/cli/reference/README.md +++ b/apps/cli/reference/README.md @@ -130,7 +130,7 @@ The base-backed modes treat the invoking directory as the default workspace root New sessions in base-backed profiles default to the `workspace-write` permission preset. Bash and filesystem mutations are restricted to the session workspace and platform temporary roots; reads and network access are not confined, while process visibility depends on the selected sandbox backend — bwrap runs commands in a private PID namespace that hides host processes, and Landlock and Seatbelt leave host process visibility unchanged. `DSH_PERMISSION_MODE` changes the process fallback. Stored General-settings permissions affect later Web sessions, not an already-open one. The standalone `sdk-minimal` tree instead pins `danger-full-access` and mounts no approval or permission-settings service. -`DSH_TOOLS_MODE` selects `native` or `ptc` for the process; another value fails at boot. The shipped `minimal` agent preset keeps that deployment presentation, fixes the complete system prompt to `You are a helpful software engineer assistant.`, and provides the platform-selected persistent shell, `read_image`, and `working_directory`. It supports automatic context compaction and the human `/compact` command. Select 极简模式 when creating a Web session; the shared browser, workspace, persistence, sandbox, and permission host remains in place. +Web and headless inherit native tool execution from the base bundle; use a [PTC overlay](../config/examples/ptc.yml) to select PTC. The shipped `minimal` agent preset keeps that deployment presentation, fixes the complete system prompt to `You are a helpful software engineer assistant.`, and provides the platform-selected persistent shell, `read_image`, and `working_directory`. It supports automatic context compaction and the human `/compact` command. Select 极简模式 when creating a Web session; the shared browser, workspace, persistence, sandbox, and permission host remains in place. ## Shared deployment behavior diff --git a/apps/cli/reference/README.zh.md b/apps/cli/reference/README.zh.md index 85312166bc..947ec64230 100644 --- a/apps/cli/reference/README.zh.md +++ b/apps/cli/reference/README.zh.md @@ -132,7 +132,7 @@ dsh web --help 基于 base 的 profile 中,新会话默认使用 `workspace-write` 权限预设。Bash 和文件系统修改仅限于会话 workspace 与平台临时根目录;读取和网络访问不受限制,进程可见性则取决于所选沙箱后端——bwrap 在私有 PID 命名空间中运行命令并隐藏宿主进程,Landlock 与 Seatbelt 保持宿主进程可见性不变。`DSH_PERMISSION_MODE` 更改进程后备值。General settings 中存储的权限影响后续 Web 会话,不改变已打开的会话。独立的 `sdk-minimal` 配置树则固定为 `danger-full-access`,且不挂载 approval 或权限 settings 服务。 -`DSH_TOOLS_MODE` 为进程选择 `native` 或 `ptc`;其他值会导致启动失败。随附的 `minimal` agent preset 会保留该部署的呈现方式,将完整系统提示词固定为 `You are a helpful software engineer assistant.`,并提供按平台选择的持久 shell、`read_image` 和 `working_directory`。它支持自动上下文压缩和用户 `/compact` 命令。创建 Web 会话时请选择极简模式;共享的浏览器、workspace、持久化、沙箱与权限宿主保持不变。 +Web 与 headless 继承 base bundle 的原生工具执行;使用 [PTC overlay](../config/examples/ptc.yml) 可选择 PTC。随附的 `minimal` agent preset 会保留该部署的呈现方式,将完整系统提示词固定为 `You are a helpful software engineer assistant.`,并提供按平台选择的持久 shell、`read_image` 和 `working_directory`。它支持自动上下文压缩和用户 `/compact` 命令。创建 Web 会话时请选择极简模式;共享的浏览器、workspace、持久化、沙箱与权限宿主保持不变。 ## 共享部署行为 diff --git a/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts b/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts index b71ffc778c..a2ae0c9f19 100644 --- a/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts @@ -555,7 +555,7 @@ describe('headless stream-json snapshots', () => { '--permission-mode', 'auto', '--approval-policy', 'never', '--json', 'Run the CLI probe.', ], tsconfigPath, - env: { DSH_AUTO_REVIEW_TEST_MODE: mode, DSH_TOOLS_MODE: 'native', DSH_TELEMETRY_DISABLED: '1' }, + env: { DSH_AUTO_REVIEW_TEST_MODE: mode, DSH_TELEMETRY_DISABLED: '1' }, inspect: async (cwd) => { const logs = await persistedLogs(cwd, join(cwd, '.dsh', 'sessions')) expect(logs).toHaveLength(1) @@ -605,7 +605,7 @@ describe('headless stream-json snapshots', () => { const first = await runLoaderSmoke({ label: 'headless Auto denied wake', cwd, binScript: dshBinScript, configPath: autoReviewOverlayPath, binArgs: [...args, '--permission-mode', 'auto', '--approval-policy', 'never', 'Run the CLI probe.'], tsconfigPath, - env: { DSH_AUTO_REVIEW_TEST_MODE: 'deny', DSH_TOOLS_MODE: 'native', DSH_TELEMETRY_DISABLED: '1' }, + env: { DSH_AUTO_REVIEW_TEST_MODE: 'deny', DSH_TELEMETRY_DISABLED: '1' }, }) const sessionId = parseJsonl(first.stdout)[0]?.sessionId if (typeof sessionId !== 'string') throw new Error('missing Auto Session identity') @@ -615,7 +615,7 @@ describe('headless stream-json snapshots', () => { const second = await runLoaderSmoke({ label: 'headless Auto authorized wake', cwd, binScript: dshBinScript, configPath: autoReviewOverlayPath, binArgs: [...args, '--session-id', sessionId, 'I authorize the exact CLI probe. Retry it.'], tsconfigPath, - env: { DSH_AUTO_REVIEW_TEST_MODE: 'allow', DSH_TOOLS_MODE: 'native', DSH_TELEMETRY_DISABLED: '1' }, + env: { DSH_AUTO_REVIEW_TEST_MODE: 'allow', DSH_TELEMETRY_DISABLED: '1' }, inspect: async (directory) => { const logs = await persistedLogs(directory, join(directory, '.dsh', 'sessions')) expect(logs).toHaveLength(1) diff --git a/apps/cli/tests/profiles/headless/tests/source-tool.built.e2e.ts b/apps/cli/tests/profiles/headless/tests/source-tool.built.e2e.ts index 4988ef80fd..03f94856f4 100644 --- a/apps/cli/tests/profiles/headless/tests/source-tool.built.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/source-tool.built.e2e.ts @@ -58,7 +58,6 @@ describe.skipIf(!existsSync(join(repoRoot, 'apps/cli/lib/bin.js')))('dsh SOURCE DSH_HOME: join(root, 'home'), DSH_AGENTS_HOME: join(root, 'agents'), DSH_TELEMETRY_DISABLED: '1', - DSH_TOOLS_MODE: 'native', DSH_CLI_MOCK_FAILURE: '0', }, input: '', diff --git a/apps/cli/tests/profiles/sdk/office-cli.e2e.ts b/apps/cli/tests/profiles/sdk/office-cli.e2e.ts index 2c90e5fc6e..7c8a94748d 100644 --- a/apps/cli/tests/profiles/sdk/office-cli.e2e.ts +++ b/apps/cli/tests/profiles/sdk/office-cli.e2e.ts @@ -58,7 +58,7 @@ it.skipIf(!process.env.DEEPSEEK_API_KEY || process.platform === 'win32')(`agent ], { cwd: root, env: { PATH: path, DSH_HOME: join(root, 'home'), DSH_AGENTS_HOME: join(root, 'agents'), - DSH_PERMISSION_MODE: 'danger-full-access', DSH_TELEMETRY_DISABLED: '1', DSH_TOOLS_MODE: 'native', + DSH_PERMISSION_MODE: 'danger-full-access', DSH_TELEMETRY_DISABLED: '1', DSH_PRIMARY_RUNTIME: carrier === 'sdk' ? undefined : '', }, timeout: 110_000, killSignal: 'SIGKILL', reject: false, diff --git a/apps/web/tests/scaffold.ts b/apps/web/tests/scaffold.ts index 61af9d9e5c..f23a3d948e 100644 --- a/apps/web/tests/scaffold.ts +++ b/apps/web/tests/scaffold.ts @@ -384,13 +384,6 @@ export interface LaunchOptions { paceMs?: number /** Synthetic model capacity for UI scenarios whose seeded history must remain uncompacted. */ replayContextWindow?: number - /** - * Tool presentation mode patched onto the shipped `tools` row (`code` - * collapses the wire to run_code + the SDK prompt section). Omit for the - * yml default. The PTC runtime row is always in the tree, so no extra - * insertion is needed. - */ - toolsMode?: 'native' | 'ptc' /** * Keep the shipped DeepSeek adapter mounted while masking the process * environment's DEEPSEEK_API_KEY for this scaffold lifetime. This is the @@ -689,7 +682,6 @@ export async function launchWebScaffold(options: LaunchOptions = {}): Promise ({ id: `preset-${config.id}`, name: '@deepseek-ai/dsh-agent-preset', config })) }, ], - ...options.toolsMode === undefined ? [] : [{ id: 'tools', config: { mode: options.toolsMode } }], ...options.deepSeekSearch === undefined ? [] : [{ diff --git a/apps/web/tests/smoke-real.e2e.ts b/apps/web/tests/smoke-real.e2e.ts index cf1546ccf2..a980c2d02d 100644 --- a/apps/web/tests/smoke-real.e2e.ts +++ b/apps/web/tests/smoke-real.e2e.ts @@ -603,7 +603,7 @@ describe('dsh web keyless CLI smoke', () => { } }, 120_000) - it('DSH_TOOLS_MODE=ptc collapses the provider wire tools to run_code with the SDK prompt section', async () => { + it.each(['native', 'ptc'] as const)('selects the %s executor through profile overlays and ignores the retired environment selector', async (mode) => { requireDist() const workspace = mkdtempSync(join(tmpdir(), 'dsh-web-ptc-')) @@ -632,14 +632,17 @@ describe('dsh web keyless CLI smoke', () => { const tsxLoader = pathToFileURL(createRequire(join(REPO_ROOT, 'package.json')).resolve('tsx')).href const child = spawn( process.execPath, - ['--import', tsxLoader, join(REPO_ROOT, 'apps/cli/src/bin.ts'), 'web', '--no-open', '--port', '0'], + ['--import', tsxLoader, join(REPO_ROOT, 'apps/cli/src/bin.ts'), 'web', + ...mode === 'ptc' ? ['--patch', join(REPO_ROOT, 'apps/cli/config/examples/ptc.yml')] : [], + '--no-open', '--port', '0', + ], { cwd: workspace, env: { ...process.env, DEEPSEEK_API_KEY: 'keyless-web-ptc', DEEPSEEK_BASE_URL: `http://127.0.0.1:${address.port}`, - DSH_TOOLS_MODE: 'ptc', + DSH_TOOLS_MODE: mode === 'native' ? 'ptc' : 'native', DSH_HOME: join(workspace, '.dsh'), DSH_AGENTS_HOME: join(workspace, '.agents'), TSX_TSCONFIG_PATH: join(REPO_ROOT, 'tsconfig.json'), @@ -662,9 +665,15 @@ describe('dsh web keyless CLI smoke', () => { setTimeout(() => { reject(new Error('provider request not received in 10s')) }, 10_000).unref() }), ]) - expect(captured.tools?.map(tool => tool.name)).toEqual(['run_code']) - expect(captured.system).toContain('## Writing code for run_code') - expect(captured.system).toContain('declare const tools') + if (mode === 'ptc') { + expect(captured.tools?.map(tool => tool.name)).toEqual(['run_code']) + expect(captured.system).toContain('## Writing code for run_code') + expect(captured.system).toContain('declare const tools') + } else { + expect(captured.tools?.map(tool => tool.name)).not.toContain('run_code') + expect(captured.tools?.length).toBeGreaterThan(0) + expect(captured.system).not.toContain('## Writing code for run_code') + } } finally { const closed = child.exitCode === null ? new Promise((resolveClose) => { child.once('close', () => { resolveClose() }) }) diff --git a/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.i18n.yaml b/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.i18n.yaml index 6a9350d082..50a174e891 100644 --- a/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.i18n.yaml +++ b/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.i18n.yaml @@ -9,8 +9,8 @@ en: 73002a2963b9ea27 zh: a67ea382d7c70c61 /mount-a-tool-executor/change: - en: f732d85027bf0236 - zh: d6597cde7ba31e47 + en: 6d9c056c4ec74661 + zh: 340be0eddcd30049 /mount-a-tool-executor/migration: - en: 79369b132e00d7be - zh: e93523fa3369ae1c + en: 60ea8b9dd1128dfc + zh: 7c648e9e5d75c49b diff --git a/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.md b/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.md index a5a378695c..48a12c3078 100644 --- a/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.md +++ b/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.md @@ -9,7 +9,7 @@ English | [中文](guide.zh.md) ## Change -`@deepseek-ai/dsh-tools` keeps tool registration and executor selection, but rejects the removed `mode` and `maxParallelSubCalls` configuration fields at load time. Native and PTC execution require `@deepseek-ai/dsh-tool-executor-native` or `@deepseek-ai/dsh-tool-executor-ptc`. Shipped profiles mount an executor; custom compositions must add one. The existing `dsh-agent-tool-presentation` selector remains supported but is deprecated. Enabled `tool-executor-native` and `tool-executor-ptc` rows are required at startup; invalid executor configuration now stops startup. +`@deepseek-ai/dsh-tools` keeps tool registration and executor selection, but rejects the removed `mode` and `maxParallelSubCalls` configuration fields at load time. Native and PTC execution require `@deepseek-ai/dsh-tool-executor-native` or `@deepseek-ai/dsh-tool-executor-ptc`. Shipped profiles mount an executor; custom compositions must add one. The existing `dsh-agent-tool-presentation` selector remains supported but is deprecated. Enabled `tool-executor-native` and `tool-executor-ptc` rows are required at startup; invalid executor configuration now stops startup. Web and headless no longer read `DSH_TOOLS_MODE`; both inherit native execution from base. ## Migration @@ -19,3 +19,17 @@ English | [中文](guide.zh.md) 4. Import PTC SDK renderers and `CodeRunFailedError` from `@deepseek-ai/dsh-tool-executor-ptc`. Run a task with the selected interaction mode and confirm its tool results appear in the Session. 5. Import `ToolExecutionPipeline` and `executeToolBatch` from `@deepseek-ai/dsh-tools/pipeline`. Complete calls, including single calls, use batches; custom schedulers call `prepare`, `dispatch`, and `complete` directly. Add `@deepseek-ai/dsh-tool-testkit` as a development dependency for tool fixtures and import them from that package. Neither is exported by the tool registry entry. 6. Import `PtcDispatchLog` from the PTC executor package using `import type`. Detached Session readers use `@deepseek-ai/dsh-tool-executor-ptc/types` for durable dispatch events. Live PTC policy and message-source declarations remain on the executor package entry. + +7. Remove `DSH_TOOLS_MODE` from launch environments. For PTC, save the following as `ptc.yml` and pass it with `--patch`; native execution needs no overlay. The same patch works with `--profile web`. Confirm that the model receives only `run_code` in PTC mode. + +```yaml +- id: tool-executor-native + disabled: true + +- id: tool-executor-ptc + disabled: false +``` + +```bash +dsh --profile headless --patch ./ptc.yml "task" +``` diff --git a/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.zh.md b/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.zh.md index ad38d78427..dd9d85657d 100644 --- a/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.zh.md +++ b/docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.zh.md @@ -9,7 +9,7 @@ description: "工具执行模式从 dsh-tools 配置移至显式执行器插件 ## 变更 -`@deepseek-ai/dsh-tools` 保留工具注册和执行器选择,但加载时会拒绝已移除的 `mode` 和 `maxParallelSubCalls` 配置字段。原生和 PTC 执行分别要求 `@deepseek-ai/dsh-tool-executor-native` 或 `@deepseek-ai/dsh-tool-executor-ptc`。内置 profile 已挂载执行器,自定义组合必须添加一个。现有 `dsh-agent-tool-presentation` 选择器仍受支持,但已弃用。启用的 `tool-executor-native` 和 `tool-executor-ptc` 条目属于启动必需项;无效的执行器配置会终止启动。 +`@deepseek-ai/dsh-tools` 保留工具注册和执行器选择,但加载时会拒绝已移除的 `mode` 和 `maxParallelSubCalls` 配置字段。原生和 PTC 执行分别要求 `@deepseek-ai/dsh-tool-executor-native` 或 `@deepseek-ai/dsh-tool-executor-ptc`。内置 profile 已挂载执行器,自定义组合必须添加一个。现有 `dsh-agent-tool-presentation` 选择器仍受支持,但已弃用。启用的 `tool-executor-native` 和 `tool-executor-ptc` 条目属于启动必需项;无效的执行器配置会终止启动。Web 与 headless 不再读取 `DSH_TOOLS_MODE`,均继承 base 的原生执行。 ## 迁移 @@ -19,3 +19,17 @@ description: "工具执行模式从 dsh-tools 配置移至显式执行器插件 4. 从 `@deepseek-ai/dsh-tool-executor-ptc` 导入 PTC SDK 渲染器和 `CodeRunFailedError`。用所选交互模式运行任务,确认工具结果出现在 Session 中。 5. 从 `@deepseek-ai/dsh-tools/pipeline` 导入 `ToolExecutionPipeline` 和 `executeToolBatch`。完整执行(包括单次调用)使用批次;自定义调度器直接调用 `prepare`、`dispatch` 和 `complete`。把 `@deepseek-ai/dsh-tool-testkit` 添加为开发依赖,并从该包导入工具 fixture。工具注册表主入口不再导出这些成员。 6. 使用 `import type` 从 PTC 执行器包导入 `PtcDispatchLog`。独立 Session 读取者通过 `@deepseek-ai/dsh-tool-executor-ptc/types` 获取持久化分派事件;PTC 实时策略与消息来源声明由执行器包主入口提供。 + +7. 从启动环境中移除 `DSH_TOOLS_MODE`。需要 PTC 时,将以下内容保存为 `ptc.yml` 并通过 `--patch` 加载;原生执行无需 overlay。同一补丁也适用于 `--profile web`。确认 PTC 模式下模型只收到 `run_code`。 + +```yaml +- id: tool-executor-native + disabled: true + +- id: tool-executor-ptc + disabled: false +``` + +```bash +dsh --profile headless --patch ./ptc.yml "task" +``` diff --git a/packages/bundle/headless/README.i18n.yaml b/packages/bundle/headless/README.i18n.yaml index 4c46daefd0..3ab283a4bb 100644 --- a/packages/bundle/headless/README.i18n.yaml +++ b/packages/bundle/headless/README.i18n.yaml @@ -48,8 +48,8 @@ en: fdef19c52550fdf8 zh: c4bab4fb6972cf10 /deepseek-ai-dsh-headless/understand-the-implementation/patch-surface-over-base: - en: 290f49af13bb0ae1 - zh: 5b599df1df8e6b7b + en: e192267973792ad1 + zh: ef78428a68dff9e4 /deepseek-ai-dsh-headless/understand-the-implementation/exit-mapping: en: c2466d3d8a26177a zh: b75743bfa2e7723a diff --git a/packages/bundle/headless/README.md b/packages/bundle/headless/README.md index b96232b7d5..f7b4cffadb 100644 --- a/packages/bundle/headless/README.md +++ b/packages/bundle/headless/README.md @@ -125,7 +125,7 @@ The runner awaits the complete application (`ctx.get('loader')?.await()`) so the ### Patch surface over base -The patch rides over `dsh-base`: it inherits the projection cache and shared PTC runtime, sets the coding persona prefix and separate cwd suffix on the base `system-prompt` row, keeps the same temporary process-wide PTC mode opt-in (`DSH_TOOLS_MODE`) as the Web surface, disables the shared HMR row, and mounts the startup provider and the runner. The cache checkpoints each persisted one-shot session for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. The startup provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the task and invocation options, prints the app's `--help`, and provides `headlessStartup`; the runner reads its task, Session identity, and output mode from lazy config. It reads the explicit CLI permission preset and approval policy directly from `headlessStartup`, with runner config as the default. The sandbox-policy row also waits for this service, retaining the base mode and workspace defaults while receiving the explicit directory grants. +The patch rides over `dsh-base`: it inherits the projection cache and shared PTC runtime, sets the coding persona prefix and separate cwd suffix on the base `system-prompt` row, inherits native tool execution and accepts a [PTC overlay](../../../apps/cli/config/examples/ptc.yml), disables the shared HMR row, and mounts the startup provider and the runner. The cache checkpoints each persisted one-shot session for later consumers; its durability barrier flushes each covered log prefix before publishing the cache row and may split otherwise coalesced JSONL runs. The startup provider ([`src/startup.ts`](src/startup.ts)) injects `ctx.cmdlineArgs` ([`dsh-cmdline`](../../boot/cmdline/README.md)), reads the task and invocation options, prints the app's `--help`, and provides `headlessStartup`; the runner reads its task, Session identity, and output mode from lazy config. It reads the explicit CLI permission preset and approval policy directly from `headlessStartup`, with runner config as the default. The sandbox-policy row also waits for this service, retaining the base mode and workspace defaults while receiving the explicit directory grants. ### Exit mapping diff --git a/packages/bundle/headless/README.zh.md b/packages/bundle/headless/README.zh.md index d52e539c49..72de7b6eb1 100644 --- a/packages/bundle/headless/README.zh.md +++ b/packages/bundle/headless/README.zh.md @@ -125,7 +125,7 @@ runner 等待整个应用结算(`ctx.get('loader')?.await()`),确保已组 ### 基于 base 的 patch 内容 -patch 叠加在 `dsh-base` 之上:继承投影缓存与共享 PTC 运行时,在基础 `system-prompt` 行上设置编码 persona 前缀与独立的 cwd 后缀,保留与 Web 表层相同的临时进程级 PTC mode 开关(`DSH_TOOLS_MODE`),禁用共享的 HMR(热模块替换)行,并挂载启动提供方与 runner。缓存为每个已持久化的一次性会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 连续段。启动提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取任务和调用选项、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 从延迟配置读取任务、Session 标识与输出模式。它直接从 `headlessStartup` 读取显式 CLI 权限预设与审批策略,以 runner 配置作为默认值。sandbox-policy 行也等待此服务,在保留基础模式和工作区默认值的同时接收显式目录授权。 +patch 叠加在 `dsh-base` 之上:继承投影缓存与共享 PTC 运行时,在基础 `system-prompt` 行上设置编码 persona 前缀与独立的 cwd 后缀,继承原生工具执行,并支持 [PTC overlay](../../../apps/cli/config/examples/ptc.yml),禁用共享的 HMR(热模块替换)行,并挂载启动提供方与 runner。缓存为每个已持久化的一次性会话写入检查点,供后续消费方使用;其持久性屏障会在发布缓存行前 flush 所覆盖的日志前缀,因此可能拆分原本会合并的 JSONL 连续段。启动提供方([`src/startup.ts`](src/startup.ts))注入 `ctx.cmdlineArgs`([`dsh-cmdline`](../../boot/cmdline/README.zh.md)),读取任务和调用选项、打印应用自己的 `--help`,并提供 `headlessStartup`;runner 从延迟配置读取任务、Session 标识与输出模式。它直接从 `headlessStartup` 读取显式 CLI 权限预设与审批策略,以 runner 配置作为默认值。sandbox-policy 行也等待此服务,在保留基础模式和工作区默认值的同时接收显式目录授权。 ### 退出映射 diff --git a/packages/bundle/headless/cordis.patch.yml b/packages/bundle/headless/cordis.patch.yml index 7d366b3fe8..0a0ea09b7f 100644 --- a/packages/bundle/headless/cordis.patch.yml +++ b/packages/bundle/headless/cordis.patch.yml @@ -11,18 +11,6 @@ personaPrefix: >- You are a coding agent powered by the {{model}} model. -- id: tool-executor-native - name: '@deepseek-ai/dsh-tool-executor-native' - disabled: !!js | - (() => { - const mode = process.env.DSH_TOOLS_MODE ?? 'native' - if (mode !== 'native' && mode !== 'ptc') throw new Error('DSH_TOOLS_MODE must be native or ptc') - return mode === 'ptc' - })() - -- id: tool-executor-ptc - disabled: !!js process.env.DSH_TOOLS_MODE !== 'ptc' - - insert: - id: headless-startup name: '@deepseek-ai/dsh-headless/startup' diff --git a/packages/bundle/web-app/README.i18n.yaml b/packages/bundle/web-app/README.i18n.yaml index 1d8e07a01e..6dc5f64ac6 100644 --- a/packages/bundle/web-app/README.i18n.yaml +++ b/packages/bundle/web-app/README.i18n.yaml @@ -36,8 +36,8 @@ en: 323742a00cfe67b2 zh: eff5245a82fc16e7 /deepseek-ai-dsh-web-app/understand-the-implementation/patch-semantics: - en: 8a2554b3d17daa1f - zh: 66f18946791cabb6 + en: a6d126f30b1594d7 + zh: 7f0ccc8a55d6f743 /deepseek-ai-dsh-web-app/understand-the-implementation/advertised-application-url: en: 0b22e03e28d41420 zh: fd45a97dd4336c25 diff --git a/packages/bundle/web-app/README.md b/packages/bundle/web-app/README.md index 2305d402cd..67448234b9 100644 --- a/packages/bundle/web-app/README.md +++ b/packages/bundle/web-app/README.md @@ -102,7 +102,7 @@ The bundle is one patch layer of five files plus one runtime glue plugin: `cordi ### Patch semantics -A patch replaces the targeted row's whole `config`, so each web row restates every key it owns: the persona prefix template, the `DSH_TOOLS_MODE` PTC mode opt-in, and the `session-query-sqlite` values on the base rows, then `insert` adds the web host rows, transport, and browser roster. The `webserver`, `web-runtime`, and `connection` rows inject the `webStartup` provider and read their invocation values directly; Connection composes its accepted authorities from `ctx.webStartup.trustedHosts`, and the Host/Origin fence accepts the listener's bind IP independently of that list. The per-agent tool rows the base mounts process-wide are disabled here and the preset roster takes over; the reasoning for each host-plane versus preset-plane decision is inline in the patch. +A patch replaces the targeted row's whole `config`, so each web row restates every key it owns: the persona prefix template and the `session-query-sqlite` values on the base rows, then `insert` adds the web host rows, transport, and browser roster. The `webserver`, `web-runtime`, and `connection` rows inject the `webStartup` provider and read their invocation values directly; Connection composes its accepted authorities from `ctx.webStartup.trustedHosts`, and the Host/Origin fence accepts the listener's bind IP independently of that list. The bundle inherits native tool execution; a [PTC overlay](../../../apps/cli/config/examples/ptc.yml) changes the deployment executor. The per-agent tool rows the base mounts process-wide are disabled here and the preset roster takes over; the reasoning for each host-plane versus preset-plane decision is inline in the patch. ### Advertised application URL diff --git a/packages/bundle/web-app/README.zh.md b/packages/bundle/web-app/README.zh.md index 8d08ede8af..ef5e029f53 100644 --- a/packages/bundle/web-app/README.zh.md +++ b/packages/bundle/web-app/README.zh.md @@ -102,7 +102,7 @@ dsh --profile web --no-open --host "$(hostname -i | awk '{print $1}')" --public- ### patch 语义 -patch 会替换目标行的整个 `config`,因此每个 Web 行都重述自己拥有的每个键:基础行上的 persona 前缀模板、`DSH_TOOLS_MODE` PTC mode 开关与 `session-query-sqlite` 值,随后 `insert` 添加 Web 宿主行、传输层与浏览器名录。`webserver`、`web-runtime` 与 `connection` 行都注入 `webStartup` 提供方并直接读取本次调用的取值;Connection 的被接受 authority 全部来自 `ctx.webStartup.trustedHosts`,Host/Origin 栅栏则独立于该列表接受监听器的绑定 IP。base 以进程级挂载的按 agent 工具行在这里被禁用,由 preset 名录接管;每项宿主层与 preset 层归属决策的理由以行内注释写在 patch 里。 +patch 会替换目标行的整个 `config`,因此每个 Web 行都重述自己拥有的每个键:基础行上的 persona 前缀模板与 `session-query-sqlite` 值,随后 `insert` 添加 Web 宿主行、传输层与浏览器名录。`webserver`、`web-runtime` 与 `connection` 行都注入 `webStartup` 提供方并直接读取本次调用的取值;Connection 的被接受 authority 全部来自 `ctx.webStartup.trustedHosts`,Host/Origin 栅栏则独立于该列表接受监听器的绑定 IP。bundle 继承原生工具执行;[PTC overlay](../../../apps/cli/config/examples/ptc.yml) 可切换部署执行器。base 以进程级挂载的按 agent 工具行在这里被禁用,由 preset 名录接管;每项宿主层与 preset 层归属决策的理由以行内注释写在 patch 里。 ### 公告应用 URL diff --git a/packages/bundle/web-app/cordis.patch.yml b/packages/bundle/web-app/cordis.patch.yml index 06d5a8424a..9eda9fccde 100644 --- a/packages/bundle/web-app/cordis.patch.yml +++ b/packages/bundle/web-app/cordis.patch.yml @@ -29,18 +29,6 @@ path: ':memory:' openAt: never -- id: tool-executor-native - name: '@deepseek-ai/dsh-tool-executor-native' - disabled: !!js | - (() => { - const mode = process.env.DSH_TOOLS_MODE ?? 'native' - if (mode !== 'native' && mode !== 'ptc') throw new Error('DSH_TOOLS_MODE must be native or ptc') - return mode === 'ptc' - })() - -- id: tool-executor-ptc - disabled: !!js process.env.DSH_TOOLS_MODE !== 'ptc' - # ── web-only host rows, the transport layer, and the browser roster ───────── # `dsh.client` rows are the browser roster the modules node half scans into diff --git a/packages/experimental/auto-review/README.md b/packages/experimental/auto-review/README.md index 3404f3b887..d37bd56ee8 100644 --- a/packages/experimental/auto-review/README.md +++ b/packages/experimental/auto-review/README.md @@ -50,7 +50,7 @@ Enable the experimental layer explicitly for the headless profile: ```sh dsh plugin --profile headless add @deepseek-ai/dsh-experimental-auto-review -DSH_TOOLS_MODE=native dsh headless --permission-mode auto --approval-policy never --json - < task.txt > run.jsonl +dsh headless --permission-mode auto --approval-policy never --json - < task.txt > run.jsonl ``` For a one-off run from this checkout, pass `--patch /absolute/path/to/packages/experimental/auto-review/cordis.patch.yml` instead of installing a profile layer. An unavailable Auto integration fails preset selection before reading stdin or starting model work. Auto uses full host access; `--add-dir` is unnecessary in this mode. Native tools keep every tool body on the review path; direct Node effects in PTC remain outside it. diff --git a/packages/experimental/auto-review/README.zh.md b/packages/experimental/auto-review/README.zh.md index 4f7551f7cd..32602f6874 100644 --- a/packages/experimental/auto-review/README.zh.md +++ b/packages/experimental/auto-review/README.zh.md @@ -50,7 +50,7 @@ pnpm dsh plugin --profile web remove @deepseek-ai/dsh-experimental-auto-review ```sh dsh plugin --profile headless add @deepseek-ai/dsh-experimental-auto-review -DSH_TOOLS_MODE=native dsh headless --permission-mode auto --approval-policy never --json - < task.txt > run.jsonl +dsh headless --permission-mode auto --approval-policy never --json - < task.txt > run.jsonl ``` 如需从此 checkout 单次运行,可传入 `--patch /absolute/path/to/packages/experimental/auto-review/cordis.patch.yml`,无需安装 profile 层。Auto integration 不可用时,权限预设选择会在读取 stdin 或开始模型工作前失败。Auto 使用完全主机访问权限;此模式不需要 `--add-dir`。原生工具让每个工具 body 都经过审查;PTC 的直接 Node 效果仍不在审查范围内。 diff --git a/packages/skill/skill-office/tests/docx-validation.e2e.ts b/packages/skill/skill-office/tests/docx-validation.e2e.ts index a9a7a60fe9..6d2a2d8130 100644 --- a/packages/skill/skill-office/tests/docx-validation.e2e.ts +++ b/packages/skill/skill-office/tests/docx-validation.e2e.ts @@ -19,8 +19,9 @@ it.skipIf(!process.env.DEEPSEEK_API_KEY || !runtime).each(['native', 'ptc'])('ch binScript: join(repo, 'apps/cli/src/bin.ts'), sourceImport: 'tsx/esm', tsconfigPath: join(repo, 'tsconfig.base.json'), configPath: 'office.patch.json', processTimeoutMs: 110_000, binArgs: ['--profile', 'headless', '--patch', 'office.patch.json', + ...mode === 'ptc' ? ['--patch', join(repo, 'apps/cli/config/examples/ptc.yml')] : [], 'In input.docx, change the title paragraph text to Quarterly results without changing its formatting; save result.docx. Do not render or visually inspect the finished file. Respond briefly in English.'], - env: { DSH_PRIMARY_RUNTIME: runtime, DSH_PERMISSION_MODE: 'danger-full-access', DSH_TELEMETRY_DISABLED: '1', DSH_TOOLS_MODE: mode, + env: { DSH_PRIMARY_RUNTIME: runtime, DSH_PERMISSION_MODE: 'danger-full-access', DSH_TELEMETRY_DISABLED: '1', HTTP_PROXY: process.env.DSH_OFFICE_TEST_PROXY, HTTPS_PROXY: process.env.DSH_OFFICE_TEST_PROXY }, async prepare(cwd) { await execa(python!, ['-c', ` diff --git a/packages/skill/skill-office/tests/render-tools.e2e.ts b/packages/skill/skill-office/tests/render-tools.e2e.ts index f2dc0cfbf2..7d7653e555 100644 --- a/packages/skill/skill-office/tests/render-tools.e2e.ts +++ b/packages/skill/skill-office/tests/render-tools.e2e.ts @@ -15,7 +15,7 @@ const python = runtime && join(runtime, 'dependencies/python', process.platform const launch = { binScript: join(repo, 'apps/cli/src/bin.ts'), sourceImport: 'tsx/esm' as const, tsconfigPath: join(repo, 'tsconfig.base.json'), configPath: 'office.patch.json', processTimeoutMs: 110_000, - env: { DSH_PRIMARY_RUNTIME: runtime, DSH_PERMISSION_MODE: 'danger-full-access', DSH_TELEMETRY_DISABLED: '1', DSH_TOOLS_MODE: 'native', + env: { DSH_PRIMARY_RUNTIME: runtime, DSH_PERMISSION_MODE: 'danger-full-access', DSH_TELEMETRY_DISABLED: '1', HTTP_PROXY: process.env.DSH_OFFICE_TEST_PROXY, HTTPS_PROXY: process.env.DSH_OFFICE_TEST_PROXY }, } @@ -39,8 +39,9 @@ async function sessionEvents(cwd: string) { it.skipIf(!process.env.DEEPSEEK_API_KEY || !runtime).each([{ format: 'docx', mode: 'native' }, { format: 'pdf', mode: 'native' }, { format: 'docx', mode: 'ptc' }])('loads the operation skill and renders $format through the shell in $mode mode', async ({ format, mode }) => { await runLoaderSmoke({ label: 'Office CLI rendering', tempDirPrefix: 'dsh-office-render-', - ...launch, env: { ...launch.env, DSH_TOOLS_MODE: mode }, + ...launch, binArgs: ['--profile', 'headless', '--patch', 'office.patch.json', + ...mode === 'ptc' ? ['--patch', join(repo, 'apps/cli/config/examples/ptc.yml')] : [], `Export page 1 of input.${format} as PNG into preview and check that the exported image is readable. Report briefly in English.`], async prepare(cwd) { await execa(python!, ['-c', "from docx import Document\nd=Document()\nd.add_heading('Quarterly revenue',0)\nd.add_paragraph('Q1: 12; Q2: 18; Total: 30')\nd.save('input.docx')"], { cwd }) diff --git a/packages/skill/skill-office/tests/visual-intent.e2e.ts b/packages/skill/skill-office/tests/visual-intent.e2e.ts index 55419e5ad1..d37377813d 100644 --- a/packages/skill/skill-office/tests/visual-intent.e2e.ts +++ b/packages/skill/skill-office/tests/visual-intent.e2e.ts @@ -76,7 +76,7 @@ it.skipIf(!process.env.DEEPSEEK_API_KEY || !runtime).each(cases)('$format $kind // The CLI fixture and its log live outside the workspace; bwrap's private /tmp hides both. env: { DSH_PRIMARY_RUNTIME: runtime, DSH_PERMISSION_MODE: 'danger-full-access', DSH_HOME: join(controlRoot, 'home'), DSH_AGENTS_HOME: join(controlRoot, 'agents'), - DSH_TELEMETRY_DISABLED: '1', DSH_TOOLS_MODE: 'native', + DSH_TELEMETRY_DISABLED: '1', TMPDIR: previewRoot, TMP: previewRoot, TEMP: previewRoot, HTTP_PROXY: process.env.DSH_OFFICE_TEST_PROXY, HTTPS_PROXY: process.env.DSH_OFFICE_TEST_PROXY }, async prepare(cwd) { diff --git a/packages/skill/skill-office/tests/xlsx-validation.e2e.ts b/packages/skill/skill-office/tests/xlsx-validation.e2e.ts index b6036ac505..bf3cdf218e 100644 --- a/packages/skill/skill-office/tests/xlsx-validation.e2e.ts +++ b/packages/skill/skill-office/tests/xlsx-validation.e2e.ts @@ -80,6 +80,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY || !runtime).each(['native', 'ptc' tsconfigPath: join(repo, 'tsconfig.base.json'), configPath, binArgs: ['--profile', 'headless', '--patch', configPath, + ...mode === 'ptc' ? ['--patch', join(repo, 'apps/cli/config/examples/ptc.yml')] : [], `${task} Use input.xlsx and save the finished workbook as result.xlsx. Preserve the Notes sheet and leave input.xlsx unchanged. Respond briefly in English.`, ], processTimeoutMs: 110_000, @@ -89,7 +90,6 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY || !runtime).each(['native', 'ptc' DSH_HOME: join(controlRoot, 'home'), DSH_AGENTS_HOME: join(controlRoot, 'agents'), DSH_TELEMETRY_DISABLED: '1', - DSH_TOOLS_MODE: mode, HTTP_PROXY: process.env.DSH_OFFICE_TEST_PROXY, HTTPS_PROXY: process.env.DSH_OFFICE_TEST_PROXY, }, diff --git a/scripts/demo-ptc.mjs b/scripts/demo-ptc.mjs index d0c5c059bc..61702824af 100644 --- a/scripts/demo-ptc.mjs +++ b/scripts/demo-ptc.mjs @@ -10,9 +10,10 @@ const child = spawn(process.execPath, [ 'apps/cli/src/bin.ts', '--profile', 'headless', + '--patch', + 'apps/cli/config/examples/ptc.yml', task, ], { stdio: 'inherit', - env: { ...process.env, DSH_TOOLS_MODE: 'ptc' }, }) child.on('exit', (code, signal) => { process.exit(signal !== null ? 1 : code ?? 1) }) diff --git a/snapshots/session/headless-auto-review/snapshot.yml b/snapshots/session/headless-auto-review/snapshot.yml index fca50a171e..196aac20e3 100644 --- a/snapshots/session/headless-auto-review/snapshot.yml +++ b/snapshots/session/headless-auto-review/snapshot.yml @@ -10,5 +10,3 @@ header: toolSchemasSource: text-turn workspace: final: true -environment: - DSH_TOOLS_MODE: native