@lmzhen/dsh-evolution-merge 0.30.17 → 0.30.19
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 +25 -7
- package/data/minhash_params.json +1063 -0
- package/lib/index.js +7308 -1800
- package/lib/types/adapters/channel.d.ts +1 -0
- package/lib/types/adapters/channel.js +15 -1
- package/lib/types/adapters/engine-screen.d.ts +1 -1
- package/lib/types/adapters/engine-screen.js +5 -2
- package/lib/types/adapters/library.js +4 -1
- package/lib/types/adapters/model-runner.d.ts +0 -5
- package/lib/types/adapters/model-runner.js +8 -2
- package/lib/types/adapters/model.d.ts +31 -0
- package/lib/types/adapters/model.js +47 -0
- package/lib/types/adapters/select.js +12 -8
- package/lib/types/approve-command.d.ts +14 -1
- package/lib/types/approve-command.js +72 -44
- package/lib/types/authoring-flow.d.ts +19 -1
- package/lib/types/authoring-flow.js +49 -15
- package/lib/types/channel-port.d.ts +3 -1
- package/lib/types/channel-port.js +8 -3
- package/lib/types/command-defs.d.ts +5 -0
- package/lib/types/command-defs.js +33 -0
- package/lib/types/command-surface.d.ts +5 -0
- package/lib/types/command-surface.js +11 -0
- package/lib/types/commands.d.ts +3 -4
- package/lib/types/commands.js +33 -72
- package/lib/types/commit-assembly.d.ts +16 -1
- package/lib/types/commit-assembly.js +72 -11
- package/lib/types/commit-execute.d.ts +14 -2
- package/lib/types/commit-execute.js +76 -14
- package/lib/types/commit-plan.d.ts +7 -1
- package/lib/types/commit-plan.js +4 -0
- package/lib/types/commit-receipt.d.ts +3 -0
- package/lib/types/commit-recovery.d.ts +7 -2
- package/lib/types/commit-recovery.js +73 -96
- package/lib/types/contracts.d.ts +44 -1
- package/lib/types/contracts.js +1 -1
- package/lib/types/coordinator.d.ts +3 -2
- package/lib/types/coordinator.js +4 -3
- package/lib/types/decision.js +1 -1
- package/lib/types/doctor.d.ts +18 -1
- package/lib/types/doctor.js +164 -10
- package/lib/types/domain/authoring-manifest.d.ts +26 -0
- package/lib/types/domain/authoring-manifest.js +43 -0
- package/lib/types/domain/claim.d.ts +4 -1
- package/lib/types/domain/claim.js +2 -2
- package/lib/types/domain/commit-journal-store.js +5 -1
- package/lib/types/domain/commit-journal.js +1 -1
- package/lib/types/domain/consistency.d.ts +22 -0
- package/lib/types/domain/consistency.js +50 -0
- package/lib/types/domain/engine-criteria.d.ts +10 -0
- package/lib/types/domain/engine-criteria.js +11 -0
- package/lib/types/domain/engine-input.d.ts +10 -0
- package/lib/types/domain/engine-input.js +26 -0
- package/lib/types/domain/errors.d.ts +15 -1
- package/lib/types/domain/errors.js +23 -4
- package/lib/types/domain/evidence-hash.d.ts +3 -0
- package/lib/types/domain/evidence-hash.js +14 -0
- package/lib/types/domain/failure-route.d.ts +14 -0
- package/lib/types/domain/failure-route.js +12 -0
- package/lib/types/domain/ledger.js +10 -1
- package/lib/types/domain/live-facts.d.ts +31 -0
- package/lib/types/domain/live-facts.js +54 -0
- package/lib/types/domain/merge-limits.d.ts +18 -0
- package/lib/types/domain/merge-limits.js +22 -0
- package/lib/types/domain/merge-report.d.ts +41 -0
- package/lib/types/domain/merge-report.js +2 -0
- package/lib/types/domain/merge-snapshot.d.ts +99 -0
- package/lib/types/domain/merge-snapshot.js +82 -0
- package/lib/types/domain/migrate-v3.d.ts +20 -0
- package/lib/types/domain/migrate-v3.js +68 -0
- package/lib/types/domain/pair-ledger.d.ts +34 -0
- package/lib/types/domain/pair-ledger.js +96 -0
- package/lib/types/domain/pre-1b-backup.d.ts +43 -0
- package/lib/types/domain/pre-1b-backup.js +140 -0
- package/lib/types/domain/provider-key.d.ts +2 -0
- package/lib/types/domain/provider-key.js +11 -0
- package/lib/types/domain/read-probe.d.ts +5 -0
- package/lib/types/domain/read-probe.js +11 -0
- package/lib/types/domain/reconcile.d.ts +47 -0
- package/lib/types/domain/reconcile.js +178 -0
- package/lib/types/domain/resolve-pair.d.ts +2 -9
- package/lib/types/domain/resolve-pair.js +2 -15
- package/lib/types/domain/row-tx.d.ts +27 -0
- package/lib/types/domain/row-tx.js +31 -0
- package/lib/types/domain/row-v3-bridge.d.ts +6 -0
- package/lib/types/domain/row-v3-bridge.js +47 -0
- package/lib/types/domain/row-v3.d.ts +113 -0
- package/lib/types/domain/row-v3.js +107 -0
- package/lib/types/domain/row-v4.d.ts +148 -0
- package/lib/types/domain/row-v4.js +159 -0
- package/lib/types/domain/row.d.ts +1 -0
- package/lib/types/domain/run-coordinator.d.ts +51 -0
- package/lib/types/domain/run-coordinator.js +45 -0
- package/lib/types/domain/schema4.d.ts +53 -0
- package/lib/types/domain/schema4.js +129 -0
- package/lib/types/domain/selection-policy.d.ts +14 -0
- package/lib/types/domain/selection-policy.js +6 -0
- package/lib/types/domain/skipped-expiry.d.ts +5 -0
- package/lib/types/domain/skipped-expiry.js +16 -0
- package/lib/types/domain/stage-aggregate.d.ts +96 -0
- package/lib/types/domain/stage-aggregate.js +293 -0
- package/lib/types/domain/staging-index.d.ts +15 -0
- package/lib/types/domain/staging-index.js +41 -0
- package/lib/types/domain/state-bootstrap.d.ts +43 -0
- package/lib/types/domain/state-bootstrap.js +267 -0
- package/lib/types/domain/state-v3.d.ts +4 -0
- package/lib/types/domain/state-v3.js +4 -0
- package/lib/types/domain/undo-journal.d.ts +19 -0
- package/lib/types/domain/undo-journal.js +20 -0
- package/lib/types/domain/undo-receipt.d.ts +18 -0
- package/lib/types/domain/undo-receipt.js +22 -0
- package/lib/types/domain/validation-plan.d.ts +65 -0
- package/lib/types/domain/validation-plan.js +49 -0
- package/lib/types/facts.d.ts +26 -0
- package/lib/types/facts.js +65 -0
- package/lib/types/gate-filter.d.ts +31 -0
- package/lib/types/gate-filter.js +64 -0
- package/lib/types/index.d.ts +7 -2
- package/lib/types/index.js +185 -82
- package/lib/types/ladder.js +1 -1
- package/lib/types/merge-command.d.ts +3 -1
- package/lib/types/merge-command.js +10 -6
- package/lib/types/merge-routes.d.ts +12 -7
- package/lib/types/merge-routes.js +87 -23
- package/lib/types/merge-tool.d.ts +1 -1
- package/lib/types/merge-tool.js +2 -2
- package/lib/types/metrics.d.ts +16 -0
- package/lib/types/metrics.js +27 -0
- package/lib/types/new-child-authoring.d.ts +5 -1
- package/lib/types/new-child-authoring.js +71 -13
- package/lib/types/params.d.ts +2 -4
- package/lib/types/params.js +13 -0
- package/lib/types/pending-actions.d.ts +2 -2
- package/lib/types/pending-actions.js +17 -17
- package/lib/types/prompts.d.ts +2 -4
- package/lib/types/prompts.js +21 -8
- package/lib/types/propose.d.ts +26 -0
- package/lib/types/propose.js +69 -0
- package/lib/types/read-commands.d.ts +4 -4
- package/lib/types/read-commands.js +19 -26
- package/lib/types/recovery-coordinator.d.ts +34 -0
- package/lib/types/recovery-coordinator.js +84 -0
- package/lib/types/recovery-projection.d.ts +31 -0
- package/lib/types/recovery-projection.js +96 -0
- package/lib/types/resume-command.d.ts +7 -1
- package/lib/types/resume-command.js +50 -14
- package/lib/types/revision.d.ts +17 -0
- package/lib/types/revision.js +39 -3
- package/lib/types/round2-outcome.d.ts +2 -1
- package/lib/types/round2-outcome.js +68 -12
- package/lib/types/run-shell.d.ts +16 -2
- package/lib/types/run-shell.js +49 -5
- package/lib/types/runtime.d.ts +4 -6
- package/lib/types/runtime.js +341 -86
- package/lib/types/scan-command.d.ts +1 -0
- package/lib/types/scan-command.js +2 -0
- package/lib/types/scan-ports.d.ts +21 -1
- package/lib/types/scan-ports.js +51 -23
- package/lib/types/scan-run.d.ts +17 -0
- package/lib/types/scan-run.js +221 -63
- package/lib/types/screening-port.d.ts +47 -0
- package/lib/types/screening-port.js +93 -0
- package/lib/types/screening.d.ts +30 -0
- package/lib/types/screening.js +75 -0
- package/lib/types/stage-tool-registry.d.ts +7 -0
- package/lib/types/stage-tool-registry.js +12 -1
- package/lib/types/state.d.ts +15 -1
- package/lib/types/state.js +39 -17
- package/lib/types/status.d.ts +8 -0
- package/lib/types/status.js +64 -0
- package/lib/types/surface/body.d.ts +4 -0
- package/lib/types/surface/body.js +16 -0
- package/lib/types/surface/merge-view.d.ts +14 -0
- package/lib/types/surface/merge-view.js +126 -0
- package/lib/types/target-claim.d.ts +1 -0
- package/lib/types/target-claim.js +63 -18
- package/lib/types/undo-operation.d.ts +24 -0
- package/lib/types/undo-operation.js +135 -0
- package/lib/types/unit-claim.d.ts +5 -0
- package/lib/types/unit-claim.js +15 -0
- package/lib/types/wire-projection.d.ts +36 -0
- package/lib/types/wire-projection.js +69 -0
- package/lib/types/wire.d.ts +273 -0
- package/lib/types/wire.js +3 -0
- package/package.json +10 -4
- package/lib/types/routes.d.ts +0 -70
- package/lib/types/routes.js +0 -78
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { pairKey } from "./contracts.js";
|
|
2
|
+
import { runLadder, selectedForAudit } from "./ladder.js";
|
|
3
|
+
function withGate(candidate, verdicts, auditOverride) {
|
|
4
|
+
const last = verdicts[verdicts.length - 1];
|
|
5
|
+
if (last === undefined)
|
|
6
|
+
return candidate;
|
|
7
|
+
return {
|
|
8
|
+
...candidate,
|
|
9
|
+
gate: {
|
|
10
|
+
level: last.level,
|
|
11
|
+
verdict: last.verdict,
|
|
12
|
+
reason: last.reason,
|
|
13
|
+
...(last.cached ? { cached: true } : {}),
|
|
14
|
+
...(auditOverride ? { auditOverride: true } : {}),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export async function filterCandidates(candidates, options) {
|
|
19
|
+
const kept = [];
|
|
20
|
+
const dropped = [];
|
|
21
|
+
const errors = [];
|
|
22
|
+
let calls = 0;
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
const first = await runLadder({
|
|
25
|
+
level: 'L0', candidate, facts: options.facts, model: options.model,
|
|
26
|
+
modelName: options.modelName, signal: options.signal,
|
|
27
|
+
...(options.cache === undefined ? {} : { cache: options.cache }),
|
|
28
|
+
});
|
|
29
|
+
calls += first.calls;
|
|
30
|
+
if (first.route === 'error') {
|
|
31
|
+
errors.push({ candidate, message: first.error ?? 'gate error' });
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (first.route !== 'keep' || first.droppedAt !== 'L0') {
|
|
35
|
+
if (first.route === 'merge' || first.route === 'undecided')
|
|
36
|
+
kept.push({ candidate: withGate(candidate, first.verdicts, false), verdicts: first.verdicts, auditOverride: false });
|
|
37
|
+
else
|
|
38
|
+
dropped.push({ candidate, verdicts: first.verdicts, auditOverride: false });
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!selectedForAudit(pairKey(candidate.a, candidate.b), options.runSeed, options.auditRate ?? 0.1)) {
|
|
42
|
+
dropped.push({ candidate, verdicts: first.verdicts, auditOverride: false });
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const audit = await runLadder({
|
|
46
|
+
level: 'L0', startLevel: 'L2', candidate, facts: options.facts, model: options.model,
|
|
47
|
+
modelName: options.modelName, signal: options.signal,
|
|
48
|
+
...(options.cache === undefined ? {} : { cache: options.cache }),
|
|
49
|
+
});
|
|
50
|
+
calls += audit.calls;
|
|
51
|
+
if (audit.route === 'merge' || audit.route === 'undecided') {
|
|
52
|
+
const all = [...first.verdicts, ...audit.verdicts];
|
|
53
|
+
kept.push({ candidate: withGate(candidate, all, true), verdicts: all, auditOverride: true });
|
|
54
|
+
}
|
|
55
|
+
else if (audit.route === 'error') {
|
|
56
|
+
errors.push({ candidate, message: audit.error ?? 'audit gate error' });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
dropped.push({ candidate, verdicts: [...first.verdicts, ...audit.verdicts], auditOverride: true });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { kept, dropped, errors, calls };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=gate-filter.js.map
|
package/lib/types/index.d.ts
CHANGED
|
@@ -22,7 +22,11 @@ export interface MergePresentationScope {
|
|
|
22
22
|
}, owner?: unknown) => unknown;
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
export
|
|
25
|
+
export interface EngineModelConfig {
|
|
26
|
+
readonly embedder_model: string;
|
|
27
|
+
readonly embedder_model_last: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function engineConfigFromRoot(configuredRoot?: string): EngineModelConfig | undefined;
|
|
26
30
|
export interface MergePresentationContext {
|
|
27
31
|
inject(names: string[], run: (scope: MergePresentationScope) => void): void;
|
|
28
32
|
readonly fiber?: unknown;
|
|
@@ -66,10 +70,11 @@ export * from './merge-tool.ts';
|
|
|
66
70
|
export * from './commands.ts';
|
|
67
71
|
export * from './scan-ports.ts';
|
|
68
72
|
export * from './new-child-authoring.ts';
|
|
73
|
+
export * from './domain/authoring-manifest.ts';
|
|
74
|
+
export * from './domain/live-facts.ts';
|
|
69
75
|
export * from './status.ts';
|
|
70
76
|
export * from './skipped.ts';
|
|
71
77
|
export * from './read-commands.ts';
|
|
72
|
-
export * from './routes.ts';
|
|
73
78
|
export * from './merge-routes.ts';
|
|
74
79
|
export * from './merge-command.ts';
|
|
75
80
|
export * from './params.ts';
|
package/lib/types/index.js
CHANGED
|
@@ -4,13 +4,30 @@ import { screen as engineScreen } from '@lmzhen/dsh-evolution-merge-engine';
|
|
|
4
4
|
import { selectWithErrors } from "./adapters/select.js";
|
|
5
5
|
import { createEngineScreening } from "./adapters/engine-screen.js";
|
|
6
6
|
import { createEngineRunner } from "./adapters/model-runner.js";
|
|
7
|
-
import { authorityFrom, createSubagentChannel,
|
|
7
|
+
import { authorityFrom, createSubagentChannel, evolutionHome, evolutionIoAdapter, newRunRegistry, newSkillLibrary, reportsSweepLockTarget, sweepReports, transactIo } from '@lmzhen/dsh-evolution-core';
|
|
8
8
|
import { registerHostRoutes } from '@lmzhen/dsh-evolution-core/http-routes';
|
|
9
|
-
import { loadMergeState
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { loadMergeState } from "./state.js";
|
|
10
|
+
import { pairLedger, normalizePairLedger } from "./domain/pair-ledger.js";
|
|
11
|
+
import { legacyToV3 } from "./domain/row-v3-bridge.js";
|
|
12
|
+
import { readV4Readiness } from "./domain/schema4.js";
|
|
13
|
+
import { readMergeSnapshot } from "./domain/merge-snapshot.js";
|
|
12
14
|
import { readCommitReceipts } from "./commit-receipt.js";
|
|
13
|
-
import {
|
|
15
|
+
import { UnitTransactions, skillLibraryUnitBackend } from '@lmzhen/dsh-evolution-core';
|
|
16
|
+
import { targetClaimUnitPort } from "./unit-claim.js";
|
|
17
|
+
import { resolveLimitsSnapshot } from "./domain/merge-limits.js";
|
|
18
|
+
import { resolveEngineInput } from "./domain/engine-input.js";
|
|
19
|
+
import { readReconcileReport, reconcileMergeState } from "./domain/reconcile.js";
|
|
20
|
+
import { readRunMeta, writeRunMeta } from "./domain/run-coordinator.js";
|
|
21
|
+
import { liveRowFactsOf } from "./domain/live-facts.js";
|
|
22
|
+
import { filterSelection } from "./domain/selection-policy.js";
|
|
23
|
+
import { cleanupStage } from "./domain/stage-aggregate.js";
|
|
24
|
+
import { buildDoctorView } from "./surface/merge-view.js";
|
|
25
|
+
import { mergeDoctorFact } from "./doctor.js";
|
|
26
|
+
import { commandError, formatMergeError } from "./domain/errors.js";
|
|
27
|
+
import { resolveBody } from "./surface/body.js";
|
|
28
|
+
import { ensureMergeStateReady } from "./domain/state-bootstrap.js";
|
|
29
|
+
import { recoverCommitUnits } from "./recovery-coordinator.js";
|
|
30
|
+
import { pairKey, rowIdOf } from "./contracts.js";
|
|
14
31
|
import { makeMergeRoutes } from "./merge-routes.js";
|
|
15
32
|
import { createMergeCommand, runMergeCommand } from "./merge-command.js";
|
|
16
33
|
import { createMergeRuntime } from "./runtime.js";
|
|
@@ -19,6 +36,21 @@ import { subagentChannelAdapter } from "./adapters/channel.js";
|
|
|
19
36
|
import { StageToolRegistry } from "./stage-tool-registry.js";
|
|
20
37
|
import { createMergeStageTool } from "./merge-tool.js";
|
|
21
38
|
import { supersedeMergeRuns } from "./supersede.js";
|
|
39
|
+
/**
|
|
40
|
+
* One library read used by both the runtime card port and the read-time convergence
|
|
41
|
+
* facts. Read-only: the snapshot/doctor consumers must never write.
|
|
42
|
+
* @param skills - the mounted skill store.
|
|
43
|
+
* @returns every live skill as a merge card (body + support files).
|
|
44
|
+
*/
|
|
45
|
+
async function readSkillCards(skills) {
|
|
46
|
+
const listed = await skills.list({ withContent: true });
|
|
47
|
+
return await Promise.all(listed.map(async (item) => {
|
|
48
|
+
const probe = await skills.listSupportFiles(item.name);
|
|
49
|
+
const paths = probe.kind === 'present' ? probe.value : [];
|
|
50
|
+
const files = await Promise.all(paths.map(async (path) => ({ path, content: await skills.readSupportFile(item.name, path) ?? '' })));
|
|
51
|
+
return { name: item.name, description: item.description, body: item.content ?? '', files, protectedBy: item.protectedBy, protectionUnknown: item.protectionUnknown, managed: item.managed };
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
22
54
|
/** E3 knobs are volatile (user layer); the E2 pair timeout is deployment-only. */
|
|
23
55
|
export const Config = z.object({
|
|
24
56
|
mergeApproval: z.boolean().default(false).volatile(),
|
|
@@ -27,15 +59,11 @@ export const Config = z.object({
|
|
|
27
59
|
mergePairTimeoutMinutes: z.number().min(1).default(15),
|
|
28
60
|
mergeModelsRoot: z.string().default(''),
|
|
29
61
|
});
|
|
30
|
-
export function
|
|
31
|
-
const root = configuredRoot
|
|
32
|
-
if (root ===
|
|
62
|
+
export function engineConfigFromRoot(configuredRoot) {
|
|
63
|
+
const root = configuredRoot?.trim() ?? '';
|
|
64
|
+
if (root === '')
|
|
33
65
|
return undefined;
|
|
34
66
|
return {
|
|
35
|
-
models_dir: '',
|
|
36
|
-
offline: true,
|
|
37
|
-
auto_provision: false,
|
|
38
|
-
embed_backend: 'local',
|
|
39
67
|
embedder_model: join(root, 'potion-multilingual-128M-int8'),
|
|
40
68
|
embedder_model_last: join(root, 'potion-multilingual-128M-pca64-int8'),
|
|
41
69
|
};
|
|
@@ -88,24 +116,37 @@ export * from "./merge-tool.js";
|
|
|
88
116
|
export * from "./commands.js";
|
|
89
117
|
export * from "./scan-ports.js";
|
|
90
118
|
export * from "./new-child-authoring.js";
|
|
119
|
+
export * from "./domain/authoring-manifest.js";
|
|
120
|
+
export * from "./domain/live-facts.js";
|
|
91
121
|
export * from "./status.js";
|
|
92
122
|
export * from "./skipped.js";
|
|
93
123
|
export * from "./read-commands.js";
|
|
94
|
-
export * from "./routes.js";
|
|
95
124
|
export * from "./merge-routes.js";
|
|
96
125
|
export * from "./merge-command.js";
|
|
97
126
|
export * from "./params.js";
|
|
98
127
|
export * from "./doctor.js";
|
|
99
128
|
export * from "./supersede.js";
|
|
100
129
|
export * from "./runtime.js";
|
|
130
|
+
/** Not-yet-mounted command face: three lines so a script can read code/hint/next. */
|
|
131
|
+
function formatUnreadyError() {
|
|
132
|
+
return formatMergeError(commandError('merge', 'merge-not-ready', 'evolutionIo 尚未挂载', '等待插件加载完成后重试;运行 /evolution doctor 查看挂载状态'));
|
|
133
|
+
}
|
|
101
134
|
export const inject = [];
|
|
102
135
|
export function apply(ctx, rawConfig = {}) {
|
|
103
136
|
configureMergePresentation(ctx);
|
|
104
|
-
|
|
137
|
+
let params = resolveMergeParams(rawConfig);
|
|
105
138
|
let mergeRun;
|
|
139
|
+
let source;
|
|
140
|
+
let doctorFact;
|
|
106
141
|
const mergeService = {
|
|
107
142
|
async run(rawInput, invocation) {
|
|
108
|
-
return mergeRun === undefined ? { ok: false, text:
|
|
143
|
+
return mergeRun === undefined ? { ok: false, text: formatUnreadyError() } : mergeRun(rawInput, invocation);
|
|
144
|
+
},
|
|
145
|
+
/** Global-doctor consumer: merges buildDoctorView over the shared snapshot with the reconcile report. */
|
|
146
|
+
async doctorFact() {
|
|
147
|
+
if (doctorFact === undefined)
|
|
148
|
+
return { status: 'unknown', detail: '合并服务尚未完成挂载', remedy: '稍后重新运行 /evolution doctor' };
|
|
149
|
+
return await doctorFact();
|
|
109
150
|
},
|
|
110
151
|
};
|
|
111
152
|
const mergeContext = ctx;
|
|
@@ -113,7 +154,6 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
113
154
|
;
|
|
114
155
|
mergeContext.provide('evolutionMerge', mergeService);
|
|
115
156
|
}
|
|
116
|
-
let source;
|
|
117
157
|
registerHostRoutes(ctx, audit => makeMergeRoutes({ getSource: () => source }, audit));
|
|
118
158
|
ctx.inject(['commands', 'evolutionIo'], (scope) => {
|
|
119
159
|
const registry = scope.get('evolutionIo');
|
|
@@ -121,9 +161,19 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
121
161
|
return;
|
|
122
162
|
const io = evolutionIoAdapter(() => registry.provider());
|
|
123
163
|
const home = evolutionHome();
|
|
124
|
-
|
|
164
|
+
const ready = (async () => {
|
|
165
|
+
const v4 = await readV4Readiness(io, home);
|
|
166
|
+
if (v4.ok) {
|
|
167
|
+
const loaded = await loadMergeState(io, home);
|
|
168
|
+
return { ok: true, schema: 3, migrated: false, state: { schemaVersion: 3, rows: loaded.state.rows.map(legacyToV3).filter((row) => row !== null) }, quarantine: [], report: null };
|
|
169
|
+
}
|
|
170
|
+
const state = await ensureMergeStateReady(io, home);
|
|
171
|
+
if (!state.ok)
|
|
172
|
+
return v4.code === 'merge-state-forward-only' || v4.code === 'merge-state-needs-repair' ? { ok: false, code: v4.code, message: v4.message } : state;
|
|
173
|
+
const normalized = await normalizePairLedger(io, home);
|
|
174
|
+
return normalized.ok ? state : { ok: false, code: 'merge-state-needs-repair', message: normalized.message ?? 'v4 normalize failed' };
|
|
175
|
+
})().catch(error => ({ ok: false, code: 'merge-state-migration-blocked', message: error instanceof Error ? error.message : String(error) }));
|
|
125
176
|
const runs = newRunRegistry({ io, home, warn: (message) => { ctx.logger.warn(message); } });
|
|
126
|
-
void runs.load();
|
|
127
177
|
ctx.effect(() => {
|
|
128
178
|
const host = ctx;
|
|
129
179
|
if (typeof host.on !== 'function')
|
|
@@ -134,22 +184,56 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
134
184
|
});
|
|
135
185
|
}, 'evolution-merge: restore supersede');
|
|
136
186
|
const policySnapshot = ctx.get('evolutionPolicy');
|
|
187
|
+
const limitsSnapshot = resolveLimitsSnapshot(policySnapshot?.get?.());
|
|
188
|
+
const hostApproval = ctx.get('evolutionApproval');
|
|
189
|
+
const platformWrite = typeof hostApproval?.platformWrite === 'boolean' ? hostApproval.platformWrite : (params.platformWrite ?? false);
|
|
190
|
+
params = { ...params, limitsSnapshot, platformWrite };
|
|
137
191
|
const skills = newSkillLibrary({
|
|
138
192
|
io, ctx, authority: authorityFrom({ origin: 'subagent' }),
|
|
139
|
-
limits:
|
|
193
|
+
limits: limitsSnapshot.skillLimits,
|
|
140
194
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
195
|
+
void ready.then(value => {
|
|
196
|
+
if (!value.ok) {
|
|
197
|
+
ctx.logger.warn('merge state is not ready: ' + value.code + ': ' + value.message);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const unit = new UnitTransactions({ io, home, backend: skillLibraryUnitBackend(skills), claim: targetClaimUnitPort(io, home, () => Date.now()), now: () => Date.now() });
|
|
201
|
+
const cards = async () => await readSkillCards(skills);
|
|
202
|
+
void (async () => {
|
|
203
|
+
// NFR-05: load the shared run index BEFORE recovery. Recovery settles through the
|
|
204
|
+
// registry's controlled orphan upgrade (settleRecovered), which is the only way a
|
|
205
|
+
// committed unit can turn an orphan-stamped run back into succeeded.
|
|
206
|
+
const loaded = await runs.load();
|
|
207
|
+
if (!loaded.ok)
|
|
208
|
+
ctx.logger.warn('merge run index: ' + (loaded.note ?? 'unreadable'));
|
|
209
|
+
const reports = await recoverCommitUnits({ io, home, unit, now: () => Date.now(), isActive: () => false, runs });
|
|
210
|
+
if (reports.length > 0)
|
|
211
|
+
ctx.logger.info('merge unit recovery: ' + reports.map(report => report.runId + '/' + report.unitId + '=' + report.action).join(', '));
|
|
212
|
+
const v4 = await readV4Readiness(io, home);
|
|
213
|
+
if (v4.ok) {
|
|
214
|
+
const cleanup = await cleanupStage(io, home, Date.now(), new Map(v4.state.rows.map(row => [row.id, { state: row.state }])));
|
|
215
|
+
if (cleanup.expired.length > 0 || cleanup.removed.length > 0)
|
|
216
|
+
ctx.logger.info('merge stage cleanup: ' + String(cleanup.expired.length) + ' expired, ' + String(cleanup.removed.length) + ' removed, ' + String(cleanup.orphans.length) + ' orphan(s)');
|
|
217
|
+
// Reconcile is the one row-state convergence writer; startup simply invokes it.
|
|
218
|
+
// A live library is only supplied when it is non-empty (an empty/unreadable list
|
|
219
|
+
// must never authorize source-gone removal).
|
|
220
|
+
const pending = v4.state.rows.some(row => row.state === 'pending');
|
|
221
|
+
const library = pending ? await cards().catch(() => []) : [];
|
|
222
|
+
const live = library.length === 0 ? undefined : liveRowFactsOf(library);
|
|
223
|
+
const reconciled = await reconcileMergeState({ io, home, now: () => Date.now(), ttlDays: params.mergeSkippedTtlDays, ...(live === undefined ? {} : { live }) });
|
|
224
|
+
if (reconciled.actions.length > 0)
|
|
225
|
+
ctx.logger?.info?.('merge reconcile: ' + String(reconciled.actions.length) + ' action(s)');
|
|
226
|
+
await transactIo(io, reportsSweepLockTarget(), async () => {
|
|
227
|
+
await sweepReports({ io, dir: join(home, 'reports'), buckets: [{ prefix: 'merge-', keep: 20 }], owner: 'evolution-merge', warn: message => ctx.logger.warn(message) });
|
|
228
|
+
return null;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
})().catch((error) => { ctx.logger.warn('merge recovery failed: ' + (error instanceof Error ? error.message : String(error))); });
|
|
232
|
+
}).catch((error) => { ctx.logger.warn('merge state readiness failed: ' + (error instanceof Error ? error.message : String(error))); });
|
|
150
233
|
const channel = subagentChannelAdapter(createSubagentChannel(ctx));
|
|
151
234
|
const stage = new StageToolRegistry();
|
|
152
|
-
const
|
|
235
|
+
const engineInput = resolveEngineInput({ modelsRoot: params.mergeModelsRoot });
|
|
236
|
+
const engineConfig = engineConfigFromRoot(engineInput.modelsRoot);
|
|
153
237
|
const llm = ctx.get('llm');
|
|
154
238
|
const defaultModel = ctx.get('agentDefaultModel');
|
|
155
239
|
const runner = llm === undefined || defaultModel === undefined
|
|
@@ -160,63 +244,80 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
160
244
|
home,
|
|
161
245
|
engine: { screen: engineScreen },
|
|
162
246
|
select: selectWithErrors,
|
|
163
|
-
cfg: { ...
|
|
247
|
+
cfg: { ...engineConfig, judge: runner === undefined ? 'stub' : 'deepseek', ...(engineInput.maxTokens === 0 ? {} : { maxTokens: engineInput.maxTokens }), ...(engineInput.timeoutMs === 0 ? {} : { timeoutMs: engineInput.timeoutMs }) },
|
|
164
248
|
...(runner === undefined ? {} : { runner }),
|
|
165
|
-
async onSelection(selection) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
...(skipped.reason === undefined ? {} : { reason: skipped.reason }), at,
|
|
174
|
-
});
|
|
175
|
-
if (next.ok && next.row !== null)
|
|
176
|
-
await upsertRow(io, home, () => Date.now(), next.row);
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
});
|
|
180
|
-
source = {
|
|
181
|
-
rows() { return loadMergeState(io, home).then(loaded => loaded.state.rows); },
|
|
182
|
-
run(id) { return Promise.resolve(runs.runs().find(record => record.id === id)); },
|
|
183
|
-
runs() { return Promise.resolve(runs.runs()); },
|
|
184
|
-
async body(id, side) {
|
|
185
|
-
const row = (await loadMergeState(io, home)).state.rows.find(item => item.id === id);
|
|
186
|
-
if (row === undefined)
|
|
187
|
-
return null;
|
|
188
|
-
return await skills.read(side === 'a' ? row.a : row.b);
|
|
189
|
-
},
|
|
190
|
-
async history() {
|
|
191
|
-
const receipts = await readCommitReceipts(io, home);
|
|
192
|
-
const items = new Map();
|
|
193
|
-
for (const receipt of receipts) {
|
|
194
|
-
for (const source of receipt.sources) {
|
|
195
|
-
const key = source + ':' + receipt.runId;
|
|
196
|
-
if (items.has(key))
|
|
249
|
+
async onSelection(selection, _runId, cards) {
|
|
250
|
+
try {
|
|
251
|
+
const byName = new Map(cards.map(card => [card.name, card]));
|
|
252
|
+
const filtered = filterSelection(selection, name => byName.get(name)?.managed === true, name => { const card = byName.get(name); return card?.protectionUnknown === true || card?.protectedBy != null; });
|
|
253
|
+
const loaded = await loadMergeState(io, home);
|
|
254
|
+
const at = new Date().toISOString();
|
|
255
|
+
for (const skipped of filtered.skipped) {
|
|
256
|
+
if (loaded.state.rows.some(row => row.pairKey === skipped.pairKey))
|
|
197
257
|
continue;
|
|
198
|
-
|
|
258
|
+
const key = pairKey(skipped.a, skipped.b);
|
|
259
|
+
await pairLedger(io, home).transact(tx => tx.createSkipped({
|
|
260
|
+
id: rowIdOf(key), pairKey: key, a: skipped.a, b: skipped.b, version: 0,
|
|
261
|
+
createdAt: at, updatedAt: at, origin: 'agent',
|
|
262
|
+
state: 'skipped', reason: skipped.reason ?? 'gate-keep', attempts: 0,
|
|
263
|
+
}).ok);
|
|
199
264
|
}
|
|
200
265
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
items.set('archived:' + name, { name, kind: 'other', reason: 'archived', lastVersion: 1, archivedAt: new Date(Date.now()).toISOString() });
|
|
266
|
+
catch (error) {
|
|
267
|
+
const meta = await readRunMeta(io, home, _runId);
|
|
268
|
+
if (meta !== null)
|
|
269
|
+
await writeRunMeta(io, home, { ...meta, degraded: true, updatedAt: new Date().toISOString() });
|
|
270
|
+
ctx.logger.warn('merge selection reportOnly write failed (degraded): ' + (error instanceof Error ? error.message : String(error)));
|
|
207
271
|
}
|
|
208
|
-
return [...items.values()];
|
|
209
272
|
},
|
|
273
|
+
});
|
|
274
|
+
source = {
|
|
275
|
+
async snapshot() {
|
|
276
|
+
return await readMergeSnapshot({ io, home, runs: runs.runs('merge'), approval: params.mergeApproval, live: async () => liveRowFactsOf(await readSkillCards(skills)), history: async () => {
|
|
277
|
+
const items = new Map();
|
|
278
|
+
const receipts = [...await readCommitReceipts(io, home)].sort((a, b) => a.at - b.at);
|
|
279
|
+
const generationOf = new Map(receipts.map((receipt, index) => [receipt.runId, index + 1]));
|
|
280
|
+
for (const receipt of receipts)
|
|
281
|
+
for (const name of receipt.sources)
|
|
282
|
+
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 });
|
|
283
|
+
const archived = await skills.archivedNames?.() ?? [];
|
|
284
|
+
const reasonOf = skills.readArchiveReason;
|
|
285
|
+
for (const name of archived) {
|
|
286
|
+
if (items.has(name))
|
|
287
|
+
continue;
|
|
288
|
+
const rawReason = reasonOf === undefined ? null : await reasonOf.call(skills, name).catch(() => null);
|
|
289
|
+
const lower = (rawReason ?? '').toLowerCase();
|
|
290
|
+
const kind = lower.includes('split') ? 'split' : lower.includes('delet') || lower.includes('remove') ? 'deleted' : 'other';
|
|
291
|
+
items.set(name, { name, kind, reason: rawReason ?? '技能已归档,最后一版保留可查。', reasonCode: kind === 'other' ? 'archived' : kind, lastVersion: 1, archivedAt: Date.now() });
|
|
292
|
+
}
|
|
293
|
+
const out = [];
|
|
294
|
+
for (const item of items.values()) {
|
|
295
|
+
const versions = await skills.listVersions(item.name).catch(() => []);
|
|
296
|
+
const lastVersion = versions.at(-1)?.v ?? item.lastVersion;
|
|
297
|
+
out.push({ ...item, lastVersion, generation: item.generation ?? lastVersion });
|
|
298
|
+
}
|
|
299
|
+
return out;
|
|
300
|
+
} });
|
|
301
|
+
},
|
|
302
|
+
async bodyRef(ref) { return await resolveBody(io, home, ref, async () => ref.side === 'archive' ? await skills.readArchived(ref.name) : await skills.read(ref.name)); },
|
|
210
303
|
approval() { return params.mergeApproval; },
|
|
304
|
+
readiness() { return ready; },
|
|
211
305
|
commands() {
|
|
212
|
-
return ['merge scan', 'merge doctor', 'merge status', 'merge pending', 'merge skipped', 'merge report', 'merge runs', 'merge cancel'];
|
|
306
|
+
return ['merge scan', 'merge doctor', 'merge status', 'merge pending', 'merge skipped', 'merge report', 'merge runs', 'merge cancel', 'merge reconcile'];
|
|
213
307
|
},
|
|
214
308
|
};
|
|
309
|
+
doctorFact = async () => {
|
|
310
|
+
const current = source;
|
|
311
|
+
if (current?.snapshot === undefined)
|
|
312
|
+
return { status: 'unknown', detail: '合并快照来源尚未挂载', remedy: '稍后重新运行 /evolution doctor' };
|
|
313
|
+
const view = buildDoctorView(await current.snapshot());
|
|
314
|
+
return mergeDoctorFact(view, await readReconcileReport(io, home));
|
|
315
|
+
};
|
|
215
316
|
const commands = scope.get('commands');
|
|
216
317
|
if (commands === undefined)
|
|
217
318
|
return;
|
|
218
319
|
const runtime = createMergeRuntime({
|
|
219
|
-
io, home, registry: runs, skills, now: () => Date.now(), params, platformWrite
|
|
320
|
+
io, home, registry: runs, skills, now: () => Date.now(), params, platformWrite,
|
|
220
321
|
channel, parent: undefined, allow: ['merge_stage'], timeoutMinutes: params.mergeRunTimeoutMinutes,
|
|
221
322
|
isBlocked: async (name) => {
|
|
222
323
|
const curator = ctx.get('evolutionCurator');
|
|
@@ -227,15 +328,9 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
227
328
|
return run !== undefined && run.state === 'running';
|
|
228
329
|
},
|
|
229
330
|
async cards(signal) {
|
|
230
|
-
const listed = await skills.list({ withContent: true });
|
|
231
331
|
if (signal.aborted)
|
|
232
332
|
return [];
|
|
233
|
-
return
|
|
234
|
-
const probe = await skills.listSupportFiles(item.name);
|
|
235
|
-
const paths = probe.kind === 'present' ? probe.value : [];
|
|
236
|
-
const files = await Promise.all(paths.map(async (path) => ({ path, content: await skills.readSupportFile(item.name, path) ?? '' })));
|
|
237
|
-
return { name: item.name, description: item.description, body: item.content ?? '', files, protectedBy: item.protectedBy, protectionUnknown: item.protectionUnknown, managed: item.managed };
|
|
238
|
-
}));
|
|
333
|
+
return await readSkillCards(skills);
|
|
239
334
|
},
|
|
240
335
|
screeningForRun: engineScreening,
|
|
241
336
|
stage: {
|
|
@@ -243,14 +338,22 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
243
338
|
unbind: (childId) => { stage.unbind(childId); },
|
|
244
339
|
},
|
|
245
340
|
});
|
|
246
|
-
mergeRun = (rawInput, invocation) =>
|
|
341
|
+
mergeRun = async (rawInput, invocation) => {
|
|
342
|
+
const state = await ready;
|
|
343
|
+
// These verbs answer usefully on a blocked state; reconcile reports the block without writing.
|
|
344
|
+
const allowedWhenBlocked = /(?:^|\s)(doctor|status|pending|skipped|report|runs|reconcile)(?:\s|$)/.test(rawInput);
|
|
345
|
+
if (!state.ok && !allowedWhenBlocked)
|
|
346
|
+
return { ok: false, text: formatMergeError(commandError('merge', state.code, state.message, '先修复合并状态(/evolution merge doctor),再重试')) };
|
|
347
|
+
return runMergeCommand(runtime, rawInput, invocation);
|
|
348
|
+
};
|
|
247
349
|
const command = createMergeCommand(runtime);
|
|
248
|
-
|
|
350
|
+
if (command !== null)
|
|
351
|
+
scope.effect(() => commands.register(command), 'evolution-merge: command');
|
|
249
352
|
ctx.inject(['tools'], (toolScope) => {
|
|
250
353
|
const tools = toolScope.get('tools');
|
|
251
354
|
if (tools === undefined)
|
|
252
355
|
return;
|
|
253
|
-
const tool = createMergeStageTool(io, home, exec => stage.resolve(exec));
|
|
356
|
+
const tool = createMergeStageTool(io, home, exec => stage.resolve(exec), exec => stage.snapshot(exec));
|
|
254
357
|
toolScope.effect(() => tools.register(tool), 'evolution-merge: merge_stage');
|
|
255
358
|
});
|
|
256
359
|
scope.effect(() => () => { runs.cancelAll(); }, 'evolution-merge: runs');
|
package/lib/types/ladder.js
CHANGED
|
@@ -57,7 +57,7 @@ function payloadFor(level, candidate, facts) {
|
|
|
57
57
|
}
|
|
58
58
|
const SYSTEM = 'Judge whether two skill documents describe the same reusable capability. Shared vocabulary or a shared preamble is not duplication. Return only strict JSON: {"verdict":1|0|-1,"reason":"..."}';
|
|
59
59
|
function extractJson(text) {
|
|
60
|
-
const fenced =
|
|
60
|
+
const fenced = /`\``(?:json)?\s*([\s\S]*?)`\``/i.exec(text);
|
|
61
61
|
const body = fenced?.[1] ?? text;
|
|
62
62
|
const start = body.indexOf('{');
|
|
63
63
|
if (start < 0)
|
|
@@ -11,6 +11,8 @@ export interface MergeCommandResult {
|
|
|
11
11
|
export interface MergeCommandContext {
|
|
12
12
|
/** The invoking agent, when a command (not a route) initiated the run. */
|
|
13
13
|
readonly parent?: unknown;
|
|
14
|
+
/** True when the invocation carried no agent at all (the E-305 shape). */
|
|
15
|
+
readonly agentless?: boolean;
|
|
14
16
|
}
|
|
15
17
|
export interface MergeCommandService {
|
|
16
18
|
execute(command: MergeCliCommand, context?: MergeCommandContext): Promise<MergeCommandResult>;
|
|
@@ -35,5 +37,5 @@ export declare function createReadOnlyMergeService(io: EvolutionIoLike, home: st
|
|
|
35
37
|
export declare function runMergeCommand(service: MergeCommandService, rawInput: string, invocation?: {
|
|
36
38
|
readonly agent?: unknown;
|
|
37
39
|
}): Promise<MergeCommandResult>;
|
|
38
|
-
export declare function createMergeCommand(service: MergeCommandService): MergeCommandDefinitionLike;
|
|
40
|
+
export declare function createMergeCommand(service: MergeCommandService, version?: string): MergeCommandDefinitionLike | null;
|
|
39
41
|
//# sourceMappingURL=merge-command.d.ts.map
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { parseMergeCommand, mergeUsage } from "./commands.js";
|
|
2
|
+
import { aliasRegistration } from '@lmzhen/dsh-evolution-commands';
|
|
3
|
+
import { MERGE_ALIAS_DEF, MERGE_PACKAGE_VERSION } from "./command-defs.js";
|
|
2
4
|
import { handleReadOnlyMergeCommand } from "./read-commands.js";
|
|
3
|
-
import { formatMergeError } from "./domain/errors.js";
|
|
5
|
+
import { commandError, formatMergeError } from "./domain/errors.js";
|
|
4
6
|
export function createReadOnlyMergeService(io, home) {
|
|
5
7
|
return {
|
|
6
8
|
async execute(command) {
|
|
7
9
|
const text = await handleReadOnlyMergeCommand(command, io, home);
|
|
8
10
|
return text === null
|
|
9
|
-
? { ok: false, text: 'merge '
|
|
11
|
+
? { ok: false, text: formatMergeError(commandError(command.verb, 'merge-engine-required', '该命令需要合并运行时(当前只挂载了只读命令)', '安装 evolution-merge 完整行后重试;只读队列见 /evolution merge status')) }
|
|
10
12
|
: { ok: true, text };
|
|
11
13
|
},
|
|
12
14
|
};
|
|
@@ -23,16 +25,18 @@ export async function runMergeCommand(service, rawInput, invocation) {
|
|
|
23
25
|
if (parsed.help === true)
|
|
24
26
|
return { ok: true, text: mergeUsage() };
|
|
25
27
|
try {
|
|
26
|
-
return await service.execute(parsed.command, invocation?.agent === undefined ? {} : { parent: invocation.agent });
|
|
28
|
+
return await service.execute(parsed.command, invocation?.agent === undefined ? { agentless: true } : { parent: invocation.agent });
|
|
27
29
|
}
|
|
28
30
|
catch (error) {
|
|
29
|
-
return { ok: false, text: formatMergeError(
|
|
31
|
+
return { ok: false, text: formatMergeError(commandError('command', 'merge-command-failed', error instanceof Error ? error.message : String(error), '重试;若持续失败,运行 /evolution merge doctor 查看状态')) };
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
|
-
export function createMergeCommand(service) {
|
|
34
|
+
export function createMergeCommand(service, version = MERGE_PACKAGE_VERSION) {
|
|
35
|
+
if (aliasRegistration(version, MERGE_ALIAS_DEF) === 'removed')
|
|
36
|
+
return null;
|
|
33
37
|
return {
|
|
34
38
|
name: 'merge',
|
|
35
|
-
description: '技能自动合并:扫描、待批准、重跑、报告与 undo(兼容别名;请用 /evolution merge)',
|
|
39
|
+
description: (aliasRegistration(version, MERGE_ALIAS_DEF) === 'deprecated' ? '已弃用,请用 /evolution merge。' : '') + '技能自动合并:扫描、待批准、重跑、报告与 undo(兼容别名;请用 /evolution merge)',
|
|
36
40
|
input: { hint: mergeUsage() },
|
|
37
41
|
async handler(invocation) {
|
|
38
42
|
const result = await runMergeCommand(service, invocation.rawInput, invocation);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Four read-only merge routes: view / rows /
|
|
3
|
-
*
|
|
2
|
+
* Four read-only merge routes: view / rows / body / history.
|
|
3
|
+
* The host loads rows and runs; these handlers only project wire envelopes.
|
|
4
4
|
*/
|
|
5
5
|
import type { RunRecord } from '@lmzhen/dsh-evolution-core';
|
|
6
6
|
import { type ConnectionFetchRouteLike, type DecisionAudit } from '@lmzhen/dsh-evolution-core/http-routes';
|
|
7
7
|
import type { MergeRow } from './contracts.ts';
|
|
8
|
+
import type { StateReadiness } from './domain/state-bootstrap.ts';
|
|
9
|
+
import type { MergeSnapshot, BodyRef } from './domain/merge-snapshot.ts';
|
|
8
10
|
export declare const MERGE_ROUTES: {
|
|
9
11
|
view: `/api/dsh-evolution/${string}`;
|
|
10
12
|
rows: `/api/dsh-evolution/${string}`;
|
|
@@ -20,13 +22,16 @@ export interface MergeHistoryItem {
|
|
|
20
22
|
readonly sourceRunId?: string;
|
|
21
23
|
}
|
|
22
24
|
export interface MergeRouteSource {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
snapshot?(): Promise<MergeSnapshot>;
|
|
26
|
+
bodyRef?(ref: BodyRef): Promise<string | null>;
|
|
27
|
+
rows?(): Promise<readonly MergeRow[]>;
|
|
28
|
+
run?(id: string): Promise<RunRecord | undefined>;
|
|
29
|
+
runs?(cursor?: string, limit?: number): Promise<readonly RunRecord[]>;
|
|
30
|
+
body?(id: string, side: 'a' | 'b'): Promise<string | null>;
|
|
31
|
+
history?(): Promise<readonly MergeHistoryItem[]>;
|
|
28
32
|
commands(): readonly string[];
|
|
29
33
|
approval?(): boolean;
|
|
34
|
+
readiness?(): Promise<StateReadiness>;
|
|
30
35
|
}
|
|
31
36
|
export declare function makeMergeRoutes(deps: {
|
|
32
37
|
getSource: () => MergeRouteSource | undefined;
|