dsh-loop-engine 1.0.0-rc14 → 1.0.0-rc15
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/lib/index.js +55 -14
- package/lib/types/engine-pi/agent.d.ts +14 -1
- package/lib/types/engine-pi/loop.d.ts +2 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2980,7 +2980,7 @@ function specsEqual(a, b) {
|
|
|
2980
2980
|
return a.cwd === b.cwd && a.env === b.env && a.argv.length === b.argv.length && a.argv.every((value, index) => value === b.argv[index]);
|
|
2981
2981
|
}
|
|
2982
2982
|
var PiAgent = class {
|
|
2983
|
-
constructor(loopCtx, id, options, session, config, spawn4, bin) {
|
|
2983
|
+
constructor(loopCtx, id, options, session, config, spawn4, bin, catalog) {
|
|
2984
2984
|
this.loopCtx = loopCtx;
|
|
2985
2985
|
this.id = id;
|
|
2986
2986
|
this.options = options;
|
|
@@ -2988,6 +2988,7 @@ var PiAgent = class {
|
|
|
2988
2988
|
this.config = config;
|
|
2989
2989
|
this.spawn = spawn4;
|
|
2990
2990
|
this.bin = bin;
|
|
2991
|
+
this.catalog = catalog;
|
|
2991
2992
|
this.dispatch = agentEvents3(loopCtx, this);
|
|
2992
2993
|
this.inbox = new Inbox3(session, {
|
|
2993
2994
|
inserted: (message) => {
|
|
@@ -3016,6 +3017,7 @@ var PiAgent = class {
|
|
|
3016
3017
|
config;
|
|
3017
3018
|
spawn;
|
|
3018
3019
|
bin;
|
|
3020
|
+
catalog;
|
|
3019
3021
|
inbox;
|
|
3020
3022
|
phase;
|
|
3021
3023
|
activityDone = Promise.resolve();
|
|
@@ -3337,9 +3339,28 @@ var PiAgent = class {
|
|
|
3337
3339
|
return void 0;
|
|
3338
3340
|
}
|
|
3339
3341
|
/** Build the `pi --mode rpc` argv/cwd/env for one step's child process. */
|
|
3342
|
+
/**
|
|
3343
|
+
* Resolve the `--model` for the RPC child. The session-selected model (last
|
|
3344
|
+
* `model/selection` event) is honored only when it is one of pi's discovered
|
|
3345
|
+
* models; an unknown harness model (e.g. another provider's model such as
|
|
3346
|
+
* `anyai-v1`, which pi cannot serve) is dropped so the child falls back to pi's
|
|
3347
|
+
* own default instead of exiting with "Model ... not found". An empty catalog
|
|
3348
|
+
* (probe not concluded) keeps the candidate, matching prior behavior.
|
|
3349
|
+
*/
|
|
3350
|
+
pickModel() {
|
|
3351
|
+
const candidate = this.dynamicModel() ?? this.config.model;
|
|
3352
|
+
if (candidate === void 0) return void 0;
|
|
3353
|
+
if (this.catalog.entries.length > 0) {
|
|
3354
|
+
const known = this.catalog.entries.some(
|
|
3355
|
+
(entry) => entry.model === candidate || `${entry.provider}/${entry.model}` === candidate
|
|
3356
|
+
);
|
|
3357
|
+
if (!known) return void 0;
|
|
3358
|
+
}
|
|
3359
|
+
return candidate;
|
|
3360
|
+
}
|
|
3340
3361
|
spawnSpec(cwd) {
|
|
3341
3362
|
const argv = [];
|
|
3342
|
-
const model = this.
|
|
3363
|
+
const model = this.pickModel();
|
|
3343
3364
|
if (this.config.provider !== void 0) argv.push("--provider", this.config.provider);
|
|
3344
3365
|
if (model !== void 0 && this.config.thinkingLevel !== void 0) {
|
|
3345
3366
|
argv.push("--model", `${model}:${this.config.thinkingLevel}`);
|
|
@@ -3582,6 +3603,8 @@ var PiAgent = class {
|
|
|
3582
3603
|
} finally {
|
|
3583
3604
|
signal.removeEventListener("abort", cancel);
|
|
3584
3605
|
controller.abort();
|
|
3606
|
+
this.rpc?.dispose();
|
|
3607
|
+
this.rpc = void 0;
|
|
3585
3608
|
}
|
|
3586
3609
|
}
|
|
3587
3610
|
/** Append one Pi tool result to the durable log as a `tool/result` message. */
|
|
@@ -3608,24 +3631,30 @@ function waitForExit(child) {
|
|
|
3608
3631
|
);
|
|
3609
3632
|
});
|
|
3610
3633
|
}
|
|
3611
|
-
function
|
|
3634
|
+
function collectOutput(child) {
|
|
3612
3635
|
return new Promise((resolve5) => {
|
|
3613
|
-
|
|
3614
|
-
if (child.stdout.on === void 0) {
|
|
3636
|
+
if (child.stdout.on === void 0 || child.stderr.on === void 0) {
|
|
3615
3637
|
resolve5("");
|
|
3616
3638
|
return;
|
|
3617
3639
|
}
|
|
3640
|
+
let text = "";
|
|
3641
|
+
let pending = 2;
|
|
3642
|
+
const finish = () => {
|
|
3643
|
+
pending -= 1;
|
|
3644
|
+
if (pending === 0) resolve5(text);
|
|
3645
|
+
};
|
|
3618
3646
|
const out = child.stdout;
|
|
3647
|
+
const err = child.stderr;
|
|
3619
3648
|
out.setEncoding("utf8");
|
|
3620
3649
|
out.on("data", (data) => {
|
|
3621
3650
|
text += String(data);
|
|
3622
3651
|
});
|
|
3623
|
-
out.on("
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
resolve5(text);
|
|
3652
|
+
out.on("close", finish);
|
|
3653
|
+
err.setEncoding("utf8");
|
|
3654
|
+
err.on("data", (data) => {
|
|
3655
|
+
text += String(data);
|
|
3628
3656
|
});
|
|
3657
|
+
err.on("close", finish);
|
|
3629
3658
|
});
|
|
3630
3659
|
}
|
|
3631
3660
|
function parsePiModelList(output) {
|
|
@@ -3653,11 +3682,11 @@ async function probePiModels(bin, spawn4) {
|
|
|
3653
3682
|
} catch {
|
|
3654
3683
|
return [];
|
|
3655
3684
|
}
|
|
3656
|
-
const
|
|
3685
|
+
const outputPromise = collectOutput(child);
|
|
3657
3686
|
const exitCode = await waitForExit(child);
|
|
3658
3687
|
if (exitCode !== 0) return [];
|
|
3659
|
-
const
|
|
3660
|
-
return parsePiModelList(
|
|
3688
|
+
const output = await outputPromise;
|
|
3689
|
+
return parsePiModelList(output);
|
|
3661
3690
|
}
|
|
3662
3691
|
|
|
3663
3692
|
// src/engine-pi/loop.ts
|
|
@@ -3730,12 +3759,15 @@ var PiLoop = class extends Service3 {
|
|
|
3730
3759
|
spawn;
|
|
3731
3760
|
/** Resolved Pi CLI entrypoint; `argv[0]` of every Pi RPC child. */
|
|
3732
3761
|
bin;
|
|
3762
|
+
/** Discovered Pi model catalog, forwarded to each agent so it can validate the session-selected model against what pi can actually serve. */
|
|
3763
|
+
catalog;
|
|
3733
3764
|
constructor(ctx, config) {
|
|
3734
3765
|
super(ctx, "agentLoopPi");
|
|
3735
3766
|
this.config = resolveConfig3(config);
|
|
3736
3767
|
this.ownership = new FactoryOwnership(ctx.fiber);
|
|
3737
3768
|
this.runtime = { ctx };
|
|
3738
3769
|
this.bin = piCliEntrypoint();
|
|
3770
|
+
this.catalog = config.piCatalogHolder ?? { entries: [] };
|
|
3739
3771
|
this.spawn = (spec) => fromSubprocess(this.runtime.ctx.subprocess.spawn(piSubprocessSpec(spec, PI_DISPOSE_GRACE_MS)));
|
|
3740
3772
|
const holder = config.piCatalogHolder;
|
|
3741
3773
|
if (holder !== void 0) {
|
|
@@ -3823,7 +3855,16 @@ var PiLoop = class extends Service3 {
|
|
|
3823
3855
|
throw abort.signal.reason instanceof Error ? abort.signal.reason : new Error(String(abort.signal.reason));
|
|
3824
3856
|
};
|
|
3825
3857
|
try {
|
|
3826
|
-
const agent = machine = new PiAgent(
|
|
3858
|
+
const agent = machine = new PiAgent(
|
|
3859
|
+
loopCtx,
|
|
3860
|
+
id,
|
|
3861
|
+
options,
|
|
3862
|
+
session,
|
|
3863
|
+
this.config,
|
|
3864
|
+
this.spawn,
|
|
3865
|
+
this.bin,
|
|
3866
|
+
this.catalog
|
|
3867
|
+
);
|
|
3827
3868
|
machineReady.resolve();
|
|
3828
3869
|
assertLive();
|
|
3829
3870
|
return {
|
|
@@ -16,6 +16,7 @@ import type { Scope } from '@deepseek-ai/dsh-scope';
|
|
|
16
16
|
import type { Session, SessionId, UserMessage } from '@deepseek-ai/dsh-session';
|
|
17
17
|
import type { Context } from '@deepseek-ai/cordis';
|
|
18
18
|
import type { ResolvedConfig } from './types.ts';
|
|
19
|
+
import type { PiModelEntry } from './probe.ts';
|
|
19
20
|
import { type PiSpawnCapability } from './rpc/client.ts';
|
|
20
21
|
/** Provider route label used for logged header snapshots and message provenance. */
|
|
21
22
|
export declare const PROVIDER = "pi";
|
|
@@ -28,6 +29,7 @@ export declare class PiAgent implements Agent {
|
|
|
28
29
|
private readonly config;
|
|
29
30
|
private readonly spawn;
|
|
30
31
|
private readonly bin;
|
|
32
|
+
private readonly catalog;
|
|
31
33
|
readonly inbox: Inbox;
|
|
32
34
|
private phase;
|
|
33
35
|
private activityDone;
|
|
@@ -42,7 +44,9 @@ export declare class PiAgent implements Agent {
|
|
|
42
44
|
private rpc;
|
|
43
45
|
/** The spawn spec the cached client was built from; a change forces a respawn. */
|
|
44
46
|
private lastSpec;
|
|
45
|
-
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig, spawn: PiSpawnCapability, bin: string
|
|
47
|
+
constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, config: ResolvedConfig, spawn: PiSpawnCapability, bin: string, catalog: {
|
|
48
|
+
readonly entries: readonly PiModelEntry[];
|
|
49
|
+
});
|
|
46
50
|
/** Return the cached RPC client, respawning when the spec or process changed. */
|
|
47
51
|
private rpcClient;
|
|
48
52
|
get status(): AgentStatus;
|
|
@@ -118,6 +122,15 @@ export declare class PiAgent implements Agent {
|
|
|
118
122
|
*/
|
|
119
123
|
private dynamicModel;
|
|
120
124
|
/** Build the `pi --mode rpc` argv/cwd/env for one step's child process. */
|
|
125
|
+
/**
|
|
126
|
+
* Resolve the `--model` for the RPC child. The session-selected model (last
|
|
127
|
+
* `model/selection` event) is honored only when it is one of pi's discovered
|
|
128
|
+
* models; an unknown harness model (e.g. another provider's model such as
|
|
129
|
+
* `anyai-v1`, which pi cannot serve) is dropped so the child falls back to pi's
|
|
130
|
+
* own default instead of exiting with "Model ... not found". An empty catalog
|
|
131
|
+
* (probe not concluded) keeps the candidate, matching prior behavior.
|
|
132
|
+
*/
|
|
133
|
+
private pickModel;
|
|
121
134
|
private spawnSpec;
|
|
122
135
|
/**
|
|
123
136
|
* Run one Pi RPC query for the current step and map its event stream into the
|
|
@@ -70,6 +70,8 @@ export declare class PiLoop extends Service implements AgentFactory {
|
|
|
70
70
|
readonly spawn: (spec: PiSpawnSpec) => PiProcess;
|
|
71
71
|
/** Resolved Pi CLI entrypoint; `argv[0]` of every Pi RPC child. */
|
|
72
72
|
readonly bin: string;
|
|
73
|
+
/** Discovered Pi model catalog, forwarded to each agent so it can validate the session-selected model against what pi can actually serve. */
|
|
74
|
+
private readonly catalog;
|
|
73
75
|
constructor(ctx: Context, config: Config);
|
|
74
76
|
/**
|
|
75
77
|
* Construct the driver, scope, and one memoized reverse teardown for a new
|
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": "1.0.0-
|
|
4
|
+
"version": "1.0.0-rc15",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|