billion-context-pi 0.1.56 → 0.1.57-pr.284.92
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 +46 -2
- package/README.zh-CN.md +46 -2
- package/dist/config.d.ts +43 -5
- package/dist/delegate-tool.d.ts +28 -1
- package/dist/delegate-watchdog.d.ts +4 -2
- package/dist/index.js +603 -323
- package/dist/index.js.map +1 -1
- package/dist/tokens.d.ts +6 -1
- package/dist/update.d.ts +13 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ The model decides <em>when</em> and <em>what</em> to compress — not a hard lim
|
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
> **Host support:** this plugin is for **Pi**. It does **not** support **OMP (oh-my-pi)** — on an OMP host it refuses to run. OMP users: use [billion-context](https://github.com/ranxianglei/billion-context) instead (`bili omp
|
|
25
|
+
> **Host support:** this plugin is for **Pi**. It does **not** support **OMP (oh-my-pi)** — on an OMP host it refuses to run. OMP users: use [billion-context](https://github.com/ranxianglei/billion-context) instead (`bili omp`, built-in plugin). Full client → package table: see [Which do I need?](#which-do-i-need); OMP details: [docs/omp.md](./docs/omp.md).
|
|
26
26
|
|
|
27
27
|
## Why?
|
|
28
28
|
|
|
@@ -39,6 +39,19 @@ This means:
|
|
|
39
39
|
1. **A single session handles enormous workloads.** Per simulation tests of the three-tier architecture (see [opencode-acp](https://github.com/ranxianglei/opencode-acp)), one session can process on the order of 10–60 billion cumulative tokens — while retaining long-term memory of distant key information (paths, decisions, signatures). You can work in the **same session for months** without outgrowing the context.
|
|
40
40
|
2. **Context stays lean over the long run.** In practice context typically holds under ~150K tokens (opencode-acp keeps it under ~200K), so compared to traditional compaction that lets context balloon toward 1M, **a single session costs roughly 5× less in tokens**.
|
|
41
41
|
|
|
42
|
+
## Which do I need?
|
|
43
|
+
|
|
44
|
+
Pick by your client:
|
|
45
|
+
|
|
46
|
+
| Client | Use |
|
|
47
|
+
|---|---|
|
|
48
|
+
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi) (in-process extension) |
|
|
49
|
+
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp) (in-process extension) |
|
|
50
|
+
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context) via `bili omp` (built-in plugin) |
|
|
51
|
+
| **everything else** | [`billion-context`](https://github.com/ranxianglei/billion-context) — `bili <client>` (launcher, preferred) or `/bili/` prefix |
|
|
52
|
+
|
|
53
|
+
> Why OMP can't use this in-process extension, and what happens if you try anyway: see [Host support](#host-support) and [docs/omp.md](./docs/omp.md).
|
|
54
|
+
|
|
42
55
|
## Install
|
|
43
56
|
|
|
44
57
|
```bash
|
|
@@ -76,7 +89,7 @@ This has two practical implications:
|
|
|
76
89
|
|
|
77
90
|
## Host support
|
|
78
91
|
|
|
79
|
-
billion-context-pi is built for the **Pi** coding agent (`@earendil-works/pi-coding-agent`) and detects the host at session start:
|
|
92
|
+
billion-context-pi is built for the **Pi** coding agent (`@earendil-works/pi-coding-agent`) and detects the host at session start — the full client → package table lives in [Which do I need?](#which-do-i-need):
|
|
80
93
|
|
|
81
94
|
- **Pi** — fully supported.
|
|
82
95
|
- **OMP (`can1357/oh-my-pi`)** — **not supported.** OMP's in-process session API diverges from Pi's, so the compression refs the extension injects can drift out of sync with the session's real refs and `compress` calls fail with `does not exist in this session` (issue [#234](https://github.com/ranxianglei/billion-context-pi/issues/234)). On OMP the extension now **refuses service**: it prints a warning, disables the ACP tools, and leaves the host's own context handling untouched.
|
|
@@ -196,6 +209,37 @@ billion-context protects three categories of content from compression:
|
|
|
196
209
|
2. **Soft recent-zone** — the last N messages (default 5) and last ~5K tokens are soft-protected so the model keeps its working set. Tool results from `decompress`, `search_context`, `read`, and `bash` are **excluded** from this zone: they're large and meant to be compressible once consumed, so they don't eat the protected budget.
|
|
197
210
|
3. **Last user message** — always protected (user intent must survive).
|
|
198
211
|
|
|
212
|
+
## Session storage & migration
|
|
213
|
+
|
|
214
|
+
billion-context-pi persists each session's compression state in a **sidecar file** next to the session transcript. Every session lives as two files in Pi's sessions directory (`~/.pi/agent/sessions/`):
|
|
215
|
+
|
|
216
|
+
| File | Contents |
|
|
217
|
+
|------|----------|
|
|
218
|
+
| `<id>.jsonl` | The conversation transcript (messages, tool calls) |
|
|
219
|
+
| `<id>.jsonl.acp.json` | The ACP compression state (compressed blocks, message refs, nudge + stats) |
|
|
220
|
+
|
|
221
|
+
The `.acp.json` sidecar is what holds your compressed blocks. Without it, the session runs on its full raw history until ACP compresses again.
|
|
222
|
+
|
|
223
|
+
### Migrating a session (cross-machine copy / backup-restore)
|
|
224
|
+
|
|
225
|
+
Pi's built-in export/import moves only the **transcript**, not the ACP state. Two things are lost:
|
|
226
|
+
|
|
227
|
+
1. **The `.acp.json` sidecar is not carried.** An imported session therefore has *no* compressed blocks: every LLM call resends the entire raw history until the nudge re-compresses it — a one-time full re-cache cost plus context bloat back to original size. Very long sessions can approach or exceed the model window before re-compression kicks in (Pi's native compaction is disabled while this plugin is active, so ACP is the only context manager).
|
|
228
|
+
2. **Export drops the `parentSession` header.** Clone/fork child sessions rely on that header field to inherit their parent's compression state; once exported, the link is gone even if the parent's files still exist on the target machine.
|
|
229
|
+
|
|
230
|
+
**To migrate a session with its compression state intact, copy both files together** (they share the same base name):
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# copy the pair
|
|
234
|
+
cp <id>.jsonl <id>.jsonl.acp.json <dest>/
|
|
235
|
+
# ... or back up / restore the whole directory
|
|
236
|
+
cp -r ~/.pi/agent/sessions <backup>/pi-sessions
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Restore them next to each other on the target machine. For clone/fork children, also bring the parent's pair so `parentSession` resolves.
|
|
240
|
+
|
|
241
|
+
> A permanent fix — the host carrying the sidecar through import/export and preserving `parentSession` — belongs upstream in pi-coding-agent and is tracked in issue [#299](https://github.com/ranxianglei/billion-context-pi/issues/299). Until it lands, copy the pair manually.
|
|
242
|
+
|
|
199
243
|
## Built on acp-kernel
|
|
200
244
|
|
|
201
245
|
The compression engine is [`acp-kernel`](https://github.com/ranxianglei/acp-kernel) — a platform-agnostic, MIT-licensed library with 208 tests. It's bundled inline into `dist/index.js`, so there are zero runtime dependencies.
|
package/README.zh-CN.md
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
> **宿主支持:** 本插件面向 **Pi**。它**不支持 OMP(oh-my-pi)** —— 在 OMP 宿主上会拒绝运行。OMP
|
|
23
|
+
> **宿主支持:** 本插件面向 **Pi**。它**不支持 OMP(oh-my-pi)** —— 在 OMP 宿主上会拒绝运行。OMP 用户请直接改用 [billion-context](https://github.com/ranxianglei/billion-context)(启动命令 bili omp);其他客户端的完整对照见[该选哪个?](#该选哪个)。OMP 详细说明:[docs/omp.zh-CN.md](./docs/omp.zh-CN.md)。
|
|
24
24
|
|
|
25
25
|
## 为什么选择 billion-context
|
|
26
26
|
|
|
@@ -38,6 +38,19 @@
|
|
|
38
38
|
1. **一个会话即可支撑海量工作。** 根据三级压缩架构的模拟测试(见 [opencode-acp](https://github.com/ranxianglei/opencode-acp)),单会话累计可处理约 100 亿至 600 亿 token —— 同时对遥远的关键信息(路径、决策、签名)保持长久记忆。用户可以在**同一个会话里连续工作几个月**,而无需因为上下文膨胀而开新会话丢上下文。
|
|
39
39
|
2. **上下文长期保持精简。** 实际运行中上下文通常稳定在 15 万 token 以下(opencode-acp 实测维持在 20 万以下),相比传统压缩方案动辄撑到 100 万上下文,**单会话累计可节省近 5 倍的 token 费用**。
|
|
40
40
|
|
|
41
|
+
## 该选哪个?
|
|
42
|
+
|
|
43
|
+
按客户端选:
|
|
44
|
+
|
|
45
|
+
| 客户端 | 用这个 |
|
|
46
|
+
|---|---|
|
|
47
|
+
| **pi** | [`billion-context-pi`](https://github.com/ranxianglei/billion-context-pi)(进程内扩展) |
|
|
48
|
+
| **opencode** | [`opencode-acp`](https://github.com/ranxianglei/opencode-acp)(进程内扩展) |
|
|
49
|
+
| **omp** | [`billion-context`](https://github.com/ranxianglei/billion-context),`bili omp`(内置插件) |
|
|
50
|
+
| **其余所有** | [`billion-context`](https://github.com/ranxianglei/billion-context) —— `bili <client>`(启动器,优先)或 `/bili/` 前缀 |
|
|
51
|
+
|
|
52
|
+
> 为什么 OMP 不能用本进程内扩展、以及强行使用的后果:见下文[宿主支持](#宿主支持)与 [docs/omp.zh-CN.md](./docs/omp.zh-CN.md)。
|
|
53
|
+
|
|
41
54
|
## 安装
|
|
42
55
|
|
|
43
56
|
```bash
|
|
@@ -75,7 +88,7 @@ billion-context 通过拦截 Pi 的 `context` 事件接管上下文管理。**Pi
|
|
|
75
88
|
|
|
76
89
|
## 宿主支持
|
|
77
90
|
|
|
78
|
-
billion-context-pi 面向 **Pi** 编码代理(`@earendil-works/pi-coding-agent`)
|
|
91
|
+
billion-context-pi 面向 **Pi** 编码代理(`@earendil-works/pi-coding-agent`)构建,并在会话开始时检测宿主——完整的「客户端 → 包」对照表见[该选哪个?](#该选哪个):
|
|
79
92
|
|
|
80
93
|
- **Pi** — 完全支持。
|
|
81
94
|
- **OMP(`can1357/oh-my-pi`)** — **不支持。** OMP 的进程内会话 API 与 Pi 不同,扩展注入的压缩引用可能与会话的真实引用漂移失步,导致 `compress` 调用失败,报错 `does not exist in this session`(issue [#234](https://github.com/ranxianglei/billion-context-pi/issues/234))。在 OMP 上,扩展现在会**拒绝服务**:打印警告、禁用 ACP 工具,并保持宿主自身的上下文处理不受影响。
|
|
@@ -196,6 +209,37 @@ billion-context 保护三类内容不被压缩:
|
|
|
196
209
|
2. **软近期区** — 最后 N 条消息(默认 5)和最后约 5K token 被软保护,让模型保留工作集。来自 `decompress`、`search_context`、`read`、`bash` 的工具结果被**排除**出此区:它们体量大、消费后就该能压缩,所以不该占用保护预算。
|
|
197
210
|
3. **最后一条用户消息** — 始终保护(用户意图必须存活)。
|
|
198
211
|
|
|
212
|
+
## 会话存储与迁移
|
|
213
|
+
|
|
214
|
+
billion-context-pi 把每个会话的压缩状态持久化在会话转录文件旁边的一个**旁挂(sidecar)文件**里。每个会话在 Pi 的会话目录(`~/.pi/agent/sessions/`)下都有两个文件:
|
|
215
|
+
|
|
216
|
+
| 文件 | 内容 |
|
|
217
|
+
|------|------|
|
|
218
|
+
| `<id>.jsonl` | 会话转录(消息、工具调用) |
|
|
219
|
+
| `<id>.jsonl.acp.json` | ACP 压缩状态(压缩块、消息引用、nudge 与统计) |
|
|
220
|
+
|
|
221
|
+
`.acp.json` 旁挂文件承载了你的压缩块。没有它,会话就会以完整原始历史运行,直到 ACP 再次压缩。
|
|
222
|
+
|
|
223
|
+
### 迁移会话(跨机器拷贝 / 备份恢复)
|
|
224
|
+
|
|
225
|
+
Pi 内置的导出/导入只搬运**转录**,不搬 ACP 状态。会丢失两样东西:
|
|
226
|
+
|
|
227
|
+
1. **`.acp.json` 旁挂文件不会被携带。** 因此导入后的会话*没有任何*压缩块:每次 LLM 调用都会重发完整原始历史,直到 nudge 重新压缩 —— 一次性全量重缓存成本 + 上下文膨胀回原始大小。超长会话可能在重新压缩生效前逼近甚至超出模型窗口(本插件激活时 Pi 的原生 compaction 被禁用,ACP 是唯一的上下文管理者)。
|
|
228
|
+
2. **导出会丢弃 `parentSession` 头。** clone/fork 子会话依赖这个头字段来继承父会话的压缩状态;一旦导出,这条链接就断了 —— 即使目标机器上父会话的文件仍然存在。
|
|
229
|
+
|
|
230
|
+
**要带着完整压缩状态迁移会话,请把两个文件一起拷贝**(它们共享同一基础名):
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# 成对拷贝
|
|
234
|
+
cp <id>.jsonl <id>.jsonl.acp.json <目标目录>/
|
|
235
|
+
# ... 或整体备份 / 恢复整个目录
|
|
236
|
+
cp -r ~/.pi/agent/sessions <备份>/pi-sessions
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
在目标机器上把它们放回彼此相邻的位置。对 clone/fork 子会话,还要带上父会话的那一对,以便 `parentSession` 能解析。
|
|
240
|
+
|
|
241
|
+
> 根治方案 —— 让宿主在 import/export 时一并携带旁挂文件并保留 `parentSession` —— 属于上游 pi-coding-agent,已在 issue [#299](https://github.com/ranxianglei/billion-context-pi/issues/299) 跟踪。落地前请手动成对拷贝。
|
|
242
|
+
|
|
199
243
|
## 基于 acp-kernel
|
|
200
244
|
|
|
201
245
|
压缩引擎是 [`acp-kernel`](https://github.com/ranxianglei/acp-kernel) — 平台无关、MIT 许可的库,有 208 个测试。它被内联打包进 `dist/index.js`,因此零运行时依赖。
|
package/dist/config.d.ts
CHANGED
|
@@ -12,7 +12,46 @@ export interface DelegateConfig {
|
|
|
12
12
|
* "merged" — delegate token usage folded into the tool-result usage field,
|
|
13
13
|
* counted as part of the main session totals. */
|
|
14
14
|
displayUsage?: "merged" | "separate";
|
|
15
|
+
/** Maximum acp_delegate nesting depth. Default: 2 (main → child → grandchild;
|
|
16
|
+
* the grandchild cannot delegate further). Set 1 to forbid nested delegation
|
|
17
|
+
* (orchestrator → leaf workers only). The resolved value is propagated to
|
|
18
|
+
* child processes via PI_ACP_DELEGATE_MAX_DEPTH so the cap follows the whole
|
|
19
|
+
* delegation tree, even when a child loads a different project acp.json. */
|
|
20
|
+
maxDepth?: number;
|
|
21
|
+
/** Hard timeout for synchronous delegates (async=false, or async auto-downgraded
|
|
22
|
+
* on one-shot hosts), in minutes. Default: 5. 0 or null disables the timeout
|
|
23
|
+
* (the run blocks until the child exits or the tool call is cancelled). */
|
|
24
|
+
syncTimeoutMinutes?: number | null;
|
|
25
|
+
/** Idle watchdog for async delegates: kill when no output arrives for this many
|
|
26
|
+
* minutes. Default: 5. This is the main defense against a stuck child holding
|
|
27
|
+
* its stdout fd open, so disabling it (0/null) logs a warning — use
|
|
28
|
+
* acp_delegate_cancel as the manual escape hatch. */
|
|
29
|
+
idleTimeoutMinutes?: number | null;
|
|
30
|
+
/** Hard time limit for async delegates, in minutes. Default: 30. 0 or null
|
|
31
|
+
* disables the limit. */
|
|
32
|
+
asyncTimeoutMinutes?: number | null;
|
|
33
|
+
/** Cap on how many background (async) delegate processes run at once.
|
|
34
|
+
* `1` forces strict serial execution; `N` allows up to N in parallel;
|
|
35
|
+
* omitted means unlimited (existing behavior). Extra launches are queued and
|
|
36
|
+
* start automatically as slots free. Invalid values (non-integer, <1) fall
|
|
37
|
+
* back to unlimited with a warning. Env `PI_ACP_DELEGATE_MAX_CONCURRENT`
|
|
38
|
+
* overrides this. See #294. */
|
|
39
|
+
maxConcurrent?: number;
|
|
15
40
|
}
|
|
41
|
+
/** Resolved delegate policy: what actually takes effect after merging acp.json,
|
|
42
|
+
* env overrides and defaults. Timeout fields are milliseconds; null means the
|
|
43
|
+
* corresponding timeout/watchdog is disabled. */
|
|
44
|
+
export interface DelegatePolicy {
|
|
45
|
+
enabled: boolean;
|
|
46
|
+
displayUsage: "merged" | "separate";
|
|
47
|
+
maxDepth: number;
|
|
48
|
+
syncTimeoutMs: number | null;
|
|
49
|
+
idleMs: number | null;
|
|
50
|
+
asyncTimeoutMs: number | null;
|
|
51
|
+
/** Resolved cap on concurrent background delegates; Infinity = unlimited. */
|
|
52
|
+
maxConcurrent: number;
|
|
53
|
+
}
|
|
54
|
+
export declare const DEFAULT_DELEGATE_POLICY: DelegatePolicy;
|
|
16
55
|
/** Compression tuning fields, shared by all three levels (global, provider,
|
|
17
56
|
* model). Percentage fields accept a ratio (0.75) or percent string ("75%").
|
|
18
57
|
* Resolution is per-field, deepest-wins (model > provider > global); an
|
|
@@ -117,11 +156,10 @@ export interface AdapterConfig {
|
|
|
117
156
|
export declare const DEFAULT_TOOL_BASH_TIMEOUT = 60;
|
|
118
157
|
export declare const DEFAULT_TOOL_OUTPUT_MAX_BYTES = 200000;
|
|
119
158
|
/** Resolve delegate config from the adapter, handling the boolean shorthand
|
|
120
|
-
* and the legacy flat `displayUsage` alias.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
};
|
|
159
|
+
* and the legacy flat `displayUsage` alias. Precedence: env > acp.json >
|
|
160
|
+
* default (same convention as ACP_MODEL_CONTEXT_LIMIT). Invalid values fall
|
|
161
|
+
* back to the default with a logged warning — they never fail the session. */
|
|
162
|
+
export declare function resolveDelegate(adapter: AdapterConfig): DelegatePolicy;
|
|
125
163
|
/** Per-field deepest-wins merge of the three compression levels (global →
|
|
126
164
|
* provider → model). An undefined field at a deeper level does NOT clear a
|
|
127
165
|
* value set at a shallower level — only a defined value overrides. */
|
package/dist/delegate-tool.d.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { type ChildProcess, type SpawnOptions } from "node:child_process";
|
|
2
2
|
import { Type, type Static } from "typebox";
|
|
3
3
|
import type { AgentToolResult, ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { type DelegatePolicy } from "./config.js";
|
|
4
5
|
import { type Usage } from "./delegate-events.js";
|
|
5
6
|
export declare function delegateStdinText(resumeFrom: boolean, task: string | undefined): string;
|
|
6
7
|
export declare function delegateSpawnOptions(cwd: string, env: NodeJS.ProcessEnv): SpawnOptions;
|
|
8
|
+
/** Child process env for a nested delegate: depth increments by one, and the
|
|
9
|
+
* resolved maxDepth rides along so the cap binds the whole delegation tree
|
|
10
|
+
* even when the child loads a different project acp.json. */
|
|
11
|
+
export declare function delegateChildEnv(parentDepth: number, maxDepth: number): NodeJS.ProcessEnv;
|
|
7
12
|
/** Resolve the pi CLI entry for delegate child processes.
|
|
8
13
|
* argv[1] is only the pi CLI under a CLI host; embedded hosts (e.g. pi-web)
|
|
9
14
|
* run the SDK inside another node process, so probe instead. Non-pi hosts
|
|
10
15
|
* (omp) keep argv[1] untouched. */
|
|
11
16
|
export declare function resolvePiCliEntry(argv1: string, env?: NodeJS.ProcessEnv, piHost?: boolean): string;
|
|
12
|
-
export type RunStatus = "running" | "completed" | "failed" | "cancelled";
|
|
17
|
+
export type RunStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
13
18
|
interface DelegateRun {
|
|
14
19
|
runId: string;
|
|
15
20
|
agent: string;
|
|
@@ -54,6 +59,24 @@ export declare function addDelegateUsage(u: Usage): void;
|
|
|
54
59
|
export declare function getDelegateUsage(): Usage | undefined;
|
|
55
60
|
export declare function resetDelegateUsage(): void;
|
|
56
61
|
export declare function setDelegateDisplayUsage(mode: "merged" | "separate"): void;
|
|
62
|
+
export declare function setDelegatePolicy(policy: DelegatePolicy): void;
|
|
63
|
+
export declare class ConcurrencyGate {
|
|
64
|
+
private active;
|
|
65
|
+
private queue;
|
|
66
|
+
private readonly capacityOf;
|
|
67
|
+
constructor(capacityOf: () => number);
|
|
68
|
+
private get capacity();
|
|
69
|
+
get unlimited(): boolean;
|
|
70
|
+
get activeCount(): number;
|
|
71
|
+
get queuedCount(): number;
|
|
72
|
+
/** Start `launch` immediately when a slot is free and nothing waits ahead;
|
|
73
|
+
* otherwise hold it in the FIFO queue. True when it started right away. */
|
|
74
|
+
launchOrQueue(runId: string, launch: () => void): boolean;
|
|
75
|
+
cancelQueued(runId: string): boolean;
|
|
76
|
+
/** A live child hit a terminal state: free its slot, then start waiting
|
|
77
|
+
* launches FIFO (skipping any cancelled while queued). */
|
|
78
|
+
release(): void;
|
|
79
|
+
}
|
|
57
80
|
/** Snapshot of currently-running delegate runs, for the TUI status widget. */
|
|
58
81
|
export declare function runningRunsSnapshot(): {
|
|
59
82
|
runId: string;
|
|
@@ -196,6 +219,10 @@ export declare function buildChildArgs(args: DelegateArgs, rolePrompt: string, c
|
|
|
196
219
|
useJsonStream: boolean;
|
|
197
220
|
sessionFile: string | null;
|
|
198
221
|
}>;
|
|
222
|
+
/** Model-facing summary of the ACTIVE async watchdog limits — built from the
|
|
223
|
+
* resolved policy so it stays truthful when timeouts are customized or
|
|
224
|
+
* disabled via acp.json/env. */
|
|
225
|
+
export declare function asyncWatchdogDescription(): string;
|
|
199
226
|
/** Watchdog/EOF finalize arrives with code === null (the child was killed or
|
|
200
227
|
* never exited). If a result was delivered (non-empty reply or stderr), the
|
|
201
228
|
* run counts as completed (0); otherwise it stays null = genuine failure. */
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Readable } from "node:stream";
|
|
2
2
|
export interface WatchdogOptions {
|
|
3
3
|
eofGraceMs: number;
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
/** Kill when no output for this long. 0/null disables the idle timer. */
|
|
5
|
+
idleMs?: number | null;
|
|
6
|
+
/** Hard time limit. 0/null disables the limit timer. */
|
|
7
|
+
timeoutMs?: number | null;
|
|
6
8
|
killGraceMs: number;
|
|
7
9
|
}
|
|
8
10
|
export interface WatchdogHooks {
|