@deepseek-ai/dsh-tool-pwsh 0.2.0-rc.2 → 0.2.1-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +14 -14
- package/README.md +7 -5
- package/README.zh.md +7 -5
- package/lib/index.js +43 -26
- package/package.json +36 -33
package/README.i18n.yaml
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
en: d152484eb41ac6b4
|
|
16
16
|
zh: 09388d293f9be9cb
|
|
17
17
|
/deepseek-ai-dsh-tool-pwsh/use-this-package:
|
|
18
|
-
en:
|
|
19
|
-
zh:
|
|
18
|
+
en: 873b8b99a2018ed7
|
|
19
|
+
zh: c4481ffd20168071
|
|
20
20
|
/deepseek-ai-dsh-tool-pwsh/use-this-package/when-to-choose-it:
|
|
21
21
|
en: d274c33ae8a646f8
|
|
22
22
|
zh: 99e3e64e61735e50
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
en: addf35c4e607d33c
|
|
25
25
|
zh: 081f511d6b5a79d6
|
|
26
26
|
/deepseek-ai-dsh-tool-pwsh/use-this-package/running-a-command:
|
|
27
|
-
en:
|
|
28
|
-
zh:
|
|
27
|
+
en: 80195c9b61b29737
|
|
28
|
+
zh: 0a187f3f52814b90
|
|
29
29
|
/deepseek-ai-dsh-tool-pwsh/use-this-package/foreground-commands-as-jobs:
|
|
30
30
|
en: 4a487e48ef146846
|
|
31
31
|
zh: 0a34dc49bffb03af
|
|
@@ -36,20 +36,20 @@
|
|
|
36
36
|
en: c1a9cc1201ed3a89
|
|
37
37
|
zh: a43a659d350e9d4a
|
|
38
38
|
/deepseek-ai-dsh-tool-pwsh/understand-the-implementation:
|
|
39
|
-
en:
|
|
40
|
-
zh:
|
|
39
|
+
en: 165313776a851b51
|
|
40
|
+
zh: 828f48807036aff0
|
|
41
41
|
/deepseek-ai-dsh-tool-pwsh/understand-the-implementation/design-philosophy:
|
|
42
|
-
en:
|
|
43
|
-
zh:
|
|
42
|
+
en: 86a8ad5203f7e239
|
|
43
|
+
zh: 056c0d6d6ca5f36a
|
|
44
44
|
/deepseek-ai-dsh-tool-pwsh/understand-the-implementation/source-map:
|
|
45
|
-
en:
|
|
46
|
-
zh:
|
|
45
|
+
en: aa2adb29a0f27fcd
|
|
46
|
+
zh: 612829167064acd1
|
|
47
47
|
/deepseek-ai-dsh-tool-pwsh/understand-the-implementation/rendering-and-exit-markers:
|
|
48
48
|
en: facd01af9a403fec
|
|
49
49
|
zh: 7a943dac19a01c39
|
|
50
50
|
/deepseek-ai-dsh-tool-pwsh/further-exploration:
|
|
51
|
-
en:
|
|
52
|
-
zh:
|
|
51
|
+
en: 8b2f3ab83ada2fe5
|
|
52
|
+
zh: b7803b21d5373ece
|
|
53
53
|
/deepseek-ai-dsh-tool-pwsh/model-experience:
|
|
54
54
|
en: 215e7ba838619b7b
|
|
55
55
|
zh: a311da3843709f90
|
|
@@ -117,8 +117,8 @@
|
|
|
117
117
|
en: f5d5f97a153f7c70
|
|
118
118
|
zh: 31911b4f3506d6c8
|
|
119
119
|
/deepseek-ai-dsh-tool-pwsh/known-limitations-and-deferred-work:
|
|
120
|
-
en:
|
|
121
|
-
zh:
|
|
120
|
+
en: 04c2102bcc5ebc6d
|
|
121
|
+
zh: d13640d3fddb8e9c
|
|
122
122
|
/deepseek-ai-dsh-tool-pwsh/known-limitations-and-deferred-work/dev-note:
|
|
123
123
|
en: e67f633dc6fe7773
|
|
124
124
|
zh: 3bb0c20c93f16f98
|
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ English | [中文](README.zh.md)
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
+
New commands start in the Session's current working directory. Relative `workdir` values resolve from that directory; per-call overrides and background jobs do not change it.
|
|
29
|
+
|
|
28
30
|
Load this plugin in any composition where the agent should run PowerShell commands — typically a Windows composition whose `ctx.shell` is backed by a PowerShell executor. It registers the `pwsh` tool once the executor provider and the `dsh-shell-env` registry are mounted.
|
|
29
31
|
|
|
30
32
|
### When to choose it
|
|
@@ -52,7 +54,7 @@ The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-a
|
|
|
52
54
|
|
|
53
55
|
### Running a command
|
|
54
56
|
|
|
55
|
-
The tool executes `pwsh -Command <command>` and returns the combined output. Commands run in a fresh pwsh process every call, so state never persists — pass `workdir` instead of `cd`. Paths use native Windows form and environment variables are read with `$env:NAME`. A non-zero exit is reported as `[exit code: N]`; on Windows a force-killed command settles as `[exit code: 1]` without a signal marker, so the agent treats a bare exit 1 after an interruption as a termination, not a command failure. The description tells the agent to verify the resolved absolute target path before any delete or move and not to assign to automatic variables such as `$HOME`. Background runs, output truncation, and the `description`/`timeoutMs`/`workdir` arguments behave exactly as in [`dsh-tool-bash`](../tool-bash/README.md#running-long-commands-in-the-background), including job-owned cancellation during asynchronous shell preparation.
|
|
57
|
+
The tool executes `pwsh -Command <command>` and returns the combined output. Commands run in a fresh pwsh process every call, so state never persists — pass `workdir` instead of `cd`. Paths use native Windows form and environment variables are read with `$env:NAME`. A non-zero exit is reported as `[exit code: N]`; on Windows a force-killed command settles as `[exit code: 1]` without a signal marker, so the agent treats a bare exit 1 after an interruption as a termination, not a command failure. The schema lists `description` before `command` and asks the model to emit it first, without enforcing JSON member order. The description tells the agent to verify the resolved absolute target path before any delete or move and not to assign to automatic variables such as `$HOME`. Background runs, output truncation, and the `description`/`timeoutMs`/`workdir` arguments behave exactly as in [`dsh-tool-bash`](../tool-bash/README.md#running-long-commands-in-the-background), including job-owned cancellation during asynchronous shell preparation.
|
|
56
58
|
|
|
57
59
|
### Foreground commands as jobs
|
|
58
60
|
|
|
@@ -74,11 +76,13 @@ A composition with no PowerShell executor never activates the tool, and the inje
|
|
|
74
76
|
<details>
|
|
75
77
|
<summary>Implementation internals — click to expand</summary>
|
|
76
78
|
|
|
79
|
+
Structured foreground and background results expose `cwd`, the executor-resolved launch directory. Persisted presentation metadata carries the same value so settled cards retain the directory used by that call. A command’s own `cd` does not change this launch value; native result text remains stdout, stderr, and status markers.
|
|
80
|
+
|
|
77
81
|
This section explains the design decisions behind the tool and points at the code that realizes them; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
78
82
|
|
|
79
83
|
### Design philosophy
|
|
80
84
|
|
|
81
|
-
- **A deliberate twin of `dsh-tool-bash`.** Foreground and background execution, the managed environment, the sandbox escalation surface, and the marker/truncation rendering mirror the bash tool call-for-call, so consumers of one accept the other's wire shape ([pwsh tool bash parity
|
|
85
|
+
- **A deliberate twin of `dsh-tool-bash`.** Foreground and background execution, the managed environment, the sandbox escalation surface, and the marker/truncation rendering mirror the bash tool call-for-call, so consumers of one accept the other's wire shape ([pwsh tool bash parity reference](README.md)).
|
|
82
86
|
- **PowerShell-dialect contract.** The tool contract is PowerShell: native paths and `$env:` variables, executed via `pwsh -Command` with no intermediate shell.
|
|
83
87
|
- **Windows sandbox facts taught in the description.** The ConstrainedLanguage and named-pipe contracts are Windows-restricted-token behavior; the gate for teaching them is "any confining executor is mounted", which is safe because every shipped pairing is win32-only.
|
|
84
88
|
- **Non-zero exits are reported, not errored.** Only infrastructure failures (spawn errors, aborts) surface as tool errors, matching the bash story.
|
|
@@ -90,7 +94,6 @@ This section explains the design decisions behind the tool and points at the cod
|
|
|
90
94
|
| [`src/index.ts`](src/index.ts) | Plugin entry: tool registration, prompt section, arg validation, escalation, request assembly |
|
|
91
95
|
| [`src/background.ts`](src/background.ts) | Map a settled process onto generic job outcome vocabulary and render a ring read as a process read |
|
|
92
96
|
| [`src/render.ts`](src/render.ts) | Model-facing result text: streams, markers, truncation notices (bash twin) |
|
|
93
|
-
| — | No runtime invariant companion is published; this package exposes no independent event sequence or mutable data relation beyond contracts enforced at its owning seam. |
|
|
94
97
|
|
|
95
98
|
### Rendering and exit markers
|
|
96
99
|
|
|
@@ -109,7 +112,7 @@ Read these pages when the package-level contract is not enough. They move from t
|
|
|
109
112
|
- [Bash executor subsystem](../../../docs/subsystems/shell.md) — request/spec vocabulary, results, and background processes.
|
|
110
113
|
- [shell-env](../shell-env/README.md) — the managed `DSH_*` environment every call receives.
|
|
111
114
|
- [tool-jobs](../../jobs/tool-jobs/README.md) — `job_output`, `job_list`, and `job_kill` controls for background runs.
|
|
112
|
-
- [pwsh tool bash parity Agent Note](../../../.agents/notes/
|
|
115
|
+
- [historical pwsh tool bash parity Agent Note](../../../.agents/notes/archived/feature/2026-08-02-pwsh-tool-bash-parity.md) — why the tool mirrors the bash tool.
|
|
113
116
|
- [Windows ACL restricted-token sandbox Agent Note](../../../.agents/notes/implemented/feature/2026-08-08-windows-acl-restricted-token-sandbox.md) — the language-mode and named-pipe contracts.
|
|
114
117
|
- [Generated tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-tool-pwsh) — the exact `pwsh` argument schema.
|
|
115
118
|
- [Generated configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-tool-pwsh) — every accepted config field and its source declaration.
|
|
@@ -205,7 +208,6 @@ These limits define when the tool is a poor fit or needs special care. They are
|
|
|
205
208
|
- **Language mode and named-pipe capture under the Windows sandbox** — under the [Windows ACL sandbox](../../sandbox/sandbox-windows-acl/README.md), read-only pwsh starts in ConstrainedLanguage because its temp write denial makes PowerShell's AppLocker probe fail closed: `Add-Type`, non-core .NET statics (`[System.IO.*]::`, `[math]::`), COM objects, and reflection fail with "only core types" errors, and the mode cannot be lifted from inside. Workspace-write's private temp lets the probe complete, so it stays in FullLanguage unless host policy says otherwise. Both confined modes deny named-pipe opens, so a piped-stdio spawn inside a confined command fails with EPERM. The tool description teaches both contracts to the model; the backend README owns the full limitations.
|
|
206
209
|
- **No persistent shell** — every call starts a fresh `pwsh -Command`; the persistent-shell counterpart is [`@deepseek-ai/dsh-tool-pwsh-persistent`](../tool-pwsh-persistent/README.md), which keeps one owner-scoped pwsh alive across calls.
|
|
207
210
|
- **PowerShell-dialect contract** — the model must write PowerShell (native paths, `$env:` variables), not bash; there is no dialect translation.
|
|
208
|
-
- **Session-cwd identity is not canonicalized** — the workdir base is the session header cwd as-is, unlike the bash tool's sandbox-root-canonicalized identity. Under a confining executor the policy's workspace root IS canonicalized (by the shared policy service), so the workdir and the confinement root can diverge when the raw session cwd differs from its canonical form — a parity gap deferred to the shared shell-tool base extraction.
|
|
209
211
|
|
|
210
212
|
<a id="dev-note"></a>
|
|
211
213
|
### Dev Note
|
package/README.zh.md
CHANGED
|
@@ -25,6 +25,8 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
+
新命令从 Session 的当前工作目录启动。相对 `workdir` 从该目录解析;单次调用的覆盖值与后台任务不会改变该目录。
|
|
29
|
+
|
|
28
30
|
在 agent 需要运行 PowerShell 命令的任何组合中加载本插件——通常是 `ctx.shell` 由 PowerShell 执行器支撑的 Windows 组合。一旦挂载执行器提供方与 `dsh-shell-env` 注册表,它就注册 `pwsh` 工具。
|
|
29
31
|
|
|
30
32
|
### 何时选择
|
|
@@ -52,7 +54,7 @@ kind: "package-reference"
|
|
|
52
54
|
|
|
53
55
|
### 运行命令
|
|
54
56
|
|
|
55
|
-
工具执行 `pwsh -Command <command>` 并返回合并后的输出。命令每次调用都运行在全新 pwsh 进程中,因此状态从不保留——请传 `workdir` 而不是 `cd`。路径使用原生 Windows 形式,环境变量用 `$env:NAME` 读取。非零退出以 `[exit code: N]` 报告;在 Windows 上,强制终止的命令以 `[exit code: 1]` 结算且没有信号标记,因此 agent 把中断后的裸 exit 1
|
|
57
|
+
工具执行 `pwsh -Command <command>` 并返回合并后的输出。命令每次调用都运行在全新 pwsh 进程中,因此状态从不保留——请传 `workdir` 而不是 `cd`。路径使用原生 Windows 形式,环境变量用 `$env:NAME` 读取。非零退出以 `[exit code: N]` 报告;在 Windows 上,强制终止的命令以 `[exit code: 1]` 结算且没有信号标记,因此 agent 把中断后的裸 exit 1 当作终止而非命令失败。schema 将 `description` 列在 `command` 前并要求模型先输出它,但不强制 JSON 成员顺序。工具描述要求 agent 在任何删除或移动前校验解析后的绝对目标路径,并且不要给 `$HOME` 等自动变量赋值。后台运行、输出截断以及 `description`/`timeoutMs`/`workdir` 参数的行为与 [`dsh-tool-bash`](../tool-bash/README.zh.md#running-long-commands-in-the-background) 完全一致,包括异步 shell 准备过程中由任务负责的取消。
|
|
56
58
|
|
|
57
59
|
### 前台命令即任务
|
|
58
60
|
|
|
@@ -74,11 +76,13 @@ kind: "package-reference"
|
|
|
74
76
|
<details>
|
|
75
77
|
<summary>实现细节——点击展开</summary>
|
|
76
78
|
|
|
79
|
+
前台与后台的结构化结果均包含 `cwd`,即执行器解析后的启动目录。持久化展示元数据携带同一值,使已结束的调用卡片保留该次调用使用的目录。命令内部的 `cd` 不会改变这个启动值;原生结果文本仍由 stdout、stderr 与状态标记组成。
|
|
80
|
+
|
|
77
81
|
本节解释工具背后的设计决策,并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
78
82
|
|
|
79
83
|
### 设计理念
|
|
80
84
|
|
|
81
|
-
- **`dsh-tool-bash` 的刻意孪生。** 前台与后台执行、受管环境、沙箱升权面以及标记/截断渲染都逐调用镜像 bash 工具,因此其中之一的消费方也能接受另一个的协议形状([pwsh 工具与 bash 对齐
|
|
85
|
+
- **`dsh-tool-bash` 的刻意孪生。** 前台与后台执行、受管环境、沙箱升权面以及标记/截断渲染都逐调用镜像 bash 工具,因此其中之一的消费方也能接受另一个的协议形状([pwsh 工具与 bash 对齐 参考](README.zh.md))。
|
|
82
86
|
- **PowerShell 方言约定。** 工具约定是 PowerShell:原生路径与 `$env:` 变量,经由 `pwsh -Command` 执行,没有中间 shell。
|
|
83
87
|
- **Windows 沙箱事实写进描述。** ConstrainedLanguage 与命名管道约定是 Windows 受限令牌行为;教授它们的条件是「已挂载任意约束执行器」,之所以安全,是因为每个已发布的配对都是 win32-only。
|
|
84
88
|
- **非零退出只报告、不失败。** 只有基础设施故障(spawn 错误、中止)才会作为工具错误暴露,与 bash 的故事一致。
|
|
@@ -90,7 +94,6 @@ kind: "package-reference"
|
|
|
90
94
|
| [`src/index.ts`](src/index.ts) | 插件入口:工具注册、提示词区段、参数校验、升权、请求组装 |
|
|
91
95
|
| [`src/background.ts`](src/background.ts) | 把已结算的进程映射为通用任务结果词汇,并把输出环读取渲染为进程读取 |
|
|
92
96
|
| [`src/render.ts`](src/render.ts) | 模型侧结果文本:流、标记、截断通知(bash 孪生) |
|
|
93
|
-
| — | 不发布运行时不变式伴生入口;除所属 seam 强制执行的约定外,本包不公开独立的事件序列或可变数据关系。 |
|
|
94
97
|
|
|
95
98
|
### 渲染与退出标记
|
|
96
99
|
|
|
@@ -109,7 +112,7 @@ kind: "package-reference"
|
|
|
109
112
|
- [Bash 执行器子系统](../../../docs/subsystems/shell.zh.md)——请求/spec 词汇、结果与后台进程。
|
|
110
113
|
- [shell-env](../shell-env/README.zh.md)——每次调用都会收到的受管 `DSH_*` 环境。
|
|
111
114
|
- [tool-jobs](../../jobs/tool-jobs/README.zh.md)——后台运行的 `job_output`、`job_list` 与 `job_kill` 控制。
|
|
112
|
-
- [pwsh 工具与 bash 对齐 Agent Note](../../../.agents/notes/
|
|
115
|
+
- [历史pwsh 工具与 bash 对齐 Agent Note](../../../.agents/notes/archived/feature/2026-08-02-pwsh-tool-bash-parity.md)——为什么工具镜像 bash 工具。
|
|
113
116
|
- [Windows ACL 受限令牌沙箱 Agent Note](../../../.agents/notes/implemented/feature/2026-08-08-windows-acl-restricted-token-sandbox.zh.md)——语言模式与命名管道约定。
|
|
114
117
|
- [生成的工具目录](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-pwsh)——`pwsh` 参数 schema 的确切内容。
|
|
115
118
|
- [生成的配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-tool-pwsh)——每个受支持配置字段及其源声明。
|
|
@@ -205,7 +208,6 @@ Non-zero exits are reported as `[exit code: N]` markers; investigate failures be
|
|
|
205
208
|
- **Windows 沙箱下的语言模式与命名管道捕获**——在 [Windows ACL 沙箱](../../sandbox/sandbox-windows-acl/README.zh.md)下,只读 pwsh 以 ConstrainedLanguage 启动,因为其临时目录写入被拒绝,导致 PowerShell 的 AppLocker 探测失败并按拒绝处理:`Add-Type`、非核心 .NET 静态调用(`[System.IO.*]::`、`[math]::`)、COM 对象与反射会以 "only core types" 错误失败,且该模式无法从内部解除。workspace-write 的私有临时目录让探测完成,因此除非宿主策略另有规定,它保持 FullLanguage。两种受限模式都拒绝命名管道打开,因此受限命令内部的管道 stdio spawn 会以 EPERM 失败。工具描述把两条约定都教给模型;完整限制以后端 README 为准。
|
|
206
209
|
- **没有持久 shell**——每次调用都启动全新的 `pwsh -Command`;持久 shell 对应物是 [`@deepseek-ai/dsh-tool-pwsh-persistent`](../tool-pwsh-persistent/README.zh.md),它跨调用保持一个按所有者隔离的 pwsh 存活。
|
|
207
210
|
- **PowerShell 方言约定**——模型必须编写 PowerShell(原生路径、`$env:` 变量),而不是 bash;没有方言翻译。
|
|
208
|
-
- **会话 cwd 身份未规范化**——workdir 基准就是会话头部 cwd 原样,不像 bash 工具那样以沙箱根规范化身份为准。在约束执行器下,策略的 workspace root 确实被规范化(由共享策略服务完成),因此当原始会话 cwd 与其规范形式不同时,workdir 与约束根可能分叉——这是推迟到共享 shell 工具基座抽取的对齐差距。
|
|
209
211
|
|
|
210
212
|
<a id="dev-note"></a>
|
|
211
213
|
### 开发备注
|
package/lib/index.js
CHANGED
|
@@ -238,7 +238,8 @@ const inject = [
|
|
|
238
238
|
"tools",
|
|
239
239
|
"shell",
|
|
240
240
|
"systemPrompt",
|
|
241
|
-
"shellEnv"
|
|
241
|
+
"shellEnv",
|
|
242
|
+
"workingDirectory"
|
|
242
243
|
];
|
|
243
244
|
/** Runtime configuration schema for the pwsh tool plugin. */
|
|
244
245
|
const Config = z.object({
|
|
@@ -252,28 +253,25 @@ function validatePwshArgs(args) {
|
|
|
252
253
|
validateEscalationArgs(args.sandbox_permissions, args.justification);
|
|
253
254
|
}
|
|
254
255
|
function pwshDescription(windowsSandbox) {
|
|
255
|
-
const base = "Execute a PowerShell command (`pwsh -Command`) and return its stdout/stderr. Each call runs in a fresh pwsh process; pass `workdir` instead of using `cd`. Paths use native Windows form (`C:\\...`); read environment variables with `$env:NAME`. Managed `$env:DSH_*` variables expose current harness environment facts. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. On Windows a force-killed command settles as `[exit code: 1]` without a signal marker — treat it as an interruption, not a command failure. Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. Do not assign to automatic variables such as `$HOME`; variable names are case-insensitive, so `$home` is the same read-only variable. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]`, a policy denial: do not retry another way.";
|
|
256
|
+
const base = "Execute a PowerShell command (`pwsh -Command`) and return its stdout/stderr. Each call runs in a fresh pwsh process; pass `workdir` instead of using `cd`. Paths use native Windows form (`C:\\...`); read environment variables with `$env:NAME`. Managed `$env:DSH_*` variables expose current harness environment facts. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. On Windows a force-killed command settles as `[exit code: 1]` without a signal marker — treat it as an interruption, not a command failure. Provide `description` before `command` in the arguments. Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. Do not assign to automatic variables such as `$HOME`; variable names are case-insensitive, so `$home` is the same read-only variable. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]`, a policy denial: do not retry another way.";
|
|
256
257
|
if (!windowsSandbox) return base;
|
|
257
|
-
return "Execute a PowerShell command (`pwsh -Command`) and return its stdout/stderr. Each call runs in a fresh pwsh process; pass `workdir` instead of using `cd`. Paths use native Windows form (`C:\\...`); read environment variables with `$env:NAME`. Managed `$env:DSH_*` variables expose current harness environment facts. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. On Windows a force-killed command settles as `[exit code: 1]` without a signal marker — treat it as an interruption, not a command failure. Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. Do not assign to automatic variables such as `$HOME`; variable names are case-insensitive, so `$home` is the same read-only variable. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]`, a policy denial: do not retry another way. Under the Windows sandbox, read-only pwsh runs in PowerShell ConstrainedLanguage mode, while workspace-write stays in FullLanguage unless host policy says otherwise. In read-only, prefer cmdlets and core types (`[string]`, `[datetime]`, `[regex]`, `[guid]`); .NET static calls (`[System.IO.*]::`, `[math]::`), `Add-Type`, COM objects, and reflection fail with \"only core types\" errors. `-f` formatting, property access, and core cmdlets work. In both confined modes, programs cannot open named pipes, so a command that captures another program's output through piped stdio (Node.js `child_process.spawn`/`exec` with the default `stdio: 'pipe'`) fails with EPERM, while `stdio: 'inherit'` and `stdio: 'ignore'` spawns work and PowerShell's own pipelines are unaffected. That EPERM is the documented boundary: do not retry the command another way — escalate the exact command once or restructure it to avoid capturing output.";
|
|
258
|
+
return "Execute a PowerShell command (`pwsh -Command`) and return its stdout/stderr. Each call runs in a fresh pwsh process; pass `workdir` instead of using `cd`. Paths use native Windows form (`C:\\...`); read environment variables with `$env:NAME`. Managed `$env:DSH_*` variables expose current harness environment facts. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. On Windows a force-killed command settles as `[exit code: 1]` without a signal marker — treat it as an interruption, not a command failure. Provide `description` before `command` in the arguments. Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. Do not assign to automatic variables such as `$HOME`; variable names are case-insensitive, so `$home` is the same read-only variable. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]`, a policy denial: do not retry another way. Under the Windows sandbox, read-only pwsh runs in PowerShell ConstrainedLanguage mode, while workspace-write stays in FullLanguage unless host policy says otherwise. In read-only, prefer cmdlets and core types (`[string]`, `[datetime]`, `[regex]`, `[guid]`); .NET static calls (`[System.IO.*]::`, `[math]::`), `Add-Type`, COM objects, and reflection fail with \"only core types\" errors. `-f` formatting, property access, and core cmdlets work. In both confined modes, programs cannot open named pipes, so a command that captures another program's output through piped stdio (Node.js `child_process.spawn`/`exec` with the default `stdio: 'pipe'`) fails with EPERM, while `stdio: 'inherit'` and `stdio: 'ignore'` spawns work and PowerShell's own pipelines are unaffected. That EPERM is the documented boundary: do not retry the command another way — escalate the exact command once or restructure it to avoid capturing output.";
|
|
258
259
|
}
|
|
259
|
-
/**
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
function resolveWorkdir(modelWorkdir, exec) {
|
|
264
|
-
const headerCwd = exec.agent?.session.header.cwd;
|
|
265
|
-
if (modelWorkdir === void 0) return headerCwd;
|
|
266
|
-
if (headerCwd !== void 0 && !isAbsolute(modelWorkdir)) return resolve(headerCwd, modelWorkdir);
|
|
260
|
+
/** Resolve a relative per-call override against the Session's current directory. */
|
|
261
|
+
function resolveWorkdir(modelWorkdir, sessionCwd) {
|
|
262
|
+
if (modelWorkdir === void 0) return sessionCwd;
|
|
263
|
+
if (sessionCwd !== void 0 && !isAbsolute(modelWorkdir)) return resolve(sessionCwd, modelWorkdir);
|
|
267
264
|
return modelWorkdir;
|
|
268
265
|
}
|
|
269
266
|
/** Detach the executor DTO from readonly Service Definition types into plain JSON data. */
|
|
270
|
-
function canonicalPwshResult(result) {
|
|
267
|
+
function canonicalPwshResult(result, cwd) {
|
|
271
268
|
const output = (stream) => ({
|
|
272
269
|
text: stream.text,
|
|
273
270
|
truncated: stream.truncated,
|
|
274
271
|
...stream.spillPath !== void 0 ? { spillPath: stream.spillPath } : {}
|
|
275
272
|
});
|
|
276
273
|
return {
|
|
274
|
+
cwd,
|
|
277
275
|
kind: "foreground",
|
|
278
276
|
exitCode: result.exitCode,
|
|
279
277
|
signal: result.signal,
|
|
@@ -306,6 +304,10 @@ const BACKGROUND_OUTPUT_PROPERTIES = {
|
|
|
306
304
|
jobId: {
|
|
307
305
|
type: "string",
|
|
308
306
|
required: true
|
|
307
|
+
},
|
|
308
|
+
cwd: {
|
|
309
|
+
type: "string",
|
|
310
|
+
required: true
|
|
309
311
|
}
|
|
310
312
|
};
|
|
311
313
|
function apply(ctx, config = {}) {
|
|
@@ -416,6 +418,7 @@ function apply(ctx, config = {}) {
|
|
|
416
418
|
await stop("timed out during preparation");
|
|
417
419
|
return {
|
|
418
420
|
kind: "foreground",
|
|
421
|
+
cwd: spec.workdir,
|
|
419
422
|
exitCode: null,
|
|
420
423
|
signal: null,
|
|
421
424
|
timedOut: true,
|
|
@@ -439,6 +442,7 @@ function apply(ctx, config = {}) {
|
|
|
439
442
|
const read = registry.read(attached.id, owner);
|
|
440
443
|
return {
|
|
441
444
|
kind: "promoted",
|
|
445
|
+
cwd: spec.workdir,
|
|
442
446
|
jobId: attached.id,
|
|
443
447
|
timeoutMs,
|
|
444
448
|
output: renderPwshJobRead(ringDelta(read.chunks), read.lossy, read.job.output.spillPaths ?? [], attached.process()?.sandbox, escalationModes)
|
|
@@ -450,7 +454,7 @@ function apply(ctx, config = {}) {
|
|
|
450
454
|
const result = await process.result();
|
|
451
455
|
const stopped = attached.stopped();
|
|
452
456
|
return {
|
|
453
|
-
...canonicalPwshResult(result),
|
|
457
|
+
...canonicalPwshResult(result, spec.workdir),
|
|
454
458
|
...stopped !== void 0 ? { stopped } : {}
|
|
455
459
|
};
|
|
456
460
|
};
|
|
@@ -458,15 +462,15 @@ function apply(ctx, config = {}) {
|
|
|
458
462
|
name: "pwsh",
|
|
459
463
|
description: pwshDescription(escalationModes.length > 0),
|
|
460
464
|
parameters: {
|
|
461
|
-
|
|
465
|
+
description: {
|
|
462
466
|
type: "string",
|
|
463
467
|
required: true,
|
|
464
|
-
description: "
|
|
468
|
+
description: "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"Get-Process\" → \"List running processes\"."
|
|
465
469
|
},
|
|
466
|
-
|
|
470
|
+
command: {
|
|
467
471
|
type: "string",
|
|
468
472
|
required: true,
|
|
469
|
-
description: "
|
|
473
|
+
description: "The PowerShell command to execute."
|
|
470
474
|
},
|
|
471
475
|
timeoutMs: {
|
|
472
476
|
type: "number",
|
|
@@ -508,6 +512,10 @@ function apply(ctx, config = {}) {
|
|
|
508
512
|
required: true,
|
|
509
513
|
const: "promoted"
|
|
510
514
|
},
|
|
515
|
+
cwd: {
|
|
516
|
+
type: "string",
|
|
517
|
+
required: true
|
|
518
|
+
},
|
|
511
519
|
jobId: {
|
|
512
520
|
type: "string",
|
|
513
521
|
required: true
|
|
@@ -531,6 +539,10 @@ function apply(ctx, config = {}) {
|
|
|
531
539
|
required: true,
|
|
532
540
|
const: "foreground"
|
|
533
541
|
},
|
|
542
|
+
cwd: {
|
|
543
|
+
type: "string",
|
|
544
|
+
required: true
|
|
545
|
+
},
|
|
534
546
|
exitCode: {
|
|
535
547
|
required: true,
|
|
536
548
|
oneOf: [{ type: "integer" }, { type: "null" }]
|
|
@@ -606,7 +618,8 @@ function apply(ctx, config = {}) {
|
|
|
606
618
|
render: (_args, value) => [{
|
|
607
619
|
type: "text",
|
|
608
620
|
text: value.kind === "background" ? `started background job ${value.jobId}` : value.kind === "promoted" ? renderPwshPromoted(value) : renderPwshResult(value, escalationModes)
|
|
609
|
-
}]
|
|
621
|
+
}],
|
|
622
|
+
presentationMeta: (_args, value) => ({ cwd: value.cwd })
|
|
610
623
|
},
|
|
611
624
|
async execute(args, exec) {
|
|
612
625
|
validatePwshArgs(args);
|
|
@@ -616,7 +629,8 @@ function apply(ctx, config = {}) {
|
|
|
616
629
|
...standingPolicy,
|
|
617
630
|
mode: approvedMode
|
|
618
631
|
};
|
|
619
|
-
const
|
|
632
|
+
const cwd = exec.agent === void 0 ? void 0 : await ctx.workingDirectory.ensure(exec.agent, exec.signal);
|
|
633
|
+
const workdir = resolveWorkdir(args.workdir, cwd);
|
|
620
634
|
const request = {
|
|
621
635
|
command: args.command,
|
|
622
636
|
...workdir !== void 0 ? { workdir } : {},
|
|
@@ -628,12 +642,14 @@ function apply(ctx, config = {}) {
|
|
|
628
642
|
if (!backgroundEnabled) throw new Error("run_in_background is disabled for this deployment (enableRunInBackground: false)");
|
|
629
643
|
if (jobs === void 0) throw new Error("background jobs unavailable: load @deepseek-ai/dsh-jobs and @deepseek-ai/dsh-tool-jobs");
|
|
630
644
|
if (exec.signal.aborted) throw toolAborted();
|
|
645
|
+
const spec = ctx.shell.resolve({
|
|
646
|
+
...request,
|
|
647
|
+
onExpiry: "none"
|
|
648
|
+
});
|
|
631
649
|
return {
|
|
632
650
|
kind: "background",
|
|
633
|
-
jobId: startJob(jobs, args, exec,
|
|
634
|
-
|
|
635
|
-
onExpiry: "none"
|
|
636
|
-
})).id
|
|
651
|
+
jobId: startJob(jobs, args, exec, spec).id,
|
|
652
|
+
cwd: spec.workdir
|
|
637
653
|
};
|
|
638
654
|
}
|
|
639
655
|
if (jobs !== void 0 && promote) {
|
|
@@ -649,12 +665,13 @@ function apply(ctx, config = {}) {
|
|
|
649
665
|
}
|
|
650
666
|
if (attached !== void 0) return waitOnJob(jobs, attached, exec, spec);
|
|
651
667
|
}
|
|
652
|
-
const
|
|
668
|
+
const spec = ctx.shell.resolve({
|
|
653
669
|
...request,
|
|
654
670
|
signal: exec.signal
|
|
655
|
-
})
|
|
671
|
+
});
|
|
672
|
+
const result = await (await ctx.shell.execute(spec)).result();
|
|
656
673
|
if (result.aborted) throw toolAborted();
|
|
657
|
-
return canonicalPwshResult(result);
|
|
674
|
+
return canonicalPwshResult(result, spec.workdir);
|
|
658
675
|
},
|
|
659
676
|
presentCall: (args) => {
|
|
660
677
|
if (args.run_in_background === true) return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-tool-pwsh",
|
|
3
3
|
"description": "Model-facing pwsh tool over the bash executor seam",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.1-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,41 +27,44 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@deepseek-ai/
|
|
31
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
32
|
-
"@deepseek-ai/dsh-
|
|
33
|
-
"@deepseek-ai/dsh-
|
|
34
|
-
"@deepseek-ai/dsh-
|
|
35
|
-
"@deepseek-ai/dsh-
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
37
|
-
"@deepseek-ai/dsh-
|
|
38
|
-
"@deepseek-ai/dsh-
|
|
39
|
-
"@deepseek-ai/dsh-
|
|
40
|
-
"@deepseek-ai/
|
|
30
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
31
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
|
|
32
|
+
"@deepseek-ai/dsh-jobs": "0.2.1-alpha.2",
|
|
33
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
34
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.2",
|
|
35
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-shell": "0.2.1-alpha.2",
|
|
37
|
+
"@deepseek-ai/dsh-shell-env": "0.2.1-alpha.2",
|
|
38
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.2",
|
|
39
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.2",
|
|
40
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-alpha.2",
|
|
41
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-alpha.2"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"@deepseek-ai/schemastery": "~3.18.
|
|
44
|
+
"@deepseek-ai/schemastery": "~3.18.5-alpha.1"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
|
-
"@deepseek-ai/cordis": "~4.0.
|
|
47
|
-
"@deepseek-ai/dsh-agent": "0.2.
|
|
48
|
-
"@deepseek-ai/dsh-agent-loop": "0.2.
|
|
49
|
-
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.
|
|
50
|
-
"@deepseek-ai/dsh-app-boot": "0.2.
|
|
51
|
-
"@deepseek-ai/dsh-jobs": "0.2.
|
|
52
|
-
"@deepseek-ai/dsh-jobs-local": "0.2.
|
|
53
|
-
"@deepseek-ai/dsh-llm": "0.2.
|
|
54
|
-
"@deepseek-ai/dsh-
|
|
55
|
-
"@deepseek-ai/dsh-
|
|
56
|
-
"@deepseek-ai/dsh-sandbox
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
58
|
-
"@deepseek-ai/dsh-
|
|
59
|
-
"@deepseek-ai/dsh-shell
|
|
60
|
-
"@deepseek-ai/dsh-
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
63
|
-
"@deepseek-ai/dsh-tool-jobs": "0.2.
|
|
64
|
-
"@deepseek-ai/dsh-
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
47
|
+
"@deepseek-ai/cordis": "~4.0.5-alpha.1",
|
|
48
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-agent-loop": "0.2.1-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "0.2.1-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-app-boot": "0.2.1-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-jobs": "0.2.1-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-jobs-local": "0.2.1-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-loader-smoke": "0.2.1-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-pwsh-local": "0.2.1-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-alpha.2",
|
|
58
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-alpha.2",
|
|
59
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-shell": "0.2.1-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-shell-env": "0.2.1-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-subprocess-local": "0.2.1-alpha.2",
|
|
63
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-tool-jobs": "0.2.1-alpha.2",
|
|
65
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.2",
|
|
66
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-alpha.2",
|
|
67
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-alpha.2",
|
|
68
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-alpha.2"
|
|
66
69
|
}
|
|
67
70
|
}
|