@hasna-internal/kai-subprocess 0.1.1-rc.2 → 0.1.1-rc.3
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.md +1 -29
- package/index.js +2 -0
- package/package.json +10 -36
- package/LICENSE +0 -21
- package/README.i18n.yaml +0 -6
- package/README.zh.md +0 -31
- package/lib/index.js +0 -85
- package/lib/invariant.js +0 -17
- package/lib/types/index.d.ts +0 -102
- package/lib/types/invariant.d.ts +0 -13
- package/lib/types/types.d.ts +0 -248
package/README.md
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
1
|
# @hasna-internal/kai-subprocess
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
The subprocess seam (`ctx.subprocess`) is the process half of one execution world. The abstract `SubprocessRuntime` exposes executable lookup, ordinary managed `spawn`, and one terminal-process primitive; its vocabulary covers raw/collected stdio, process and terminal handles, exit facts, tree/session cleanup, and the managed `DSH_*` environment namespace. The local implementation lives in [`dsh-subprocess-local`](../subprocess-local/README.md).
|
|
6
|
-
|
|
7
|
-
## Contract
|
|
8
|
-
|
|
9
|
-
- `spawn(spec)` returns immediately with a live handle; `done` resolves at process close with exit facts (`SubprocessOutcome` carries no output and no cause classification) and rejects only for spawn-level failures.
|
|
10
|
-
- Spawn working directories and executable paths belong to the provider's execution world. `resolveExecutable(command, env?, signal?)` verifies absolute commands or resolves bare names against that world's scrubbed PATH plus explicit overrides.
|
|
11
|
-
- The spec is fully explicit — argv, cwd, per-stream stdio dispositions, grace — because deployment-varying defaults belong to the caller's config, not to a hidden subprocess-service default (the `dsh-shell` request/spec split is the owning template). `argv` is never shell-interpreted; a consumer that wants a shell passes `['bash', '-c', command]` itself.
|
|
12
|
-
- Stdio is Node-shaped per stream: `'pipe'` hands the caller the raw stream for its own protocol framing (LSP JSON-RPC, ACP ndjson), `'inherit'` passes the parent descriptor through for diagnostics, and collect mode (`{ maxBytes, spill? }`) buffers a bounded tail with an optional full-stream spill file. Collect readers take whole-stream byte offsets and never consume, so independent readers cannot steal one another's deltas; a read whose offset slid out of the in-memory tail is `lossy` and points at the spill file when one exists. Collected output stays readable after settlement.
|
|
13
|
-
- Termination is tree-scoped on every platform (POSIX detached groups with direct-child fallback; Windows `taskkill /T`): `terminate()` — the only termination verb — escalates SIGTERM→grace→SIGKILL (idempotent, driven by the spec's abort signal too, a no-op once the tree is gone), and `waitForExit(signal?)` observes whole-tree liveness so a consumer-owned teardown ladder holds each tier on real quiescence — the manager reacts but never classifies why (callers own deadlines, teardown ladders, and cause classification).
|
|
14
|
-
- `spawnTerminal(spec)` is the only non-pipe primitive. Its handle owns a real PTY, UTF-8 text I/O, foreground-process-group inspection/signalling, and one awaited `terminate()` operation that reaches quiescence for every session member the provider can still observe and settles in-flight handle calls; providers document substrate-specific observability limits. The spec signal cancels allocation only; the published handle owns its lifetime. The output stream ends after queued output when the top-level process exits, and a live transport failure rejects `done`. These operations remain one substrate primitive because ordinary pipes cannot allocate a controlling terminal or clean terminal-session members; readiness, scrollback, and owner policy remain in the PTY consumer.
|
|
15
|
-
- `scrubbedParentEnv()` / `SENSITIVE_ENV_PATTERN` are the one shared scrub definition: ambient credential-shaped and `DSH_*` names are dropped, and explicit `env` merges after the scrub. The local ordinary and terminal spawns both apply it; SDK-managed transports that own their spawn may import it directly.
|
|
16
|
-
- Disposal of the service terminates all still-running managed processes and awaits their exit.
|
|
17
|
-
|
|
18
|
-
See the [subprocess subsystem page](../../../docs/subsystems/subprocess.md) and the [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.md).
|
|
19
|
-
|
|
20
|
-
## Model Experience
|
|
21
|
-
|
|
22
|
-
Indirectly, through Consumers (today the bash executor family behind `dsh-tool-bash`), which own all model-facing rendering of process output and lifecycle.
|
|
23
|
-
|
|
24
|
-
#### KV Cache effect
|
|
25
|
-
|
|
26
|
-
No direct invalidation; the named consumers own any request-prefix changes.
|
|
27
|
-
|
|
28
|
-
## Known Limitations and Deferred Work
|
|
29
|
-
|
|
30
|
-
- **SDK-managed spawns remain outside** — an SDK transport that owns its internal spawn cannot route that call through this service; it can still import `scrubbedParentEnv` so environment policy stays single-sourced.
|
|
31
|
-
- **Teardown ladders are consumer-owned** — the seam ships signalling verbs and the tree-liveness wait, not a canned quiesce sequence; each out-of-process consumer encodes its child's cooperation shape itself (the ACP backend's stdin-EOF-first ladder is the in-repo template).
|
|
3
|
+
This package has been retired by its owner. This release is an empty placeholder with no dependencies or supported functionality. Do not use it.
|
package/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,42 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna-internal/kai-subprocess",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"access": "public"
|
|
7
|
-
},
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
-
"directory": "packages/subprocess/subprocess"
|
|
12
|
-
},
|
|
13
|
-
"type": "module",
|
|
14
|
-
"main": "lib/index.js",
|
|
15
|
-
"types": "lib/types/index.d.ts",
|
|
16
|
-
"exports": {
|
|
17
|
-
".": {
|
|
18
|
-
"types": "./lib/types/index.d.ts",
|
|
19
|
-
"default": "./lib/index.js"
|
|
20
|
-
},
|
|
21
|
-
"./invariant": {
|
|
22
|
-
"types": "./lib/types/invariant.d.ts",
|
|
23
|
-
"default": "./lib/invariant.js"
|
|
24
|
-
},
|
|
25
|
-
"./src/*": "./src/*",
|
|
26
|
-
"./package.json": "./package.json"
|
|
27
|
-
},
|
|
3
|
+
"version": "0.1.1-rc.3",
|
|
4
|
+
"description": "Retired package. Empty placeholder with no dependencies.",
|
|
5
|
+
"main": "index.js",
|
|
28
6
|
"files": [
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"lib/types/**/*.d.ts"
|
|
7
|
+
"index.js",
|
|
8
|
+
"README.md"
|
|
32
9
|
],
|
|
33
10
|
"license": "MIT",
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"@hasna-internal/kai-invariants": "^0.1.1-rc.2",
|
|
40
|
-
"@deepseek-ai/cordis": "^4.0.1"
|
|
11
|
+
"deprecated": "Retired by the package owner. Empty placeholder; do not use.",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"registry": "https://registry.npmjs.org/",
|
|
14
|
+
"access": "public"
|
|
41
15
|
}
|
|
42
|
-
}
|
|
16
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 DeepSeek
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.i18n.yaml
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
-
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
-
# after editing either side, bring the other along and re-record with:
|
|
4
|
-
# pnpm run verify-translation-pairing --write packages/subprocess/subprocess/README.md
|
|
5
|
-
README.md: 5e80bc7205c2f06b3528bbcfcb775941d2fa4545
|
|
6
|
-
README.zh.md: 4744b4aeea07e18a8d149865050f7487f8816061
|
package/README.zh.md
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# @hasna-internal/kai-subprocess
|
|
2
|
-
|
|
3
|
-
[English](README.md) | 中文
|
|
4
|
-
|
|
5
|
-
子进程 seam(`ctx.subprocess`)是一个执行世界的进程部分。抽象的 `SubprocessRuntime` 公开可执行文件查找、普通受管 `spawn` 和一项终端进程原语;其词汇涵盖原始/收集式 stdio、进程与终端句柄、退出事实、进程树/会话清理,以及受管的 `DSH_*` 环境命名空间。本地实现位于 [`dsh-subprocess-local`](../subprocess-local/README.zh.md)。
|
|
6
|
-
|
|
7
|
-
## 约定
|
|
8
|
-
|
|
9
|
-
- `spawn(spec)` 立即返回一个活动句柄;`done` 在进程关闭时以退出事实 resolve(`SubprocessOutcome` 不携带输出,也不携带原因分类),仅在 spawn 层面失败时 reject。
|
|
10
|
-
- spawn 工作目录和可执行文件路径属于提供方的执行世界。`resolveExecutable(command, env?, signal?)` 验证绝对命令,或根据该执行世界清理后的 PATH 加显式覆盖来解析裸名称。
|
|
11
|
-
- spec 完全显式(argv、cwd、按流划分的 stdio 处置方式(disposition)、宽限期),因为随部署变化的默认值属于调用方的配置,而不属于某个隐藏的子进程服务默认值(`dsh-shell` 的 request/spec 拆分是这条规则的所属模板)。`argv` 绝不经过 shell 解释;需要 shell 的消费方自行传入 `['bash', '-c', command]`。
|
|
12
|
-
- stdio 按流采用 Node 风格:`'pipe'` 把原始流交给调用方做自己的协议分帧(LSP 的 JSON-RPC、ACP(Agent Client Protocol)的 ndjson),`'inherit'` 直通父进程描述符以承载诊断输出,收集模式(collect)`{ maxBytes, spill? }` 则缓冲一段有界尾部,外加可选的完整流 spill 文件。收集模式的读取器接受全流字节偏移量且从不消费,因此独立的读取器不会抢走彼此的增量;偏移量滑出内存尾部窗口的读取标记为 `lossy`,并在 spill 文件存在时指向它。收集到的输出在结算后仍可读取。
|
|
13
|
-
- 终止在每个平台上都以进程树为范围(POSIX 用 detached 进程组并以直接子进程回退;Windows 用 `taskkill /T`):`terminate()`(唯一的终止动词)执行 SIGTERM→宽限期→SIGKILL 升级(幂等,也由 spec 的 abort 信号驱动,进程树消亡后为空操作);`waitForExit(signal?)` 观察整棵进程树的存活状态,使消费方自有的拆卸阶梯能在真正完全停稳后才进入下一层。管理器只响应中止,但绝不判定原因(deadline、拆卸阶梯与原因分类归调用方所有)。
|
|
14
|
-
- `spawnTerminal(spec)` 是唯一的非管道原语。其句柄负责真实 PTY、UTF-8 文本 I/O、前台进程组检查/信号发送,以及一项须等待的 `terminate()` 操作;该操作会使提供方仍可观察到的每个会话成员完全停稳,并结算在途句柄调用;提供方会记录执行基底特有的可观察性限制。spec 信号只取消分配;句柄一经发布,便负责自身生命周期。顶层进程退出时,输出流在已排队输出之后结束;仍处于活动状态的传输若发生故障,会使 `done` 拒绝。这些操作保留为一项执行基底原语,因为普通管道无法分配控制终端或清理终端会话成员;就绪状态、scrollback 和所有者策略仍归 PTY 消费方所有。
|
|
15
|
-
- `scrubbedParentEnv()` / `SENSITIVE_ENV_PATTERN` 是唯一一份共享的环境清理定义:环境中形似凭据的名称与 `DSH_*` 名称都会被丢弃,显式 `env` 在清除之后合并。本地的普通 spawn 与终端 spawn 都应用该定义;拥有自身 spawn 的 SDK 管理传输可直接导入它。
|
|
16
|
-
- 服务自身的 dispose(资源释放)会终止所有仍在运行的受管进程并等待其退出。
|
|
17
|
-
|
|
18
|
-
参见[子进程子系统页面](../../../docs/subsystems/subprocess.zh.md)与[seam Agent Note](../../../.agents/notes/implemented/architecture/2026-07-26-subprocess-seam.zh.md)。
|
|
19
|
-
|
|
20
|
-
## 模型体验
|
|
21
|
-
|
|
22
|
-
通过 Consumer 间接影响(目前是 `dsh-tool-bash` 背后的 bash 执行器家族);进程输出和生命周期的全部面向模型渲染均由 Consumer 负责。
|
|
23
|
-
|
|
24
|
-
#### KV Cache 影响
|
|
25
|
-
|
|
26
|
-
不会直接导致 KV Cache 失效;请求前缀变更由上述消费方负责。
|
|
27
|
-
|
|
28
|
-
## 已知限制与暂缓事项
|
|
29
|
-
|
|
30
|
-
- **由 SDK 管理的 spawn 仍在服务之外**:拥有内部 spawn 的 SDK 传输无法把该调用路由到本服务;它仍可导入 `scrubbedParentEnv`,使环境策略保持单一来源。
|
|
31
|
-
- **拆卸阶梯归消费方所有**:该 seam 只提供信号动词与进程树存活等待,不提供现成的停稳序列;每个进程外消费方自行编码其子进程的配合方式(ACP 后端以 stdin EOF 打头的阶梯是仓库内模板)。
|
package/lib/index.js
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Service } from "@deepseek-ai/cordis";
|
|
2
|
-
//#region lib/types/types.js
|
|
3
|
-
/**
|
|
4
|
-
* Vocabulary for the subprocess Service Definition: fully-specified spawn requests with
|
|
5
|
-
* Node-shaped per-stream stdio modes, bounded collected output with spill
|
|
6
|
-
* recovery, raw piped streams, and tree-scoped termination. Command
|
|
7
|
-
* defaulting, shell semantics, protocol framing, and presentation belong to
|
|
8
|
-
* consumers such as the bash executor seam.
|
|
9
|
-
* @module dsh-subprocess/types
|
|
10
|
-
*/
|
|
11
|
-
/** Namespace prefix reserved for DeepSeek Harness-managed child environment facts. */
|
|
12
|
-
const DSH_ENV_PREFIX = "DSH_";
|
|
13
|
-
//#endregion
|
|
14
|
-
//#region lib/types/index.js
|
|
15
|
-
/**
|
|
16
|
-
* Service Definition for the subprocess capability seam (`ctx.subprocess`): execution-world executable lookup,
|
|
17
|
-
* fully specified managed process trees with raw or
|
|
18
|
-
* collected stdio, and one terminal-process primitive. Command defaulting,
|
|
19
|
-
* shell semantics, deadlines, protocol framing, terminal readiness, and
|
|
20
|
-
* presentation belong to consumers. The local implementation lives in
|
|
21
|
-
* `@hasna-internal/kai-subprocess-local`.
|
|
22
|
-
* @module @hasna-internal/kai-subprocess
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* Credential-shaped environment names are NOT forwarded to children (the
|
|
26
|
-
* harness's own `DEEPSEEK_API_KEY`/secrets must not leak into a spawned
|
|
27
|
-
* process implicitly). One heuristic for every in-repo spawner; a
|
|
28
|
-
* deliberately supplied entry survives because explicit env layers merge
|
|
29
|
-
* after the scrub.
|
|
30
|
-
*/
|
|
31
|
-
const SENSITIVE_ENV_PATTERN = /KEY|PASSWORD|SECRET|TOKEN/i;
|
|
32
|
-
/**
|
|
33
|
-
* The ambient parent environment minus credential-shaped names and minus all
|
|
34
|
-
* `DSH_*` names — the canonical base every harness child starts from. `PATH`,
|
|
35
|
-
* `HOME`, locale, and proxy variables survive, so child CLIs run normally;
|
|
36
|
-
* harness identity never leaks implicitly (a deliberately forwarded
|
|
37
|
-
* credential or current `DSH_*` fact goes through the spec's explicit `env`,
|
|
38
|
-
* which merges after this scrub). Both scrubs match case-insensitively:
|
|
39
|
-
* Windows environment names are case-insensitive, so a parent `dsh_*` entry
|
|
40
|
-
* would otherwise survive and read back as `$env:DSH_*` in the child;
|
|
41
|
-
* deliberate lowercase `dsh_*` names on POSIX are implausible. Exported as a plain function so spawners
|
|
42
|
-
* that cannot route through the service (node-pty backends, SDK-managed
|
|
43
|
-
* transports) share the one scrub definition.
|
|
44
|
-
* @returns a fresh environment object safe to hand to a child spawn.
|
|
45
|
-
*/
|
|
46
|
-
function scrubbedParentEnv() {
|
|
47
|
-
const env = {};
|
|
48
|
-
for (const [key, value] of Object.entries(process.env)) if (value !== void 0 && !SENSITIVE_ENV_PATTERN.test(key) && !key.toUpperCase().startsWith("DSH_")) env[key] = value;
|
|
49
|
-
return env;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Abstract subprocess service. Subclass, implement {@link spawn}, and load the
|
|
53
|
-
* subclass as a plugin — it registers as `ctx.subprocess` (one implementation
|
|
54
|
-
* per context; loading a second throws, which is cordis' standard
|
|
55
|
-
* duplicate-service behavior).
|
|
56
|
-
*
|
|
57
|
-
* Implementations must honor these semantics:
|
|
58
|
-
* - Executable paths belong to one execution world shared with the mounted
|
|
59
|
-
* filesystem provider.
|
|
60
|
-
* - {@link spawn} returns immediately with a live handle; `done` resolves at
|
|
61
|
-
* process close with exit facts and rejects only for spawn-level failures.
|
|
62
|
-
* - Collect-mode readers are offset-based and non-consuming, so independent
|
|
63
|
-
* readers never consume one another's output; lossy reads report truncation
|
|
64
|
-
* and the spill file holding the complete stream when one exists. Piped
|
|
65
|
-
* streams are handed to the caller raw and never buffered here.
|
|
66
|
-
* - {@link SubprocessHandle.terminate} (and the spec's abort signal) escalates
|
|
67
|
-
* SIGTERM→grace→SIGKILL — the only termination verb — tree-scoped on every
|
|
68
|
-
* platform. {@link SubprocessHandle.waitForExit} observes whole-tree
|
|
69
|
-
* liveness, so a consumer-owned teardown ladder can hold each tier on real
|
|
70
|
-
* quiescence.
|
|
71
|
-
* - Disposal of the service terminates all still-running managed processes
|
|
72
|
-
* and awaits their exit.
|
|
73
|
-
* - {@link spawnTerminal} owns terminal allocation, text transport,
|
|
74
|
-
* foreground groups, signalling, and whole-session quiescence behind one
|
|
75
|
-
* awaited termination method; readiness and persistent-shell policy stay
|
|
76
|
-
* in the PTY consumer. Its output stream ends after queued terminal output
|
|
77
|
-
* when the top-level process exits.
|
|
78
|
-
*/
|
|
79
|
-
var SubprocessRuntime = class extends Service {
|
|
80
|
-
constructor(ctx) {
|
|
81
|
-
super(ctx, "subprocess");
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
//#endregion
|
|
85
|
-
export { DSH_ENV_PREFIX, SENSITIVE_ENV_PATTERN, SubprocessRuntime, SubprocessRuntime as default, scrubbedParentEnv };
|
package/lib/invariant.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
//#region lib/types/invariant.js
|
|
2
|
-
/** Package-owned invariant companion for the subprocess seam. @module @hasna-internal/kai-subprocess/invariant */
|
|
3
|
-
const PACKAGE_NAME = "@hasna-internal/kai-subprocess";
|
|
4
|
-
/** Cordis companion plugin name. */
|
|
5
|
-
const name = "subprocess-invariant";
|
|
6
|
-
/** Service required before the companion can reserve package ownership. */
|
|
7
|
-
const inject = ["invariants"];
|
|
8
|
-
/** No runtime invariant: this stateless Service Definition owns spawn-spec/handle types, while Service Providers own observations. */
|
|
9
|
-
const install = () => {};
|
|
10
|
-
/**
|
|
11
|
-
* Register the subprocess invariant companion.
|
|
12
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
-
*/
|
|
15
|
-
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
16
|
-
//#endregion
|
|
17
|
-
export { apply, inject, name };
|
package/lib/types/index.d.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Service Definition for the subprocess capability seam (`ctx.subprocess`): execution-world executable lookup,
|
|
3
|
-
* fully specified managed process trees with raw or
|
|
4
|
-
* collected stdio, and one terminal-process primitive. Command defaulting,
|
|
5
|
-
* shell semantics, deadlines, protocol framing, terminal readiness, and
|
|
6
|
-
* presentation belong to consumers. The local implementation lives in
|
|
7
|
-
* `@hasna-internal/kai-subprocess-local`.
|
|
8
|
-
* @module @hasna-internal/kai-subprocess
|
|
9
|
-
*/
|
|
10
|
-
import { Context, Service } from '@deepseek-ai/cordis';
|
|
11
|
-
import type { SubprocessHandle, SubprocessSpawnSpec } from './types.ts';
|
|
12
|
-
import type { SubprocessTerminalHandle, SubprocessTerminalSpawnSpec } from './types.ts';
|
|
13
|
-
export { DSH_ENV_PREFIX } from './types.ts';
|
|
14
|
-
export type { CollectedOutput, DshEnvironment, DshEnvironmentKey, SubprocessCollect, SubprocessCollectedOutputs, SubprocessHandle, SubprocessOutcome, SubprocessOutputMode, SubprocessOutputRead, SubprocessOutputReader, SubprocessSpawnSpec, SubprocessStdinMode, SubprocessStdio, SubprocessTerminalForeground, SubprocessTerminalHandle, SubprocessTerminalSignal, SubprocessTerminalSpawnSpec, } from './types.ts';
|
|
15
|
-
/**
|
|
16
|
-
* Credential-shaped environment names are NOT forwarded to children (the
|
|
17
|
-
* harness's own `DEEPSEEK_API_KEY`/secrets must not leak into a spawned
|
|
18
|
-
* process implicitly). One heuristic for every in-repo spawner; a
|
|
19
|
-
* deliberately supplied entry survives because explicit env layers merge
|
|
20
|
-
* after the scrub.
|
|
21
|
-
*/
|
|
22
|
-
export declare const SENSITIVE_ENV_PATTERN: RegExp;
|
|
23
|
-
/**
|
|
24
|
-
* The ambient parent environment minus credential-shaped names and minus all
|
|
25
|
-
* `DSH_*` names — the canonical base every harness child starts from. `PATH`,
|
|
26
|
-
* `HOME`, locale, and proxy variables survive, so child CLIs run normally;
|
|
27
|
-
* harness identity never leaks implicitly (a deliberately forwarded
|
|
28
|
-
* credential or current `DSH_*` fact goes through the spec's explicit `env`,
|
|
29
|
-
* which merges after this scrub). Both scrubs match case-insensitively:
|
|
30
|
-
* Windows environment names are case-insensitive, so a parent `dsh_*` entry
|
|
31
|
-
* would otherwise survive and read back as `$env:DSH_*` in the child;
|
|
32
|
-
* deliberate lowercase `dsh_*` names on POSIX are implausible. Exported as a plain function so spawners
|
|
33
|
-
* that cannot route through the service (node-pty backends, SDK-managed
|
|
34
|
-
* transports) share the one scrub definition.
|
|
35
|
-
* @returns a fresh environment object safe to hand to a child spawn.
|
|
36
|
-
*/
|
|
37
|
-
export declare function scrubbedParentEnv(): Record<string, string>;
|
|
38
|
-
declare module '@deepseek-ai/cordis' {
|
|
39
|
-
interface Context {
|
|
40
|
-
subprocess: SubprocessRuntime;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Abstract subprocess service. Subclass, implement {@link spawn}, and load the
|
|
45
|
-
* subclass as a plugin — it registers as `ctx.subprocess` (one implementation
|
|
46
|
-
* per context; loading a second throws, which is cordis' standard
|
|
47
|
-
* duplicate-service behavior).
|
|
48
|
-
*
|
|
49
|
-
* Implementations must honor these semantics:
|
|
50
|
-
* - Executable paths belong to one execution world shared with the mounted
|
|
51
|
-
* filesystem provider.
|
|
52
|
-
* - {@link spawn} returns immediately with a live handle; `done` resolves at
|
|
53
|
-
* process close with exit facts and rejects only for spawn-level failures.
|
|
54
|
-
* - Collect-mode readers are offset-based and non-consuming, so independent
|
|
55
|
-
* readers never consume one another's output; lossy reads report truncation
|
|
56
|
-
* and the spill file holding the complete stream when one exists. Piped
|
|
57
|
-
* streams are handed to the caller raw and never buffered here.
|
|
58
|
-
* - {@link SubprocessHandle.terminate} (and the spec's abort signal) escalates
|
|
59
|
-
* SIGTERM→grace→SIGKILL — the only termination verb — tree-scoped on every
|
|
60
|
-
* platform. {@link SubprocessHandle.waitForExit} observes whole-tree
|
|
61
|
-
* liveness, so a consumer-owned teardown ladder can hold each tier on real
|
|
62
|
-
* quiescence.
|
|
63
|
-
* - Disposal of the service terminates all still-running managed processes
|
|
64
|
-
* and awaits their exit.
|
|
65
|
-
* - {@link spawnTerminal} owns terminal allocation, text transport,
|
|
66
|
-
* foreground groups, signalling, and whole-session quiescence behind one
|
|
67
|
-
* awaited termination method; readiness and persistent-shell policy stay
|
|
68
|
-
* in the PTY consumer. Its output stream ends after queued terminal output
|
|
69
|
-
* when the top-level process exits.
|
|
70
|
-
*/
|
|
71
|
-
export declare abstract class SubprocessRuntime extends Service {
|
|
72
|
-
constructor(ctx: Context);
|
|
73
|
-
/**
|
|
74
|
-
* Resolve one configured executable in this provider's execution world.
|
|
75
|
-
* Absolute paths are verified; bare names use the provider's scrubbed PATH
|
|
76
|
-
* plus explicit environment overrides. Relative paths containing separators
|
|
77
|
-
* are rejected: the resolution base is undefined, so providers fail loud
|
|
78
|
-
* instead of guessing.
|
|
79
|
-
* @param command - absolute executable path or bare PATH name.
|
|
80
|
-
* @param env - explicit environment entries used for lookup.
|
|
81
|
-
* @param signal - aborts remote or local lookup.
|
|
82
|
-
* @returns a canonical executable path.
|
|
83
|
-
*/
|
|
84
|
-
abstract resolveExecutable(command: string, env?: Readonly<Record<string, string>>, signal?: AbortSignal): Promise<string>;
|
|
85
|
-
/**
|
|
86
|
-
* Start one managed child process from a fully-specified spec; this seam
|
|
87
|
-
* applies no defaults.
|
|
88
|
-
* @param spec - argv, directory, stdio dispositions, grace, cancellation, and environment.
|
|
89
|
-
* @returns the live process handle (streams/readers, signalling, outcome promise).
|
|
90
|
-
*/
|
|
91
|
-
abstract spawn(spec: SubprocessSpawnSpec): SubprocessHandle;
|
|
92
|
-
/**
|
|
93
|
-
* Allocate a real terminal and start one owned process session. This is the
|
|
94
|
-
* only non-pipe process primitive: implementations own terminal byte I/O,
|
|
95
|
-
* foreground groups, signals, and complete session-tree cleanup.
|
|
96
|
-
* @param spec - fully specified argv, cwd, environment, dimensions, grace, and allocation cancellation.
|
|
97
|
-
* @returns the live terminal handle after allocation succeeds.
|
|
98
|
-
*/
|
|
99
|
-
abstract spawnTerminal(spec: SubprocessTerminalSpawnSpec): Promise<SubprocessTerminalHandle>;
|
|
100
|
-
}
|
|
101
|
-
export default SubprocessRuntime;
|
|
102
|
-
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/invariant.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** Package-owned invariant companion for the subprocess seam. @module @hasna-internal/kai-subprocess/invariant */
|
|
2
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
/** Cordis companion plugin name. */
|
|
4
|
-
export declare const name = "subprocess-invariant";
|
|
5
|
-
/** Service required before the companion can reserve package ownership. */
|
|
6
|
-
export declare const inject: string[];
|
|
7
|
-
/**
|
|
8
|
-
* Register the subprocess invariant companion.
|
|
9
|
-
* @param ctx - Cordis context carrying the invariant service.
|
|
10
|
-
* @returns the installed registration's disposer after setup succeeds.
|
|
11
|
-
*/
|
|
12
|
-
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
13
|
-
//# sourceMappingURL=invariant.d.ts.map
|
package/lib/types/types.d.ts
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vocabulary for the subprocess Service Definition: fully-specified spawn requests with
|
|
3
|
-
* Node-shaped per-stream stdio modes, bounded collected output with spill
|
|
4
|
-
* recovery, raw piped streams, and tree-scoped termination. Command
|
|
5
|
-
* defaulting, shell semantics, protocol framing, and presentation belong to
|
|
6
|
-
* consumers such as the bash executor seam.
|
|
7
|
-
* @module dsh-subprocess/types
|
|
8
|
-
*/
|
|
9
|
-
import type { Readable, Writable } from 'node:stream';
|
|
10
|
-
/** Namespace prefix reserved for DeepSeek Harness-managed child environment facts. */
|
|
11
|
-
export declare const DSH_ENV_PREFIX: "DSH_";
|
|
12
|
-
/** One environment key inside the managed {@link DSH_ENV_PREFIX} namespace. */
|
|
13
|
-
export type DshEnvironmentKey = `${typeof DSH_ENV_PREFIX}${string}`;
|
|
14
|
-
/** Trusted DeepSeek Harness variables for one child-process execution. */
|
|
15
|
-
export type DshEnvironment = Readonly<Record<DshEnvironmentKey, string>>;
|
|
16
|
-
/** One captured stream: the (possibly truncated) text plus recovery info. */
|
|
17
|
-
export interface CollectedOutput {
|
|
18
|
-
/** Collected text — the TAIL of the stream when truncated. */
|
|
19
|
-
text: string;
|
|
20
|
-
/** True when bytes were dropped from `text`. */
|
|
21
|
-
truncated: boolean;
|
|
22
|
-
/** Path to a file holding the COMPLETE stream, when truncated and available. */
|
|
23
|
-
spillPath?: string;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* stdin disposition. `'ignore'` leaves fd 0 on `/dev/null`; `'pipe'` exposes
|
|
27
|
-
* {@link SubprocessHandle.stdin} for the caller's ongoing protocol writes;
|
|
28
|
-
* `{ data }` writes the bytes and closes (the batch shape).
|
|
29
|
-
*/
|
|
30
|
-
export type SubprocessStdinMode = 'ignore' | 'pipe' | {
|
|
31
|
-
readonly data: string;
|
|
32
|
-
};
|
|
33
|
-
/**
|
|
34
|
-
* Bounded in-memory collection for one output stream, with an optional
|
|
35
|
-
* full-stream spill file. Omitting `spill` keeps only the in-memory tail —
|
|
36
|
-
* the diagnostic-tail shape (a language server's stderr); including it makes
|
|
37
|
-
* the complete stream recoverable up to its cap (the bash tool shape).
|
|
38
|
-
*/
|
|
39
|
-
export interface SubprocessCollect {
|
|
40
|
-
/** In-memory cap in bytes; overflow keeps the TAIL. */
|
|
41
|
-
maxBytes: number;
|
|
42
|
-
/** Full-stream spill file; absent disables spilling entirely. */
|
|
43
|
-
spill?: {
|
|
44
|
-
/** Whole-stream byte cap; a larger stream discards its now-incomplete spill. */
|
|
45
|
-
maxBytes: number;
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* stdout/stderr disposition. `'pipe'` exposes the raw `Readable` for the
|
|
50
|
-
* caller's protocol decoding; `'inherit'` passes the parent's descriptor
|
|
51
|
-
* through (child diagnostics land on the harness's own stream); a
|
|
52
|
-
* {@link SubprocessCollect} object buffers boundedly with offset-based reads.
|
|
53
|
-
*/
|
|
54
|
-
export type SubprocessOutputMode = 'pipe' | 'inherit' | SubprocessCollect;
|
|
55
|
-
/** Per-stream stdio dispositions, all explicit — this seam applies no defaults. */
|
|
56
|
-
export interface SubprocessStdio {
|
|
57
|
-
stdin: SubprocessStdinMode;
|
|
58
|
-
stdout: SubprocessOutputMode;
|
|
59
|
-
stderr: SubprocessOutputMode;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* A fully-specified spawn request. This seam applies no defaults: every
|
|
63
|
-
* disposition, limit, and directory is explicit, so the caller's own config —
|
|
64
|
-
* not a hidden subprocess-service default — decides them (the `dsh-shell`
|
|
65
|
-
* request/spec split is the owning template).
|
|
66
|
-
*/
|
|
67
|
-
export interface SubprocessSpawnSpec {
|
|
68
|
-
/** Executable and arguments; `argv[0]` is the program. Never shell-interpreted here. */
|
|
69
|
-
argv: readonly string[];
|
|
70
|
-
/** Working directory for the child. */
|
|
71
|
-
cwd: string;
|
|
72
|
-
/** Per-stream stdio dispositions. */
|
|
73
|
-
stdio: SubprocessStdio;
|
|
74
|
-
/**
|
|
75
|
-
* Positive finite grace period in milliseconds, no greater than
|
|
76
|
-
* `MAX_TIMER_DELAY_MS`, for the {@link SubprocessHandle.terminate} escalation
|
|
77
|
-
* and for draining still-open collected pipes after the process exits (an
|
|
78
|
-
* inherited descriptor held by a surviving descendant cannot hold the
|
|
79
|
-
* outcome open indefinitely).
|
|
80
|
-
*/
|
|
81
|
-
graceMs: number;
|
|
82
|
-
/**
|
|
83
|
-
* Abort signal — starts the terminate escalation on the process tree when
|
|
84
|
-
* it fires. The caller owns deadlines and cause classification; this seam
|
|
85
|
-
* only reacts to the abort.
|
|
86
|
-
*/
|
|
87
|
-
signal?: AbortSignal | undefined;
|
|
88
|
-
/**
|
|
89
|
-
* Explicit environment entries merged onto the implementation's scrubbed
|
|
90
|
-
* parent base (see `scrubbedParentEnv`), with no namespace validation. A
|
|
91
|
-
* string is a deliberate caller opt-in, so a forwarded credential-shaped
|
|
92
|
-
* entry or current `DSH_*` fact survives the scrub; `undefined` is a
|
|
93
|
-
* tombstone that removes an ordinary ambient entry from the child.
|
|
94
|
-
*/
|
|
95
|
-
env?: NodeJS.ProcessEnv | undefined;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Exit facts of one closed process — Node's `close`-event vocabulary.
|
|
99
|
-
* Deliberately carries NO timeout or cancellation classification (the caller
|
|
100
|
-
* reads the signal it owns to classify causes) and NO output: collected
|
|
101
|
-
* streams stay readable through {@link SubprocessHandle.collected} after
|
|
102
|
-
* settlement, so batch and streaming callers share one access path.
|
|
103
|
-
*/
|
|
104
|
-
export interface SubprocessOutcome {
|
|
105
|
-
/** Exit code; null when the process died from a signal. */
|
|
106
|
-
exitCode: number | null;
|
|
107
|
-
/** Terminating signal (e.g. 'SIGTERM'); null on normal exit. */
|
|
108
|
-
signal: NodeJS.Signals | null;
|
|
109
|
-
}
|
|
110
|
-
/** One incremental {@link SubprocessOutputReader.readFrom} read. */
|
|
111
|
-
export interface SubprocessOutputRead {
|
|
112
|
-
/** Stream text from the requested offset (the whole retained tail when lossy). */
|
|
113
|
-
text: string;
|
|
114
|
-
/** Whole-stream offset to resume from on the next read. */
|
|
115
|
-
nextOffset: number;
|
|
116
|
-
/** True when the requested offset slid out of the in-memory tail window. */
|
|
117
|
-
lossy: boolean;
|
|
118
|
-
/** Path to the full-stream spill file, when one was created and remains intact. */
|
|
119
|
-
spillPath?: string;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Cursor-free incremental access to one collected output stream. Offsets are
|
|
123
|
-
* whole-stream byte coordinates owned by the caller, so independent readers
|
|
124
|
-
* cannot consume one another's output; `readFrom(0)` after settlement is the
|
|
125
|
-
* batch result (`lossy` then means the in-memory tail lost its head — the
|
|
126
|
-
* {@link CollectedOutput.truncated} fact).
|
|
127
|
-
*/
|
|
128
|
-
export interface SubprocessOutputReader {
|
|
129
|
-
/**
|
|
130
|
-
* Read everything captured since `fromByte`. When that offset has slid out
|
|
131
|
-
* of the in-memory tail window the read is `lossy` — it returns the whole
|
|
132
|
-
* retained tail and the gap is only recoverable from the spill file.
|
|
133
|
-
* @param fromByte - whole-stream offset to resume from (a prior read's `nextOffset`; 0 for the first read).
|
|
134
|
-
* @returns the delta text, the next offset, the `lossy` flag, and the spill path when one exists.
|
|
135
|
-
*/
|
|
136
|
-
readFrom(fromByte: number): SubprocessOutputRead;
|
|
137
|
-
}
|
|
138
|
-
/** Offset-based readers for the streams spawned in collect mode. */
|
|
139
|
-
export interface SubprocessCollectedOutputs {
|
|
140
|
-
/** Present iff stdout is a {@link SubprocessCollect}. */
|
|
141
|
-
readonly stdout?: SubprocessOutputReader;
|
|
142
|
-
/** Present iff stderr is a {@link SubprocessCollect}. */
|
|
143
|
-
readonly stderr?: SubprocessOutputReader;
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* A live child process rooted in its own process tree. Collected output
|
|
147
|
-
* remains readable after exit; piped streams belong to the caller.
|
|
148
|
-
*
|
|
149
|
-
* Termination is tree-scoped everywhere: POSIX signals the detached process
|
|
150
|
-
* group (falling back to the direct child when the group is gone), Windows
|
|
151
|
-
* terminates the tree via `taskkill /T`, so helper processes cannot outlive
|
|
152
|
-
* the handle unnoticed.
|
|
153
|
-
*/
|
|
154
|
-
export interface SubprocessHandle {
|
|
155
|
-
/** Process id (tree root); -1 when the spawn itself failed. */
|
|
156
|
-
readonly pid: number;
|
|
157
|
-
/** The child's stdin, present iff spawned with `stdin: 'pipe'`. */
|
|
158
|
-
readonly stdin: Writable | undefined;
|
|
159
|
-
/** The child's raw stdout, present iff spawned with `stdout: 'pipe'`. */
|
|
160
|
-
readonly stdout: Readable | undefined;
|
|
161
|
-
/** The child's raw stderr, present iff spawned with `stderr: 'pipe'`. */
|
|
162
|
-
readonly stderr: Readable | undefined;
|
|
163
|
-
/** Offset-based readers for collect-mode streams (also readable after exit). */
|
|
164
|
-
readonly collected: SubprocessCollectedOutputs;
|
|
165
|
-
/** Resolves at process close with exit facts; rejects only for spawn-level failures. */
|
|
166
|
-
readonly done: Promise<SubprocessOutcome>;
|
|
167
|
-
/**
|
|
168
|
-
* Begin the SIGTERM → `graceMs` → SIGKILL escalation on the process tree
|
|
169
|
-
* (Windows force-terminates immediately) — the seam's only termination
|
|
170
|
-
* verb. Idempotent, a no-op once the tree is gone (the pid may be reused),
|
|
171
|
-
* and also triggered by the spec's abort signal.
|
|
172
|
-
*/
|
|
173
|
-
terminate(): void;
|
|
174
|
-
/**
|
|
175
|
-
* Wait until the process tree has exited — the tree, not just the direct
|
|
176
|
-
* child, so a still-running helper is observable before teardown returns.
|
|
177
|
-
* @param signal - optional bound for the wait.
|
|
178
|
-
* @returns `true` when the tree exited, `false` when the signal aborted first.
|
|
179
|
-
*/
|
|
180
|
-
waitForExit(signal?: AbortSignal): Promise<boolean>;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Signals supported by the terminal-process primitive. Kept member-identical
|
|
184
|
-
* to `TerminalSignal` in `@hasna-internal/kai-terminal` without a cross-seam dependency;
|
|
185
|
-
* change both together.
|
|
186
|
-
*/
|
|
187
|
-
export type SubprocessTerminalSignal = 'SIGINT' | 'SIGTERM' | 'SIGKILL' | 'SIGTSTP' | 'SIGHUP';
|
|
188
|
-
/** A fully specified terminal-process spawn. */
|
|
189
|
-
export interface SubprocessTerminalSpawnSpec {
|
|
190
|
-
/** Executable and arguments; `argv[0]` is the program. */
|
|
191
|
-
argv: readonly string[];
|
|
192
|
-
/** Working directory in this subprocess provider's execution world. */
|
|
193
|
-
cwd: string;
|
|
194
|
-
/** Explicit environment layered after the provider's ambient scrub. */
|
|
195
|
-
env?: Record<string, string> | undefined;
|
|
196
|
-
/** Initial terminal row count. */
|
|
197
|
-
rows: number;
|
|
198
|
-
/** Initial terminal column count. */
|
|
199
|
-
cols: number;
|
|
200
|
-
/** TERM-to-KILL cleanup grace for the complete terminal session. */
|
|
201
|
-
graceMs: number;
|
|
202
|
-
/** Cancellation of terminal allocation; a published handle owns its later lifetime. */
|
|
203
|
-
signal?: AbortSignal | undefined;
|
|
204
|
-
}
|
|
205
|
-
/** Current foreground process-group facts for one terminal. */
|
|
206
|
-
export interface SubprocessTerminalForeground {
|
|
207
|
-
/** Foreground process-group id published by the terminal driver. */
|
|
208
|
-
processGroupId: number;
|
|
209
|
-
/** Whether the provider can currently prove that group is waiting on terminal input. */
|
|
210
|
-
inputWaiting: boolean;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* One live terminal process and its owned OS session. Terminal allocation,
|
|
214
|
-
* foreground-group inspection/signalling, and session-tree cleanup are one
|
|
215
|
-
* deep subprocess primitive because none can be reconstructed from ordinary
|
|
216
|
-
* piped stdio without substrate-specific process control.
|
|
217
|
-
*/
|
|
218
|
-
export interface SubprocessTerminalHandle {
|
|
219
|
-
/** Top-level terminal process id. */
|
|
220
|
-
readonly pid: number;
|
|
221
|
-
/** UTF-8 terminal output bytes in delivery order; ends after queued output when the terminal exits. */
|
|
222
|
-
readonly output: Readable;
|
|
223
|
-
/** Resolves when the top-level process exits; rejects only for a live transport failure. */
|
|
224
|
-
readonly done: Promise<SubprocessOutcome>;
|
|
225
|
-
/**
|
|
226
|
-
* Write text to the terminal input.
|
|
227
|
-
* @param data - text to deliver without implicit newline conversion.
|
|
228
|
-
*/
|
|
229
|
-
write(data: string): Promise<void>;
|
|
230
|
-
/**
|
|
231
|
-
* Inspect the current foreground process group.
|
|
232
|
-
* @returns its id and input-wait fact, or undefined when no foreground group can be resolved.
|
|
233
|
-
*/
|
|
234
|
-
inspectForeground(): Promise<SubprocessTerminalForeground | undefined>;
|
|
235
|
-
/**
|
|
236
|
-
* Deliver a signal to the current foreground process group.
|
|
237
|
-
* @param signal - permitted terminal signal.
|
|
238
|
-
* @returns the exact group id that received it.
|
|
239
|
-
*/
|
|
240
|
-
signalForeground(signal: SubprocessTerminalSignal): Promise<number>;
|
|
241
|
-
/**
|
|
242
|
-
* Idempotently terminate every terminal-session member the provider can still observe and await quiescence.
|
|
243
|
-
* After settlement, no write, inspection, or signal call remains in flight.
|
|
244
|
-
* Providers document substrate-specific observability limits.
|
|
245
|
-
*/
|
|
246
|
-
terminate(): Promise<void>;
|
|
247
|
-
}
|
|
248
|
-
//# sourceMappingURL=types.d.ts.map
|