From c905726461ba015bcf83b1deb5dd6cdcbb52c0ce Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 21 Oct 2026 19:51:50 +0800 Subject: [PATCH] fix(ptc): allow local process declarations in function bodies --- packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml | 4 ++-- packages/ptc-runtime/ptc-runtime-node/README.md | 2 +- packages/ptc-runtime/ptc-runtime-node/README.zh.md | 2 +- packages/ptc-runtime/ptc-runtime-node/src/process.ts | 5 +++-- .../ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts | 7 +++++++ 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml b/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml index bfd4e338e7..56c2672ca4 100644 --- a/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml +++ b/packages/ptc-runtime/ptc-runtime-node/README.i18n.yaml @@ -21,8 +21,8 @@ en: 34b5a336733ea39f zh: 7604b03999b61517 /deepseek-ai-dsh-ptc-runtime-node/use-this-package/execution-and-results: - en: b7c41598e68edd14 - zh: eee2f2392c2b59ce + en: 2a9fa3292bf07355 + zh: db0b5684820915eb /deepseek-ai-dsh-ptc-runtime-node/use-this-package/in-memory-modes: en: 647286ffa607df3b zh: ddf6d548e914cf4c diff --git a/packages/ptc-runtime/ptc-runtime-node/README.md b/packages/ptc-runtime/ptc-runtime-node/README.md index 244c2ddaf5..39bacad01c 100644 --- a/packages/ptc-runtime/ptc-runtime-node/README.md +++ b/packages/ptc-runtime/ptc-runtime-node/README.md @@ -63,7 +63,7 @@ Profile editors can import `resolvePersistentExecution` from `@deepseek-ai/dsh-p ### Execution and results -In `per-call` mode, programs are async function bodies: top-level `await` and `return` work, and only erasable TypeScript is accepted. A successful call returns its lossless-JSON value as `result.value` and captured text as `result.logs`. `result.sandbox` reports the selected mode, observed denial and the backend's full or partial enforcement independently of the program outcome. +In `per-call` mode, programs are async function bodies: top-level `await` and `return` work, and only erasable TypeScript is accepted. Node globals remain available, and a function body may declare a local `process`, including `const process = await import("node:process")`. A successful call returns its lossless-JSON value as `result.value` and captured text as `result.logs`. `result.sandbox` reports the selected mode, observed denial and the backend's full or partial enforcement independently of the program outcome. Direct filesystem, network and subprocess operations remain Node operations, subject to the selected OS sandbox. Nested host bindings cross the control channel; PTC tool calls retain the registry's visibility, ordering, logging and approval rules. Running a program does not change the Session's standing policy or automatically replay it after a denial. diff --git a/packages/ptc-runtime/ptc-runtime-node/README.zh.md b/packages/ptc-runtime/ptc-runtime-node/README.zh.md index 18512698a5..b94884b494 100644 --- a/packages/ptc-runtime/ptc-runtime-node/README.zh.md +++ b/packages/ptc-runtime/ptc-runtime-node/README.zh.md @@ -63,7 +63,7 @@ profile 编辑器可从 `@deepseek-ai/dsh-ptc-runtime-node/configuration` 导入 ### 执行与结果 -`per-call` 模式中的程序是异步函数体:支持顶层 `await` 与 `return`,且只接受可擦除 TypeScript。成功调用以 `result.value` 返回无损 JSON 值,以 `result.logs` 返回捕获文本。`result.sandbox` 独立于程序结果报告所选模式、观察到的拒绝,以及后端完整或部分的强制能力。 +`per-call` 模式中的程序是异步函数体:支持顶层 `await` 与 `return`,且只接受可擦除 TypeScript。Node 全局变量仍然可用,函数体可声明局部 `process`,例如 `const process = await import("node:process")`。成功调用以 `result.value` 返回无损 JSON 值,以 `result.logs` 返回捕获文本。`result.sandbox` 独立于程序结果报告所选模式、观察到的拒绝,以及后端完整或部分的强制能力。 直接文件系统、网络与子进程操作仍是 Node 操作,受所选 OS 沙箱约束。嵌套 Host 绑定通过控制通道调用;PTC 工具调用保留注册表的可见性、排序、日志和审批规则。运行程序不会改变 Session 的常设策略,也不会在拒绝后自动重放程序。 diff --git a/packages/ptc-runtime/ptc-runtime-node/src/process.ts b/packages/ptc-runtime/ptc-runtime-node/src/process.ts index 04536b4e08..5c8e71b749 100644 --- a/packages/ptc-runtime/ptc-runtime-node/src/process.ts +++ b/packages/ptc-runtime/ptc-runtime-node/src/process.ts @@ -1,7 +1,7 @@ /** Node child evaluation over typed RPC; the host owns process lifetime. */ -import type { PtcProgramFormat } from '@deepseek-ai/dsh-ptc-runtime' import { AsyncLocalStorage } from 'node:async_hooks' import type { Duplex } from 'node:stream' +import type { PtcProgramFormat } from '@deepseek-ai/dsh-ptc-runtime' import { createProcessRpc } from './rpc.ts' import { captureStreamWrites, LogBuffer, makeConsoleShim, makeBindingErrorClass, makeNamespaces, prepareCompletion, prepareException } from './bootstrap.ts' import type { BindingErrorConstructor, PatchableStream } from './bootstrap.ts' @@ -92,7 +92,8 @@ export async function runNodeMain( if (active?.active && error) active.finish(prepareException(error, active.logs.remainingOutputBytes(), active.maxOutputBytes)) }) evaluator.globals.console = makeConsoleShim(logSink) - Object.defineProperty(evaluator.globals, 'process', { configurable: true, enumerable: true, writable: true, value: processState }) + // Context access must not reserve a function-body parameter named process. + Object.defineProperty(evaluator.globals, 'process', { configurable: true, writable: true, value: processState }) } for (const namespace of data.namespaces) { const descriptor = namespace.errorClass diff --git a/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts b/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts index 8dabf78713..03688e1ae4 100644 --- a/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts +++ b/packages/ptc-runtime/ptc-runtime-node/tests/runtime.spec.ts @@ -39,6 +39,13 @@ function bindings(functions: Record): PtcBindingName } describe('Node program process', () => { + it('allows function-local process imports with the scrubbed program environment', async () => { + const { run } = await setup() + const result = await run({ program: 'const process = await import("node:process"); return { platform: process.platform, env: { ...process.env } }', bindings: [] }) + expect(result.error).toBeUndefined() + expect(result.value).toEqual({ platform: process.platform, env: {} }) + }) + it('runs erasable TypeScript in the resolved directory with an empty environment', async () => { const { run, cwd } = await setup() const result = await run({ program: 'const n: number = 6; console.log("ready"); return { n: n * 7, cwd: process.cwd(), env: { ...process.env } };', bindings: [] })