@lmzhen/dsh-evolution-merge 0.0.0 → 0.29.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/README.md +27 -1
- package/lib/index.js +3814 -0
- package/lib/types/adapters/channel.d.ts +42 -0
- package/lib/types/adapters/engine-screen.d.ts +55 -0
- package/lib/types/adapters/library.d.ts +8 -0
- package/lib/types/adapters/model-runner.d.ts +28 -0
- package/lib/types/adapters/undo.d.ts +9 -0
- package/lib/types/approve-command.d.ts +22 -0
- package/lib/types/authoring-flow.d.ts +40 -0
- package/lib/types/channel-port.d.ts +61 -0
- package/lib/types/commands.d.ts +55 -0
- package/lib/types/commit-assembly.d.ts +27 -0
- package/lib/types/commit-execute.d.ts +29 -0
- package/lib/types/commit-plan.d.ts +45 -0
- package/lib/types/commit-receipt.d.ts +17 -0
- package/lib/types/contracts.d.ts +141 -0
- package/lib/types/coordinator.d.ts +65 -0
- package/lib/types/decision.d.ts +18 -0
- package/lib/types/doctor.d.ts +10 -0
- package/lib/types/gate-cache.d.ts +12 -0
- package/lib/types/index.d.ts +81 -0
- package/lib/types/ladder.d.ts +65 -0
- package/lib/types/merge-command.d.ts +39 -0
- package/lib/types/merge-routes.d.ts +24 -0
- package/lib/types/merge-tool.d.ts +22 -0
- package/lib/types/new-child-authoring.d.ts +29 -0
- package/lib/types/pair-command.d.ts +20 -0
- package/lib/types/params.d.ts +14 -0
- package/lib/types/pending-actions.d.ts +17 -0
- package/lib/types/prompts.d.ts +15 -0
- package/lib/types/read-commands.d.ts +9 -0
- package/lib/types/report.d.ts +10 -0
- package/lib/types/resume-command.d.ts +24 -0
- package/lib/types/round2-outcome.d.ts +13 -0
- package/lib/types/routes.d.ts +64 -0
- package/lib/types/rules.d.ts +24 -0
- package/lib/types/run-shell.d.ts +65 -0
- package/lib/types/runtime.d.ts +37 -0
- package/lib/types/scan-command.d.ts +21 -0
- package/lib/types/scan-ports.d.ts +32 -0
- package/lib/types/scan-run.d.ts +46 -0
- package/lib/types/skill-card.d.ts +12 -0
- package/lib/types/skipped.d.ts +8 -0
- package/lib/types/stage-tool-registry.d.ts +14 -0
- package/lib/types/stage-tool.d.ts +45 -0
- package/lib/types/staged-tree.d.ts +4 -0
- package/lib/types/staging-files.d.ts +15 -0
- package/lib/types/staging.d.ts +49 -0
- package/lib/types/state.d.ts +29 -0
- package/lib/types/status.d.ts +14 -0
- package/lib/types/supersede.d.ts +8 -0
- package/lib/types/undo-merge.d.ts +13 -0
- package/package.json +31 -3
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapt the platform SubagentChannel to the merge ConversationChannelLike. The optional
|
|
3
|
+
* two-turn methods are checked at call time so an older channel degrades to E-323 instead
|
|
4
|
+
* of throwing a TypeError.
|
|
5
|
+
*/
|
|
6
|
+
import type { ConversationChannelLike } from '../channel-port.ts';
|
|
7
|
+
interface PlatformTurn {
|
|
8
|
+
readonly childId: unknown;
|
|
9
|
+
readonly stopReason: string;
|
|
10
|
+
readonly text: string;
|
|
11
|
+
}
|
|
12
|
+
type PlatformOutcome = {
|
|
13
|
+
readonly kind: 'ok';
|
|
14
|
+
readonly childId: unknown;
|
|
15
|
+
readonly turn: PlatformTurn;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'unavailable';
|
|
18
|
+
readonly code: string;
|
|
19
|
+
readonly reason: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'aborted';
|
|
22
|
+
};
|
|
23
|
+
export interface SubagentChannelLike {
|
|
24
|
+
open?(spec: {
|
|
25
|
+
parent: unknown;
|
|
26
|
+
label: string;
|
|
27
|
+
promptText: string;
|
|
28
|
+
kind: 'reviewer' | 'maintainer';
|
|
29
|
+
allow: readonly string[];
|
|
30
|
+
cwd?: string;
|
|
31
|
+
timeoutMs: number;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}): Promise<PlatformOutcome>;
|
|
34
|
+
continueTurn?(childId: string, promptText: string, options?: {
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
}): Promise<PlatformOutcome>;
|
|
38
|
+
dispose?(childId: string): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
export declare function subagentChannelAdapter(channel: SubagentChannelLike): ConversationChannelLike;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=channel.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
import type { MergeCandidate } from '../contracts.ts';
|
|
3
|
+
import type { SkillCard } from '../skill-card.ts';
|
|
4
|
+
export type EngineScreenResult = {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly report: Record<string, unknown>;
|
|
7
|
+
} | {
|
|
8
|
+
readonly ok: false;
|
|
9
|
+
readonly error: {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly stage?: string;
|
|
12
|
+
readonly detail?: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export interface EngineScreenPort {
|
|
16
|
+
screen(input: {
|
|
17
|
+
readonly corpusDir: string;
|
|
18
|
+
readonly outDir: string;
|
|
19
|
+
readonly cfg?: Record<string, unknown>;
|
|
20
|
+
readonly signal?: AbortSignal;
|
|
21
|
+
readonly onProgress?: (p: {
|
|
22
|
+
stage: string;
|
|
23
|
+
done?: number;
|
|
24
|
+
total?: number;
|
|
25
|
+
}) => void;
|
|
26
|
+
readonly runner?: (prompt: string, cfg?: Record<string, unknown>) => Promise<string>;
|
|
27
|
+
}): Promise<EngineScreenResult>;
|
|
28
|
+
}
|
|
29
|
+
export interface CandidateSelection {
|
|
30
|
+
readonly candidates: readonly MergeCandidate[];
|
|
31
|
+
readonly skipped: readonly {
|
|
32
|
+
readonly pairKey: string;
|
|
33
|
+
readonly a: string;
|
|
34
|
+
readonly b: string;
|
|
35
|
+
readonly reason?: string;
|
|
36
|
+
}[];
|
|
37
|
+
readonly reportOnly: number;
|
|
38
|
+
readonly candidatePairs?: number;
|
|
39
|
+
}
|
|
40
|
+
export type ScreeningCandidates = readonly MergeCandidate[] & {
|
|
41
|
+
readonly candidatePairs?: number;
|
|
42
|
+
};
|
|
43
|
+
export interface EngineScreeningDeps {
|
|
44
|
+
readonly io: EvolutionIoLike;
|
|
45
|
+
readonly home: string;
|
|
46
|
+
readonly engine: EngineScreenPort;
|
|
47
|
+
readonly select: (report: Record<string, unknown>) => CandidateSelection;
|
|
48
|
+
readonly cfg?: Record<string, unknown>;
|
|
49
|
+
readonly runner?: (prompt: string, cfg?: Record<string, unknown>) => Promise<string>;
|
|
50
|
+
readonly onSelection?: (selection: CandidateSelection, runId: string) => Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
/** Write the exact card bodies as a support-free corpus snapshot and return its root. */
|
|
53
|
+
export declare function snapshotCards(io: EvolutionIoLike, home: string, runId: string, cards: readonly SkillCard[]): Promise<string>;
|
|
54
|
+
export declare function createEngineScreening(deps: EngineScreeningDeps): (cards: readonly SkillCard[], runId: string, signal: AbortSignal) => Promise<readonly MergeCandidate[]>;
|
|
55
|
+
//# sourceMappingURL=engine-screen.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library adapter: the only place the merge domain touches SkillLibrary's commit kernel.
|
|
3
|
+
* The domain stays port-shaped; this file is the composition seam.
|
|
4
|
+
*/
|
|
5
|
+
import type { SkillLibrary } from '@lmzhen/dsh-evolution-core';
|
|
6
|
+
import type { CommitPort } from '../commit-execute.ts';
|
|
7
|
+
export declare function skillLibraryPort(skills: SkillLibrary): CommitPort;
|
|
8
|
+
//# sourceMappingURL=library.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeepSeek judge runner over the platform llm service. The engine calls
|
|
3
|
+
* runner(prompt, cfg) and expects raw model text; the platform owns provider
|
|
4
|
+
* selection, usage accounting and transport.
|
|
5
|
+
*/
|
|
6
|
+
export interface ModelRoute {
|
|
7
|
+
readonly provider: string;
|
|
8
|
+
readonly model: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LlmChunkLike {
|
|
11
|
+
readonly type: string;
|
|
12
|
+
readonly text?: string;
|
|
13
|
+
readonly reason?: {
|
|
14
|
+
readonly kind?: string;
|
|
15
|
+
readonly failure?: {
|
|
16
|
+
readonly message?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export interface LlmServiceLike {
|
|
21
|
+
stream?(options: Record<string, unknown>): AsyncIterable<LlmChunkLike>;
|
|
22
|
+
prepareCall?(config: Record<string, unknown>, signal?: AbortSignal): Promise<{
|
|
23
|
+
readonly config: Record<string, unknown>;
|
|
24
|
+
stream(input: Record<string, unknown>): AsyncIterable<LlmChunkLike>;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
export declare function createEngineRunner(llm: LlmServiceLike, route: () => ModelRoute): (prompt: string) => Promise<string>;
|
|
28
|
+
//# sourceMappingURL=model-runner.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillLibrary-backed undo sink. Blobs are content-addressed but the library exposes them by
|
|
3
|
+
* version, so the first readBlob call indexes all version bodies once; an undo is rare enough
|
|
4
|
+
* that this is preferable to adding a second hash index.
|
|
5
|
+
*/
|
|
6
|
+
import { type SkillLibrary } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import type { MergeUndoPort } from '../undo-merge.ts';
|
|
8
|
+
export declare function skillLibraryUndoPort(skills: SkillLibrary): MergeUndoPort;
|
|
9
|
+
//# sourceMappingURL=undo.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approve command: one pending row is atomically taken to fuse, then a new run executes
|
|
3
|
+
* round2 with the persisted decision. A second approve loses at approvePending (not-pending).
|
|
4
|
+
*/
|
|
5
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
6
|
+
import type { MergeDecision, MergeRow } from './contracts.ts';
|
|
7
|
+
import type { MergeRegistryPort, WithMergeRunResult } from './run-shell.ts';
|
|
8
|
+
import type { ScanRound2Outcome } from './scan-run.ts';
|
|
9
|
+
export interface ApprovePorts {
|
|
10
|
+
round2(row: MergeRow, decision: MergeDecision, signal: AbortSignal, runId: string): Promise<ScanRound2Outcome>;
|
|
11
|
+
}
|
|
12
|
+
export interface RunApproveCommandOptions {
|
|
13
|
+
readonly io: EvolutionIoLike;
|
|
14
|
+
readonly home: string;
|
|
15
|
+
readonly registry: MergeRegistryPort;
|
|
16
|
+
readonly ports: ApprovePorts;
|
|
17
|
+
readonly rowId: string;
|
|
18
|
+
readonly platformWrite: boolean;
|
|
19
|
+
readonly now: () => number;
|
|
20
|
+
}
|
|
21
|
+
export declare function runApproveCommand(options: RunApproveCommandOptions): Promise<WithMergeRunResult>;
|
|
22
|
+
//# sourceMappingURL=approve-command.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authoring flow: create the staging tree, let the author write it, run the deterministic
|
|
3
|
+
* rule gate, then commit through the injected CommitPort. The channel adapter and the
|
|
4
|
+
* author tool live outside; this is the seam they share.
|
|
5
|
+
*/
|
|
6
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import { type RuleFinding } from './rules.ts';
|
|
8
|
+
import { type CommitPort, type CommitStagedResult } from './commit-execute.ts';
|
|
9
|
+
import type { StagedTree } from './rules.ts';
|
|
10
|
+
import type { SkillCard } from './skill-card.ts';
|
|
11
|
+
export interface RunAuthoringOptions {
|
|
12
|
+
readonly io: EvolutionIoLike;
|
|
13
|
+
readonly home: string;
|
|
14
|
+
readonly runId: string;
|
|
15
|
+
readonly stageKey?: string;
|
|
16
|
+
readonly reuseStaging?: boolean;
|
|
17
|
+
readonly target: string;
|
|
18
|
+
readonly mode?: 'merge-into-existing' | 'create-umbrella' | 'keep';
|
|
19
|
+
readonly cards: readonly SkillCard[];
|
|
20
|
+
readonly author: (staging: {
|
|
21
|
+
readonly home: string;
|
|
22
|
+
readonly runId: string;
|
|
23
|
+
}) => Promise<string>;
|
|
24
|
+
readonly commitPort: CommitPort;
|
|
25
|
+
readonly now: () => number;
|
|
26
|
+
readonly summary: string;
|
|
27
|
+
}
|
|
28
|
+
export type AuthoringResult = {
|
|
29
|
+
readonly ok: true;
|
|
30
|
+
readonly tree: StagedTree;
|
|
31
|
+
readonly commit: Extract<CommitStagedResult, {
|
|
32
|
+
readonly ok: true;
|
|
33
|
+
}>;
|
|
34
|
+
} | {
|
|
35
|
+
readonly ok: false;
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly findings?: readonly RuleFinding[];
|
|
38
|
+
};
|
|
39
|
+
export declare function runAuthoring(options: RunAuthoringOptions): Promise<AuthoringResult>;
|
|
40
|
+
//# sourceMappingURL=authoring-flow.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel adapter for round 1. It opens the merged child, parses the decision, and exposes
|
|
3
|
+
* an author callback for round 2 when the same child continues. Commit is deliberately
|
|
4
|
+
* outside this adapter: the caller owns staging -> commitStagedRun.
|
|
5
|
+
*/
|
|
6
|
+
import type { MergeDecision, MergeRow } from './contracts.ts';
|
|
7
|
+
import type { SkillCard } from './skill-card.ts';
|
|
8
|
+
import type { ScanRound1Outcome } from './scan-run.ts';
|
|
9
|
+
export interface ChannelTurnLike {
|
|
10
|
+
readonly text: string;
|
|
11
|
+
readonly stopReason: string;
|
|
12
|
+
}
|
|
13
|
+
export type ChannelTurnOutcome = {
|
|
14
|
+
readonly kind: 'ok';
|
|
15
|
+
readonly childId: string;
|
|
16
|
+
readonly turn: ChannelTurnLike;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: 'unavailable';
|
|
19
|
+
readonly code: string;
|
|
20
|
+
readonly reason: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'aborted';
|
|
23
|
+
};
|
|
24
|
+
export interface ConversationChannelLike {
|
|
25
|
+
open(spec: {
|
|
26
|
+
readonly parent: unknown;
|
|
27
|
+
readonly label: string;
|
|
28
|
+
readonly promptText: string;
|
|
29
|
+
readonly kind: 'reviewer' | 'maintainer';
|
|
30
|
+
readonly allow: readonly string[];
|
|
31
|
+
readonly cwd?: string;
|
|
32
|
+
readonly timeoutMs: number;
|
|
33
|
+
readonly signal?: AbortSignal;
|
|
34
|
+
}): Promise<ChannelTurnOutcome>;
|
|
35
|
+
continueTurn(childId: string, promptText: string, options?: {
|
|
36
|
+
readonly timeoutMs?: number;
|
|
37
|
+
readonly signal?: AbortSignal;
|
|
38
|
+
}): Promise<ChannelTurnOutcome>;
|
|
39
|
+
dispose(childId: string): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export interface ChannelRound1Options {
|
|
42
|
+
readonly channel: ConversationChannelLike;
|
|
43
|
+
readonly parent: unknown;
|
|
44
|
+
readonly cards: readonly SkillCard[];
|
|
45
|
+
readonly allow: readonly string[];
|
|
46
|
+
readonly cwd?: string;
|
|
47
|
+
readonly timeoutMs: number;
|
|
48
|
+
readonly onChildOpened?: (childId: string) => void;
|
|
49
|
+
}
|
|
50
|
+
export interface ChannelRound1Result {
|
|
51
|
+
readonly outcome: ScanRound1Outcome;
|
|
52
|
+
readonly childId?: string;
|
|
53
|
+
/** Same-child round 2; absent for keep/fail/unavailable. */
|
|
54
|
+
readonly author?: (row: MergeRow, decision: MergeDecision, signal: AbortSignal) => Promise<string>;
|
|
55
|
+
readonly dispose: () => Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
export declare function runRound1OverChannel(options: ChannelRound1Options, candidate: {
|
|
58
|
+
readonly a: string;
|
|
59
|
+
readonly b: string;
|
|
60
|
+
}, signal: AbortSignal): Promise<ChannelRound1Result>;
|
|
61
|
+
//# sourceMappingURL=channel-port.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure command-surface parser for the /evolution merge prefix. The command layer only
|
|
3
|
+
* validates shape; run/state effects are injected by the host shell.
|
|
4
|
+
*/
|
|
5
|
+
export type MergeCliCommand = {
|
|
6
|
+
readonly verb: 'scan';
|
|
7
|
+
} | {
|
|
8
|
+
readonly verb: 'pair';
|
|
9
|
+
readonly a: string;
|
|
10
|
+
readonly b: string;
|
|
11
|
+
} | {
|
|
12
|
+
readonly verb: 'status';
|
|
13
|
+
readonly id?: string;
|
|
14
|
+
} | {
|
|
15
|
+
readonly verb: 'report';
|
|
16
|
+
readonly id?: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly verb: 'cancel';
|
|
19
|
+
readonly id?: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly verb: 'runs';
|
|
22
|
+
} | {
|
|
23
|
+
readonly verb: 'doctor';
|
|
24
|
+
} | {
|
|
25
|
+
readonly verb: 'pending';
|
|
26
|
+
} | {
|
|
27
|
+
readonly verb: 'approve';
|
|
28
|
+
readonly id: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly verb: 'reject';
|
|
31
|
+
readonly id: string;
|
|
32
|
+
} | {
|
|
33
|
+
readonly verb: 'resume';
|
|
34
|
+
readonly id?: string;
|
|
35
|
+
readonly clear?: string;
|
|
36
|
+
} | {
|
|
37
|
+
readonly verb: 'skipped';
|
|
38
|
+
readonly restore?: string;
|
|
39
|
+
} | {
|
|
40
|
+
readonly verb: 'undo';
|
|
41
|
+
readonly target: string;
|
|
42
|
+
readonly run: string;
|
|
43
|
+
};
|
|
44
|
+
export type ParseMergeCommandResult = {
|
|
45
|
+
readonly ok: true;
|
|
46
|
+
readonly command: MergeCliCommand;
|
|
47
|
+
readonly help?: boolean;
|
|
48
|
+
} | {
|
|
49
|
+
readonly ok: false;
|
|
50
|
+
readonly code: 'usage';
|
|
51
|
+
readonly message: string;
|
|
52
|
+
};
|
|
53
|
+
export declare function parseMergeCommand(argv: readonly string[]): ParseMergeCommandResult;
|
|
54
|
+
export declare function mergeUsage(): string;
|
|
55
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn a fully written staging tree into a CommitMergePlan.
|
|
3
|
+
*
|
|
4
|
+
* Source bodies come from the LIVE tree (the plan's CAS baseline), the target body from
|
|
5
|
+
* the merged tree. This function is pure except for the injected reads; it never commits.
|
|
6
|
+
*/
|
|
7
|
+
import type { CommitMergePlan, WriteOrigin } from '@lmzhen/dsh-evolution-core';
|
|
8
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
9
|
+
export interface CommitAssemblyPort {
|
|
10
|
+
/** Current SKILL.md body, or null when the skill is not live. */
|
|
11
|
+
readSkill(name: string): Promise<string | null>;
|
|
12
|
+
/** Whether the name is already occupied by an archived (tombstoned) skill. */
|
|
13
|
+
isArchived?(name: string): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
export interface CommitAssemblyOptions {
|
|
16
|
+
readonly origin: WriteOrigin;
|
|
17
|
+
readonly summary: string;
|
|
18
|
+
}
|
|
19
|
+
export type CommitAssemblyResult = {
|
|
20
|
+
readonly ok: true;
|
|
21
|
+
readonly plan: CommitMergePlan;
|
|
22
|
+
} | {
|
|
23
|
+
readonly ok: false;
|
|
24
|
+
readonly message: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function assembleStagedCommit(io: EvolutionIoLike, home: string, runId: string, port: CommitAssemblyPort, options: CommitAssemblyOptions): Promise<CommitAssemblyResult>;
|
|
27
|
+
//# sourceMappingURL=commit-assembly.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute one staged merge: assemble the plan, call the host commit port, then write the
|
|
3
|
+
* receipt. A failed commit returns without a receipt; a landed commit with a failed receipt
|
|
4
|
+
* stays successful and carries a warning.
|
|
5
|
+
*/
|
|
6
|
+
import type { CommitMergePlan, CommitMergeResult, EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
7
|
+
import { type CommitAssemblyPort } from './commit-assembly.ts';
|
|
8
|
+
import { type CommitReceipt } from './commit-receipt.ts';
|
|
9
|
+
export interface CommitPort extends CommitAssemblyPort {
|
|
10
|
+
commitMerge(plan: CommitMergePlan): Promise<CommitMergeResult>;
|
|
11
|
+
}
|
|
12
|
+
export interface CommitStagedOptions {
|
|
13
|
+
readonly origin: CommitMergePlan['origin'];
|
|
14
|
+
readonly summary: string;
|
|
15
|
+
readonly now: () => number;
|
|
16
|
+
}
|
|
17
|
+
export type CommitStagedResult = {
|
|
18
|
+
readonly ok: true;
|
|
19
|
+
readonly plan: CommitMergePlan;
|
|
20
|
+
readonly result: CommitMergeResult;
|
|
21
|
+
readonly receipt?: CommitReceipt;
|
|
22
|
+
readonly warning?: string;
|
|
23
|
+
} | {
|
|
24
|
+
readonly ok: false;
|
|
25
|
+
readonly message: string;
|
|
26
|
+
readonly plan?: CommitMergePlan;
|
|
27
|
+
};
|
|
28
|
+
export declare function commitStagedRun(io: EvolutionIoLike, home: string, runId: string, port: CommitPort, options: CommitStagedOptions): Promise<CommitStagedResult>;
|
|
29
|
+
//# sourceMappingURL=commit-execute.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure commit-plan assembly for a staged merge tree.
|
|
3
|
+
*
|
|
4
|
+
* The control plane has already chosen the target name and the child wrote merged/<target>/.
|
|
5
|
+
* This module only turns those bytes into a CommitMergePlan; it never reads the live tree and
|
|
6
|
+
* never decides whether a merge is allowed (validateStagedTree does that).
|
|
7
|
+
*/
|
|
8
|
+
import type { CommitMergePlan, WriteOrigin } from '@lmzhen/dsh-evolution-core';
|
|
9
|
+
export type ResolveTargetResult = {
|
|
10
|
+
readonly ok: true;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly suffix: number;
|
|
13
|
+
} | {
|
|
14
|
+
readonly ok: false;
|
|
15
|
+
readonly message: string;
|
|
16
|
+
};
|
|
17
|
+
/** Pick the first free target: requested, then -v2, -v3, ... (never truncate a name). */
|
|
18
|
+
export declare function resolveTargetName(requested: string, exists: (name: string) => boolean): ResolveTargetResult;
|
|
19
|
+
/** Rewrite only the frontmatter name; body bytes stay otherwise intact. */
|
|
20
|
+
export declare function rewriteFrontmatterName(body: string, name: string): string;
|
|
21
|
+
export interface BuildCommitPlanInput {
|
|
22
|
+
readonly target: string;
|
|
23
|
+
readonly create: boolean;
|
|
24
|
+
readonly mergedBody: string;
|
|
25
|
+
readonly mergedFiles: readonly {
|
|
26
|
+
readonly path: string;
|
|
27
|
+
readonly content: string;
|
|
28
|
+
}[];
|
|
29
|
+
readonly targetCurrent: string | null;
|
|
30
|
+
readonly sourceBodies: readonly {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly body: string;
|
|
33
|
+
}[];
|
|
34
|
+
readonly origin: WriteOrigin;
|
|
35
|
+
readonly summary: string;
|
|
36
|
+
}
|
|
37
|
+
export type BuildCommitPlanResult = {
|
|
38
|
+
readonly ok: true;
|
|
39
|
+
readonly plan: CommitMergePlan;
|
|
40
|
+
} | {
|
|
41
|
+
readonly ok: false;
|
|
42
|
+
readonly message: string;
|
|
43
|
+
};
|
|
44
|
+
export declare function buildCommitPlan(input: BuildCommitPlanInput): BuildCommitPlanResult;
|
|
45
|
+
//# sourceMappingURL=commit-plan.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EvolutionIoLike } from '@lmzhen/dsh-evolution-core';
|
|
2
|
+
export interface CommitReceipt {
|
|
3
|
+
readonly runId: string;
|
|
4
|
+
readonly parentRunId?: string;
|
|
5
|
+
readonly unitId: string;
|
|
6
|
+
readonly target: string;
|
|
7
|
+
readonly create: boolean;
|
|
8
|
+
readonly sources: readonly string[];
|
|
9
|
+
readonly archived: readonly string[];
|
|
10
|
+
readonly at: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function commitReceiptPath(home: string, runId: string): string;
|
|
13
|
+
export declare function durableReceiptDir(home: string, parentRunId: string): string;
|
|
14
|
+
export declare function durableReceiptPath(home: string, parentRunId: string, stageKey: string): string;
|
|
15
|
+
export declare function writeCommitReceipt(io: EvolutionIoLike, home: string, receipt: CommitReceipt): Promise<string>;
|
|
16
|
+
export declare function readCommitReceipt(io: EvolutionIoLike, home: string, runId: string, target?: string): Promise<CommitReceipt | null>;
|
|
17
|
+
//# sourceMappingURL=commit-receipt.d.ts.map
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen merge-domain vocabulary. No io, no host, no imports from the plugin shell.
|
|
3
|
+
*
|
|
4
|
+
* The data layer keeps four states; the user face collapses verify/fuse into one retry
|
|
5
|
+
* queue and keeps the stage as a chip. The API queue must never expose the raw state.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MERGE_STATE_VERSION = 1;
|
|
8
|
+
export type MergePairState = 'verify' | 'pending' | 'fuse' | 'skipped';
|
|
9
|
+
export type MergeQueue = 'pending' | 'retry' | 'skipped';
|
|
10
|
+
export type MergeStage = 'verify' | 'decide' | 'author' | 'commit';
|
|
11
|
+
export type MergeCommand = 'scan' | 'pair' | 'approve' | 'resume';
|
|
12
|
+
export declare const MERGE_PAIR_STATES: readonly MergePairState[];
|
|
13
|
+
export declare const MERGE_QUEUES: readonly MergeQueue[];
|
|
14
|
+
/** User-facing queue of one data state. verify/fuse share retry, not two tabs. */
|
|
15
|
+
export declare function queueOf(state: MergePairState): MergeQueue;
|
|
16
|
+
/** Coarse stage chip; skipped has no active stage. */
|
|
17
|
+
export declare function stageOf(state: MergePairState): MergeStage | null;
|
|
18
|
+
/** Stable pair identity; NUL cannot appear in a skill name. */
|
|
19
|
+
export declare function pairKey(a: string, b: string): string;
|
|
20
|
+
/** Deterministic, URL-safe id derived from the pair key (FNV-1a 32). */
|
|
21
|
+
export declare function rowIdOf(key: string): string;
|
|
22
|
+
export interface MergeFailure {
|
|
23
|
+
readonly stage: 'screening' | 'decide' | 'author' | 'validate' | 'commit';
|
|
24
|
+
readonly code: string;
|
|
25
|
+
readonly message: string;
|
|
26
|
+
readonly retryable: boolean;
|
|
27
|
+
readonly retryFrom: 'agent1' | 'agent2' | 'none';
|
|
28
|
+
}
|
|
29
|
+
/** One persisted pair. The id is stable for the UI; the pairKey is the natural key. */
|
|
30
|
+
export interface MergeGateFact {
|
|
31
|
+
readonly level: 'L0' | 'L1' | 'L2';
|
|
32
|
+
readonly verdict: 1 | 0 | -1;
|
|
33
|
+
readonly reason: string;
|
|
34
|
+
readonly cached?: boolean;
|
|
35
|
+
readonly auditOverride?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface MergeCandidate {
|
|
38
|
+
readonly a: string;
|
|
39
|
+
readonly b: string;
|
|
40
|
+
readonly score?: number;
|
|
41
|
+
readonly arms?: readonly string[];
|
|
42
|
+
readonly reason?: string;
|
|
43
|
+
readonly gate?: MergeGateFact;
|
|
44
|
+
readonly source?: 'lexical' | 'proposed' | 'engine';
|
|
45
|
+
}
|
|
46
|
+
export interface MergeDecision {
|
|
47
|
+
readonly shouldMerge: boolean;
|
|
48
|
+
readonly mode: 'merge-into-existing' | 'create-umbrella' | 'keep';
|
|
49
|
+
readonly target: string;
|
|
50
|
+
readonly sources: readonly string[];
|
|
51
|
+
readonly rationale: string;
|
|
52
|
+
readonly confidence?: number;
|
|
53
|
+
readonly aSummary?: string;
|
|
54
|
+
readonly bSummary?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface MergeUsage {
|
|
57
|
+
readonly inputTokens?: number;
|
|
58
|
+
readonly outputTokens?: number;
|
|
59
|
+
readonly totalTokens?: number;
|
|
60
|
+
readonly costCny?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface MergeTiming {
|
|
63
|
+
readonly screeningMs?: number;
|
|
64
|
+
readonly gateMs?: number;
|
|
65
|
+
readonly agentMs?: number;
|
|
66
|
+
readonly commitMs?: number;
|
|
67
|
+
}
|
|
68
|
+
export type MergeRunCommand = MergeCommand;
|
|
69
|
+
export interface MergeReportPair {
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly pairKey: string;
|
|
72
|
+
readonly a: string;
|
|
73
|
+
readonly b: string;
|
|
74
|
+
readonly queue: MergeQueue;
|
|
75
|
+
readonly candidate?: MergeCandidate;
|
|
76
|
+
readonly gate?: MergeGateFact;
|
|
77
|
+
readonly decision?: MergeDecision;
|
|
78
|
+
readonly outcome: 'merged' | 'kept' | 'pending' | 'failed' | 'cancelled';
|
|
79
|
+
readonly target?: {
|
|
80
|
+
readonly requested: string;
|
|
81
|
+
readonly actual: string;
|
|
82
|
+
readonly created: boolean;
|
|
83
|
+
};
|
|
84
|
+
readonly archived: readonly string[];
|
|
85
|
+
readonly unitId?: string;
|
|
86
|
+
readonly failure?: MergeFailure;
|
|
87
|
+
readonly usage?: MergeUsage;
|
|
88
|
+
readonly timing?: MergeTiming;
|
|
89
|
+
}
|
|
90
|
+
export interface MergeReport {
|
|
91
|
+
readonly schemaVersion: 1;
|
|
92
|
+
readonly runId: string;
|
|
93
|
+
readonly kind: 'merge';
|
|
94
|
+
readonly command: MergeRunCommand;
|
|
95
|
+
readonly startedAt: number;
|
|
96
|
+
readonly endedAt: number;
|
|
97
|
+
readonly stopReason: 'completed' | 'failed' | 'cancelled' | 'timeout';
|
|
98
|
+
readonly approval: {
|
|
99
|
+
readonly platformWrite: boolean;
|
|
100
|
+
readonly mergeApproval: boolean;
|
|
101
|
+
};
|
|
102
|
+
readonly scope: {
|
|
103
|
+
readonly candidatePairs: number;
|
|
104
|
+
readonly queueBefore: Record<MergeQueue, number>;
|
|
105
|
+
};
|
|
106
|
+
readonly pairs: readonly MergeReportPair[];
|
|
107
|
+
readonly totals: Record<'merged' | 'kept' | 'pending' | 'failed', number>;
|
|
108
|
+
readonly notes: readonly string[];
|
|
109
|
+
readonly remaining?: readonly {
|
|
110
|
+
readonly pairKey: string;
|
|
111
|
+
readonly queue: MergeQueue;
|
|
112
|
+
}[];
|
|
113
|
+
}
|
|
114
|
+
export interface MergeRow {
|
|
115
|
+
readonly id: string;
|
|
116
|
+
readonly pairKey: string;
|
|
117
|
+
readonly a: string;
|
|
118
|
+
readonly b: string;
|
|
119
|
+
readonly state: MergePairState;
|
|
120
|
+
readonly queue: MergeQueue;
|
|
121
|
+
readonly failure?: MergeFailure;
|
|
122
|
+
/** Round-1 decision, persisted for the later approve round. */
|
|
123
|
+
readonly decision?: MergeDecision;
|
|
124
|
+
readonly runId?: string;
|
|
125
|
+
readonly unitId?: string;
|
|
126
|
+
readonly origin?: 'agent' | 'human';
|
|
127
|
+
readonly attempts?: number;
|
|
128
|
+
readonly stagingRef?: string;
|
|
129
|
+
readonly stagingExpiresAt?: number;
|
|
130
|
+
readonly createdAt: string;
|
|
131
|
+
readonly updatedAt: string;
|
|
132
|
+
}
|
|
133
|
+
export interface MergeState {
|
|
134
|
+
readonly schemaVersion: typeof MERGE_STATE_VERSION;
|
|
135
|
+
readonly rows: readonly MergeRow[];
|
|
136
|
+
}
|
|
137
|
+
export declare function emptyMergeState(): MergeState;
|
|
138
|
+
/** One record can be retried only when the failure says so and names a stage. */
|
|
139
|
+
export declare const MERGE_MAX_ATTEMPTS = 3;
|
|
140
|
+
export declare function canRetry(record: Pick<MergeRow, 'state' | 'failure' | 'attempts'>): boolean;
|
|
141
|
+
//# sourceMappingURL=contracts.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure merge transition table. No io, no run shell, no subagents.
|
|
3
|
+
*
|
|
4
|
+
* Every transition returns the whole next row (or a drop), so the caller can persist it
|
|
5
|
+
* through mutateMergeState in one transaction. Invalid transitions are refused by name;
|
|
6
|
+
* a caller never "repairs" a row by guessing which state the operator meant.
|
|
7
|
+
*/
|
|
8
|
+
import { type MergeDecision, type MergeFailure, type MergeRow } from './contracts.ts';
|
|
9
|
+
export type MergeEvent = {
|
|
10
|
+
readonly type: 'candidate';
|
|
11
|
+
readonly a: string;
|
|
12
|
+
readonly b: string;
|
|
13
|
+
readonly at: string;
|
|
14
|
+
} | {
|
|
15
|
+
readonly type: 'round1-keep';
|
|
16
|
+
readonly at: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly type: 'round1-merge';
|
|
19
|
+
readonly approval: 'on' | 'off';
|
|
20
|
+
readonly decision?: MergeDecision;
|
|
21
|
+
readonly stagingRef?: string;
|
|
22
|
+
readonly stagingExpiresAt?: number;
|
|
23
|
+
readonly at: string;
|
|
24
|
+
} | {
|
|
25
|
+
readonly type: 'round1-fail';
|
|
26
|
+
readonly failure: MergeFailure;
|
|
27
|
+
readonly at: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly type: 'approve';
|
|
30
|
+
readonly runId?: string;
|
|
31
|
+
readonly at: string;
|
|
32
|
+
} | {
|
|
33
|
+
readonly type: 'reject';
|
|
34
|
+
readonly at: string;
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: 'round2-fail';
|
|
37
|
+
readonly failure: MergeFailure;
|
|
38
|
+
readonly at: string;
|
|
39
|
+
} | {
|
|
40
|
+
readonly type: 'round2-success';
|
|
41
|
+
readonly unitId?: string;
|
|
42
|
+
readonly at: string;
|
|
43
|
+
} | {
|
|
44
|
+
readonly type: 'restore';
|
|
45
|
+
readonly at: string;
|
|
46
|
+
} | {
|
|
47
|
+
readonly type: 'source-gone';
|
|
48
|
+
readonly at: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly type: 'gate-keep';
|
|
51
|
+
readonly a: string;
|
|
52
|
+
readonly b: string;
|
|
53
|
+
readonly reason?: string;
|
|
54
|
+
readonly at: string;
|
|
55
|
+
};
|
|
56
|
+
export type TransitionResult = {
|
|
57
|
+
readonly ok: true;
|
|
58
|
+
readonly row: MergeRow | null;
|
|
59
|
+
} | {
|
|
60
|
+
readonly ok: false;
|
|
61
|
+
readonly code: 'invalid-transition';
|
|
62
|
+
readonly message: string;
|
|
63
|
+
};
|
|
64
|
+
export declare function transition(current: MergeRow | undefined, event: MergeEvent): TransitionResult;
|
|
65
|
+
//# sourceMappingURL=coordinator.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Round-1 decision parser. The child returns text; the plugin owns strict shape checks.
|
|
3
|
+
* Accepts a whole JSON object or a fenced json block; everything else is decision-invalid.
|
|
4
|
+
*/
|
|
5
|
+
import type { MergeDecision } from './contracts.ts';
|
|
6
|
+
export type DecisionParse = {
|
|
7
|
+
readonly ok: true;
|
|
8
|
+
readonly decision: MergeDecision;
|
|
9
|
+
} | {
|
|
10
|
+
readonly ok: false;
|
|
11
|
+
readonly code: 'decision-invalid';
|
|
12
|
+
readonly message: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function parseDecision(text: string | undefined, pair: {
|
|
15
|
+
readonly a: string;
|
|
16
|
+
readonly b: string;
|
|
17
|
+
}): DecisionParse;
|
|
18
|
+
//# sourceMappingURL=decision.d.ts.map
|