dsh-pty-session
Manifest validDSH plugin exposing the harness's owner-scoped PTY seam as four protocol-agnostic tools (pty_open/send/tail/close) plus a ctx.pty consumer facade.
dsh-pty-session
DSH plugin exposing the harness's owner-scoped PTY seam (ctx.terminals
— the same seam @deepseek-ai/dsh-tool-bash-persistent runs on) as four
protocol-agnostic tools: pty_open / pty_send / pty_tail / pty_close.
Layer 0 core: byte-stream sessions with zero protocol knowledge. Line framing, REPL prompts, and protocol parsing (gdb/MI, ssh banners) belong to consumer-layer plugins built on top of this surface.
API
| Tool | Args | Returns | Notes |
|---|---|---|---|
pty_open | command (required), cwd?, env? | sessionId, pid?, status, initialOutput | Spawns on a PTY; env vars are exported (POSIX quoting) before the command on shell-type backends. Session survives turns. |
pty_send | id, data (required), submit? (default true) | delta, status | Writes bytes; returns output read while the write settled. |
pty_tail | id (required), lines? | text, lines, truncated | Incremental cursor: returns only new output since the last tail — repeated tails never resend. Backlog beyond lines returns the newest lines and sets truncated: true. |
pty_close | id (required) | closed | Clean termination + reclaim. Idempotent per close. |
Lifetime & ownership
- Every call is authorized against the calling agent (
exec.agent); another agent touching your session getsFOREIGN_SESSION. - Sessions live with the plugin instance, not the turn: a fresh turn on the same agent sees all its open sessions.
- The owner-scoped registry closes all sessions when the owning agent disposes; the plugin additionally installs a dispose effect that closes every session it opened.
Semantics worth knowing
- Lines, not bytes:
pty_tailinherits the seam's line-based scrollback contract (totalLines/lineBegin/lineEnd). Output without a trailing newline (a REPL prompt, a password prompt) becomes visible once its line completes. Byte-exact tail is the consumer layer's job if a consumer ever needs it. pty_sendsubmit: defaults totrue(backend Enter after the data) so interactive sessions behave; passsubmit: falsefor byte-exact writes.pty_closedelegates termination to the seam's awaited, idempotent cleanup (process-tree kill + quiescence) — the seam owns the mechanics.envis best-effort POSIXexportlines and only meaningful on shell-type backends (the seam's spawn request has no env field); don't passenvagainst non-shell backends.
Configuration
backendType: shell # registered PTY backend type passed to terminals.spawn
tailLines: 200 # default pty_tail budget
Consumer-plugin facade (ctx.pty)
Consumer plugins (dsh-embedded-debug T5, later ssh) do not shell out through the four tools — they inject the same core as a cordis service:
const inject = ["tools", "pty"]; // add "pty" to the plugin's inject
// inside apply(ctx): ctx.pty is provided by dsh-pty-session
const opened = await ctx.pty.open(exec.agent, { command: "gdb -q -i=mi2" }, exec.signal);
await ctx.pty.send(exec.agent, opened.sessionId, { data: "-gdb-exit" });
const page = await ctx.pty.tail(exec.agent, opened.sessionId, 100);
await ctx.pty.close(exec.agent, opened.sessionId);
The facade exposes open/send/tail/close/dispose with EXACTLY the tool
semantics (same cursors, same seam reads — one core, two surfaces). The
owner is passed explicitly and must be the consumer's own exec.agent;
the seam enforces ownership (FOREIGN_SESSION). close is idempotent
(closed:false for an already-gone session). Loading this plugin becomes a
hard startup dependency of any consumer that injects "pty".
Instance matrix
| Consumer | Status | Why |
|---|---|---|
| gdb (dsh-embedded-debug T5/T6) | first consumer — injects ctx.pty | MI framing lives in the consumer; API shape is being pressed out by gdb's needs. |
| ssh | planned — second consumer | Validates the abstraction once a second consumer exists. |
| serial (tio) / probe server / RTT | explicitly excluded | Non-PTY long-running processes: job + tail is already the correct pattern. |
Development
npm install # vitest
npm test # vitest run
Tests are the reference implementation: they drive the real tool surface
against a fake terminals registry whose backend runs real interactive
child processes (echo round-trips, no protocol parsing).
Harness deps (important)
@deepseek-ai/dsh-tools, @deepseek-ai/schemastery, and @deepseek-ai/cordis
must be junctions/symlinks into the installed harness's node_modules
(never copies): duplicated module instances make the DSH plugin loader fail
the entry with failed to import. On Windows:
New-Item -ItemType Junction -Path node_modules/@deepseek-ai/dsh-tools -Target <harness>\node_modules\@deepseek-ai\dsh-tools
# same for schemastery and cordis
Comments
Loading…
From the same category
by awesome-dsh-plugin
A curated list of plugins for DeepSeek Harness (dsh) · DeepSeek Harness 插件精选列表
★ 18k
CC0-1.0
Python
Oct 7, 2026
by 0xsline
DeepSeek Harness (DSH) ecosystem: curated plugins, tools, and infrastructure from dsh-external/hub and the public dsh-plugin topic.
★ 1.1k
CC0-1.0
Python
Sep 30, 2026
by pax-beehive
Open-source CLI, schemas, resolver, and DSH agent tools for DSH Plugin Hub
★ 458
MIT
TypeScript
Oct 6, 2026
by yjh051108
推荐组件(非必须):DeepSeek Harness 运行时注入器;已随 dsh-routing-suite 单仓库化保留,本仓库继续维护/发布。
★ 165
TypeScript
Sep 18, 2026
dsh plugin --profile web add @dsh-external/dsh-super-injectorby xiajiajun516
DeepSeek Harness (DSH) backup & restore plugin — export, import, migrate and sync your complete DSH configuration, plugins, MCP servers, skills and workspace. One-click migration to another machine.
★ 163
MIT
TypeScript
Oct 7, 2026
dsh plugin --profile web add dsh-config-managerby jigjoy-ai
A CLI that turns a goal into a pull request - and a sandbox for testing concurrent AI coding agents on the Mozaik runtime.
★ 124
MIT
TypeScript
Oct 2, 2026