@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 CHANGED
@@ -1,31 +1,3 @@
1
1
  # @hasna-internal/kai-subprocess
2
2
 
3
- English | [中文](README.zh.md)
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
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ module.exports = {};
package/package.json CHANGED
@@ -1,42 +1,16 @@
1
1
  {
2
2
  "name": "@hasna-internal/kai-subprocess",
3
- "description": "Subprocess seam (ctx.subprocess) for the DeepSeek Harness — managed process groups, bounded spill-backed output, and escalated kills behind one abstract service",
4
- "version": "0.1.1-rc.2",
5
- "publishConfig": {
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
- "lib/index.js",
30
- "lib/invariant.js",
31
- "lib/types/**/*.d.ts"
7
+ "index.js",
8
+ "README.md"
32
9
  ],
33
10
  "license": "MIT",
34
- "peerDependencies": {
35
- "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
36
- "@deepseek-ai/cordis": "^4.0.1"
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 };
@@ -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
@@ -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
@@ -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