mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-09 04:02:35 +00:00
chore: remove unrelated PTY test repair from agent refactor
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-16-persistent-pty-sessions.md
|
||||
2026-07-16-persistent-pty-sessions.md: 834f0e003350b97293d9d93ae0d8dd5259cfe1c1
|
||||
2026-07-16-persistent-pty-sessions.zh.md: fea4d0d025de71f5e753e5ed8e50065746c99a97
|
||||
2026-07-16-persistent-pty-sessions.md: d62587ced78be75de121fe907ca258d07a2830a1
|
||||
2026-07-16-persistent-pty-sessions.zh.md: a26c3ed852b3a18842585e19ecb2a6994f8d9117
|
||||
|
||||
@@ -158,8 +158,6 @@ The package ships concise tool guidance explaining persistent state, owner isola
|
||||
|
||||
## Verification
|
||||
|
||||
The disowned-descendant fixture's parent shell ignores `SIGTERM` before fork and publishes the child PID, so neither identity nor signal disposition depends on child startup. A file barrier also holds the descendant before its workload while natural shell exit and explicit close must still reach quiescence.
|
||||
|
||||
- Per-file coverage pins owner fencing, concurrent reservations, cancellation during pre-write inspection, unpublished-spawn cancellation and awaited teardown, sandbox-mode change rejection, retriable lifecycle cleanup, readiness tiers, rejection of pre-write stdin waits and delayed earlier prompts, the configured handoff grace holding the idle fallback past one poll and its rejection below `pollIntervalMs`, sanitizer carry state, complete UTF-8 bounds, task integration, schemas, and exact render intents.
|
||||
- Subprocess process fixtures cover non-leader and non-main-thread stdin waits, thread-local fd tables, the `/dev/tty` alias, supported kernel ABIs under user-mode emulation, rejection of fd 0 backed by a pipe, zombie quiescence, unreadable process state, unsupported architectures, and other false-positive rejection; macOS inspector logic is injected into the same unit suite.
|
||||
- Real `node-pty` and PTY-consumer tests jointly exercise shell state, controlling-terminal input through `/dev/tty`, the exact attribution when process syscalls are readable, its bounded idle fallback when host policy denies them, shared sandbox policy, environment scrubbing, raw-mode foreground `SIGINT`, a TERM-ignoring descendant, and immediate post-disposal quiescence. The Linux native smoke keeps the PTY PID, session leader, controlling terminal, foreground `inputWaiting`, and readiness while a reparented `setsid` descendant remains owned by the scope; fallback suites retain identity-fenced observational cleanup coverage.
|
||||
|
||||
@@ -158,8 +158,6 @@ plugins:
|
||||
|
||||
## 验证
|
||||
|
||||
脱离作业表的后代进程由父 shell 在 fork 前设置忽略 `SIGTERM`,并由父 shell 发布其 PID,因此身份与信号处置都不依赖子进程启动。文件屏障还会将后代进程阻挡在工作负载之前,此时 shell 自然退出与显式关闭仍必须使其完全停稳。
|
||||
|
||||
- 逐文件覆盖测试锁定了 owner 隔离、并发预留、写入前检查期间的取消、未发布 spawn 的取消与等待式 teardown、沙箱模式变更拒绝、可重试的生命周期清理、就绪层级、对写入前 stdin 等待与延迟到达的先前 prompt 的拒绝、配置化交接宽限把 idle fallback 顶过一次轮询以及低于 `pollIntervalMs` 时的拒绝、sanitizer carry state、完整 UTF-8 结果上限、task 集成、schema 和精确 render intent。
|
||||
- 子进程 fixture(测试前置数据)覆盖非 leader 与非主线程的 stdin 等待、线程本地 fd 表、`/dev/tty` 别名、用户态模拟下受支持的内核 ABI、拒绝把指向管道的 fd 0 当作终端输入、僵尸进程完全停稳、不可读进程状态、不支持的架构和其他误报拒绝;同一单元测试套件通过注入覆盖 macOS 检查器逻辑。
|
||||
- 真实 `node-pty` 与 PTY 消费方测试共同覆盖 shell 状态、通过 `/dev/tty` 读取控制终端输入、进程 syscall 可读时的精确归因、宿主策略拒绝读取时的有界 idle fallback、共享沙箱策略、环境清洗、raw mode 前台 `SIGINT`、忽略 `SIGTERM` 的后代进程,以及 dispose 返回后立即完全停稳。Linux native 冒烟测试会在一个 reparent 的 `setsid` 后代仍由 scope 拥有时,保持 PTY PID、session leader、控制终端、前台 `inputWaiting` 与 readiness;fallback 测试套件继续覆盖带身份围栏的观察式清理。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { existsSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
@@ -238,24 +238,27 @@ describe.skipIf(process.platform === 'win32')('terminal-bash real shell', () =>
|
||||
expect(() => process.kill(pid, 0)).toThrow()
|
||||
}, 10_000)
|
||||
|
||||
it.each([false, true])('quiesces a disowned same-session descendant after the shell exits naturally (hold descendant: %s)', async (holdDescendant) => {
|
||||
it('quiesces a disowned same-session descendant after the shell exits naturally', async () => {
|
||||
const { ctx, root, agent } = await harness('danger-full-access')
|
||||
const created = await ctx.terminals.spawn(agent, { type: 'shell' })
|
||||
const releaseFile = join(root, 'release-descendant')
|
||||
// Keep the descendant before its workload without delaying the parent's PID publication.
|
||||
const barrier = holdDescendant ? 'while [ ! -e "$1" ]; do sleep 0.01; done; ' : ''
|
||||
const pidFile = join(root, 'disowned.pid')
|
||||
let pid: number | undefined
|
||||
try {
|
||||
// Ignore TERM before forking so even an unscheduled child inherits it.
|
||||
const background = ctx.terminals.startSend(agent, created.sessionId, {
|
||||
text: `trap "" TERM; sh -c '${barrier}exec sleep 60' dsh "${releaseFile}" & disown; trap - TERM; echo CHILD=$!`,
|
||||
text: `sh -c 'trap "" TERM; printf "%s" "$$" > "$1"; sleep 60' dsh "${pidFile}" & disown`,
|
||||
submit: true,
|
||||
})
|
||||
const result = await background.done
|
||||
const childPid = Number(/CHILD=(\d+)/.exec(result.viewport)?.[1])
|
||||
if (childPid > 0) pid = childPid
|
||||
expectReadyForNextSend(result.waitReason)
|
||||
expect(childPid, result.viewport).toBeGreaterThan(0)
|
||||
await background.done
|
||||
const pidDeadline = Date.now() + 2_000
|
||||
let childPid = 0
|
||||
while (childPid === 0 && Date.now() < pidDeadline) {
|
||||
if (existsSync(pidFile)) childPid = Number(readFileSync(pidFile, 'utf8'))
|
||||
if (childPid > 0) break
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
}
|
||||
expect(existsSync(pidFile), ctx.terminals.read(agent, created.sessionId, { offset: 0, count: 100 }).text).toBe(true)
|
||||
expect(childPid).toBeGreaterThan(0)
|
||||
pid = childPid
|
||||
expect(() => process.kill(childPid, 0)).not.toThrow()
|
||||
await ctx.terminals.startSend(agent, created.sessionId, { text: 'exit', submit: true }).done
|
||||
const deadline = Date.now() + 2_000
|
||||
@@ -272,8 +275,6 @@ describe.skipIf(process.platform === 'win32')('terminal-bash real shell', () =>
|
||||
} catch (_alreadyReaped) {
|
||||
// Product cleanup is the expected path; this only contains a failed regression.
|
||||
}
|
||||
const disposedPid = pid
|
||||
await expect.poll(() => processIsRunning(disposedPid)).toBe(false)
|
||||
}
|
||||
}
|
||||
}, 10_000)
|
||||
|
||||
Reference in New Issue
Block a user