@lmzhen/dsh-evolution-merge 0.33.0 → 0.35.0
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 +140 -69
- package/lib/types/domain/merge-snapshot.d.ts +1 -1
- package/lib/types/index.js +34 -12
- package/lib/types/merge-routes.d.ts +14 -1
- package/lib/types/merge-routes.js +37 -7
- package/lib/types/runtime.d.ts +7 -2
- package/lib/types/runtime.js +43 -34
- package/lib/types/surface/merge-view.js +5 -3
- package/lib/types/wire-projection.d.ts +1 -1
- package/lib/types/wire-projection.js +4 -1
- package/lib/types/wire.d.ts +1 -1
- package/package.json +4 -4
package/lib/index.js
CHANGED
|
@@ -3700,9 +3700,11 @@ function runView(run, readAt, includeResultRef = false) {
|
|
|
3700
3700
|
const record = run.record;
|
|
3701
3701
|
const state = run.meta?.phase === "undone" ? "undone" : run.meta?.stopReason === "interrupted" ? "interrupted" : run.meta?.approval === "pending" ? "awaiting-approval" : record.state;
|
|
3702
3702
|
const stopReason = run.meta?.stopReason ?? (record.state === "cancelled" ? "cancelled" : record.state === "failed" ? "failed" : record.state === "succeeded" ? "completed" : void 0);
|
|
3703
|
-
const
|
|
3704
|
-
|
|
3705
|
-
|
|
3703
|
+
const reportPairs = run.report?.processedPairs?.length ?? run.report?.pairs.length;
|
|
3704
|
+
const terminal = state === "succeeded" || state === "undone";
|
|
3705
|
+
const progress = record.progress === void 0 && reportPairs === void 0 ? null : {
|
|
3706
|
+
done: terminal ? Math.max(record.progress?.done ?? 0, reportPairs ?? 0, run.pairs.length) : record.progress?.done ?? reportPairs ?? 0,
|
|
3707
|
+
total: record.progress?.total ?? reportPairs ?? 0
|
|
3706
3708
|
};
|
|
3707
3709
|
return {
|
|
3708
3710
|
wireVersion: 1,
|
|
@@ -5039,14 +5041,17 @@ function toHistoryView(items, readAt) {
|
|
|
5039
5041
|
return {
|
|
5040
5042
|
wireVersion: 1,
|
|
5041
5043
|
readAt,
|
|
5042
|
-
items: items.map((item) =>
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5044
|
+
items: items.map((item) => {
|
|
5045
|
+
const parsed = item.archivedAt === null ? null : typeof item.archivedAt === "number" ? item.archivedAt : Date.parse(item.archivedAt);
|
|
5046
|
+
return {
|
|
5047
|
+
name: item.name,
|
|
5048
|
+
kind: item.kind,
|
|
5049
|
+
reason: item.reason,
|
|
5050
|
+
lastVersion: item.lastVersion,
|
|
5051
|
+
archivedAt: parsed === null || Number.isFinite(parsed) ? parsed : null,
|
|
5052
|
+
...item.sourceRunId === void 0 ? {} : { sourceRunId: item.sourceRunId }
|
|
5053
|
+
};
|
|
5054
|
+
})
|
|
5050
5055
|
};
|
|
5051
5056
|
}
|
|
5052
5057
|
//#endregion
|
|
@@ -5057,7 +5062,8 @@ const MERGE_ROUTES = {
|
|
|
5057
5062
|
view: MERGE_PATHS.view,
|
|
5058
5063
|
rows: MERGE_PATHS.rows,
|
|
5059
5064
|
body: MERGE_PATHS.body ?? MERGE_BASE + "body",
|
|
5060
|
-
history: MERGE_PATHS.history ?? MERGE_BASE + "history"
|
|
5065
|
+
history: MERGE_PATHS.history ?? MERGE_BASE + "history",
|
|
5066
|
+
decision: MERGE_BASE + "decision"
|
|
5061
5067
|
};
|
|
5062
5068
|
function makeMergeRoutes(deps, _audit) {
|
|
5063
5069
|
const absent = () => json(404, {
|
|
@@ -5172,7 +5178,6 @@ function makeMergeRoutes(deps, _audit) {
|
|
|
5172
5178
|
const id = query.get("id");
|
|
5173
5179
|
const name = query.get("name");
|
|
5174
5180
|
const side = query.get("side");
|
|
5175
|
-
if (id !== null && name !== null) return bad("id and name are mutually exclusive");
|
|
5176
5181
|
if ((id === null || id === "") && (name === null || name === "")) return json(404, {
|
|
5177
5182
|
wireVersion: 1,
|
|
5178
5183
|
code: "merge-body-not-found",
|
|
@@ -5185,21 +5190,21 @@ function makeMergeRoutes(deps, _audit) {
|
|
|
5185
5190
|
code: "merge-state-blocked",
|
|
5186
5191
|
readiness: snapshot.blocked.readiness
|
|
5187
5192
|
});
|
|
5188
|
-
const
|
|
5193
|
+
const sideFilter = side === "a" ? "live" : side === "b" ? "archive" : side === "live" || side === "archive" ? side : void 0;
|
|
5194
|
+
const refs = snapshot.bodyRefs.filter((ref) => (id === null || id === "" || ref.rowId === id) && (name === null || name === "" || ref.name === name) && (sideFilter === void 0 || ref.side === sideFilter));
|
|
5189
5195
|
if (refs.length === 0) return json(404, {
|
|
5190
5196
|
wireVersion: 1,
|
|
5191
5197
|
code: "merge-body-not-found",
|
|
5192
5198
|
message: "merge body not found"
|
|
5193
5199
|
});
|
|
5194
|
-
if (
|
|
5200
|
+
if (refs.length > 1) return bad("merge body is ambiguous; pass id, name and side");
|
|
5195
5201
|
const row = snapshot.rows.find((item) => item.row.id === refs[0].rowId);
|
|
5196
5202
|
if (row?.stage?.state === "lost" || row?.stage?.state === "expired") return json(404, {
|
|
5197
5203
|
wireVersion: 1,
|
|
5198
5204
|
code: "merge-body-not-found",
|
|
5199
5205
|
message: "merge stage is lost"
|
|
5200
5206
|
});
|
|
5201
|
-
const
|
|
5202
|
-
const ref = (sideFilter === void 0 ? refs[0] : refs.find((item) => item.side === sideFilter)) ?? refs[0];
|
|
5207
|
+
const ref = refs[0];
|
|
5203
5208
|
return json(200, buildBodyView(snapshot, ref, (source.bodyRef === void 0 ? null : await source.bodyRef(ref)) ?? ""));
|
|
5204
5209
|
}
|
|
5205
5210
|
const readiness = await notReady(source);
|
|
@@ -5235,6 +5240,35 @@ function makeMergeRoutes(deps, _audit) {
|
|
|
5235
5240
|
if (readiness !== null) return readiness;
|
|
5236
5241
|
return json(200, toHistoryView(await source.history?.() ?? [], Date.now()));
|
|
5237
5242
|
}
|
|
5243
|
+
},
|
|
5244
|
+
{
|
|
5245
|
+
path: MERGE_ROUTES.decision,
|
|
5246
|
+
methods: ["POST"],
|
|
5247
|
+
requestBody: "buffered",
|
|
5248
|
+
fetch: async (request) => {
|
|
5249
|
+
if (request.method !== "POST") return methodNotAllowed(["POST"]);
|
|
5250
|
+
if (deps.decide === void 0) return absent();
|
|
5251
|
+
let body;
|
|
5252
|
+
try {
|
|
5253
|
+
body = await request.json();
|
|
5254
|
+
} catch {
|
|
5255
|
+
return bad("decision body must be JSON");
|
|
5256
|
+
}
|
|
5257
|
+
if (typeof body !== "object" || body === null) return bad("decision body must be an object");
|
|
5258
|
+
const input = body;
|
|
5259
|
+
if (typeof input.rowId !== "string" || input.rowId === "") return bad("rowId is required");
|
|
5260
|
+
if (input.verb !== "approve" && input.verb !== "reject") return bad("verb must be approve or reject");
|
|
5261
|
+
if (typeof input.sessionId !== "string" || input.sessionId === "") return bad("sessionId is required");
|
|
5262
|
+
const result = await deps.decide({
|
|
5263
|
+
rowId: input.rowId,
|
|
5264
|
+
verb: input.verb,
|
|
5265
|
+
sessionId: input.sessionId
|
|
5266
|
+
}, _audit);
|
|
5267
|
+
return json(result.ok ? 200 : result.code === "E-305" ? 409 : 400, {
|
|
5268
|
+
wireVersion: 1,
|
|
5269
|
+
...result
|
|
5270
|
+
});
|
|
5271
|
+
}
|
|
5238
5272
|
}
|
|
5239
5273
|
];
|
|
5240
5274
|
}
|
|
@@ -9262,16 +9296,22 @@ function unitFor(deps) {
|
|
|
9262
9296
|
function createMergeRuntime(deps) {
|
|
9263
9297
|
const readOnly = createReadOnlyMergeService(deps.io, deps.home);
|
|
9264
9298
|
const timeout = deps.timeoutMinutes > 0 ? deps.timeoutMinutes : void 0;
|
|
9265
|
-
const
|
|
9299
|
+
const paramsOf = () => {
|
|
9300
|
+
if (deps.getParams !== void 0) return deps.getParams();
|
|
9301
|
+
if (deps.params !== void 0) return deps.params;
|
|
9302
|
+
throw new Error("merge runtime requires getParams or params");
|
|
9303
|
+
};
|
|
9304
|
+
const platformWriteOf = () => deps.getPlatformWrite?.() ?? deps.platformWrite ?? false;
|
|
9305
|
+
const common = () => ({
|
|
9266
9306
|
io: deps.io,
|
|
9267
9307
|
home: deps.home,
|
|
9268
9308
|
registry: deps.registry,
|
|
9269
|
-
platformWrite:
|
|
9309
|
+
platformWrite: platformWriteOf(),
|
|
9270
9310
|
now: deps.now,
|
|
9271
9311
|
...deps.model === void 0 ? {} : { model: deps.model },
|
|
9272
9312
|
...deps.isOwnerActive === void 0 ? {} : { isOwnerActive: deps.isOwnerActive },
|
|
9273
9313
|
...timeout === void 0 ? {} : { timeoutMinutes: timeout }
|
|
9274
|
-
};
|
|
9314
|
+
});
|
|
9275
9315
|
const stage = deps.stage;
|
|
9276
9316
|
const stageHooks = stage === void 0 ? {} : {
|
|
9277
9317
|
onChild: (context) => {
|
|
@@ -9285,6 +9325,7 @@ function createMergeRuntime(deps) {
|
|
|
9285
9325
|
const parent = context?.parent ?? deps.parent;
|
|
9286
9326
|
const agentless = context?.agentless === true;
|
|
9287
9327
|
const detached = deps.detached !== false;
|
|
9328
|
+
const params = paramsOf();
|
|
9288
9329
|
if (command.verb === "pair") {
|
|
9289
9330
|
const signal = new AbortController().signal;
|
|
9290
9331
|
if (command.a === command.b) return fail("pair", "pair-same-source", "merge pair 需要两个不同技能源:" + command.a, "重新指定两个不同的 family-managed 技能");
|
|
@@ -9324,9 +9365,9 @@ function createMergeRuntime(deps) {
|
|
|
9324
9365
|
if (existing?.state === "pending") return fail("pair", "pair-pending", "该 pair 已在待批准队列:" + existing.id, "先运行 /evolution merge approve/reject/discard " + existing.id, existing.runId);
|
|
9325
9366
|
if (!cleared && (existing?.state === "approved" || existing?.state === "fuse")) return fail("pair", "pair-fuse", "该 pair 已在待合并队列,可复用决策重跑", "运行 /evolution merge resume --retry " + (existing.runId ?? existing.id), existing.runId);
|
|
9326
9367
|
const forced = command.force === true ? forcedDecisionFor(command.a, command.b) : void 0;
|
|
9327
|
-
const approval =
|
|
9368
|
+
const approval = params.mergeApproval;
|
|
9328
9369
|
const result = await runScanCommand({
|
|
9329
|
-
...common,
|
|
9370
|
+
...common(),
|
|
9330
9371
|
approval,
|
|
9331
9372
|
cards,
|
|
9332
9373
|
...detached ? { detached: true } : {},
|
|
@@ -9341,12 +9382,12 @@ function createMergeRuntime(deps) {
|
|
|
9341
9382
|
parent,
|
|
9342
9383
|
allow: deps.allow,
|
|
9343
9384
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9344
|
-
timeoutMs:
|
|
9385
|
+
timeoutMs: params.mergePairTimeoutMinutes * 6e4,
|
|
9345
9386
|
approval,
|
|
9346
9387
|
commitPort: skillLibraryPort(deps.skills),
|
|
9347
9388
|
now: deps.now,
|
|
9348
9389
|
...unitFor(deps) === void 0 ? {} : { unit: unitFor(deps) },
|
|
9349
|
-
...
|
|
9390
|
+
...params.limitsSnapshot === void 0 ? {} : { limits: params.limitsSnapshot.skillLimits },
|
|
9350
9391
|
...deps.isBlocked === void 0 ? {} : { isBlocked: deps.isBlocked },
|
|
9351
9392
|
...stageHooks
|
|
9352
9393
|
});
|
|
@@ -9380,12 +9421,12 @@ function createMergeRuntime(deps) {
|
|
|
9380
9421
|
for (const card of cards) if (await deps.isBlocked?.(card.name) === true) blocked.add(card.name);
|
|
9381
9422
|
const managed = new Set(cards.filter((card) => card.managed === true && card.protectedBy == null && !blocked.has(card.name)).map((card) => card.name));
|
|
9382
9423
|
const loadedState = await loadMergeState(deps.io, deps.home);
|
|
9383
|
-
const skipped = skippedPairKeys(loadedState.state.rows,
|
|
9424
|
+
const skipped = skippedPairKeys(loadedState.state.rows, params.mergeSkippedTtlDays, deps.now());
|
|
9384
9425
|
const held = new Set(loadedState.state.rows.filter((row) => row.queue === "pending" || row.queue === "fuse" || row.queue === "verify" && (row.runId !== void 0 && deps.now() - Date.parse(row.updatedAt) < 10 * 6e4 || !canRetry(row))).map((row) => row.pairKey));
|
|
9385
9426
|
const notes = loadedState.state.rows.filter((row) => row.state === "pending" || row.state === "approved" || row.state === "fuse" || row.state === "skipped").map((row) => "conflict:" + row.state + ":" + row.pairKey + ":" + row.id);
|
|
9386
9427
|
const result = await runScanCommand({
|
|
9387
|
-
...common,
|
|
9388
|
-
approval:
|
|
9428
|
+
...common(),
|
|
9429
|
+
approval: params.mergeApproval,
|
|
9389
9430
|
notes,
|
|
9390
9431
|
cards,
|
|
9391
9432
|
...detached ? { detached: true } : {},
|
|
@@ -9417,12 +9458,12 @@ function createMergeRuntime(deps) {
|
|
|
9417
9458
|
parent,
|
|
9418
9459
|
allow: deps.allow,
|
|
9419
9460
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9420
|
-
timeoutMs:
|
|
9421
|
-
approval:
|
|
9461
|
+
timeoutMs: params.mergePairTimeoutMinutes * 6e4,
|
|
9462
|
+
approval: params.mergeApproval,
|
|
9422
9463
|
commitPort: skillLibraryPort(deps.skills),
|
|
9423
9464
|
now: deps.now,
|
|
9424
9465
|
...unitFor(deps) === void 0 ? {} : { unit: unitFor(deps) },
|
|
9425
|
-
...
|
|
9466
|
+
...params.limitsSnapshot === void 0 ? {} : { limits: params.limitsSnapshot.skillLimits },
|
|
9426
9467
|
...deps.isBlocked === void 0 ? {} : { isBlocked: deps.isBlocked },
|
|
9427
9468
|
...stageHooks
|
|
9428
9469
|
})
|
|
@@ -9450,8 +9491,8 @@ function createMergeRuntime(deps) {
|
|
|
9450
9491
|
b: row.b
|
|
9451
9492
|
};
|
|
9452
9493
|
const result = await runScanCommand({
|
|
9453
|
-
...common,
|
|
9454
|
-
approval:
|
|
9494
|
+
...common(),
|
|
9495
|
+
approval: params.mergeApproval,
|
|
9455
9496
|
notes: ["candidates --retry " + row.id],
|
|
9456
9497
|
cards,
|
|
9457
9498
|
makePorts: (runId) => createChannelScanPorts({
|
|
@@ -9468,12 +9509,12 @@ function createMergeRuntime(deps) {
|
|
|
9468
9509
|
parent,
|
|
9469
9510
|
allow: deps.allow,
|
|
9470
9511
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9471
|
-
timeoutMs:
|
|
9472
|
-
approval:
|
|
9512
|
+
timeoutMs: params.mergePairTimeoutMinutes * 6e4,
|
|
9513
|
+
approval: params.mergeApproval,
|
|
9473
9514
|
commitPort: skillLibraryPort(deps.skills),
|
|
9474
9515
|
now: deps.now,
|
|
9475
9516
|
...unitFor(deps) === void 0 ? {} : { unit: unitFor(deps) },
|
|
9476
|
-
...
|
|
9517
|
+
...params.limitsSnapshot === void 0 ? {} : { limits: params.limitsSnapshot.skillLimits },
|
|
9477
9518
|
...deps.isBlocked === void 0 ? {} : { isBlocked: deps.isBlocked },
|
|
9478
9519
|
...stageHooks
|
|
9479
9520
|
})
|
|
@@ -9515,7 +9556,7 @@ function createMergeRuntime(deps) {
|
|
|
9515
9556
|
io: deps.io,
|
|
9516
9557
|
home: deps.home,
|
|
9517
9558
|
now: deps.now,
|
|
9518
|
-
ttlDays:
|
|
9559
|
+
ttlDays: params.mergeSkippedTtlDays,
|
|
9519
9560
|
...managed === void 0 ? {} : { managed },
|
|
9520
9561
|
...live === void 0 ? {} : { live }
|
|
9521
9562
|
});
|
|
@@ -9530,7 +9571,7 @@ function createMergeRuntime(deps) {
|
|
|
9530
9571
|
}
|
|
9531
9572
|
if (command.verb === "doctor") return {
|
|
9532
9573
|
ok: true,
|
|
9533
|
-
text: (await mergeDoctor(deps.io, deps.home,
|
|
9574
|
+
text: (await mergeDoctor(deps.io, deps.home, params.mergeModelsRoot)).map((row) => row.key + " [" + row.status + "] " + row.label + ":" + row.detail).join("\n")
|
|
9534
9575
|
};
|
|
9535
9576
|
if (command.verb === "report" || command.verb === "runs" || command.verb === "cancel") {
|
|
9536
9577
|
const registry = deps.registry;
|
|
@@ -9542,7 +9583,7 @@ function createMergeRuntime(deps) {
|
|
|
9542
9583
|
io: deps.io,
|
|
9543
9584
|
home: deps.home,
|
|
9544
9585
|
runs: mergeRuns,
|
|
9545
|
-
approval:
|
|
9586
|
+
approval: params.mergeApproval,
|
|
9546
9587
|
now: deps.now
|
|
9547
9588
|
})))
|
|
9548
9589
|
};
|
|
@@ -9658,13 +9699,13 @@ function createMergeRuntime(deps) {
|
|
|
9658
9699
|
round2: () => failed("author", "resume-needs-child", "fuse resume needs a new child")
|
|
9659
9700
|
};
|
|
9660
9701
|
const result = await runResumeCommand({
|
|
9661
|
-
...common,
|
|
9702
|
+
...common(),
|
|
9662
9703
|
rowId: row.id,
|
|
9663
9704
|
ports: basePorts,
|
|
9664
|
-
approval:
|
|
9705
|
+
approval: params.mergeApproval,
|
|
9665
9706
|
...agentless ? { agentless: true } : {},
|
|
9666
9707
|
...detached ? { detached: true } : {},
|
|
9667
|
-
pairTimeoutMinutes:
|
|
9708
|
+
pairTimeoutMinutes: params.mergePairTimeoutMinutes,
|
|
9668
9709
|
...deps.isOwnerActive === void 0 ? {} : { isOwnerActive: deps.isOwnerActive },
|
|
9669
9710
|
makePorts: (runId) => createChannelScanPorts({
|
|
9670
9711
|
io: deps.io,
|
|
@@ -9676,12 +9717,12 @@ function createMergeRuntime(deps) {
|
|
|
9676
9717
|
parent,
|
|
9677
9718
|
allow: deps.allow,
|
|
9678
9719
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9679
|
-
timeoutMs:
|
|
9680
|
-
approval:
|
|
9720
|
+
timeoutMs: params.mergePairTimeoutMinutes * 6e4,
|
|
9721
|
+
approval: params.mergeApproval,
|
|
9681
9722
|
commitPort: skillLibraryPort(deps.skills),
|
|
9682
9723
|
now: deps.now,
|
|
9683
9724
|
...unitFor(deps) === void 0 ? {} : { unit: unitFor(deps) },
|
|
9684
|
-
...
|
|
9725
|
+
...params.limitsSnapshot === void 0 ? {} : { limits: params.limitsSnapshot.skillLimits },
|
|
9685
9726
|
...deps.isBlocked === void 0 ? {} : { isBlocked: deps.isBlocked }
|
|
9686
9727
|
}),
|
|
9687
9728
|
round2WithRun: createNewChildRound2({
|
|
@@ -9693,11 +9734,11 @@ function createMergeRuntime(deps) {
|
|
|
9693
9734
|
parentRunId: (runId) => runId,
|
|
9694
9735
|
allow: deps.allow,
|
|
9695
9736
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9696
|
-
timeoutMs:
|
|
9737
|
+
timeoutMs: params.mergePairTimeoutMinutes * 6e4,
|
|
9697
9738
|
commitPort: skillLibraryPort(deps.skills),
|
|
9698
9739
|
now: deps.now,
|
|
9699
9740
|
...unitFor(deps) === void 0 ? {} : { unit: unitFor(deps) },
|
|
9700
|
-
...
|
|
9741
|
+
...params.limitsSnapshot === void 0 ? {} : { limits: params.limitsSnapshot.skillLimits },
|
|
9701
9742
|
...deps.isBlocked === void 0 ? {} : { isBlocked: deps.isBlocked },
|
|
9702
9743
|
...stageHooks
|
|
9703
9744
|
})
|
|
@@ -9812,11 +9853,11 @@ function createMergeRuntime(deps) {
|
|
|
9812
9853
|
}
|
|
9813
9854
|
}
|
|
9814
9855
|
const result = await runApproveCommand({
|
|
9815
|
-
...common,
|
|
9856
|
+
...common(),
|
|
9816
9857
|
rowId: command.id,
|
|
9817
9858
|
...agentless ? { agentless: true } : {},
|
|
9818
9859
|
...detached ? { detached: true } : {},
|
|
9819
|
-
pairTimeoutMinutes:
|
|
9860
|
+
pairTimeoutMinutes: params.mergePairTimeoutMinutes,
|
|
9820
9861
|
ports: {
|
|
9821
9862
|
stage: createStageForCards({
|
|
9822
9863
|
io: deps.io,
|
|
@@ -9834,11 +9875,11 @@ function createMergeRuntime(deps) {
|
|
|
9834
9875
|
parentRunId: (runId) => runId,
|
|
9835
9876
|
allow: deps.allow,
|
|
9836
9877
|
...deps.cwd === void 0 ? {} : { cwd: deps.cwd },
|
|
9837
|
-
timeoutMs:
|
|
9878
|
+
timeoutMs: params.mergePairTimeoutMinutes * 6e4,
|
|
9838
9879
|
commitPort: skillLibraryPort(deps.skills),
|
|
9839
9880
|
now: deps.now,
|
|
9840
9881
|
...unitFor(deps) === void 0 ? {} : { unit: unitFor(deps) },
|
|
9841
|
-
...
|
|
9882
|
+
...params.limitsSnapshot === void 0 ? {} : { limits: params.limitsSnapshot.skillLimits },
|
|
9842
9883
|
...deps.isBlocked === void 0 ? {} : { isBlocked: deps.isBlocked },
|
|
9843
9884
|
...stageHooks
|
|
9844
9885
|
})
|
|
@@ -9865,7 +9906,7 @@ function createMergeRuntime(deps) {
|
|
|
9865
9906
|
io: deps.io,
|
|
9866
9907
|
home: deps.home,
|
|
9867
9908
|
runs: runs !== void 0 && runs.some((item) => item.id === found.id) ? runs : [...runs ?? [], found],
|
|
9868
|
-
approval:
|
|
9909
|
+
approval: params.mergeApproval,
|
|
9869
9910
|
now: deps.now
|
|
9870
9911
|
});
|
|
9871
9912
|
if (snapshot.blocked !== void 0) return fail("status", snapshot.blocked.code, snapshot.blocked.message, "先修复合并状态(/evolution merge doctor),再查看运行");
|
|
@@ -9882,7 +9923,7 @@ function createMergeRuntime(deps) {
|
|
|
9882
9923
|
const list = cards ?? await deps.cards(new AbortController().signal).catch(() => []);
|
|
9883
9924
|
return list.length === 0 ? void 0 : liveRowFactsOf(list);
|
|
9884
9925
|
};
|
|
9885
|
-
const text = await handleReadOnlyMergeCommand(command, deps.io, deps.home, cards, runs,
|
|
9926
|
+
const text = await handleReadOnlyMergeCommand(command, deps.io, deps.home, cards, runs, params.mergeApproval, liveForRead);
|
|
9886
9927
|
if (text !== null) return {
|
|
9887
9928
|
ok: true,
|
|
9888
9929
|
text
|
|
@@ -10546,8 +10587,9 @@ const inject = [];
|
|
|
10546
10587
|
function apply(ctx, rawConfig = {}) {
|
|
10547
10588
|
registerOwnerSettings(ctx, "evolution-merge", Config);
|
|
10548
10589
|
configureMergePresentation(ctx);
|
|
10549
|
-
|
|
10590
|
+
const startupParams = resolveMergeParams(rawConfig);
|
|
10550
10591
|
let mergeRun;
|
|
10592
|
+
let decide;
|
|
10551
10593
|
let source;
|
|
10552
10594
|
let doctorFact;
|
|
10553
10595
|
const mergeService = {
|
|
@@ -10569,7 +10611,17 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10569
10611
|
};
|
|
10570
10612
|
const mergeContext = ctx;
|
|
10571
10613
|
if (typeof mergeContext.provide === "function") mergeContext.provide("evolutionMerge", mergeService);
|
|
10572
|
-
registerHostRoutes(ctx, (audit) => makeMergeRoutes({
|
|
10614
|
+
registerHostRoutes(ctx, (audit) => makeMergeRoutes({
|
|
10615
|
+
getSource: () => source,
|
|
10616
|
+
decide: async (request, _audit) => {
|
|
10617
|
+
if (decide === void 0) return {
|
|
10618
|
+
ok: false,
|
|
10619
|
+
code: "E-305",
|
|
10620
|
+
message: "合并服务尚未完成挂载"
|
|
10621
|
+
};
|
|
10622
|
+
return await decide(request);
|
|
10623
|
+
}
|
|
10624
|
+
}, audit));
|
|
10573
10625
|
ctx.inject(["commands", "evolutionIo"], (scope) => {
|
|
10574
10626
|
const registry = scope.get("evolutionIo");
|
|
10575
10627
|
if (registry === void 0) return;
|
|
@@ -10620,13 +10672,15 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10620
10672
|
});
|
|
10621
10673
|
}, "evolution-merge: restore supersede");
|
|
10622
10674
|
const limitsSnapshot = resolveLimitsSnapshot(ctx.get("evolutionPolicy")?.get?.());
|
|
10623
|
-
const
|
|
10624
|
-
|
|
10625
|
-
|
|
10626
|
-
...params,
|
|
10627
|
-
limitsSnapshot,
|
|
10628
|
-
platformWrite
|
|
10675
|
+
const getPlatformWrite = () => {
|
|
10676
|
+
const hostApproval = ctx.get("evolutionApproval");
|
|
10677
|
+
return typeof hostApproval?.platformWrite === "boolean" ? hostApproval.platformWrite : resolveMergeParams(rawConfig).platformWrite ?? false;
|
|
10629
10678
|
};
|
|
10679
|
+
const getParams = () => ({
|
|
10680
|
+
...resolveMergeParams(rawConfig),
|
|
10681
|
+
limitsSnapshot,
|
|
10682
|
+
platformWrite: getPlatformWrite()
|
|
10683
|
+
});
|
|
10630
10684
|
const skills = newSkillLibrary({
|
|
10631
10685
|
io,
|
|
10632
10686
|
ctx,
|
|
@@ -10669,7 +10723,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10669
10723
|
io,
|
|
10670
10724
|
home,
|
|
10671
10725
|
now: () => Date.now(),
|
|
10672
|
-
ttlDays:
|
|
10726
|
+
ttlDays: getParams().mergeSkippedTtlDays,
|
|
10673
10727
|
...live === void 0 ? {} : { live }
|
|
10674
10728
|
});
|
|
10675
10729
|
if (reconciled.actions.length > 0) ctx.logger?.info?.("merge reconcile: " + String(reconciled.actions.length) + " action(s)");
|
|
@@ -10706,7 +10760,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10706
10760
|
} : void 0;
|
|
10707
10761
|
});
|
|
10708
10762
|
const stage = new StageToolRegistry();
|
|
10709
|
-
const engineInput = resolveEngineInput({ modelsRoot:
|
|
10763
|
+
const engineInput = resolveEngineInput({ modelsRoot: startupParams.mergeModelsRoot });
|
|
10710
10764
|
const engineConfig = engineConfigFromRoot(engineInput.modelsRoot);
|
|
10711
10765
|
const llm = ctx.get("llm");
|
|
10712
10766
|
const defaultModel = ctx.get("agentDefaultModel");
|
|
@@ -10762,12 +10816,13 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10762
10816
|
});
|
|
10763
10817
|
source = {
|
|
10764
10818
|
async snapshot() {
|
|
10819
|
+
const current = getParams();
|
|
10765
10820
|
return await readMergeSnapshot({
|
|
10766
10821
|
io,
|
|
10767
10822
|
home,
|
|
10768
10823
|
runs: runs.runs("merge"),
|
|
10769
|
-
approval:
|
|
10770
|
-
platformWrite,
|
|
10824
|
+
approval: current.mergeApproval,
|
|
10825
|
+
platformWrite: getPlatformWrite(),
|
|
10771
10826
|
hostOnly: hostCapabilities.hostOnly,
|
|
10772
10827
|
live: async () => liveRowFactsOf(await readSkillCards(skills)),
|
|
10773
10828
|
history: async () => {
|
|
@@ -10788,18 +10843,20 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10788
10843
|
});
|
|
10789
10844
|
const archived = await skills.archivedNames?.() ?? [];
|
|
10790
10845
|
const reasonOf = skills.readArchiveReason;
|
|
10846
|
+
const archivedAtOf = skills.readArchivedAt;
|
|
10791
10847
|
for (const name of archived) {
|
|
10792
10848
|
if (items.has(name)) continue;
|
|
10793
10849
|
const rawReason = reasonOf === void 0 ? null : await reasonOf.call(skills, name).catch(() => null);
|
|
10794
10850
|
const lower = (rawReason ?? "").toLowerCase();
|
|
10795
10851
|
const kind = lower.includes("split") ? "split" : lower.includes("delet") || lower.includes("remove") ? "deleted" : "other";
|
|
10852
|
+
const archivedAt = archivedAtOf === void 0 ? null : await archivedAtOf.call(skills, name).catch(() => null);
|
|
10796
10853
|
items.set(name, {
|
|
10797
10854
|
name,
|
|
10798
10855
|
kind,
|
|
10799
10856
|
reason: rawReason ?? "技能已归档,最后一版保留可查。",
|
|
10800
10857
|
reasonCode: kind === "other" ? "archived" : kind,
|
|
10801
10858
|
lastVersion: 1,
|
|
10802
|
-
archivedAt
|
|
10859
|
+
archivedAt
|
|
10803
10860
|
});
|
|
10804
10861
|
}
|
|
10805
10862
|
const out = [];
|
|
@@ -10819,7 +10876,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10819
10876
|
return await resolveBody(io, home, ref, async () => ref.side === "archive" ? await skills.readArchived(ref.name) : await skills.read(ref.name));
|
|
10820
10877
|
},
|
|
10821
10878
|
approval() {
|
|
10822
|
-
return
|
|
10879
|
+
return getParams().mergeApproval;
|
|
10823
10880
|
},
|
|
10824
10881
|
readiness() {
|
|
10825
10882
|
return ready;
|
|
@@ -10855,13 +10912,13 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10855
10912
|
registry: runs,
|
|
10856
10913
|
skills,
|
|
10857
10914
|
now: () => Date.now(),
|
|
10858
|
-
|
|
10859
|
-
|
|
10915
|
+
getParams,
|
|
10916
|
+
getPlatformWrite,
|
|
10860
10917
|
...defaultModel === void 0 ? {} : { model: defaultModel.currentSelection().model },
|
|
10861
10918
|
channel,
|
|
10862
10919
|
parent: void 0,
|
|
10863
10920
|
allow: ["merge_stage"],
|
|
10864
|
-
timeoutMinutes:
|
|
10921
|
+
timeoutMinutes: startupParams.mergeRunTimeoutMinutes,
|
|
10865
10922
|
hostCapabilities,
|
|
10866
10923
|
isBlocked: async (name) => {
|
|
10867
10924
|
return await ctx.get("evolutionCurator")?.isBlocked?.(name) === true;
|
|
@@ -10893,6 +10950,20 @@ function apply(ctx, rawConfig = {}) {
|
|
|
10893
10950
|
};
|
|
10894
10951
|
return runMergeCommand(runtime, rawInput, invocation);
|
|
10895
10952
|
};
|
|
10953
|
+
const agents = ctx.get("agents");
|
|
10954
|
+
decide = async ({ rowId, verb, sessionId }) => {
|
|
10955
|
+
const agent = agents?.get(sessionId);
|
|
10956
|
+
if (agent === void 0 || agent === null) return {
|
|
10957
|
+
ok: false,
|
|
10958
|
+
code: "E-305",
|
|
10959
|
+
message: "当前会话没有可用的 Agent;请在会话打开时操作,或复制命令到 CLI 执行"
|
|
10960
|
+
};
|
|
10961
|
+
const result = await mergeService.run("/evolution merge " + verb + " " + rowId, { agent });
|
|
10962
|
+
return {
|
|
10963
|
+
ok: result.ok,
|
|
10964
|
+
message: result.text
|
|
10965
|
+
};
|
|
10966
|
+
};
|
|
10896
10967
|
const command = createMergeCommand(runtime);
|
|
10897
10968
|
if (command !== null) scope.effect(() => commands.register(command), "evolution-merge: command");
|
|
10898
10969
|
ctx.inject(["tools"], (toolScope) => {
|
|
@@ -47,7 +47,7 @@ export interface HistoryItem {
|
|
|
47
47
|
readonly reason: string;
|
|
48
48
|
readonly reasonCode?: string;
|
|
49
49
|
readonly lastVersion: number;
|
|
50
|
-
readonly archivedAt: number;
|
|
50
|
+
readonly archivedAt: number | null;
|
|
51
51
|
readonly sourceRunId?: string;
|
|
52
52
|
readonly runId?: string;
|
|
53
53
|
readonly absorbedInto?: string;
|
package/lib/types/index.js
CHANGED
|
@@ -137,8 +137,9 @@ export const inject = [];
|
|
|
137
137
|
export function apply(ctx, rawConfig = {}) {
|
|
138
138
|
registerOwnerSettings(ctx, 'evolution-merge', Config);
|
|
139
139
|
configureMergePresentation(ctx);
|
|
140
|
-
|
|
140
|
+
const startupParams = resolveMergeParams(rawConfig);
|
|
141
141
|
let mergeRun;
|
|
142
|
+
let decide;
|
|
142
143
|
let source;
|
|
143
144
|
let doctorFact;
|
|
144
145
|
const mergeService = {
|
|
@@ -157,7 +158,14 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
157
158
|
;
|
|
158
159
|
mergeContext.provide('evolutionMerge', mergeService);
|
|
159
160
|
}
|
|
160
|
-
registerHostRoutes(ctx, audit => makeMergeRoutes({
|
|
161
|
+
registerHostRoutes(ctx, audit => makeMergeRoutes({
|
|
162
|
+
getSource: () => source,
|
|
163
|
+
decide: async (request, _audit) => {
|
|
164
|
+
if (decide === undefined)
|
|
165
|
+
return { ok: false, code: 'E-305', message: '合并服务尚未完成挂载' };
|
|
166
|
+
return await decide(request);
|
|
167
|
+
},
|
|
168
|
+
}, audit));
|
|
161
169
|
ctx.inject(['commands', 'evolutionIo'], (scope) => {
|
|
162
170
|
const registry = scope.get('evolutionIo');
|
|
163
171
|
if (registry === undefined)
|
|
@@ -188,9 +196,11 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
188
196
|
}, 'evolution-merge: restore supersede');
|
|
189
197
|
const policySnapshot = ctx.get('evolutionPolicy');
|
|
190
198
|
const limitsSnapshot = resolveLimitsSnapshot(policySnapshot?.get?.());
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
199
|
+
const getPlatformWrite = () => {
|
|
200
|
+
const hostApproval = ctx.get('evolutionApproval');
|
|
201
|
+
return typeof hostApproval?.platformWrite === 'boolean' ? hostApproval.platformWrite : (resolveMergeParams(rawConfig).platformWrite ?? false);
|
|
202
|
+
};
|
|
203
|
+
const getParams = () => ({ ...resolveMergeParams(rawConfig), limitsSnapshot, platformWrite: getPlatformWrite() });
|
|
194
204
|
const skills = newSkillLibrary({
|
|
195
205
|
io, ctx, authority: authorityFrom({ origin: 'subagent' }),
|
|
196
206
|
limits: limitsSnapshot.skillLimits,
|
|
@@ -225,7 +235,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
225
235
|
const pending = v4.state.rows.some(row => row.state === 'pending');
|
|
226
236
|
const library = pending ? await cards().catch(() => []) : [];
|
|
227
237
|
const live = library.length === 0 ? undefined : liveRowFactsOf(library);
|
|
228
|
-
const reconciled = await reconcileMergeState({ io, home, now: () => Date.now(), ttlDays:
|
|
238
|
+
const reconciled = await reconcileMergeState({ io, home, now: () => Date.now(), ttlDays: getParams().mergeSkippedTtlDays, ...(live === undefined ? {} : { live }) });
|
|
229
239
|
if (reconciled.actions.length > 0)
|
|
230
240
|
ctx.logger?.info?.('merge reconcile: ' + String(reconciled.actions.length) + ' action(s)');
|
|
231
241
|
await transactIo(io, reportsSweepLockTarget(), async () => {
|
|
@@ -242,7 +252,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
242
252
|
return state.kind === 'unavailable' ? { code: 'E-323', detail: state.reason } : undefined;
|
|
243
253
|
});
|
|
244
254
|
const stage = new StageToolRegistry();
|
|
245
|
-
const engineInput = resolveEngineInput({ modelsRoot:
|
|
255
|
+
const engineInput = resolveEngineInput({ modelsRoot: startupParams.mergeModelsRoot });
|
|
246
256
|
const engineConfig = engineConfigFromRoot(engineInput.modelsRoot);
|
|
247
257
|
const llm = ctx.get('llm');
|
|
248
258
|
const defaultModel = ctx.get('agentDefaultModel');
|
|
@@ -283,7 +293,8 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
283
293
|
});
|
|
284
294
|
source = {
|
|
285
295
|
async snapshot() {
|
|
286
|
-
|
|
296
|
+
const current = getParams();
|
|
297
|
+
return await readMergeSnapshot({ io, home, runs: runs.runs('merge'), approval: current.mergeApproval, platformWrite: getPlatformWrite(), hostOnly: hostCapabilities.hostOnly, live: async () => liveRowFactsOf(await readSkillCards(skills)), history: async () => {
|
|
287
298
|
const items = new Map();
|
|
288
299
|
const receipts = [...await readCommitReceipts(io, home)].sort((a, b) => a.at - b.at);
|
|
289
300
|
const generationOf = new Map(receipts.map((receipt, index) => [receipt.runId, index + 1]));
|
|
@@ -292,13 +303,15 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
292
303
|
items.set(name, { name, kind: 'merged', reason: '已合并进「' + receipt.target + '」,最后一版保留可查。', reasonCode: 'merged', lastVersion: 1, archivedAt: receipt.at, sourceRunId: receipt.runId, runId: receipt.runId, absorbedInto: receipt.target, generation: generationOf.get(receipt.runId) ?? 1 });
|
|
293
304
|
const archived = await skills.archivedNames?.() ?? [];
|
|
294
305
|
const reasonOf = skills.readArchiveReason;
|
|
306
|
+
const archivedAtOf = skills.readArchivedAt;
|
|
295
307
|
for (const name of archived) {
|
|
296
308
|
if (items.has(name))
|
|
297
309
|
continue;
|
|
298
310
|
const rawReason = reasonOf === undefined ? null : await reasonOf.call(skills, name).catch(() => null);
|
|
299
311
|
const lower = (rawReason ?? '').toLowerCase();
|
|
300
312
|
const kind = lower.includes('split') ? 'split' : lower.includes('delet') || lower.includes('remove') ? 'deleted' : 'other';
|
|
301
|
-
|
|
313
|
+
const archivedAt = archivedAtOf === undefined ? null : await archivedAtOf.call(skills, name).catch(() => null);
|
|
314
|
+
items.set(name, { name, kind, reason: rawReason ?? '技能已归档,最后一版保留可查。', reasonCode: kind === 'other' ? 'archived' : kind, lastVersion: 1, archivedAt });
|
|
302
315
|
}
|
|
303
316
|
const out = [];
|
|
304
317
|
for (const item of items.values()) {
|
|
@@ -310,7 +323,7 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
310
323
|
} });
|
|
311
324
|
},
|
|
312
325
|
async bodyRef(ref) { return await resolveBody(io, home, ref, async () => ref.side === 'archive' ? await skills.readArchived(ref.name) : await skills.read(ref.name)); },
|
|
313
|
-
approval() { return
|
|
326
|
+
approval() { return getParams().mergeApproval; },
|
|
314
327
|
readiness() { return ready; },
|
|
315
328
|
commands() {
|
|
316
329
|
return ['merge scan', 'merge doctor', 'merge status', 'merge pending', 'merge skipped', 'merge report', 'merge runs', 'merge cancel', 'merge reconcile'];
|
|
@@ -327,9 +340,9 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
327
340
|
if (commands === undefined)
|
|
328
341
|
return;
|
|
329
342
|
const runtime = createMergeRuntime({
|
|
330
|
-
io, home, registry: runs, skills, now: () => Date.now(),
|
|
343
|
+
io, home, registry: runs, skills, now: () => Date.now(), getParams, getPlatformWrite,
|
|
331
344
|
...(defaultModel === undefined ? {} : { model: defaultModel.currentSelection().model }),
|
|
332
|
-
channel, parent: undefined, allow: ['merge_stage'], timeoutMinutes:
|
|
345
|
+
channel, parent: undefined, allow: ['merge_stage'], timeoutMinutes: startupParams.mergeRunTimeoutMinutes,
|
|
333
346
|
hostCapabilities,
|
|
334
347
|
isBlocked: async (name) => {
|
|
335
348
|
const curator = ctx.get('evolutionCurator');
|
|
@@ -358,6 +371,15 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
358
371
|
return { ok: false, text: formatMergeError(commandError('merge', state.code, state.message, '先修复合并状态(/evolution merge doctor),再重试')) };
|
|
359
372
|
return runMergeCommand(runtime, rawInput, invocation);
|
|
360
373
|
};
|
|
374
|
+
const agents = ctx.get('agents');
|
|
375
|
+
decide = async ({ rowId, verb, sessionId }) => {
|
|
376
|
+
const agent = agents?.get(sessionId);
|
|
377
|
+
if (agent === undefined || agent === null) {
|
|
378
|
+
return { ok: false, code: 'E-305', message: '当前会话没有可用的 Agent;请在会话打开时操作,或复制命令到 CLI 执行' };
|
|
379
|
+
}
|
|
380
|
+
const result = await mergeService.run('/evolution merge ' + verb + ' ' + rowId, { agent });
|
|
381
|
+
return { ok: result.ok, message: result.text };
|
|
382
|
+
};
|
|
361
383
|
const command = createMergeCommand(runtime);
|
|
362
384
|
if (command !== null)
|
|
363
385
|
scope.effect(() => commands.register(command), 'evolution-merge: command');
|
|
@@ -12,13 +12,25 @@ export declare const MERGE_ROUTES: {
|
|
|
12
12
|
rows: `/api/dsh-evolution/${string}`;
|
|
13
13
|
body: `/api/dsh-evolution/${string}`;
|
|
14
14
|
history: `/api/dsh-evolution/${string}`;
|
|
15
|
+
decision: `/api/dsh-evolution/${string}`;
|
|
15
16
|
};
|
|
17
|
+
export interface MergeDecisionRequest {
|
|
18
|
+
readonly rowId: string;
|
|
19
|
+
readonly verb: 'approve' | 'reject';
|
|
20
|
+
readonly sessionId: string;
|
|
21
|
+
}
|
|
22
|
+
export interface MergeDecisionResult {
|
|
23
|
+
readonly ok: boolean;
|
|
24
|
+
readonly message: string;
|
|
25
|
+
readonly code?: string;
|
|
26
|
+
readonly runId?: string;
|
|
27
|
+
}
|
|
16
28
|
export interface MergeHistoryItem {
|
|
17
29
|
readonly name: string;
|
|
18
30
|
readonly kind: 'merged' | 'deleted' | 'split' | 'other';
|
|
19
31
|
readonly reason: string;
|
|
20
32
|
readonly lastVersion: number;
|
|
21
|
-
readonly archivedAt: string;
|
|
33
|
+
readonly archivedAt: string | null;
|
|
22
34
|
readonly sourceRunId?: string;
|
|
23
35
|
}
|
|
24
36
|
export interface MergeRouteSource {
|
|
@@ -35,5 +47,6 @@ export interface MergeRouteSource {
|
|
|
35
47
|
}
|
|
36
48
|
export declare function makeMergeRoutes(deps: {
|
|
37
49
|
getSource: () => MergeRouteSource | undefined;
|
|
50
|
+
decide?: (request: MergeDecisionRequest, audit: DecisionAudit) => Promise<MergeDecisionResult>;
|
|
38
51
|
}, _audit: DecisionAudit): ConnectionFetchRouteLike[];
|
|
39
52
|
//# sourceMappingURL=merge-routes.d.ts.map
|
|
@@ -9,6 +9,7 @@ export const MERGE_ROUTES = {
|
|
|
9
9
|
rows: MERGE_PATHS.rows,
|
|
10
10
|
body: (MERGE_PATHS.body ?? MERGE_BASE + 'body'),
|
|
11
11
|
history: (MERGE_PATHS.history ?? MERGE_BASE + 'history'),
|
|
12
|
+
decision: (MERGE_BASE + 'decision'),
|
|
12
13
|
};
|
|
13
14
|
export function makeMergeRoutes(deps, _audit) {
|
|
14
15
|
const absent = () => json(404, { wireVersion: 1, code: 'E-325', message: 'merge service is not mounted' });
|
|
@@ -110,8 +111,6 @@ export function makeMergeRoutes(deps, _audit) {
|
|
|
110
111
|
const id = query.get('id');
|
|
111
112
|
const name = query.get('name');
|
|
112
113
|
const side = query.get('side');
|
|
113
|
-
if (id !== null && name !== null)
|
|
114
|
-
return bad('id and name are mutually exclusive');
|
|
115
114
|
if ((id === null || id === '') && (name === null || name === ''))
|
|
116
115
|
return json(404, { wireVersion: 1, code: 'merge-body-not-found', message: 'merge body requires id or name' });
|
|
117
116
|
if (source.snapshot !== undefined) {
|
|
@@ -120,16 +119,18 @@ export function makeMergeRoutes(deps, _audit) {
|
|
|
120
119
|
return json(503, { wireVersion: 1, code: 'merge-state-blocked', readiness: snapshot.blocked.readiness });
|
|
121
120
|
// Body/history are read-only projections of already-written files; a host without
|
|
122
121
|
// the live agent data plane can still read past reports and archived bodies.
|
|
123
|
-
const
|
|
122
|
+
const sideFilter = side === 'a' ? 'live' : side === 'b' ? 'archive' : side === 'live' || side === 'archive' ? side : undefined;
|
|
123
|
+
const refs = snapshot.bodyRefs.filter(ref => (id === null || id === '' || ref.rowId === id)
|
|
124
|
+
&& (name === null || name === '' || ref.name === name)
|
|
125
|
+
&& (sideFilter === undefined || ref.side === sideFilter));
|
|
124
126
|
if (refs.length === 0)
|
|
125
127
|
return json(404, { wireVersion: 1, code: 'merge-body-not-found', message: 'merge body not found' });
|
|
126
|
-
if (
|
|
127
|
-
return bad('
|
|
128
|
+
if (refs.length > 1)
|
|
129
|
+
return bad('merge body is ambiguous; pass id, name and side');
|
|
128
130
|
const row = snapshot.rows.find(item => item.row.id === refs[0].rowId);
|
|
129
131
|
if (row?.stage?.state === 'lost' || row?.stage?.state === 'expired')
|
|
130
132
|
return json(404, { wireVersion: 1, code: 'merge-body-not-found', message: 'merge stage is lost' });
|
|
131
|
-
const
|
|
132
|
-
const ref = (sideFilter === undefined ? refs[0] : refs.find(item => item.side === sideFilter)) ?? refs[0];
|
|
133
|
+
const ref = refs[0];
|
|
133
134
|
const content = source.bodyRef === undefined ? null : await source.bodyRef(ref);
|
|
134
135
|
return json(200, buildBodyView(snapshot, ref, content ?? ''));
|
|
135
136
|
}
|
|
@@ -167,6 +168,35 @@ export function makeMergeRoutes(deps, _audit) {
|
|
|
167
168
|
return json(200, toHistoryView(await source.history?.() ?? [], Date.now()));
|
|
168
169
|
},
|
|
169
170
|
},
|
|
171
|
+
{
|
|
172
|
+
path: MERGE_ROUTES.decision,
|
|
173
|
+
methods: ['POST'],
|
|
174
|
+
requestBody: 'buffered',
|
|
175
|
+
fetch: async (request) => {
|
|
176
|
+
if (request.method !== 'POST')
|
|
177
|
+
return methodNotAllowed(['POST']);
|
|
178
|
+
if (deps.decide === undefined)
|
|
179
|
+
return absent();
|
|
180
|
+
let body;
|
|
181
|
+
try {
|
|
182
|
+
body = await request.json();
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return bad('decision body must be JSON');
|
|
186
|
+
}
|
|
187
|
+
if (typeof body !== 'object' || body === null)
|
|
188
|
+
return bad('decision body must be an object');
|
|
189
|
+
const input = body;
|
|
190
|
+
if (typeof input.rowId !== 'string' || input.rowId === '')
|
|
191
|
+
return bad('rowId is required');
|
|
192
|
+
if (input.verb !== 'approve' && input.verb !== 'reject')
|
|
193
|
+
return bad('verb must be approve or reject');
|
|
194
|
+
if (typeof input.sessionId !== 'string' || input.sessionId === '')
|
|
195
|
+
return bad('sessionId is required');
|
|
196
|
+
const result = await deps.decide({ rowId: input.rowId, verb: input.verb, sessionId: input.sessionId }, _audit);
|
|
197
|
+
return json(result.ok ? 200 : result.code === 'E-305' ? 409 : 400, { wireVersion: 1, ...result });
|
|
198
|
+
},
|
|
199
|
+
},
|
|
170
200
|
];
|
|
171
201
|
}
|
|
172
202
|
//# sourceMappingURL=merge-routes.js.map
|
package/lib/types/runtime.d.ts
CHANGED
|
@@ -18,8 +18,13 @@ export interface MergeRuntimeDeps {
|
|
|
18
18
|
readonly registry: MergeRegistryPort;
|
|
19
19
|
readonly skills: SkillLibrary;
|
|
20
20
|
readonly now: () => number;
|
|
21
|
-
|
|
22
|
-
readonly
|
|
21
|
+
/** Live params: re-resolve at each operation, never cache an apply-time value. */
|
|
22
|
+
readonly getParams?: () => MergeParams;
|
|
23
|
+
/** Legacy fixture shape; used only when getParams is absent. */
|
|
24
|
+
readonly params?: MergeParams;
|
|
25
|
+
readonly getPlatformWrite?: () => boolean;
|
|
26
|
+
/** Legacy fixture shape; used only when getPlatformWrite is absent. */
|
|
27
|
+
readonly platformWrite?: boolean;
|
|
23
28
|
readonly channel: ConversationChannelLike;
|
|
24
29
|
readonly hostCapabilities?: HostCapabilities;
|
|
25
30
|
readonly parent: unknown;
|
package/lib/types/runtime.js
CHANGED
|
@@ -78,13 +78,21 @@ export function unitFor(deps) {
|
|
|
78
78
|
export function createMergeRuntime(deps) {
|
|
79
79
|
const readOnly = createReadOnlyMergeService(deps.io, deps.home);
|
|
80
80
|
const timeout = deps.timeoutMinutes > 0 ? deps.timeoutMinutes : undefined;
|
|
81
|
-
const
|
|
81
|
+
const paramsOf = () => {
|
|
82
|
+
if (deps.getParams !== undefined)
|
|
83
|
+
return deps.getParams();
|
|
84
|
+
if (deps.params !== undefined)
|
|
85
|
+
return deps.params;
|
|
86
|
+
throw new Error('merge runtime requires getParams or params');
|
|
87
|
+
};
|
|
88
|
+
const platformWriteOf = () => deps.getPlatformWrite?.() ?? deps.platformWrite ?? false;
|
|
89
|
+
const common = () => ({
|
|
82
90
|
io: deps.io, home: deps.home, registry: deps.registry,
|
|
83
|
-
platformWrite:
|
|
91
|
+
platformWrite: platformWriteOf(), now: deps.now,
|
|
84
92
|
...(deps.model === undefined ? {} : { model: deps.model }),
|
|
85
93
|
...(deps.isOwnerActive === undefined ? {} : { isOwnerActive: deps.isOwnerActive }),
|
|
86
94
|
...(timeout === undefined ? {} : { timeoutMinutes: timeout }),
|
|
87
|
-
};
|
|
95
|
+
});
|
|
88
96
|
const stage = deps.stage;
|
|
89
97
|
const stageHooks = stage === undefined ? {} : {
|
|
90
98
|
onChild: (context) => { stage.bind(context); },
|
|
@@ -95,6 +103,7 @@ export function createMergeRuntime(deps) {
|
|
|
95
103
|
const parent = context?.parent ?? deps.parent;
|
|
96
104
|
const agentless = context?.agentless === true;
|
|
97
105
|
const detached = deps.detached !== false;
|
|
106
|
+
const params = paramsOf();
|
|
98
107
|
if (command.verb === 'pair') {
|
|
99
108
|
const signal = new AbortController().signal;
|
|
100
109
|
if (command.a === command.b)
|
|
@@ -155,9 +164,9 @@ export function createMergeRuntime(deps) {
|
|
|
155
164
|
return fail('pair', 'pair-fuse', '该 pair 已在待合并队列,可复用决策重跑', '运行 /evolution merge resume --retry ' + (existing.runId ?? existing.id), existing.runId);
|
|
156
165
|
}
|
|
157
166
|
const forced = command.force === true ? forcedDecisionFor(command.a, command.b) : undefined;
|
|
158
|
-
const approval =
|
|
167
|
+
const approval = params.mergeApproval;
|
|
159
168
|
const result = await runScanCommand({
|
|
160
|
-
...common,
|
|
169
|
+
...common(),
|
|
161
170
|
approval,
|
|
162
171
|
cards,
|
|
163
172
|
...(detached ? { detached: true } : {}),
|
|
@@ -167,10 +176,10 @@ export function createMergeRuntime(deps) {
|
|
|
167
176
|
screening: () => Promise.resolve(pair),
|
|
168
177
|
channel: deps.channel, parent, allow: deps.allow,
|
|
169
178
|
...(deps.cwd === undefined ? {} : { cwd: deps.cwd }),
|
|
170
|
-
timeoutMs:
|
|
179
|
+
timeoutMs: params.mergePairTimeoutMinutes * 60_000,
|
|
171
180
|
approval,
|
|
172
181
|
commitPort: skillLibraryPort(deps.skills), now: deps.now, ...(unitFor(deps) === undefined ? {} : { unit: unitFor(deps) }),
|
|
173
|
-
...(
|
|
182
|
+
...(params.limitsSnapshot === undefined ? {} : { limits: params.limitsSnapshot.skillLimits }),
|
|
174
183
|
...(deps.isBlocked === undefined ? {} : { isBlocked: deps.isBlocked }),
|
|
175
184
|
...stageHooks,
|
|
176
185
|
});
|
|
@@ -201,7 +210,7 @@ export function createMergeRuntime(deps) {
|
|
|
201
210
|
.filter(card => card.managed === true && card.protectedBy == null && !blocked.has(card.name))
|
|
202
211
|
.map(card => card.name));
|
|
203
212
|
const loadedState = await loadMergeState(deps.io, deps.home);
|
|
204
|
-
const skipped = skippedPairKeys(loadedState.state.rows,
|
|
213
|
+
const skipped = skippedPairKeys(loadedState.state.rows, params.mergeSkippedTtlDays, deps.now());
|
|
205
214
|
const held = new Set(loadedState.state.rows.filter(row => row.queue === 'pending' || row.queue === 'fuse'
|
|
206
215
|
|| (row.queue === 'verify' && (row.runId !== undefined && deps.now() - Date.parse(row.updatedAt) < 10 * 60_000 || !canRetry(row))))
|
|
207
216
|
.map(row => row.pairKey));
|
|
@@ -209,8 +218,8 @@ export function createMergeRuntime(deps) {
|
|
|
209
218
|
.filter(row => row.state === 'pending' || row.state === 'approved' || row.state === 'fuse' || row.state === 'skipped')
|
|
210
219
|
.map(row => 'conflict:' + row.state + ':' + row.pairKey + ':' + row.id);
|
|
211
220
|
const result = await runScanCommand({
|
|
212
|
-
...common,
|
|
213
|
-
approval:
|
|
221
|
+
...common(),
|
|
222
|
+
approval: params.mergeApproval,
|
|
214
223
|
notes,
|
|
215
224
|
cards,
|
|
216
225
|
...(detached ? { detached: true } : {}),
|
|
@@ -238,10 +247,10 @@ export function createMergeRuntime(deps) {
|
|
|
238
247
|
},
|
|
239
248
|
channel: deps.channel, parent, allow: deps.allow,
|
|
240
249
|
...(deps.cwd === undefined ? {} : { cwd: deps.cwd }),
|
|
241
|
-
timeoutMs:
|
|
242
|
-
approval:
|
|
250
|
+
timeoutMs: params.mergePairTimeoutMinutes * 60_000,
|
|
251
|
+
approval: params.mergeApproval,
|
|
243
252
|
commitPort: skillLibraryPort(deps.skills), now: deps.now, ...(unitFor(deps) === undefined ? {} : { unit: unitFor(deps) }),
|
|
244
|
-
...(
|
|
253
|
+
...(params.limitsSnapshot === undefined ? {} : { limits: params.limitsSnapshot.skillLimits }),
|
|
245
254
|
...(deps.isBlocked === undefined ? {} : { isBlocked: deps.isBlocked }),
|
|
246
255
|
...stageHooks,
|
|
247
256
|
}),
|
|
@@ -267,8 +276,8 @@ export function createMergeRuntime(deps) {
|
|
|
267
276
|
const cards = await deps.cards(signal);
|
|
268
277
|
const pairCandidate = { a: row.a, b: row.b };
|
|
269
278
|
const result = await runScanCommand({
|
|
270
|
-
...common,
|
|
271
|
-
approval:
|
|
279
|
+
...common(),
|
|
280
|
+
approval: params.mergeApproval,
|
|
272
281
|
notes: ['candidates --retry ' + row.id],
|
|
273
282
|
cards,
|
|
274
283
|
makePorts: runId => createChannelScanPorts({
|
|
@@ -280,10 +289,10 @@ export function createMergeRuntime(deps) {
|
|
|
280
289
|
},
|
|
281
290
|
channel: deps.channel, parent, allow: deps.allow,
|
|
282
291
|
...(deps.cwd === undefined ? {} : { cwd: deps.cwd }),
|
|
283
|
-
timeoutMs:
|
|
284
|
-
approval:
|
|
292
|
+
timeoutMs: params.mergePairTimeoutMinutes * 60_000,
|
|
293
|
+
approval: params.mergeApproval,
|
|
285
294
|
commitPort: skillLibraryPort(deps.skills), now: deps.now, ...(unitFor(deps) === undefined ? {} : { unit: unitFor(deps) }),
|
|
286
|
-
...(
|
|
295
|
+
...(params.limitsSnapshot === undefined ? {} : { limits: params.limitsSnapshot.skillLimits }),
|
|
287
296
|
...(deps.isBlocked === undefined ? {} : { isBlocked: deps.isBlocked }),
|
|
288
297
|
...stageHooks,
|
|
289
298
|
}),
|
|
@@ -318,7 +327,7 @@ export function createMergeRuntime(deps) {
|
|
|
318
327
|
const managed = cards === undefined || cards.length === 0 ? undefined : new Set(cards.map(card => card.name));
|
|
319
328
|
const live = cards === undefined || cards.length === 0 ? undefined : liveRowFactsOf(cards);
|
|
320
329
|
try {
|
|
321
|
-
const report = await reconcileMergeState({ io: deps.io, home: deps.home, now: deps.now, ttlDays:
|
|
330
|
+
const report = await reconcileMergeState({ io: deps.io, home: deps.home, now: deps.now, ttlDays: params.mergeSkippedTtlDays, ...(managed === undefined ? {} : { managed }), ...(live === undefined ? {} : { live }) });
|
|
322
331
|
if (report.state === 'blocked')
|
|
323
332
|
return fail('reconcile', report.code ?? 'merge-state-blocked', report.message ?? '合并状态不可读', '先修复合并状态,再运行 /evolution merge doctor');
|
|
324
333
|
return { ok: true, text: formatReconcileReport(report) };
|
|
@@ -328,14 +337,14 @@ export function createMergeRuntime(deps) {
|
|
|
328
337
|
}
|
|
329
338
|
}
|
|
330
339
|
if (command.verb === 'doctor') {
|
|
331
|
-
const rows = await mergeDoctor(deps.io, deps.home,
|
|
340
|
+
const rows = await mergeDoctor(deps.io, deps.home, params.mergeModelsRoot);
|
|
332
341
|
return { ok: true, text: rows.map(row => row.key + ' [' + row.status + '] ' + row.label + ':' + row.detail).join('\n') };
|
|
333
342
|
}
|
|
334
343
|
if (command.verb === 'report' || command.verb === 'runs' || command.verb === 'cancel') {
|
|
335
344
|
const registry = deps.registry;
|
|
336
345
|
if (command.verb === 'runs') {
|
|
337
346
|
const mergeRuns = registry.runs('merge');
|
|
338
|
-
const snapshot = await readMergeSnapshot({ io: deps.io, home: deps.home, runs: mergeRuns, approval:
|
|
347
|
+
const snapshot = await readMergeSnapshot({ io: deps.io, home: deps.home, runs: mergeRuns, approval: params.mergeApproval, now: deps.now });
|
|
339
348
|
return { ok: true, text: formatRunsView(buildRunsView(snapshot)) };
|
|
340
349
|
}
|
|
341
350
|
if (command.verb === 'cancel') {
|
|
@@ -443,27 +452,27 @@ export function createMergeRuntime(deps) {
|
|
|
443
452
|
round2: () => failed('author', 'resume-needs-child', 'fuse resume needs a new child'),
|
|
444
453
|
};
|
|
445
454
|
const result = await runResumeCommand({
|
|
446
|
-
...common, rowId: row.id, ports: basePorts, approval:
|
|
455
|
+
...common(), rowId: row.id, ports: basePorts, approval: params.mergeApproval,
|
|
447
456
|
...(agentless ? { agentless: true } : {}),
|
|
448
457
|
...(detached ? { detached: true } : {}),
|
|
449
|
-
pairTimeoutMinutes:
|
|
458
|
+
pairTimeoutMinutes: params.mergePairTimeoutMinutes,
|
|
450
459
|
...(deps.isOwnerActive === undefined ? {} : { isOwnerActive: deps.isOwnerActive }),
|
|
451
460
|
makePorts: runId => createChannelScanPorts({
|
|
452
461
|
io: deps.io, home: deps.home, runId, cards, screening: pair,
|
|
453
462
|
channel: deps.channel, parent, allow: deps.allow,
|
|
454
463
|
...(deps.cwd === undefined ? {} : { cwd: deps.cwd }),
|
|
455
|
-
timeoutMs:
|
|
456
|
-
approval:
|
|
457
|
-
...(
|
|
464
|
+
timeoutMs: params.mergePairTimeoutMinutes * 60_000,
|
|
465
|
+
approval: params.mergeApproval, commitPort: skillLibraryPort(deps.skills), now: deps.now, ...(unitFor(deps) === undefined ? {} : { unit: unitFor(deps) }),
|
|
466
|
+
...(params.limitsSnapshot === undefined ? {} : { limits: params.limitsSnapshot.skillLimits }),
|
|
458
467
|
...(deps.isBlocked === undefined ? {} : { isBlocked: deps.isBlocked }),
|
|
459
468
|
}),
|
|
460
469
|
round2WithRun: createNewChildRound2({
|
|
461
470
|
io: deps.io, home: deps.home, cards, channel: deps.channel, parent,
|
|
462
471
|
parentRunId: (runId) => runId,
|
|
463
472
|
allow: deps.allow, ...(deps.cwd === undefined ? {} : { cwd: deps.cwd }),
|
|
464
|
-
timeoutMs:
|
|
473
|
+
timeoutMs: params.mergePairTimeoutMinutes * 60_000,
|
|
465
474
|
commitPort: skillLibraryPort(deps.skills), now: deps.now, ...(unitFor(deps) === undefined ? {} : { unit: unitFor(deps) }),
|
|
466
|
-
...(
|
|
475
|
+
...(params.limitsSnapshot === undefined ? {} : { limits: params.limitsSnapshot.skillLimits }),
|
|
467
476
|
...(deps.isBlocked === undefined ? {} : { isBlocked: deps.isBlocked }),
|
|
468
477
|
...stageHooks,
|
|
469
478
|
}),
|
|
@@ -563,20 +572,20 @@ export function createMergeRuntime(deps) {
|
|
|
563
572
|
}
|
|
564
573
|
}
|
|
565
574
|
const result = await runApproveCommand({
|
|
566
|
-
...common,
|
|
575
|
+
...common(),
|
|
567
576
|
rowId: command.id,
|
|
568
577
|
...(agentless ? { agentless: true } : {}),
|
|
569
578
|
...(detached ? { detached: true } : {}),
|
|
570
|
-
pairTimeoutMinutes:
|
|
579
|
+
pairTimeoutMinutes: params.mergePairTimeoutMinutes,
|
|
571
580
|
ports: {
|
|
572
581
|
stage: createStageForCards({ io: deps.io, home: deps.home, runId: command.id, cards, now: deps.now }),
|
|
573
582
|
round2: createNewChildRound2({
|
|
574
583
|
io: deps.io, home: deps.home, cards, channel: deps.channel, parent,
|
|
575
584
|
parentRunId: (runId) => runId,
|
|
576
585
|
allow: deps.allow, ...(deps.cwd === undefined ? {} : { cwd: deps.cwd }),
|
|
577
|
-
timeoutMs:
|
|
586
|
+
timeoutMs: params.mergePairTimeoutMinutes * 60_000,
|
|
578
587
|
commitPort: skillLibraryPort(deps.skills), now: deps.now, ...(unitFor(deps) === undefined ? {} : { unit: unitFor(deps) }),
|
|
579
|
-
...(
|
|
588
|
+
...(params.limitsSnapshot === undefined ? {} : { limits: params.limitsSnapshot.skillLimits }),
|
|
580
589
|
...(deps.isBlocked === undefined ? {} : { isBlocked: deps.isBlocked }),
|
|
581
590
|
...stageHooks,
|
|
582
591
|
}),
|
|
@@ -597,7 +606,7 @@ export function createMergeRuntime(deps) {
|
|
|
597
606
|
const found = runs?.find(item => item.id === command.id) ?? registry.find(command.id);
|
|
598
607
|
if (found !== undefined && found.kind === 'merge') {
|
|
599
608
|
// CLI consumer of the RunDetail envelope: current pair, per-pair outcome, stop reason.
|
|
600
|
-
const snapshot = await readMergeSnapshot({ io: deps.io, home: deps.home, runs: runs !== undefined && runs.some(item => item.id === found.id) ? runs : [...(runs ?? []), found], approval:
|
|
609
|
+
const snapshot = await readMergeSnapshot({ io: deps.io, home: deps.home, runs: runs !== undefined && runs.some(item => item.id === found.id) ? runs : [...(runs ?? []), found], approval: params.mergeApproval, now: deps.now });
|
|
601
610
|
if (snapshot.blocked !== undefined)
|
|
602
611
|
return fail('status', snapshot.blocked.code, snapshot.blocked.message, '先修复合并状态(/evolution merge doctor),再查看运行');
|
|
603
612
|
const detail = buildRunDetail(snapshot, found.id);
|
|
@@ -615,7 +624,7 @@ export function createMergeRuntime(deps) {
|
|
|
615
624
|
const list = cards ?? await deps.cards(new AbortController().signal).catch(() => []);
|
|
616
625
|
return list.length === 0 ? undefined : liveRowFactsOf(list);
|
|
617
626
|
};
|
|
618
|
-
const text = await handleReadOnlyMergeCommand(command, deps.io, deps.home, cards, runs,
|
|
627
|
+
const text = await handleReadOnlyMergeCommand(command, deps.io, deps.home, cards, runs, params.mergeApproval, liveForRead);
|
|
619
628
|
if (text !== null)
|
|
620
629
|
return { ok: true, text };
|
|
621
630
|
return readOnly.execute(command);
|
|
@@ -88,9 +88,11 @@ function runView(run, readAt, includeResultRef = false) {
|
|
|
88
88
|
const stopReason = run.meta?.stopReason ?? (record.state === 'cancelled' ? 'cancelled' : record.state === 'failed' ? 'failed' : record.state === 'succeeded' ? 'completed' : undefined);
|
|
89
89
|
// The maker records progress while running; a terminal run that actually produced pairs
|
|
90
90
|
// must not keep the last in-flight 0/N. The UI shows what happened, not the last tick.
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
const reportPairs = run.report?.processedPairs?.length ?? run.report?.pairs.length;
|
|
92
|
+
const terminal = state === 'succeeded' || state === 'undone';
|
|
93
|
+
const progress = record.progress === undefined && reportPairs === undefined ? null : {
|
|
94
|
+
done: terminal ? Math.max(record.progress?.done ?? 0, reportPairs ?? 0, run.pairs.length) : record.progress?.done ?? reportPairs ?? 0,
|
|
95
|
+
total: record.progress?.total ?? reportPairs ?? 0,
|
|
94
96
|
};
|
|
95
97
|
return { wireVersion: 1, readAt, id: record.id, state, command: record.command ?? 'scan', ...(record.stage === undefined ? {} : { stage: record.stage }), progress, startedAt: record.startedAt, ...(record.endedAt === undefined ? {} : { endedAt: record.endedAt }), ...(stopReason === undefined ? {} : { stopReason }), ...(record.failure === undefined ? {} : { failure: { stage: 'settle', code: 'run-failed', retryable: false, retryFrom: 'none', detail: record.failure } }), ...(record.parentRunId === undefined ? {} : { parentRunId: record.parentRunId }), ...(run.pairs[0]?.pairKey === undefined ? {} : { currentPairKey: run.pairs[0].pairKey }), ...(record.resultRef === undefined ? {} : { resultRef: record.resultRef }), actions: [{ id: 'status', command: base + 'status ' + record.id, confirm: false, enabled: true }, { id: 'report', command: base + 'report ' + record.id, confirm: false, enabled: record.resultRef !== undefined }, { id: 'cancel', command: base + 'cancel ' + record.id, confirm: false, enabled: record.state === 'running' }] };
|
|
96
98
|
}
|
|
@@ -30,7 +30,7 @@ export declare function toHistoryView(items: readonly {
|
|
|
30
30
|
kind: 'merged' | 'deleted' | 'split' | 'other';
|
|
31
31
|
reason: string;
|
|
32
32
|
lastVersion: number;
|
|
33
|
-
archivedAt: string;
|
|
33
|
+
archivedAt: string | number | null;
|
|
34
34
|
sourceRunId?: string;
|
|
35
35
|
}[], readAt: number): HistoryView;
|
|
36
36
|
//# sourceMappingURL=wire-projection.d.ts.map
|
|
@@ -69,6 +69,9 @@ export function toBodyView(name, side, text, readAt) {
|
|
|
69
69
|
return { wireVersion: 1, readAt, name, side, text, markdown: true, truncated: false, totalBytes: Buffer.byteLength(text, 'utf8') };
|
|
70
70
|
}
|
|
71
71
|
export function toHistoryView(items, readAt) {
|
|
72
|
-
return { wireVersion: 1, readAt, items: items.map(item =>
|
|
72
|
+
return { wireVersion: 1, readAt, items: items.map(item => {
|
|
73
|
+
const parsed = item.archivedAt === null ? null : typeof item.archivedAt === 'number' ? item.archivedAt : Date.parse(item.archivedAt);
|
|
74
|
+
return { name: item.name, kind: item.kind, reason: item.reason, lastVersion: item.lastVersion, archivedAt: parsed === null || Number.isFinite(parsed) ? parsed : null, ...(item.sourceRunId === undefined ? {} : { sourceRunId: item.sourceRunId }) };
|
|
75
|
+
}) };
|
|
73
76
|
}
|
|
74
77
|
//# sourceMappingURL=wire-projection.js.map
|
package/lib/types/wire.d.ts
CHANGED
|
@@ -184,7 +184,7 @@ export interface HistoryItem {
|
|
|
184
184
|
readonly reasonCode?: string;
|
|
185
185
|
readonly generation?: number;
|
|
186
186
|
readonly lastVersion: number;
|
|
187
|
-
readonly archivedAt: number;
|
|
187
|
+
readonly archivedAt: number | null;
|
|
188
188
|
readonly sourceRunId?: string;
|
|
189
189
|
readonly runId?: string;
|
|
190
190
|
readonly absorbedInto?: string;
|
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.
|
|
4
|
+
"version": "0.35.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@deepseek-ai/schemastery": "^3.18.5",
|
|
35
|
-
"@lmzhen/dsh-evolution-core": "^0.
|
|
36
|
-
"@lmzhen/dsh-evolution-commands": "^0.
|
|
37
|
-
"@lmzhen/dsh-evolution-merge-engine": "0.
|
|
35
|
+
"@lmzhen/dsh-evolution-core": "^0.35.0",
|
|
36
|
+
"@lmzhen/dsh-evolution-commands": "^0.35.0",
|
|
37
|
+
"@lmzhen/dsh-evolution-merge-engine": "0.35.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@deepseek-ai/cordis": "^4.0.5"
|