@lmzhen/dsh-evolution-merge 0.30.25 → 0.30.27
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 +48 -18
- package/lib/types/authoring-flow.js +6 -1
- package/lib/types/host-capabilities.d.ts +22 -0
- package/lib/types/host-capabilities.js +24 -0
- package/lib/types/index.js +7 -9
- package/lib/types/runtime.d.ts +2 -0
- package/lib/types/runtime.js +12 -0
- package/package.json +5 -5
package/lib/index.js
CHANGED
|
@@ -8377,7 +8377,8 @@ async function runAuthoring(options) {
|
|
|
8377
8377
|
message: "target must be a bare skill name: " + options.target
|
|
8378
8378
|
};
|
|
8379
8379
|
const targetCard = options.cards.find((card) => card.name === options.target);
|
|
8380
|
-
const
|
|
8380
|
+
const requestedMode = options.mode ?? (targetCard === void 0 ? "create-umbrella" : "merge-into-existing");
|
|
8381
|
+
const mode = requestedMode === "merge-into-existing" && targetCard === void 0 ? "create-umbrella" : requestedMode;
|
|
8381
8382
|
const createMode = mode === "create-umbrella";
|
|
8382
8383
|
if (!createMode && targetCard !== void 0) {
|
|
8383
8384
|
if (targetCard.managed !== true) return {
|
|
@@ -9080,6 +9081,11 @@ function failRun(stage, hint, next, result) {
|
|
|
9080
9081
|
text: formatMergeError(result.error)
|
|
9081
9082
|
};
|
|
9082
9083
|
}
|
|
9084
|
+
async function hostOnlyFailure(verb, capabilities) {
|
|
9085
|
+
if (capabilities === void 0) return void 0;
|
|
9086
|
+
const state = await capabilities.hostOnly();
|
|
9087
|
+
return state === void 0 ? void 0 : fail(verb, "e-323", "host data plane unavailable: " + state.code + (state.detail === void 0 ? "" : " (" + state.detail + ")"), "run /evolution merge doctor");
|
|
9088
|
+
}
|
|
9083
9089
|
/**
|
|
9084
9090
|
* The approved/fuse row that already holds a target. Pair/approve move a row to
|
|
9085
9091
|
* approved/fuse BEFORE their run starts, so this row IS the in-flight claim a second
|
|
@@ -9162,6 +9168,8 @@ function createMergeRuntime(deps) {
|
|
|
9162
9168
|
text: "merge: 技能名不能包含 provider/name 或 provider:name 语法:" + command.a + " / " + command.b + " (provider-conflict)\nusage: /evolution merge pair <a> <b>"
|
|
9163
9169
|
};
|
|
9164
9170
|
if (agentless) return fail("pair", "e-305", errorText("e-305-this-invocation-carries-no", { a1: "pair" }), "在 GUI 或 CLI 的会话里运行 /evolution merge pair " + command.a + " " + command.b);
|
|
9171
|
+
const pairHost = await hostOnlyFailure("pair", deps.hostCapabilities);
|
|
9172
|
+
if (pairHost !== void 0) return pairHost;
|
|
9165
9173
|
const pairKeyValue = pairKey(command.a, command.b);
|
|
9166
9174
|
const inFlight = inFlightPairRun(await readRunMetas(deps.io, deps.home), pairKeyValue);
|
|
9167
9175
|
let ownerActive = false;
|
|
@@ -9239,6 +9247,8 @@ function createMergeRuntime(deps) {
|
|
|
9239
9247
|
}
|
|
9240
9248
|
if (command.verb === "scan") {
|
|
9241
9249
|
if (agentless) return fail("scan", "e-305", errorText("e-305-this-invocation-carries-no", { a1: "scan" }), "在 GUI 或 CLI 的会话里运行 /evolution merge scan");
|
|
9250
|
+
const scanHost = await hostOnlyFailure("scan", deps.hostCapabilities);
|
|
9251
|
+
if (scanHost !== void 0) return scanHost;
|
|
9242
9252
|
const signal = new AbortController().signal;
|
|
9243
9253
|
const cards = await deps.cards(signal);
|
|
9244
9254
|
const blocked = /* @__PURE__ */ new Set();
|
|
@@ -9756,6 +9766,31 @@ function createMergeRuntime(deps) {
|
|
|
9756
9766
|
} };
|
|
9757
9767
|
}
|
|
9758
9768
|
//#endregion
|
|
9769
|
+
//#region lib/types/host-capabilities.js
|
|
9770
|
+
/**
|
|
9771
|
+
* The host's data-plane capability as ONE value shared by snapshot, doctor, HTTP and run preflight.
|
|
9772
|
+
* The probe is injected so the Cordis wiring stays in index.ts; a rejected probe is the same E-323
|
|
9773
|
+
* answer as an unavailable one, never an unhandled promise.
|
|
9774
|
+
* @module @lmzhen/dsh-evolution-merge
|
|
9775
|
+
*/
|
|
9776
|
+
/**
|
|
9777
|
+
* Wrap one probe with the shared error normalization.
|
|
9778
|
+
* @param probe - the platform-specific availability check.
|
|
9779
|
+
* @returns the capability value consumed by snapshot and runtime.
|
|
9780
|
+
*/
|
|
9781
|
+
function createHostCapabilities(probe) {
|
|
9782
|
+
return { hostOnly: async () => {
|
|
9783
|
+
try {
|
|
9784
|
+
return await probe();
|
|
9785
|
+
} catch (error) {
|
|
9786
|
+
return {
|
|
9787
|
+
code: "E-323",
|
|
9788
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
9789
|
+
};
|
|
9790
|
+
}
|
|
9791
|
+
} };
|
|
9792
|
+
}
|
|
9793
|
+
//#endregion
|
|
9759
9794
|
//#region lib/types/params.js
|
|
9760
9795
|
/**
|
|
9761
9796
|
* Merge parameter resolution. E2/E3 values arrive as one JSON object; this is the only
|
|
@@ -10534,6 +10569,16 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10534
10569
|
});
|
|
10535
10570
|
const platformChannel = createSubagentChannel(ctx);
|
|
10536
10571
|
const channel = subagentChannelAdapter(platformChannel);
|
|
10572
|
+
const hostCapabilities = createHostCapabilities(async () => {
|
|
10573
|
+
const state = await platformChannel.prepare({
|
|
10574
|
+
kind: "reviewer",
|
|
10575
|
+
allow: ["skill"]
|
|
10576
|
+
});
|
|
10577
|
+
return state.kind === "unavailable" ? {
|
|
10578
|
+
code: "E-323",
|
|
10579
|
+
detail: state.reason
|
|
10580
|
+
} : void 0;
|
|
10581
|
+
});
|
|
10537
10582
|
const stage = new StageToolRegistry();
|
|
10538
10583
|
const engineInput = resolveEngineInput({ modelsRoot: params.mergeModelsRoot });
|
|
10539
10584
|
const engineConfig = engineConfigFromRoot(engineInput.modelsRoot);
|
|
@@ -10597,23 +10642,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10597
10642
|
runs: runs.runs("merge"),
|
|
10598
10643
|
approval: params.mergeApproval,
|
|
10599
10644
|
platformWrite,
|
|
10600
|
-
hostOnly:
|
|
10601
|
-
try {
|
|
10602
|
-
const state = await platformChannel.prepare({
|
|
10603
|
-
kind: "reviewer",
|
|
10604
|
-
allow: ["skill"]
|
|
10605
|
-
});
|
|
10606
|
-
return state.kind === "unavailable" ? {
|
|
10607
|
-
code: "E-323",
|
|
10608
|
-
detail: state.reason
|
|
10609
|
-
} : void 0;
|
|
10610
|
-
} catch (error) {
|
|
10611
|
-
return {
|
|
10612
|
-
code: "E-323",
|
|
10613
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
10614
|
-
};
|
|
10615
|
-
}
|
|
10616
|
-
},
|
|
10645
|
+
hostOnly: hostCapabilities.hostOnly,
|
|
10617
10646
|
live: async () => liveRowFactsOf(await readSkillCards(skills)),
|
|
10618
10647
|
history: async () => {
|
|
10619
10648
|
const items = /* @__PURE__ */ new Map();
|
|
@@ -10707,6 +10736,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10707
10736
|
parent: void 0,
|
|
10708
10737
|
allow: ["merge_stage"],
|
|
10709
10738
|
timeoutMinutes: params.mergeRunTimeoutMinutes,
|
|
10739
|
+
hostCapabilities,
|
|
10710
10740
|
isBlocked: async (name) => {
|
|
10711
10741
|
return await ctx.get("evolutionCurator")?.isBlocked?.(name) === true;
|
|
10712
10742
|
},
|
|
@@ -10,7 +10,12 @@ export async function runAuthoring(options) {
|
|
|
10
10
|
if (options.target.includes('/') || options.target.includes(':'))
|
|
11
11
|
return { ok: false, code: 'target-invalid', message: 'target must be a bare skill name: ' + options.target };
|
|
12
12
|
const targetCard = options.cards.find(card => card.name === options.target);
|
|
13
|
-
|
|
13
|
+
// A decision can name a fresh target yet say merge-into-existing (the model's vocabulary is
|
|
14
|
+
// about intent, not existence). The target card is the authoritative existence fact: when it is
|
|
15
|
+
// absent, the only safe mode is create-umbrella; otherwise commit-assembly correctly refuses to
|
|
16
|
+
// re-create a target that disappeared later.
|
|
17
|
+
const requestedMode = options.mode ?? (targetCard === undefined ? 'create-umbrella' : 'merge-into-existing');
|
|
18
|
+
const mode = requestedMode === 'merge-into-existing' && targetCard === undefined ? 'create-umbrella' : requestedMode;
|
|
14
19
|
const createMode = mode === 'create-umbrella';
|
|
15
20
|
if (!createMode && targetCard !== undefined) {
|
|
16
21
|
if (targetCard.managed !== true)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host's data-plane capability as ONE value shared by snapshot, doctor, HTTP and run preflight.
|
|
3
|
+
* The probe is injected so the Cordis wiring stays in index.ts; a rejected probe is the same E-323
|
|
4
|
+
* answer as an unavailable one, never an unhandled promise.
|
|
5
|
+
* @module @lmzhen/dsh-evolution-merge
|
|
6
|
+
*/
|
|
7
|
+
/** The one unavailable answer the merge surface understands. */
|
|
8
|
+
export interface HostOnlyUnavailable {
|
|
9
|
+
readonly code: 'E-323';
|
|
10
|
+
readonly detail?: string;
|
|
11
|
+
}
|
|
12
|
+
/** A host capability probe; undefined means the data plane is usable. */
|
|
13
|
+
export interface HostCapabilities {
|
|
14
|
+
readonly hostOnly: () => Promise<HostOnlyUnavailable | undefined>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Wrap one probe with the shared error normalization.
|
|
18
|
+
* @param probe - the platform-specific availability check.
|
|
19
|
+
* @returns the capability value consumed by snapshot and runtime.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createHostCapabilities(probe: () => Promise<HostOnlyUnavailable | undefined>): HostCapabilities;
|
|
22
|
+
//# sourceMappingURL=host-capabilities.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host's data-plane capability as ONE value shared by snapshot, doctor, HTTP and run preflight.
|
|
3
|
+
* The probe is injected so the Cordis wiring stays in index.ts; a rejected probe is the same E-323
|
|
4
|
+
* answer as an unavailable one, never an unhandled promise.
|
|
5
|
+
* @module @lmzhen/dsh-evolution-merge
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Wrap one probe with the shared error normalization.
|
|
9
|
+
* @param probe - the platform-specific availability check.
|
|
10
|
+
* @returns the capability value consumed by snapshot and runtime.
|
|
11
|
+
*/
|
|
12
|
+
export function createHostCapabilities(probe) {
|
|
13
|
+
return {
|
|
14
|
+
hostOnly: async () => {
|
|
15
|
+
try {
|
|
16
|
+
return await probe();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
return { code: 'E-323', detail: error instanceof Error ? error.message : String(error) };
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=host-capabilities.js.map
|
package/lib/types/index.js
CHANGED
|
@@ -31,6 +31,7 @@ import { pairKey, rowIdOf } from "./contracts.js";
|
|
|
31
31
|
import { makeMergeRoutes } from "./merge-routes.js";
|
|
32
32
|
import { createMergeCommand, runMergeCommand } from "./merge-command.js";
|
|
33
33
|
import { createMergeRuntime } from "./runtime.js";
|
|
34
|
+
import { createHostCapabilities } from "./host-capabilities.js";
|
|
34
35
|
import { resolveMergeParams } from "./params.js";
|
|
35
36
|
import { subagentChannelAdapter } from "./adapters/channel.js";
|
|
36
37
|
import { StageToolRegistry } from "./stage-tool-registry.js";
|
|
@@ -234,6 +235,10 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
234
235
|
}).catch((error) => { ctx.logger.warn('merge state readiness failed: ' + (error instanceof Error ? error.message : String(error))); });
|
|
235
236
|
const platformChannel = createSubagentChannel(ctx);
|
|
236
237
|
const channel = subagentChannelAdapter(platformChannel);
|
|
238
|
+
const hostCapabilities = createHostCapabilities(async () => {
|
|
239
|
+
const state = await platformChannel.prepare({ kind: 'reviewer', allow: ['skill'] });
|
|
240
|
+
return state.kind === 'unavailable' ? { code: 'E-323', detail: state.reason } : undefined;
|
|
241
|
+
});
|
|
237
242
|
const stage = new StageToolRegistry();
|
|
238
243
|
const engineInput = resolveEngineInput({ modelsRoot: params.mergeModelsRoot });
|
|
239
244
|
const engineConfig = engineConfigFromRoot(engineInput.modelsRoot);
|
|
@@ -276,15 +281,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
276
281
|
});
|
|
277
282
|
source = {
|
|
278
283
|
async snapshot() {
|
|
279
|
-
return await readMergeSnapshot({ io, home, runs: runs.runs('merge'), approval: params.mergeApproval, platformWrite, hostOnly: async () => {
|
|
280
|
-
try {
|
|
281
|
-
const state = await platformChannel.prepare({ kind: 'reviewer', allow: ['skill'] });
|
|
282
|
-
return state.kind === 'unavailable' ? { code: 'E-323', detail: state.reason } : undefined;
|
|
283
|
-
}
|
|
284
|
-
catch (error) {
|
|
285
|
-
return { code: 'E-323', detail: error instanceof Error ? error.message : String(error) };
|
|
286
|
-
}
|
|
287
|
-
}, live: async () => liveRowFactsOf(await readSkillCards(skills)), history: async () => {
|
|
284
|
+
return await readMergeSnapshot({ io, home, runs: runs.runs('merge'), approval: params.mergeApproval, platformWrite, hostOnly: hostCapabilities.hostOnly, live: async () => liveRowFactsOf(await readSkillCards(skills)), history: async () => {
|
|
288
285
|
const items = new Map();
|
|
289
286
|
const receipts = [...await readCommitReceipts(io, home)].sort((a, b) => a.at - b.at);
|
|
290
287
|
const generationOf = new Map(receipts.map((receipt, index) => [receipt.runId, index + 1]));
|
|
@@ -331,6 +328,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
331
328
|
io, home, registry: runs, skills, now: () => Date.now(), params, platformWrite,
|
|
332
329
|
...(defaultModel === undefined ? {} : { model: defaultModel.currentSelection().model }),
|
|
333
330
|
channel, parent: undefined, allow: ['merge_stage'], timeoutMinutes: params.mergeRunTimeoutMinutes,
|
|
331
|
+
hostCapabilities,
|
|
334
332
|
isBlocked: async (name) => {
|
|
335
333
|
const curator = ctx.get('evolutionCurator');
|
|
336
334
|
return (await curator?.isBlocked?.(name)) === true;
|
package/lib/types/runtime.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { MergeRegistryPort } from './run-shell.ts';
|
|
|
6
6
|
import type { MergeCommandService } from './merge-command.ts';
|
|
7
7
|
import type { StageToolContext } from './stage-tool.ts';
|
|
8
8
|
import type { ConversationChannelLike } from './channel-port.ts';
|
|
9
|
+
import type { HostCapabilities } from './host-capabilities.ts';
|
|
9
10
|
export declare function forcedDecisionFor(a: string, b: string): MergeDecision;
|
|
10
11
|
export interface MergeStageBindingPort {
|
|
11
12
|
bind(context: StageToolContext): void;
|
|
@@ -20,6 +21,7 @@ export interface MergeRuntimeDeps {
|
|
|
20
21
|
readonly params: MergeParams;
|
|
21
22
|
readonly platformWrite: boolean;
|
|
22
23
|
readonly channel: ConversationChannelLike;
|
|
24
|
+
readonly hostCapabilities?: HostCapabilities;
|
|
23
25
|
readonly parent: unknown;
|
|
24
26
|
readonly allow: readonly string[];
|
|
25
27
|
readonly cwd?: string;
|
package/lib/types/runtime.js
CHANGED
|
@@ -38,6 +38,12 @@ function fail(stage, code, hint, next, runId) {
|
|
|
38
38
|
function failRun(stage, hint, next, result) {
|
|
39
39
|
return result.error === undefined ? fail(stage, 'run-failed', hint, next, result.runId) : { ok: false, text: formatMergeError(result.error) };
|
|
40
40
|
}
|
|
41
|
+
async function hostOnlyFailure(verb, capabilities) {
|
|
42
|
+
if (capabilities === undefined)
|
|
43
|
+
return undefined;
|
|
44
|
+
const state = await capabilities.hostOnly();
|
|
45
|
+
return state === undefined ? undefined : fail(verb, 'e-323', 'host data plane unavailable: ' + state.code + (state.detail === undefined ? '' : ' (' + state.detail + ')'), 'run /evolution merge doctor');
|
|
46
|
+
}
|
|
41
47
|
/**
|
|
42
48
|
* The approved/fuse row that already holds a target. Pair/approve move a row to
|
|
43
49
|
* approved/fuse BEFORE their run starts, so this row IS the in-flight claim a second
|
|
@@ -100,6 +106,9 @@ export function createMergeRuntime(deps) {
|
|
|
100
106
|
// the three-line shape instead of opening a run that is guaranteed to fail.
|
|
101
107
|
if (agentless)
|
|
102
108
|
return fail('pair', 'e-305', errorText('e-305-this-invocation-carries-no', { a1: 'pair' }), '在 GUI 或 CLI 的会话里运行 /evolution merge pair ' + command.a + ' ' + command.b);
|
|
109
|
+
const pairHost = await hostOnlyFailure('pair', deps.hostCapabilities);
|
|
110
|
+
if (pairHost !== undefined)
|
|
111
|
+
return pairHost;
|
|
103
112
|
// FR-EXEC-17/NFR-04: two pair runs for one pair must not race for the same row.
|
|
104
113
|
const pairKeyValue = pairKey(command.a, command.b);
|
|
105
114
|
const inFlight = inFlightPairRun(await readRunMetas(deps.io, deps.home), pairKeyValue);
|
|
@@ -179,6 +188,9 @@ export function createMergeRuntime(deps) {
|
|
|
179
188
|
if (command.verb === 'scan') {
|
|
180
189
|
if (agentless)
|
|
181
190
|
return fail('scan', 'e-305', errorText('e-305-this-invocation-carries-no', { a1: 'scan' }), '在 GUI 或 CLI 的会话里运行 /evolution merge scan');
|
|
191
|
+
const scanHost = await hostOnlyFailure('scan', deps.hostCapabilities);
|
|
192
|
+
if (scanHost !== undefined)
|
|
193
|
+
return scanHost;
|
|
182
194
|
const signal = new AbortController().signal;
|
|
183
195
|
const cards = await deps.cards(signal);
|
|
184
196
|
const blocked = new Set();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-merge",
|
|
3
3
|
"description": "Skill auto-merge domain: screening, two-round authoring, queues and commit orchestration (community build)",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.27",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@deepseek-ai/schemastery": "^3.18.5",
|
|
35
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
36
|
-
"@lmzhen/dsh-evolution-commands": "^0.30.
|
|
37
|
-
"@lmzhen/dsh-evolution-merge-engine": "0.30.
|
|
35
|
+
"@lmzhen/dsh-evolution-core": "^0.30.27",
|
|
36
|
+
"@lmzhen/dsh-evolution-commands": "^0.30.27",
|
|
37
|
+
"@lmzhen/dsh-evolution-merge-engine": "0.30.27"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@deepseek-ai/cordis": "^4.0.5"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
|
-
"dsh": ">=0.2.1-rc.
|
|
43
|
+
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|
|
44
44
|
}
|
|
45
45
|
}
|