Files
deepseek-harness/packages/shell/tool-pwsh-persistent
Turtle 38c7a9fbf4 feat(release): publish package source, declaration maps, and documentation (#5884)
* feat(release): publish package source, declaration maps, and documentation

Every release member publishes its TypeScript source and the declaration
maps that resolve into it, and @deepseek-ai/dsh publishes the repository
Markdown documentation staged from docs/ at prepack time.

The ./src/* export most members already declared now resolves for an
installed consumer, and a consumer editor follows a declaration to the
implementation. Bundled JavaScript maps stay unpublished; the vendored
family and the Desktop runtime file policy are unchanged.

Rationale: .agents/notes/implemented/process/2026-10-08-published-source-and-docs.md

* feat(release): publish JavaScript maps and compress bundles

Every emitted JavaScript map ships beside the bundle it describes, and the
workspace build and the client preset compress bundles without mangling
identifiers.

The documentation payload now mirrors the website publication manifest and
is removed again by `postpack`; Desktop and the Python executable exclude
the published source, documentation, and maps; `clean` removes a staged
copy. Typert's publication contract gains the Remote declaration map.

* fix(release): keep node bundles readable for the worker image

The browser worker image lowers `yield*` only as a whole statement and
rewrites `for await` loops by their source layout, so compressed node-side
entries either fail that lowering or lower to unparseable code. Only the
browser bundles keep compression, which removes about a third of their text.

The bundle-purity assertion now matches the module-loader call regardless of
the quote style compression picks, and the release-pack fixture exercises
the installed package's new documentation requirement.

* feat(release): publish the whole documentation tree

`@deepseek-ai/dsh` copies `docs/` in full instead of the website's page
set: the maintainer documents, the generated migration records, the
translation guidelines, and the `*.i18n.yaml` consistency records ship
with the installed package, so the documentation's own links resolve
without a checkout.

* feat(release): publish the upgrade guides with the documentation

The staged documentation keeps the website's page set and adds every
`docs/upgrade-guide/` document, which is what an installed reader consults
when a released change breaks something. The rest of the maintainer tree
stays out.

* test(release): expect source and maps in master's new payload cases

* feat(release): keep installed docs and source readable in Desktop

Desktop keeps the published src/ and docs/ trees inside app.asar, and
every model shell call receives DSH_INSTALL_DIR and DSH_NODE_EXECUTABLE
so an agent can read them through Electron's Node mode. Staging also
generates docs/upgrade-guide/README.md, an index of the upgrade guides
grouped by the release they upgrade from.

Minified client bundles print string literals with backticks: the
worker image packer now recognizes those chunk requests, client license
banners are prepended after minification so their text survives, and
the affected artifact tests accept either quote.

* test(desktop): read archived docs through the Windows PowerShell form

Regenerate the config catalog for the shifted shell-env source line.

* test(desktop): wait for the GUI-subsystem Electron process from PowerShell

* build(release): publish source and maps from the message-quotes package

* build(release): publish source and maps from packages new on master

* build(release): publish source and maps from the tool-office package
2026-11-05 00:22:36 +08:00
..

description, kind
description kind
The model-facing persistent pwsh tool for users and maintainers choosing, configuring, or debugging owner-scoped PowerShell state that survives across calls. package-reference

@deepseek-ai/dsh-tool-pwsh-persistent

English | 中文

Summary

dsh-tool-pwsh-persistent gives each agent a pwsh tool that preserves its current directory, environment variables, functions, and background jobs across calls. Commands for one agent run sequentially, while different agents keep separate shell state. Choose it for multi-step PowerShell work; use dsh-tool-pwsh when every command should start clean, and use a terminal tool when commands require interactive stdin. Configure a pwsh-capable backend and per-command timeout; timeout or explicit exit discards the shell, so the next call starts fresh.

Table of Contents


Use this package

A new shell starts in the Session's current working directory. Once created, its directory and environment remain independent of later Session directory changes.

Load this plugin in any composition where the agent should keep PowerShell state between commands — the persistent counterpart of dsh-tool-pwsh for work that needs cross-call state. It registers the pwsh tool and requires the ctx.tools and ctx.terminals services plus an owning agent session at execution time.

When to choose it

Choose the persistent tool when work depends on cross-call PowerShell state, and choose dsh-tool-pwsh when every command should start from a known, clean environment. Commands that need interactive stdin are unsupported here — a foreground child that reads input blocks until the command timeout, which resets the shell — so interactive work belongs to the terminal tools.

Minimal configuration

The default shell backend starts a PowerShell shell through a dsh-terminal-bash instance configured with shellDialect: pwsh; deployments may register another pwsh-dialect PTY backend and select it by name.

- name: '@deepseek-ai/dsh-terminal'
- name: '@deepseek-ai/dsh-terminal-bash'
  config:
    shellDialect: pwsh
- name: '@deepseek-ai/dsh-tool-pwsh-persistent'
Field Default Meaning
backendType shell Registered PTY backend used for each agent's shell
timeoutMs 300,000 Wall-clock limit for one command; timeout closes the shell
maxOutputChars 16,000 Maximum retained command-output characters; fixed diagnostics are added afterward
description Run commands in a persistent PowerShell shell. State, including the current directory and exported environment variables, persists across calls for this agent. Model-facing environment contract; deployments may describe their environment

The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.

What the agent can rely on

Commands share one shell per agent, so cwd, $env: variables, functions, and background jobs persist across calls. Results exclude the private completion markers and the echoed input line. When the shell reads stdin again without having printed the completion marker — an interrupt, a replaced shell, or an interactive foreground child whose stdin wait the provider proves — the call returns the captured partial output, which can end with the backend's own prompt text. A non-zero wrapped command appends [exit code: N] — the exact native exit code when the command ran a native program, 1 for a terminating PowerShell error. A shell that exits before reporting that status instead appends [shell exited: code N], [shell killed by signal: SIG], or [shell exited] (Windows forced termination reports exit 1 without a signal), then resets and tells the agent the next call starts fresh. Long output keeps the earliest retained prefix plus a clipping notice; if the terminal has already dropped that prefix, the result says so explicitly.

What can go wrong

A call without an owning agent session fails with pwsh requires an owning agent session, and a composition without a pwsh-dialect PTY backend activates the tool but fails its first call with no PTY backend registered for "shell". A model redefinition of the prompt function removes the readiness marker, and the shell then settles on the silence tier instead of the marker fast path. Raw ESC characters inside a command are consumed by PSReadLine before execution and are unsupported. A timeout closes the uncertain shell and reports bounded partial output and the reset. Cancellation resets the shell and discards command output. After cleanup settles, ToolRuntime publishes Error: tool call aborted with code ABORTED; cancellation reasons are not rendered as command output. A queued cancelled call sends no command.


Understand the implementation

Implementation internals — click to expand

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.

Design philosophy

  • A deliberate twin of dsh-tool-bash-persistent. The session registry, polling loop, and reset contract mirror the persistent bash tool by design (pwsh persistent PTY Agent Note).
  • Backend-owned prompt readiness. The shell's prompt function belongs to the backend: it prints a BEL-terminated OSC marker plus the controlled printable prompt. The backend accepts recognized prompt text through its fast path and otherwise uses its own readiness checks or silence tier. The tool neither installs nor matches a prompt of its own.
  • Readiness after scrolling and restart. The Loader composition test checks the complete send-settlement sequence, including a command on the same shell after large output and the first command after exit. Its failure timeline distinguishes the bundled OpenConsole used on Windows from the unused system conhost.
  • PSReadLine echo stripped by anchoring. PowerShell renders submitted input back into the stream; the marker-anchored extraction and a wrapper-source strip remove the echo, and a wrapper that wraps across the terminal width may leave a partial echo in partial-output results.
  • Reset, never repair. Any uncertain state — an explicit exit, a timeout, a send failure, an abort — closes the shell and starts the next call fresh.

Source map

File Role
src/index.ts Plugin entry: shell registry, command wrapping, scrollback polling, extraction and rendering

Command flow

A first command spawns the shell through ctx.terminals.spawn, whose pwsh startup installs the controlled prompt function and returns only once the shell reached readiness. Each command is wrapped into one physical line — Write-Output of the start marker, the body escaped with backtick escapes into a double-quoted string, and Write-Output of the end marker plus the exit status — so PSReadLine's echo of a wrapped line cannot fabricate completion. The parser accepts ASCII-space padding after the status digits but still requires LF or CRLF. Spaces in command output remain unchanged. The tool polls the scrollback in 1,000-line pages until a complete end-marker status line is available or the send settles as stdin_read, extracts the span, strips the echoed wrapper, and renders it with any status marker. A timeout aborts the deadline, captures the partial output, and resets the shell.


Further Exploration

Read these pages when the package-level contract is not enough. They move from the terminal family to the seam, the backends, and the design notes behind the persistent-shell design.


Model Experience

Tool schema

What the model sees

The generated pwsh schema, including the configured description. The plugin contributes no standalone system-prompt section; the deployment owns persona and environment guidance.

Token effect

Fixed schema cost while pwsh is visible.

KV Cache effect

Prefix-stable while the configured description and schema remain unchanged.

Tool results

What the model sees

Commands share one shell per Agent, so cwd, $env: variables, functions, and background jobs persist across calls. Results exclude private completion markers and the echoed input line (PSReadLine renders submitted input back into the stream; the marker-anchored extraction and the wrapper-source strip remove it). When the shell reads stdin again without having printed the completion marker — an interrupt, a replaced shell, or an interactive foreground child whose stdin wait the provider proves — the call returns the captured partial output, which can end with the backend's own prompt text. A nonzero wrapped command appends [exit code: N] — the exact native exit code when the command ran a native program, 1 for a terminating PowerShell error. A shell that exits before reporting that status instead appends [shell exited: code N], [shell killed by signal: SIG], or [shell exited] when the backend supplies neither (Windows forced termination reports exit 1 without a signal), then resets and tells the model that the next call starts fresh. Long output keeps the earliest retained prefix plus a clipping notice; if the terminal has already dropped that prefix, the result says so explicitly. Timeout returns bounded partial output, closes the uncertain shell, and reports the reset.

Token effect

Data-dependent. maxOutputChars bounds retained command output; fixed clipping, lost-prefix, status, timeout, and reset diagnostics can extend the result.

KV Cache effect

Append-only tool results follow the reusable request prefix.

Known Limitations and Deferred Work

These limits define when the tool is a poor fit or needs special care. They are current package constraints, not a task backlog.

  • The tool requires an owning Agent and a real terminal backend with a pwsh dialect — Windows ConPTY or a POSIX pwsh.
  • Input echo is unavoidable — PowerShell's PSReadLine renders submitted input back into the terminal stream, and there is no stty -echo equivalent. The marker-anchored extraction excludes the echo in complete results; the wrapper-source strip covers fallback paths, but a wrapper that wraps across the terminal width may leave a partial echo in partial-output results, bounded by maxOutputChars.
  • Raw ESC characters inside model commands are unsupported — PSReadLine consumes them before execution. The wrapper escapes the control bytes it needs ([char]27-built OSC markers, backtick escapes for the body).
  • A model redefinition of the prompt function removes the readiness marker — the shell then settles on the silence tier instead of the marker fast path.
  • There is no interactive stdin during a command — where the provider proves a foreground stdin wait, a child that reads input returns early with partial output and leaves the session open, so that child can consume the next call's command until the call reaches timeoutMs and resets the shell. Everywhere else the original call itself runs to timeoutMs, which resets the shell.
  • SIGTSTP/SIGHUP are unavailable on Windows (backend-rejected); SIGINT is delivered as a console-wide Ctrl-C input write, which at a prompt cancels the pending line instead of signalling a process.
  • Under the Windows ACL sandbox's read-only mode, pwsh starts in ConstrainedLanguage, which may deny the bootstrap's [Console]:: encoding pin and prompt marker. Commands can still settle through the printable prompt and silence tier, but non-ASCII output may follow the host code page.
  • The BEL-terminated OSC marker remains a readiness signal only — a BEL event channel to the model stays deferred, aligned with the current implementation.

Dev Note

Working context for maintainers — click to expand

None.