synergyspec-selfevolving 2.1.0 → 2.1.1
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/dist/core/self-evolution/episode-orchestrator.js +20 -4
- package/dist/core/self-evolution/reward-agent.js +16 -2
- package/dist/ui/ascii-patterns.d.ts +7 -8
- package/dist/ui/ascii-patterns.js +54 -120
- package/dist/ui/welcome-screen.d.ts +8 -0
- package/dist/ui/welcome-screen.js +2 -2
- package/package.json +1 -1
|
@@ -80,14 +80,30 @@ export async function captureMainArm(opts) {
|
|
|
80
80
|
* Whether the episode SKIPS the rollback decision + 演进智能体 EVOLVING AGENT:
|
|
81
81
|
* the judge 弃权 abstained (no nameable gap), found no gaps, OR returned the
|
|
82
82
|
* ⑤ `insufficient-signal` verdict (within the A/A noise floor, or a blocked
|
|
83
|
-
* tamper).
|
|
83
|
+
* tamper). These mean "do not evolve on this episode".
|
|
84
|
+
*
|
|
85
|
+
* EXCEPTION (cold-start bootstrap): `insufficient-signal` is honored ONLY as a
|
|
86
|
+
* genuine can't-tell — a within-noise-floor result (the baseline ran) or a blocked
|
|
87
|
+
* tamper. On a baseline-skipped episode (policyVersions.baseline === null) there is
|
|
88
|
+
* no comparison to be uncertain about, so a stray `insufficient-signal` emitted
|
|
89
|
+
* alongside real gaps (and no tamper) must NOT block: the first v0→v1 evolution has
|
|
90
|
+
* to be reachable from absolute signal, or a fresh target stays at v0 forever. This
|
|
91
|
+
* is defense-in-depth behind {@link deriveSingleSampleVerdict}, which already drops
|
|
92
|
+
* a volunteered verdict to `undefined` on a baseline skip.
|
|
84
93
|
*/
|
|
85
94
|
function shouldSkipEvolution(diagnosis) {
|
|
86
95
|
if (diagnosis === null)
|
|
87
96
|
return true;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
if (diagnosis.abstained || diagnosis.gaps.length === 0)
|
|
98
|
+
return true;
|
|
99
|
+
if (diagnosis.verdict === 'insufficient-signal') {
|
|
100
|
+
const baselineSkipped = diagnosis.policyVersions?.baseline === null;
|
|
101
|
+
const tamper = diagnosis.integrity?.testTamperSuspected ?? false;
|
|
102
|
+
if (baselineSkipped && !tamper)
|
|
103
|
+
return false;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
91
107
|
}
|
|
92
108
|
/**
|
|
93
109
|
* Count the consecutive trailing rolled-back episodes in the 版本账本 ledger.
|
|
@@ -154,6 +154,11 @@ export function assembleRewardAgentPrompt(input) {
|
|
|
154
154
|
'(no prior policy version in the 版本账本 ledger, or skipped by policy).',
|
|
155
155
|
'NO comparison is possible: set "rewardBaseline" to null and "advantage" to',
|
|
156
156
|
'null. Score ONLY the main arm and diagnose against the artifacts below.',
|
|
157
|
+
'A comparative verdict is impossible with one arm: do NOT emit "main-better",',
|
|
158
|
+
'"baseline-better", "tie", or "insufficient-signal" (insufficient-signal means',
|
|
159
|
+
'a genuine can\'t-tell, NOT "no baseline ran"). OMIT "verdict" entirely when you',
|
|
160
|
+
'can still name a real gap (keep "abstained": false with a "textualGradient");',
|
|
161
|
+
'use "no-gap" ONLY if the main arm is already clean with nothing to improve.',
|
|
157
162
|
'',
|
|
158
163
|
];
|
|
159
164
|
}
|
|
@@ -849,12 +854,21 @@ export async function runRewardAgent(opts) {
|
|
|
849
854
|
* A/A noise floor.
|
|
850
855
|
*/
|
|
851
856
|
export function deriveSingleSampleVerdict(parsed) {
|
|
857
|
+
// A comparative/insufficient verdict is meaningless without a baseline arm. On a
|
|
858
|
+
// baseline skip (advantage === null — a cold-start episode) IGNORE any verdict the
|
|
859
|
+
// judge volunteered and return `undefined` (or 'no-gap' if it truly abstained), so
|
|
860
|
+
// a real diagnosis (named gaps + a textual gradient) is NOT mis-read as the
|
|
861
|
+
// 'insufficient-signal' abstain that shouldSkipEvolution honors — which would
|
|
862
|
+
// strand a fresh target at v0 forever. This mirrors the aggregated path
|
|
863
|
+
// (advantageMean === null ⇒ verdict undefined). A genuine within-noise-floor or
|
|
864
|
+
// tamper-block 'insufficient-signal' is produced with a baseline present (or
|
|
865
|
+
// re-forced downstream), so it is unaffected.
|
|
866
|
+
if (parsed.advantage === null)
|
|
867
|
+
return parsed.abstained ? 'no-gap' : undefined;
|
|
852
868
|
if (parsed.verdict !== undefined)
|
|
853
869
|
return parsed.verdict;
|
|
854
870
|
if (parsed.abstained)
|
|
855
871
|
return 'no-gap';
|
|
856
|
-
if (parsed.advantage === null)
|
|
857
|
-
return undefined;
|
|
858
872
|
if (parsed.advantage > 1e-9)
|
|
859
873
|
return 'main-better';
|
|
860
874
|
if (parsed.advantage < -1e-9)
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ASCII art animation patterns for the welcome screen.
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Grid: 6 cols × 2 chars = 12 chars wide
|
|
3
|
+
*
|
|
4
|
+
* SynergySpec-SelfEvolving theme: a DNA double-helix that "evolves" across
|
|
5
|
+
* policy versions — the policy version climbs v01 → v08 while a reward bar
|
|
6
|
+
* fills, so the art itself tells the in-context-RL story (the design template
|
|
7
|
+
* 策略/POLICY — the main agent's "weights" — maturing as reward accrues each
|
|
8
|
+
* episode). The final frame is the fully-evolved helix at policy v08 with a
|
|
9
|
+
* maxed reward readout.
|
|
11
10
|
*/
|
|
12
11
|
export declare const WELCOME_ANIMATION: {
|
|
13
12
|
interval: number;
|
|
@@ -1,133 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ASCII art animation patterns for the welcome screen.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* SynergySpec-SelfEvolving theme: a DNA double-helix that "evolves" across
|
|
5
|
+
* policy versions — the policy version climbs v01 → v08 while a reward bar
|
|
6
|
+
* fills, so the art itself tells the in-context-RL story (the design template
|
|
7
|
+
* 策略/POLICY — the main agent's "weights" — maturing as reward accrues each
|
|
8
|
+
* episode). The final frame is the fully-evolved helix at policy v08 with a
|
|
9
|
+
* maxed reward readout.
|
|
4
10
|
*/
|
|
5
11
|
// Detect if full Unicode is supported
|
|
6
12
|
const supportsUnicode = process.platform !== 'win32' ||
|
|
7
13
|
!!process.env.WT_SESSION || // Windows Terminal
|
|
8
14
|
!!process.env.TERM_PROGRAM; // Modern terminal
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
15
|
+
// Strand node + reward-bar glyphs (ASCII fallback keeps the `o` node so the
|
|
16
|
+
// helix still reads, and stays within the right-column width budget).
|
|
17
|
+
const NODE = supportsUnicode ? '●' : 'o';
|
|
18
|
+
const LINK = supportsUnicode ? '─' : '-';
|
|
19
|
+
const BAR_FULL = supportsUnicode ? '█' : '#';
|
|
20
|
+
const BAR_EMPTY = supportsUnicode ? '░' : '.';
|
|
21
|
+
const TOTAL_VERSIONS = 8;
|
|
22
|
+
const REWARD_BAR_WIDTH = 8;
|
|
23
|
+
function pad2(n) {
|
|
24
|
+
return n < 10 ? `0${n}` : String(n);
|
|
25
|
+
}
|
|
26
|
+
function rewardBar(version) {
|
|
27
|
+
const filled = Math.round((version / TOTAL_VERSIONS) * REWARD_BAR_WIDTH);
|
|
28
|
+
return BAR_FULL.repeat(filled) + BAR_EMPTY.repeat(REWARD_BAR_WIDTH - filled);
|
|
29
|
+
}
|
|
17
30
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* Grid: 6 cols × 2 chars = 12 chars wide
|
|
31
|
+
* One animation frame: the double-helix (constant height so the cursor-redraw
|
|
32
|
+
* invariant holds) plus a `policy vNN` header and a `reward ▓▓░░` footer that
|
|
33
|
+
* advance with the policy version. The twist phase shifts each version so the
|
|
34
|
+
* strands appear to rotate as the policy evolves.
|
|
23
35
|
*/
|
|
36
|
+
function helixFrame(version) {
|
|
37
|
+
// Two strands crossing over a fixed 7-char span; the cross point walks across
|
|
38
|
+
// the span by version so successive frames read as a rotating helix.
|
|
39
|
+
const span = 7;
|
|
40
|
+
const phase = (version - 1) % span;
|
|
41
|
+
const strandRow = (offset) => {
|
|
42
|
+
const a = (phase + offset) % span;
|
|
43
|
+
const b = (span - 1 - ((phase + offset) % span));
|
|
44
|
+
const cells = Array.from({ length: span }, () => ' ');
|
|
45
|
+
cells[a] = NODE;
|
|
46
|
+
cells[b] = cells[b] === NODE ? NODE : LINK;
|
|
47
|
+
if (a === b)
|
|
48
|
+
cells[a] = NODE;
|
|
49
|
+
return ` ${cells.join('')}`;
|
|
50
|
+
};
|
|
51
|
+
return [
|
|
52
|
+
` policy v${pad2(version)}`,
|
|
53
|
+
'',
|
|
54
|
+
strandRow(0),
|
|
55
|
+
strandRow(1),
|
|
56
|
+
strandRow(2),
|
|
57
|
+
strandRow(3),
|
|
58
|
+
strandRow(4),
|
|
59
|
+
'',
|
|
60
|
+
` reward ${rewardBar(version)}`,
|
|
61
|
+
];
|
|
62
|
+
}
|
|
24
63
|
export const WELCOME_ANIMATION = {
|
|
25
64
|
interval: 120,
|
|
26
|
-
frames:
|
|
27
|
-
// Frame 1: Empty
|
|
28
|
-
[
|
|
29
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
30
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
31
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
32
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
33
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
34
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
35
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
36
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
37
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
38
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
39
|
-
],
|
|
40
|
-
// Frame 2: Center blocks appear (dim) - 2x3 center bar
|
|
41
|
-
[
|
|
42
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
43
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
44
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
45
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
46
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
47
|
-
`${_}${_}${_}${_}${D}${D}${_}${_}`,
|
|
48
|
-
`${_}${_}${_}${_}${D}${D}${_}${_}`,
|
|
49
|
-
`${_}${_}${_}${_}${D}${D}${_}${_}`,
|
|
50
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
51
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
52
|
-
],
|
|
53
|
-
// Frame 3: Center blocks solidify
|
|
54
|
-
[
|
|
55
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
56
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
57
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
58
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
59
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
60
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
61
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
62
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
63
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
64
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
65
|
-
],
|
|
66
|
-
// Frame 4: Top and bottom points appear
|
|
67
|
-
[
|
|
68
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
69
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
70
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
71
|
-
`${_}${_}${_}${_}${D}${D}${_}${_}`,
|
|
72
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
73
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
74
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
75
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
76
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`,
|
|
77
|
-
`${_}${_}${_}${_}${D}${D}${_}${_}`,
|
|
78
|
-
],
|
|
79
|
-
// Frame 5: Inner ring forming
|
|
80
|
-
[
|
|
81
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
82
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
83
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
84
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
85
|
-
`${_}${_}${_}${D}${_}${_}${D}${_}`,
|
|
86
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
87
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
88
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
89
|
-
`${_}${_}${_}${D}${_}${_}${D}${_}`,
|
|
90
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
91
|
-
],
|
|
92
|
-
// Frame 6: Outer ring appearing
|
|
93
|
-
[
|
|
94
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
95
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
96
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
97
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
98
|
-
`${_}${_}${_}${F}${_}${_}${F}${_}`,
|
|
99
|
-
`${_}${_}${D}${_}${F}${F}${_}${D}`,
|
|
100
|
-
`${_}${_}${D}${_}${F}${F}${_}${D}`,
|
|
101
|
-
`${_}${_}${D}${_}${F}${F}${_}${D}`,
|
|
102
|
-
`${_}${_}${_}${F}${_}${_}${F}${_}`,
|
|
103
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
104
|
-
],
|
|
105
|
-
// Frame 7: Full logo
|
|
106
|
-
[
|
|
107
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
108
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
109
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
110
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
111
|
-
`${_}${_}${_}${F}${_}${_}${F}${_}`,
|
|
112
|
-
`${_}${_}${F}${_}${F}${F}${_}${F}`,
|
|
113
|
-
`${_}${_}${F}${_}${F}${F}${_}${F}`,
|
|
114
|
-
`${_}${_}${F}${_}${F}${F}${_}${F}`,
|
|
115
|
-
`${_}${_}${_}${F}${_}${_}${F}${_}`,
|
|
116
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
117
|
-
],
|
|
118
|
-
// Frame 8: Hold complete logo
|
|
119
|
-
[
|
|
120
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 1
|
|
121
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 2
|
|
122
|
-
`${_}${_}${_}${_}${_}${_}${_}${_}`, // padding row 3
|
|
123
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
124
|
-
`${_}${_}${_}${F}${_}${_}${F}${_}`,
|
|
125
|
-
`${_}${_}${F}${_}${F}${F}${_}${F}`,
|
|
126
|
-
`${_}${_}${F}${_}${F}${F}${_}${F}`,
|
|
127
|
-
`${_}${_}${F}${_}${F}${F}${_}${F}`,
|
|
128
|
-
`${_}${_}${_}${F}${_}${_}${F}${_}`,
|
|
129
|
-
`${_}${_}${_}${_}${F}${F}${_}${_}`,
|
|
130
|
-
],
|
|
131
|
-
],
|
|
65
|
+
frames: Array.from({ length: TOTAL_VERSIONS }, (_v, i) => helixFrame(i + 1)),
|
|
132
66
|
};
|
|
133
67
|
//# sourceMappingURL=ascii-patterns.js.map
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
* Animated welcome screen for the experimental artifact workflow setup.
|
|
3
3
|
* Shows side-by-side layout with animated ASCII art on left and welcome text on right.
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* Welcome text content (right column)
|
|
7
|
+
*/
|
|
8
|
+
export declare function getWelcomeText(): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Renders a single frame with side-by-side layout
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderFrame(artLines: string[], textLines: string[]): string;
|
|
5
13
|
/**
|
|
6
14
|
* Shows the animated welcome screen.
|
|
7
15
|
* Returns when user presses Enter.
|
|
@@ -11,7 +11,7 @@ const ART_COLUMN_WIDTH = 24;
|
|
|
11
11
|
/**
|
|
12
12
|
* Welcome text content (right column)
|
|
13
13
|
*/
|
|
14
|
-
function getWelcomeText() {
|
|
14
|
+
export function getWelcomeText() {
|
|
15
15
|
return [
|
|
16
16
|
chalk.white.bold('Welcome to SynergySpec-SelfEvolving'),
|
|
17
17
|
chalk.dim('A lightweight spec-driven framework'),
|
|
@@ -31,7 +31,7 @@ function getWelcomeText() {
|
|
|
31
31
|
/**
|
|
32
32
|
* Renders a single frame with side-by-side layout
|
|
33
33
|
*/
|
|
34
|
-
function renderFrame(artLines, textLines) {
|
|
34
|
+
export function renderFrame(artLines, textLines) {
|
|
35
35
|
const maxLines = Math.max(artLines.length, textLines.length);
|
|
36
36
|
const lines = [];
|
|
37
37
|
for (let i = 0; i < maxLines; i++) {
|