fix(ptc): allow local process declarations in function bodies

This commit is contained in:
_Kerman committed 2026-10-21 19:51:50 +08:00
1 parent 25ede01a8b
commit c905726461
5 files changed
+14 -6

No files matched your search

@@ -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
@@ -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.
@@ -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 的常设策略,也不会在拒绝后自动重放程序。
@@ -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
@@ -39,6 +39,13 @@ function bindings(functions: Record<string, PtcBindingFunction>): 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: [] })