feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
import { spawnSync } from 'node:child_process'
|
fix(terminal-bash): handle terminal protocol replies
Unix PowerShell emits cursor-position requests while PSReadLine starts and
redraws prompts. The subprocess PTY is only a transport, so those requests
went unanswered. Startup could then accept the dsh> literal echoed from its
setup source as a rendered prompt, and later sends were lost or clipped.
Feed raw PTY output into a zero-scrollback @xterm/headless state machine and
write generated replies through the provider-owned terminal handle. Drain
replies before caller input, repeat foreground inspection when terminal
activity races the sample, and retain send ownership until parser and reply
work quiesce. Coalesce raw chunks behind one active parser write so large
Windows output cannot create thousands of queued parse callbacks.
Publish pwsh only from backend stdin_read evidence and start one timeoutMs
deadline before the complete startup retry loop, so inferred-idle follow-ups
cannot reset the bound. Dispose the emulator when the
terminal or cleanup fails. Document the fail-loud ConstrainedLanguage path
and add focused coverage for split queries, reply ordering, foreground
resampling, failure containment, batching, timeout, and disposal.
2026-08-23 19:24:50 +08:00
|
|
|
import { mkdtemp, realpath, rm, writeFile } from 'node:fs/promises'
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
import { tmpdir } from 'node:os'
|
|
|
|
|
import { join } from 'node:path'
|
|
|
|
|
import { pathToFileURL } from 'node:url'
|
|
|
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
|
|
|
import { Context } from '@deepseek-ai/cordis'
|
|
|
|
|
import Loader from '@deepseek-ai/cordis-plugin-loader'
|
|
|
|
|
import Include from '@deepseek-ai/cordis-plugin-include'
|
|
|
|
|
import { CallId } from '@deepseek-ai/dsh-llm'
|
|
|
|
|
import { Session, SessionId } from '@deepseek-ai/dsh-session'
|
|
|
|
|
import AgentRegistry, { Inbox } from '@deepseek-ai/dsh-agent'
|
|
|
|
|
import type { Agent } from '@deepseek-ai/dsh-agent'
|
2026-08-13 09:52:56 +08:00
|
|
|
import TerminalSessionService from '@deepseek-ai/dsh-terminal'
|
|
|
|
|
import * as TerminalBash from '@deepseek-ai/dsh-terminal-bash'
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
import SandboxProvider from '@deepseek-ai/dsh-sandbox'
|
|
|
|
|
import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
|
|
|
|
import SandboxPolicyService from '@deepseek-ai/dsh-sandbox-policy'
|
|
|
|
|
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
|
|
|
|
|
import { resolvePwshPath } from '@deepseek-ai/dsh-pwsh-local/src/resolve.ts'
|
|
|
|
|
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
|
|
|
|
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
|
|
|
|
import * as ToolPwshPersistent from '@deepseek-ai/dsh-tool-pwsh-persistent'
|
|
|
|
|
|
|
|
|
|
const hasPwsh = spawnSync(
|
|
|
|
|
resolvePwshPath(), ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command', '$true'],
|
|
|
|
|
{ encoding: 'utf8' },
|
|
|
|
|
).status === 0
|
|
|
|
|
|
|
|
|
|
let root: string | undefined
|
|
|
|
|
let context: Context | undefined
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
await context?.fiber.dispose()
|
|
|
|
|
context = undefined
|
|
|
|
|
if (root !== undefined) await rm(root, { recursive: true, force: true })
|
|
|
|
|
root = undefined
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
class PassthroughSandbox extends SandboxProvider {
|
|
|
|
|
confine(argv: readonly string[], _policy: SandboxPolicy): ConfinedArgv {
|
|
|
|
|
return { argv: [...argv], enforcement: 'full', denialSignatures: [], runnerFailureRules: [] }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function agent(ctx: Context, cwd: string): Agent {
|
|
|
|
|
const id = SessionId('persistent-pwsh-loader-agent')
|
|
|
|
|
const scope = ctx.plugin(() => {})
|
|
|
|
|
const session = Session.create(id, [], { version: 0, id, createdAt: 0, cwd })
|
|
|
|
|
const value: Agent = {
|
|
|
|
|
id,
|
|
|
|
|
options: {},
|
|
|
|
|
session,
|
|
|
|
|
inbox: new Inbox(session, { inserted: () => {}, discarded: () => {}, claimed: () => {} }),
|
|
|
|
|
status: 'idle',
|
|
|
|
|
ctx: scope.ctx,
|
|
|
|
|
send: () => {},
|
|
|
|
|
followup: () => {},
|
|
|
|
|
steer: () => ({ outcome: Promise.resolve({ status: 'rejected' as const }) }),
|
|
|
|
|
inject: () => {},
|
|
|
|
|
cancel() {},
|
|
|
|
|
runMaintenance: task => task(new AbortController().signal),
|
|
|
|
|
whenIdle: () => Promise.resolve(),
|
|
|
|
|
}
|
|
|
|
|
ctx.agents.register(value)
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function text(result: { content: { type: string; text?: string }[] }): string {
|
|
|
|
|
return result.content.filter(block => block.type === 'text').map(block => block.text).join('')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe.skipIf(!hasPwsh)('persistent pwsh through a real cordis.yml Loader composition', () => {
|
|
|
|
|
it('preserves cwd and environment across calls', async () => {
|
fix(terminal-bash): handle terminal protocol replies
Unix PowerShell emits cursor-position requests while PSReadLine starts and
redraws prompts. The subprocess PTY is only a transport, so those requests
went unanswered. Startup could then accept the dsh> literal echoed from its
setup source as a rendered prompt, and later sends were lost or clipped.
Feed raw PTY output into a zero-scrollback @xterm/headless state machine and
write generated replies through the provider-owned terminal handle. Drain
replies before caller input, repeat foreground inspection when terminal
activity races the sample, and retain send ownership until parser and reply
work quiesce. Coalesce raw chunks behind one active parser write so large
Windows output cannot create thousands of queued parse callbacks.
Publish pwsh only from backend stdin_read evidence and start one timeoutMs
deadline before the complete startup retry loop, so inferred-idle follow-ups
cannot reset the bound. Dispose the emulator when the
terminal or cleanup fails. Document the fail-loud ConstrainedLanguage path
and add focused coverage for split queries, reply ordering, foreground
resampling, failure containment, batching, timeout, and disposal.
2026-08-23 19:24:50 +08:00
|
|
|
root = await realpath(await mkdtemp(join(tmpdir(), 'dsh-persistent-pwsh-loader-')))
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
const configPath = join(root, 'cordis.yml')
|
|
|
|
|
await writeFile(configPath, [
|
|
|
|
|
"- name: '@deepseek-ai/dsh-agent'",
|
|
|
|
|
"- name: '@deepseek-ai/dsh-system-prompt'",
|
|
|
|
|
"- name: '@deepseek-ai/dsh-tools'",
|
2026-08-13 09:52:56 +08:00
|
|
|
"- name: '@deepseek-ai/dsh-terminal'",
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
"- name: '@deepseek-ai/dsh-test-sandbox'",
|
|
|
|
|
"- name: '@deepseek-ai/dsh-sandbox-policy'",
|
|
|
|
|
' config:',
|
|
|
|
|
' mode: danger-full-access',
|
|
|
|
|
` workspaceRoot: ${JSON.stringify(root)}`,
|
|
|
|
|
"- name: '@deepseek-ai/dsh-subprocess-local'",
|
2026-08-13 09:52:56 +08:00
|
|
|
"- name: '@deepseek-ai/dsh-terminal-bash'",
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
' config:',
|
|
|
|
|
' shellDialect: pwsh',
|
|
|
|
|
' pollIntervalMs: 10',
|
|
|
|
|
' exactProbeAfterMs: 20',
|
|
|
|
|
' idleSilenceMs: 300',
|
|
|
|
|
' handoffGraceMs: 300',
|
|
|
|
|
' scrollbackLines: 20000',
|
fix(terminal-bash): handle terminal protocol replies
Unix PowerShell emits cursor-position requests while PSReadLine starts and
redraws prompts. The subprocess PTY is only a transport, so those requests
went unanswered. Startup could then accept the dsh> literal echoed from its
setup source as a rendered prompt, and later sends were lost or clipped.
Feed raw PTY output into a zero-scrollback @xterm/headless state machine and
write generated replies through the provider-owned terminal handle. Drain
replies before caller input, repeat foreground inspection when terminal
activity races the sample, and retain send ownership until parser and reply
work quiesce. Coalesce raw chunks behind one active parser write so large
Windows output cannot create thousands of queued parse callbacks.
Publish pwsh only from backend stdin_read evidence and start one timeoutMs
deadline before the complete startup retry loop, so inferred-idle follow-ups
cannot reset the bound. Dispose the emulator when the
terminal or cleanup fails. Document the fail-loud ConstrainedLanguage path
and add focused coverage for split queries, reply ordering, foreground
resampling, failure containment, batching, timeout, and disposal.
2026-08-23 19:24:50 +08:00
|
|
|
' timeoutMs: 60000',
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
' disposeGraceMs: 500',
|
|
|
|
|
"- name: '@deepseek-ai/dsh-tool-pwsh-persistent'",
|
|
|
|
|
' config:',
|
2026-08-23 23:58:42 +08:00
|
|
|
' timeoutMs: 60000',
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
'',
|
|
|
|
|
].join('\n'))
|
|
|
|
|
|
|
|
|
|
context = new Context()
|
|
|
|
|
context.baseUrl = pathToFileURL(root).href + '/'
|
|
|
|
|
await context.plugin(Loader)
|
|
|
|
|
context.loader.builtins.include = Include
|
|
|
|
|
const modules = new Map<string, unknown>([
|
|
|
|
|
['@deepseek-ai/dsh-agent', AgentRegistry],
|
|
|
|
|
['@deepseek-ai/dsh-system-prompt', SystemPrompt],
|
|
|
|
|
['@deepseek-ai/dsh-tools', ToolRegistry],
|
2026-08-13 09:52:56 +08:00
|
|
|
['@deepseek-ai/dsh-terminal', TerminalSessionService],
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
['@deepseek-ai/dsh-test-sandbox', PassthroughSandbox],
|
|
|
|
|
['@deepseek-ai/dsh-sandbox-policy', SandboxPolicyService],
|
|
|
|
|
['@deepseek-ai/dsh-subprocess-local', LocalSubprocessService],
|
2026-08-13 09:52:56 +08:00
|
|
|
['@deepseek-ai/dsh-terminal-bash', TerminalBash],
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
['@deepseek-ai/dsh-tool-pwsh-persistent', ToolPwshPersistent],
|
|
|
|
|
])
|
|
|
|
|
context.loader.internal = {
|
|
|
|
|
version: 'v2',
|
|
|
|
|
async import(specifier: string) {
|
|
|
|
|
if (!modules.has(specifier)) throw new Error(`unexpected Loader import: ${specifier}`)
|
|
|
|
|
return modules.get(specifier)
|
|
|
|
|
},
|
|
|
|
|
} as unknown as NonNullable<typeof context.loader.internal>
|
|
|
|
|
await context.loader.create({ name: 'cordis:include', config: { path: pathToFileURL(configPath).href } })
|
|
|
|
|
await context.loader.await()
|
|
|
|
|
|
|
|
|
|
const owner = agent(context, root)
|
|
|
|
|
const signal = new AbortController().signal
|
|
|
|
|
const execute = (id: string, command: string) => context!.tools.execute({
|
|
|
|
|
signal,
|
|
|
|
|
callId: CallId(id),
|
|
|
|
|
name: 'pwsh',
|
|
|
|
|
arguments: { command },
|
|
|
|
|
agent: owner,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(context.tools.schemas().map(schema => schema.name)).toEqual(['pwsh'])
|
|
|
|
|
await execute('state', '$env:KEEP = "loader"; New-Item -ItemType Directory -Force -Path nested | Out-Null; Set-Location nested')
|
|
|
|
|
const observed = text(await execute('observe', 'Write-Output "cwd=$PWD keep=$env:KEEP"'))
|
|
|
|
|
expect(observed).toContain(`cwd=${join(root, 'nested')} keep=loader`)
|
|
|
|
|
expect(observed).not.toContain('DSH_PERSISTENT_PWSH')
|
|
|
|
|
|
|
|
|
|
const multiline = text(await execute(
|
|
|
|
|
'multiline',
|
|
|
|
|
'$value = "line one"\nWrite-Output "${value}:it\'s fine"',
|
|
|
|
|
))
|
|
|
|
|
expect(multiline).toBe("line one:it's fine")
|
|
|
|
|
expect(multiline).not.toContain('DSH_PERSISTENT_PWSH')
|
|
|
|
|
|
|
|
|
|
const hereString = text(await execute(
|
|
|
|
|
'here-string',
|
|
|
|
|
"$h = @'\nalpha\nbeta\n'@\nWrite-Output $h",
|
|
|
|
|
))
|
|
|
|
|
expect(hereString).toBe('alpha\nbeta')
|
|
|
|
|
|
|
|
|
|
const large = text(await execute('large-output', '1..12050 | ForEach-Object { $_ }'))
|
|
|
|
|
expect(large.startsWith('1\n2\n3\n')).toBe(true)
|
|
|
|
|
expect(large).toContain('<response clipped>')
|
|
|
|
|
expect(large).not.toContain('beginning of this command output was dropped')
|
|
|
|
|
|
|
|
|
|
const exited = text(await execute('exit', 'exit'))
|
|
|
|
|
expect(exited).toContain('next pwsh call starts from the workspace')
|
|
|
|
|
expect(text(await execute('after-exit', 'Write-Output "$PWD"'))).toBe(root)
|
2026-08-23 23:58:42 +08:00
|
|
|
}, 120_000)
|
feat(pty): persistent pwsh tool and the minimal-preset Windows stack
Adds @deepseek-ai/dsh-tool-pwsh-persistent, the mirror of
tool-bash-persistent for PowerShell: one owner-scoped persistent pwsh
per agent, an Invoke-Expression wrapper with backtick-escaped bodies and
exact native exit codes ( reset, \True fallback, catch to 1),
PSReadLine-echo tolerance (the echoed wrapper is stripped from captured
output and can never fabricate completion), and the same
timeout/cancel/exit reset semantics with pwsh-flavored diagnostics.
The minimal preset now gates its persistent shell stack by platform with
the #2234 disabled interpolation: the bash rows mount on POSIX and the
pwsh rows (pty-local shellDialect pwsh + the new tool) on win32, keeping
exactly one persistent shell per host. windows-shell.spec pins the
per-platform roster; the real Loader composition proves cwd/env
persistence, multiline and here-string commands, large-output clipping,
and exit/reset over a real ConPTY pwsh.
2026-08-12 00:15:27 +08:00
|
|
|
})
|