dsh-loop-engine 0.1.7-rc4 → 0.1.7-rc5
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 +2 -1
- package/README.zh.md +1 -1
- package/lib/index.js +89 -10
- package/lib/types/driver-core/idle-child.d.ts +57 -0
- package/lib/types/engine-codex/agent.d.ts +8 -0
- package/lib/types/engine-codex/loop.d.ts +6 -0
- package/lib/types/engine-codex/types.d.ts +7 -0
- package/lib/types/engine-kimi/agent.d.ts +8 -0
- package/lib/types/engine-kimi/loop.d.ts +6 -0
- package/lib/types/engine-kimi/types.d.ts +7 -0
- package/lib/types/index.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ A **published** dsh needs no extra setup. Booting the harness from its **source
|
|
|
22
22
|
|
|
23
23
|
## Version compatibility
|
|
24
24
|
|
|
25
|
-
dsh-loop-engine is versioned **in lockstep with the harness it targets**: `<harness version>-rcN`. From `0.1.7-rc1` on, **one release serves two harness generations**: the 0.1.5 line (`>=0.1.5-rc.1 <0.1.6-0`, all sharing the old settings API) and the 0.1.7 line (`>=0.1.7-rc.1 <0.1.8-0`, whose `rc.1` and `rc.2` are both verified). It detects the running generation at load and takes the matching code path, so a single published package installs onto either. Every harness package it consumes declares that union range in `peerDependencies`; a harness outside it fails loudly at boot or session resume. Within a covered line a new `rc` needs no plugin release unless the API surfaces it depends on moved — `docs/compatibility.md` §1 lists the surfaces and the diff command that decides it. `0.1.5-rc3` … `0.1.5-rc5` target harness `0.1.5-rc.2`; `0.1.5-rc1`/`0.1.5-rc2` target `0.1.5-rc.1`; `1.0.0-rc8` … `1.0.0-rc15` target `0.1.2-rc.1`; `1.0.0-rc7` and earlier target `0.1.1-rc.2`.
|
|
25
|
+
dsh-loop-engine is versioned **in lockstep with the harness it targets**: `<harness version>-rcN`. From `0.1.7-rc1` on, **one release serves two harness generations**: the 0.1.5 line (`>=0.1.5-rc.1 <0.1.6-0`, all sharing the old settings API) and the 0.1.7 line (`>=0.1.7-rc.1 <0.1.8-0`, whose `rc.1` and `rc.2` are both verified). It detects the running generation at load and takes the matching code path, so a single published package installs onto either. Every harness package it consumes declares that union range in `peerDependencies`; a harness outside it fails loudly at boot or session resume. Within a covered line a new `rc` needs no plugin release unless the API surfaces it depends on moved — `docs/compatibility.md` §1 lists the surfaces and the diff command that decides it. Read `rcN` as **this plugin's own release counter for that harness line**, not as the harness's `rc` number (`0.1.7-rc1` … `0.1.7-rc5` all serve `0.1.7-rc.1` and `0.1.7-rc.2`). `0.1.7-rc1` … `0.1.7-rc5` target harness `0.1.7-rc.2`; `0.1.5-rc3` … `0.1.5-rc5` target harness `0.1.5-rc.2`; `0.1.5-rc1`/`0.1.5-rc2` target `0.1.5-rc.1`; `1.0.0-rc8` … `1.0.0-rc15` target `0.1.2-rc.1`; `1.0.0-rc7` and earlier target `0.1.1-rc.2`.
|
|
26
26
|
|
|
27
27
|
### Requirements
|
|
28
28
|
|
|
@@ -48,6 +48,7 @@ dsh-loop-engine is versioned **in lockstep with the harness it targets**: `<harn
|
|
|
48
48
|
|
|
49
49
|
- Its preset is a copy of `standard` minus the dsh-native rows an external engine replaces — dsh's `/plan`, `/compact` (and auto-compaction), the model-facing goal tool, the human `/goal` command, and the dsh skill rows (one stripped preset per engine). The preset is a harness-generation-dependent artifact: a directory under `$DSH_HOME/.agent-presets/loop-engine-<engine>/` on the 0.1.5 line, and an `@deepseek-ai/dsh-agent-preset` row inserted into the profile patch on the 0.1.7 line, which no longer reads that directory. See [docs/architecture.md](docs/architecture.md) §3.5.
|
|
50
50
|
- The engine's own slash commands and skill catalog are registered into **that agent's own scope**, so two sessions on different engines never see each other's menus, and the whole surface is released with the agent.
|
|
51
|
+
- **Kimi Code and Codex keep ONE child process per session** (`kimi acp` / `codex app-server`), reused across steps — that is what saves a spawn per step, and it also means a session that finished a turn and is left alone keeps paying for that process (the harness does not release an agent at turn end). Claude Code and Pi spawn per step and hold nothing. Set `childIdleMs` (milliseconds) in the composition entry — the plugin's row in the profile's `cordis.patch.yml` — to shut an idle session's child down, respawning it on the next step; `0` or omitted keeps the child for the session's whole life, which is the default. It closes the child only: the agent, the session, and the page are untouched, so nothing reloads and `session/disposed` is never emitted. See [docs/driver-core.md](docs/driver-core.md) §7.8.
|
|
51
52
|
- Engine-agnostic dsh commands (`/export`, `/feedback`, `/permission`) keep working and stay.
|
|
52
53
|
|
|
53
54
|
## Known limitations
|
package/README.zh.md
CHANGED
|
@@ -22,7 +22,7 @@ dsh plugin --profile web add dsh-loop-engine
|
|
|
22
22
|
|
|
23
23
|
## 版本兼容
|
|
24
24
|
|
|
25
|
-
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:`<harness version>-rcN`。自 `0.1.7-rc1` 起,**一个发布版本同时服务两代 harness**:0.1.5 线(`>=0.1.5-rc.1 <0.1.6-0`,三段 `rc` 共用同一套旧 settings API)与 0.1.7 线(`>=0.1.7-rc.1 <0.1.8-0`,其中 `rc.1` 与 `rc.2` 都已核对)。它在加载期探测当前是哪一代、走对应分支,所以同一份已发布产物在两代上都能安装运行。它消费的每个 harness 包都在 `peerDependencies` 里声明这个并集范围;超出范围会在启动或会话恢复时响亮地失败。在被覆盖的那条线里,新的 `rc` **不需要**插件跟版——除非它依赖的 API 面动了;`docs/compatibility.md` §1 列出了这些面与"要不要改代码"的那条判定命令。`0.1.5-rc3` … `0.1.5-rc5` 针对 harness `0.1.5-rc.2`;`0.1.5-rc1`/`0.1.5-rc2` 针对 `0.1.5-rc.1`;`1.0.0-rc8` … `1.0.0-rc15` 针对 `0.1.2-rc.1`;`1.0.0-rc7` 及更早针对 `0.1.1-rc.2`。
|
|
25
|
+
`dsh-loop-engine` 与它针对的 harness **同版本对齐**:`<harness version>-rcN`。自 `0.1.7-rc1` 起,**一个发布版本同时服务两代 harness**:0.1.5 线(`>=0.1.5-rc.1 <0.1.6-0`,三段 `rc` 共用同一套旧 settings API)与 0.1.7 线(`>=0.1.7-rc.1 <0.1.8-0`,其中 `rc.1` 与 `rc.2` 都已核对)。它在加载期探测当前是哪一代、走对应分支,所以同一份已发布产物在两代上都能安装运行。它消费的每个 harness 包都在 `peerDependencies` 里声明这个并集范围;超出范围会在启动或会话恢复时响亮地失败。在被覆盖的那条线里,新的 `rc` **不需要**插件跟版——除非它依赖的 API 面动了;`docs/compatibility.md` §1 列出了这些面与"要不要改代码"的那条判定命令。`rcN` 读作**本插件对那条 harness 线的第 N 次发布号**,不是 harness 自己的 `rc` 号(`0.1.7-rc1` … `0.1.7-rc5` 都服务 `0.1.7-rc.1` 与 `0.1.7-rc.2`)。`0.1.7-rc1` … `0.1.7-rc5` 针对 harness `0.1.7-rc.2`;`0.1.5-rc3` … `0.1.5-rc5` 针对 harness `0.1.5-rc.2`;`0.1.5-rc1`/`0.1.5-rc2` 针对 `0.1.5-rc.1`;`1.0.0-rc8` … `1.0.0-rc15` 针对 `0.1.2-rc.1`;`1.0.0-rc7` 及更早针对 `0.1.1-rc.2`。
|
|
26
26
|
|
|
27
27
|
### 环境要求
|
|
28
28
|
|
package/lib/index.js
CHANGED
|
@@ -2389,6 +2389,34 @@ import { LlmError as LlmError3, createAssistantMessage as createAssistantMessage
|
|
|
2389
2389
|
import { createScope as createScope2 } from "@deepseek-ai/dsh-scope";
|
|
2390
2390
|
import { canonicalHeader as canonicalHeader2 } from "@deepseek-ai/dsh-session";
|
|
2391
2391
|
|
|
2392
|
+
// src/driver-core/idle-child.ts
|
|
2393
|
+
function createIdleChildCloser(options) {
|
|
2394
|
+
const { idleMs, close, warn } = options;
|
|
2395
|
+
let timer;
|
|
2396
|
+
const cancel = () => {
|
|
2397
|
+
if (timer === void 0) return;
|
|
2398
|
+
clearTimeout(timer);
|
|
2399
|
+
timer = void 0;
|
|
2400
|
+
};
|
|
2401
|
+
return {
|
|
2402
|
+
arm: () => {
|
|
2403
|
+
if (!(idleMs > 0)) return;
|
|
2404
|
+
cancel();
|
|
2405
|
+
timer = setTimeout(() => {
|
|
2406
|
+
timer = void 0;
|
|
2407
|
+
try {
|
|
2408
|
+
close();
|
|
2409
|
+
} catch (error) {
|
|
2410
|
+
warn(`loop-engine: idle shutdown failed to close a child process: ${String(error)}`);
|
|
2411
|
+
}
|
|
2412
|
+
}, idleMs);
|
|
2413
|
+
timer.unref?.();
|
|
2414
|
+
},
|
|
2415
|
+
cancel,
|
|
2416
|
+
dispose: cancel
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
|
|
2392
2420
|
// src/engine-codex/model-handover.ts
|
|
2393
2421
|
var CODEX_DSH_PROVIDER = "dsh";
|
|
2394
2422
|
var CODEX_DSH_API_KEY_ENV = "DSH_LOOP_ENGINE_API_KEY";
|
|
@@ -2928,9 +2956,18 @@ var CodexAgent = class {
|
|
|
2928
2956
|
this.phase = { kind: "idle", lastTurn };
|
|
2929
2957
|
this.scope = createScope2(loopCtx, this);
|
|
2930
2958
|
this.ctx = this.scope.ctx.extend({ agent: this });
|
|
2959
|
+
this.idleChild = createIdleChildCloser({
|
|
2960
|
+
idleMs: this.config.childIdleMs,
|
|
2961
|
+
close: () => {
|
|
2962
|
+
this.closeChild();
|
|
2963
|
+
},
|
|
2964
|
+
warn: (message) => {
|
|
2965
|
+
loopCtx.logger.warn(message);
|
|
2966
|
+
}
|
|
2967
|
+
});
|
|
2931
2968
|
this.scope.ctx.effect(() => () => {
|
|
2932
|
-
this.
|
|
2933
|
-
this.
|
|
2969
|
+
this.idleChild.dispose();
|
|
2970
|
+
this.closeChild();
|
|
2934
2971
|
}, "codex.appServerClient()");
|
|
2935
2972
|
}
|
|
2936
2973
|
loopCtx;
|
|
@@ -2982,6 +3019,17 @@ var CodexAgent = class {
|
|
|
2982
3019
|
* must respawn the child rather than reuse one configured for another endpoint.
|
|
2983
3020
|
*/
|
|
2984
3021
|
appServerConfig;
|
|
3022
|
+
/**
|
|
3023
|
+
* Countdown that shuts the child down once this agent has been idle past the
|
|
3024
|
+
* configured window, so an idle session stops paying for a process nothing
|
|
3025
|
+
* is driving. Armed and cancelled by {@link setPhase}.
|
|
3026
|
+
*/
|
|
3027
|
+
idleChild;
|
|
3028
|
+
/** Shut the cached child down; the next step's {@link appServerClient} spawns a fresh one. */
|
|
3029
|
+
closeChild() {
|
|
3030
|
+
this.appServer?.dispose();
|
|
3031
|
+
this.appServer = void 0;
|
|
3032
|
+
}
|
|
2985
3033
|
/**
|
|
2986
3034
|
* Return the cached app-server client, spawning one on first use, after a dead
|
|
2987
3035
|
* process, or when the resolved dsh endpoint changed since the last spawn.
|
|
@@ -3075,6 +3123,8 @@ var CodexAgent = class {
|
|
|
3075
3123
|
setPhase(next) {
|
|
3076
3124
|
const previousStatus = this.status;
|
|
3077
3125
|
this.phase = next;
|
|
3126
|
+
if (next.kind === "idle") this.idleChild.arm();
|
|
3127
|
+
else this.idleChild.cancel();
|
|
3078
3128
|
const status = this.status;
|
|
3079
3129
|
if (status !== previousStatus) {
|
|
3080
3130
|
this.dispatch.emit("agent/status", { status });
|
|
@@ -3610,14 +3660,16 @@ var Config2 = z2.object({
|
|
|
3610
3660
|
sandboxMode: z2.union([...CODEX_SANDBOX_MODES]),
|
|
3611
3661
|
approvalPolicy: z2.union([...CODEX_APPROVAL_POLICIES]),
|
|
3612
3662
|
env: z2.dict(z2.string()).default({}),
|
|
3613
|
-
model: z2.string()
|
|
3663
|
+
model: z2.string(),
|
|
3664
|
+
childIdleMs: z2.number()
|
|
3614
3665
|
});
|
|
3615
3666
|
function resolveConfig2(config) {
|
|
3616
3667
|
return {
|
|
3617
3668
|
sandboxMode: config.sandboxMode,
|
|
3618
3669
|
approvalPolicy: config.approvalPolicy,
|
|
3619
3670
|
env: config.env ?? {},
|
|
3620
|
-
model: config.model
|
|
3671
|
+
model: config.model,
|
|
3672
|
+
childIdleMs: config.childIdleMs ?? 0
|
|
3621
3673
|
};
|
|
3622
3674
|
}
|
|
3623
3675
|
var CODEX_ENGINE_LABEL = "agentLoopCodex";
|
|
@@ -5122,9 +5174,18 @@ var KimiAgent = class {
|
|
|
5122
5174
|
this.phase = { kind: "idle", lastTurn };
|
|
5123
5175
|
this.scope = createScope4(loopCtx, this);
|
|
5124
5176
|
this.ctx = this.scope.ctx.extend({ agent: this });
|
|
5177
|
+
this.idleChild = createIdleChildCloser({
|
|
5178
|
+
idleMs: this.config.childIdleMs,
|
|
5179
|
+
close: () => {
|
|
5180
|
+
this.closeChild();
|
|
5181
|
+
},
|
|
5182
|
+
warn: (message) => {
|
|
5183
|
+
loopCtx.logger.warn(message);
|
|
5184
|
+
}
|
|
5185
|
+
});
|
|
5125
5186
|
this.scope.ctx.effect(() => () => {
|
|
5126
|
-
this.
|
|
5127
|
-
this.
|
|
5187
|
+
this.idleChild.dispose();
|
|
5188
|
+
this.closeChild();
|
|
5128
5189
|
}, "kimi.acpClient()");
|
|
5129
5190
|
}
|
|
5130
5191
|
loopCtx;
|
|
@@ -5157,6 +5218,17 @@ var KimiAgent = class {
|
|
|
5157
5218
|
* every step.
|
|
5158
5219
|
*/
|
|
5159
5220
|
handoverEnvCache;
|
|
5221
|
+
/**
|
|
5222
|
+
* Countdown that shuts the child down once this agent has been idle past the
|
|
5223
|
+
* configured window, so an idle session stops paying for a process nothing
|
|
5224
|
+
* is driving. Armed and cancelled by {@link setPhase}.
|
|
5225
|
+
*/
|
|
5226
|
+
idleChild;
|
|
5227
|
+
/** Shut the cached child down; the next step's {@link acpClient} spawns a fresh one. */
|
|
5228
|
+
closeChild() {
|
|
5229
|
+
this.acp?.dispose();
|
|
5230
|
+
this.acp = void 0;
|
|
5231
|
+
}
|
|
5160
5232
|
get status() {
|
|
5161
5233
|
return this.phase.kind === "idle" || this.phase.kind === "maintenance" ? "idle" : "running";
|
|
5162
5234
|
}
|
|
@@ -5164,6 +5236,8 @@ var KimiAgent = class {
|
|
|
5164
5236
|
setPhase(next) {
|
|
5165
5237
|
const previousStatus = this.status;
|
|
5166
5238
|
this.phase = next;
|
|
5239
|
+
if (next.kind === "idle") this.idleChild.arm();
|
|
5240
|
+
else this.idleChild.cancel();
|
|
5167
5241
|
const status = this.status;
|
|
5168
5242
|
if (status !== previousStatus) {
|
|
5169
5243
|
this.dispatch.emit("agent/status", { status });
|
|
@@ -5732,13 +5806,15 @@ var KIMI_DISPOSE_GRACE_MS = 3e3;
|
|
|
5732
5806
|
var Config4 = z4.object({
|
|
5733
5807
|
model: z4.string(),
|
|
5734
5808
|
env: z4.dict(z4.string()).default({}),
|
|
5735
|
-
bin: z4.string()
|
|
5809
|
+
bin: z4.string(),
|
|
5810
|
+
childIdleMs: z4.number()
|
|
5736
5811
|
});
|
|
5737
5812
|
function resolveConfig4(config) {
|
|
5738
5813
|
return {
|
|
5739
5814
|
model: config.model,
|
|
5740
5815
|
env: config.env ?? {},
|
|
5741
|
-
bin: kimiBinResolver(config.bin)
|
|
5816
|
+
bin: kimiBinResolver(config.bin),
|
|
5817
|
+
childIdleMs: config.childIdleMs ?? 0
|
|
5742
5818
|
};
|
|
5743
5819
|
}
|
|
5744
5820
|
var KIMI_ENGINE_LABEL = "agentLoopKimi";
|
|
@@ -7498,6 +7574,7 @@ var Config5 = z6.object({
|
|
|
7498
7574
|
piProvider: z6.string(),
|
|
7499
7575
|
piThinking: z6.string(),
|
|
7500
7576
|
kimiBin: z6.string(),
|
|
7577
|
+
childIdleMs: z6.number(),
|
|
7501
7578
|
// The live fields exist only on the 0.1.7 line; the 0.1.5 line carries the
|
|
7502
7579
|
// selection in a settings section instead. The empty arm never runs when the
|
|
7503
7580
|
// coverage job is on 0.1.7; the 0.1.5 dep set is exercised by
|
|
@@ -7568,7 +7645,8 @@ function codexConfig(config) {
|
|
|
7568
7645
|
...config.sandboxMode === void 0 ? {} : { sandboxMode: config.sandboxMode },
|
|
7569
7646
|
...config.approvalPolicy === void 0 ? {} : { approvalPolicy: config.approvalPolicy },
|
|
7570
7647
|
...config.env === void 0 ? {} : { env: config.env },
|
|
7571
|
-
...config.model === void 0 ? {} : { model: config.model }
|
|
7648
|
+
...config.model === void 0 ? {} : { model: config.model },
|
|
7649
|
+
...config.childIdleMs === void 0 ? {} : { childIdleMs: config.childIdleMs }
|
|
7572
7650
|
};
|
|
7573
7651
|
}
|
|
7574
7652
|
function piConfig(config) {
|
|
@@ -7584,7 +7662,8 @@ function kimiConfig(config) {
|
|
|
7584
7662
|
return {
|
|
7585
7663
|
...config.model === void 0 ? {} : { model: config.model },
|
|
7586
7664
|
...config.env === void 0 ? {} : { env: config.env },
|
|
7587
|
-
...config.kimiBin === void 0 ? {} : { bin: config.kimiBin }
|
|
7665
|
+
...config.kimiBin === void 0 ? {} : { bin: config.kimiBin },
|
|
7666
|
+
...config.childIdleMs === void 0 ? {} : { childIdleMs: config.childIdleMs }
|
|
7588
7667
|
};
|
|
7589
7668
|
}
|
|
7590
7669
|
function apply(ctx, config) {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idle shutdown for a driver's persistent child process.
|
|
3
|
+
*
|
|
4
|
+
* Two engines keep one child alive per session so a step never pays a spawn:
|
|
5
|
+
* Kimi's `kimi acp` and Codex's app-server. Nothing in the harness releases an
|
|
6
|
+
* agent when a turn ends — an agent lives as long as its session's scope — so
|
|
7
|
+
* that child outlives the work that needed it and an idle session keeps
|
|
8
|
+
* costing what its child costs (tens to hundreds of MB for a Node CLI). The
|
|
9
|
+
* closer arms a countdown whenever its owner goes idle and runs the owner's
|
|
10
|
+
* close when the countdown expires, so the next step's own lazy accessor
|
|
11
|
+
* spawns a fresh child.
|
|
12
|
+
*
|
|
13
|
+
* The closer is inert unless `idleMs` is positive: a deployment that does not
|
|
14
|
+
* configure idle shutdown keeps every child for the process's lifetime, which
|
|
15
|
+
* is the behaviour that predates this reasoner.
|
|
16
|
+
*
|
|
17
|
+
* This is deliberately NOT an agent release: disposing the agent emits
|
|
18
|
+
* `session/disposed`, which the browser half reads as "this session is gone"
|
|
19
|
+
* with no way back in that page's lifetime (`router-loop.ts` `move`). Closing
|
|
20
|
+
* the child leaves the agent, the session, and the page exactly where they are.
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-loop-engine/driver-core/idle-child
|
|
23
|
+
*/
|
|
24
|
+
/** An armed-on-idle countdown that closes one child process. */
|
|
25
|
+
export interface IdleChildCloser {
|
|
26
|
+
/**
|
|
27
|
+
* (Re)start the countdown. Called when the owner settles into an idle phase;
|
|
28
|
+
* a no-op while idle shutdown is disabled.
|
|
29
|
+
*/
|
|
30
|
+
arm(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Cancel a pending countdown, for the moment the child is needed again.
|
|
33
|
+
* Cancelling a countdown that never armed is a no-op.
|
|
34
|
+
*/
|
|
35
|
+
cancel(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Cancel the countdown and drop the timer. Called from the owner's teardown,
|
|
38
|
+
* so a disposed agent never closes a child through a stale callback.
|
|
39
|
+
*/
|
|
40
|
+
dispose(): void;
|
|
41
|
+
}
|
|
42
|
+
/** Input of {@link createIdleChildCloser}. */
|
|
43
|
+
export interface IdleChildCloserOptions {
|
|
44
|
+
/** Idle window in milliseconds; zero or less leaves the child alone forever. */
|
|
45
|
+
readonly idleMs: number;
|
|
46
|
+
/** Close the child; the owner's next accessor call spawns a fresh one. */
|
|
47
|
+
readonly close: () => void;
|
|
48
|
+
/** Report a failing close without letting it escape the countdown. */
|
|
49
|
+
readonly warn: (message: string) => void;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build one owner's idle countdown.
|
|
53
|
+
* @param options - the idle window, the close to run, and a diagnostic sink.
|
|
54
|
+
* @returns the closer, inert while the idle window is not positive.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createIdleChildCloser(options: IdleChildCloserOptions): IdleChildCloser;
|
|
57
|
+
//# sourceMappingURL=idle-child.d.ts.map
|
|
@@ -70,7 +70,15 @@ export declare class CodexAgent implements Agent {
|
|
|
70
70
|
* must respawn the child rather than reuse one configured for another endpoint.
|
|
71
71
|
*/
|
|
72
72
|
private appServerConfig;
|
|
73
|
+
/**
|
|
74
|
+
* Countdown that shuts the child down once this agent has been idle past the
|
|
75
|
+
* configured window, so an idle session stops paying for a process nothing
|
|
76
|
+
* is driving. Armed and cancelled by {@link setPhase}.
|
|
77
|
+
*/
|
|
78
|
+
private readonly idleChild;
|
|
73
79
|
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig);
|
|
80
|
+
/** Shut the cached child down; the next step's {@link appServerClient} spawns a fresh one. */
|
|
81
|
+
private closeChild;
|
|
74
82
|
/**
|
|
75
83
|
* Return the cached app-server client, spawning one on first use, after a dead
|
|
76
84
|
* process, or when the resolved dsh endpoint changed since the last spawn.
|
|
@@ -40,6 +40,12 @@ export interface Config {
|
|
|
40
40
|
env?: Record<string, string>;
|
|
41
41
|
/** Fallback model for the app-server thread, used when the session selects none; Codex native settings own the model when omitted. */
|
|
42
42
|
model?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Idle window in milliseconds before a session's `codex app-server` child is
|
|
45
|
+
* shut down, spawned again by the next step. Omitted or non-positive keeps
|
|
46
|
+
* the child for the session's whole life.
|
|
47
|
+
*/
|
|
48
|
+
childIdleMs?: number;
|
|
43
49
|
}
|
|
44
50
|
/** Schema of the Codex loop plugin configuration. */
|
|
45
51
|
export declare const Config: z<Config>;
|
|
@@ -15,5 +15,12 @@ export interface ResolvedConfig {
|
|
|
15
15
|
readonly approvalPolicy: CodexApprovalPolicy | undefined;
|
|
16
16
|
readonly env: Record<string, string>;
|
|
17
17
|
readonly model: string | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* How long an idle session keeps its `codex app-server` child, in
|
|
20
|
+
* milliseconds; zero or less keeps it for the session's whole life. See
|
|
21
|
+
* `driver-core/idle-child.ts` for why the child, and not the agent, is what
|
|
22
|
+
* gets released.
|
|
23
|
+
*/
|
|
24
|
+
readonly childIdleMs: number;
|
|
18
25
|
}
|
|
19
26
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -56,7 +56,15 @@ export declare class KimiAgent implements Agent {
|
|
|
56
56
|
* every step.
|
|
57
57
|
*/
|
|
58
58
|
private handoverEnvCache;
|
|
59
|
+
/**
|
|
60
|
+
* Countdown that shuts the child down once this agent has been idle past the
|
|
61
|
+
* configured window, so an idle session stops paying for a process nothing
|
|
62
|
+
* is driving. Armed and cancelled by {@link setPhase}.
|
|
63
|
+
*/
|
|
64
|
+
private readonly idleChild;
|
|
59
65
|
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig, spawn: KimiSpawnCapability, bin: string);
|
|
66
|
+
/** Shut the cached child down; the next step's {@link acpClient} spawns a fresh one. */
|
|
67
|
+
private closeChild;
|
|
60
68
|
get status(): AgentStatus;
|
|
61
69
|
/** Commit a phase and publish its externally visible status transition. */
|
|
62
70
|
private setPhase;
|
|
@@ -32,6 +32,12 @@ export interface Config {
|
|
|
32
32
|
env?: Record<string, string>;
|
|
33
33
|
/** Kimi CLI executable; `'kimi'` resolves through PATH when not pinned to an absolute path. */
|
|
34
34
|
bin?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Idle window in milliseconds before a session's `kimi acp` child is shut
|
|
37
|
+
* down, spawned again by the next step. Omitted or non-positive keeps the
|
|
38
|
+
* child for the session's whole life.
|
|
39
|
+
*/
|
|
40
|
+
childIdleMs?: number;
|
|
35
41
|
}
|
|
36
42
|
/** Schema of the Kimi loop plugin configuration. */
|
|
37
43
|
export declare const Config: z<Config>;
|
|
@@ -19,5 +19,12 @@ export interface ResolvedConfig {
|
|
|
19
19
|
readonly env: Record<string, string>;
|
|
20
20
|
/** Kimi CLI executable; `'kimi'` resolves through PATH when not pinned to an absolute path. */
|
|
21
21
|
readonly bin: string;
|
|
22
|
+
/**
|
|
23
|
+
* How long an idle session keeps its `kimi acp` child, in milliseconds; zero
|
|
24
|
+
* or less keeps it for the session's whole life. See
|
|
25
|
+
* `driver-core/idle-child.ts` for why the child, and not the agent, is what
|
|
26
|
+
* gets released.
|
|
27
|
+
*/
|
|
28
|
+
readonly childIdleMs: number;
|
|
22
29
|
}
|
|
23
30
|
//# sourceMappingURL=types.d.ts.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -74,6 +74,14 @@ export interface Config extends ClaudeCodeConfig {
|
|
|
74
74
|
piThinking?: string;
|
|
75
75
|
/** Kimi CLI executable; `'kimi'` resolves through PATH when not pinned to an absolute path. */
|
|
76
76
|
kimiBin?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Idle window in milliseconds after which a hosted session's PERSISTENT child
|
|
79
|
+
* process (Kimi's `kimi acp`, Codex's app-server) is shut down and spawned
|
|
80
|
+
* again by the next step. Zero or omitted keeps the child for the session's
|
|
81
|
+
* whole life. Only these two engines hold a child between steps: the Pi and
|
|
82
|
+
* Claude Code drivers spawn per step, so the knob does not reach them.
|
|
83
|
+
*/
|
|
84
|
+
childIdleMs?: number;
|
|
77
85
|
/**
|
|
78
86
|
* Default engine NEW sessions are created on. A live, profile-backed field:
|
|
79
87
|
* the settings shell edits it and the running plugin reads it through this
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-loop-engine",
|
|
3
3
|
"description": "Web-switchable agent loop engine selection for the DeepSeek Harness - out-of-tree plugin (Claude Code / Codex / Pi / Kimi Code drivers) maintained by @kuun993, zero main-repo changes",
|
|
4
|
-
"version": "0.1.7-
|
|
4
|
+
"version": "0.1.7-rc5",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|