synergyspec-selfevolving 2.1.5 → 2.1.7

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.
Files changed (38) hide show
  1. package/dist/commands/learn.js +80 -24
  2. package/dist/commands/self-evolution-dream.d.ts +15 -1
  3. package/dist/commands/self-evolution-dream.js +111 -6
  4. package/dist/commands/self-evolution-episode.d.ts +3 -0
  5. package/dist/commands/self-evolution-episode.js +157 -108
  6. package/dist/commands/workflow/status.js +4 -0
  7. package/dist/core/archive.js +17 -9
  8. package/dist/core/change-readiness.d.ts +16 -1
  9. package/dist/core/change-readiness.js +441 -15
  10. package/dist/core/fitness/loss.d.ts +3 -5
  11. package/dist/core/fitness/loss.js +2 -2
  12. package/dist/core/fitness/test-metrics.d.ts +1 -0
  13. package/dist/core/fitness/test-metrics.js +49 -0
  14. package/dist/core/learn.js +129 -11
  15. package/dist/core/migration.d.ts +6 -14
  16. package/dist/core/migration.js +63 -21
  17. package/dist/core/runner-evidence.d.ts +53 -0
  18. package/dist/core/runner-evidence.js +613 -0
  19. package/dist/core/self-evolution/candidates.js +0 -2
  20. package/dist/core/self-evolution/dream.d.ts +57 -3
  21. package/dist/core/self-evolution/dream.js +480 -9
  22. package/dist/core/self-evolution/episode-orchestrator.d.ts +2 -0
  23. package/dist/core/self-evolution/episode-orchestrator.js +17 -5
  24. package/dist/core/self-evolution/episode-store.d.ts +5 -0
  25. package/dist/core/self-evolution/episode-store.js +6 -2
  26. package/dist/core/self-evolution/evolving-agent.d.ts +33 -4
  27. package/dist/core/self-evolution/evolving-agent.js +138 -11
  28. package/dist/core/self-evolution/host-harness.d.ts +35 -12
  29. package/dist/core/self-evolution/host-harness.js +188 -49
  30. package/dist/core/self-evolution/reward-aggregator.js +2 -2
  31. package/dist/core/templates/workflows/archive-change.js +18 -18
  32. package/dist/core/templates/workflows/dream.js +57 -47
  33. package/dist/core/templates/workflows/learn.js +7 -5
  34. package/dist/core/templates/workflows/run-tests.js +48 -29
  35. package/dist/core/templates/workflows/self-evolving.js +11 -8
  36. package/dist/core/trajectory/facts.d.ts +1 -1
  37. package/dist/core/trajectory/registry.js +39 -8
  38. package/package.json +1 -1
@@ -1,5 +1,9 @@
1
- import { type CanonicalTargetKind } from './canonical-targets.js';
2
- import { type CanonicalCandidate } from './candidates.js';
1
+ import { type CanonicalTarget, type CanonicalTargetKind } from './canonical-targets.js';
2
+ import { type CandidateEdit, type CanonicalCandidate } from './candidates.js';
3
+ import { type ResolvedTargetFile } from './local-targets.js';
4
+ import { type PromotionApplyResult } from './promote.js';
5
+ import { type StaticGateResult } from './candidate-gates.js';
6
+ import type { TargetEvolutionPolicy } from './target-evolution.js';
3
7
  export declare const DREAM_RUN_SCHEMA_VERSION: 1;
4
8
  export type DreamEvidenceKind = 'change' | 'trajectory' | 'episode-diagnosis' | 'policy-ledger' | 'reject-buffer' | 'candidate-verdict';
5
9
  export interface DreamEvidenceItem {
@@ -39,6 +43,43 @@ export interface DreamTargetProposal {
39
43
  riskLevel: CanonicalCandidate['riskLevel'];
40
44
  changedFiles: string[];
41
45
  }
46
+ export interface DreamOptimizerInput {
47
+ projectRoot: string;
48
+ proposal: DreamTargetProposal;
49
+ target: CanonicalTarget;
50
+ evidence: DreamEvidenceItem[];
51
+ files: ResolvedTargetFile[];
52
+ }
53
+ export interface DreamOptimizerOutput {
54
+ edits: CandidateEdit[];
55
+ rationale: string;
56
+ validationPlan?: string;
57
+ riskReport?: string;
58
+ }
59
+ export type DreamOptimizer = (input: DreamOptimizerInput) => Promise<DreamOptimizerOutput>;
60
+ export interface DreamPolicyEditSynthesizerInput {
61
+ candidateId: string;
62
+ targetId: string;
63
+ acceptedBy: string;
64
+ proposalMd: string;
65
+ rationaleMd: string;
66
+ evalPlanMd: string;
67
+ riskReportMd: string;
68
+ }
69
+ export type DreamPolicyEditSynthesizer = (input: DreamPolicyEditSynthesizerInput) => Promise<DreamOptimizerOutput>;
70
+ export type DreamPolicyUpdateOutcome = 'promoted' | 'refused-no-local-files' | 'refused-optimizer' | 'refused-static-gate' | 'error-runtime';
71
+ export interface DreamPolicyUpdateResult {
72
+ candidateId: string;
73
+ targetId: string;
74
+ outcome: DreamPolicyUpdateOutcome;
75
+ promoted: boolean;
76
+ gatePassed: boolean;
77
+ promotedFiles: string[];
78
+ policyVersion: number | null;
79
+ reason?: string;
80
+ gate?: StaticGateResult;
81
+ applied?: PromotionApplyResult;
82
+ }
42
83
  export interface DreamPreview {
43
84
  schemaVersion: typeof DREAM_RUN_SCHEMA_VERSION;
44
85
  mode: 'preview';
@@ -47,7 +88,7 @@ export interface DreamPreview {
47
88
  proposals: DreamTargetProposal[];
48
89
  safety: {
49
90
  createsNewSkills: false;
50
- autoPromotes: false;
91
+ autoPromotes: boolean;
51
92
  directlyWritesPolicy: false;
52
93
  allowedTargetKinds: readonly CanonicalTargetKind[];
53
94
  };
@@ -57,6 +98,7 @@ export interface DreamRunManifest extends Omit<DreamPreview, 'mode'> {
57
98
  runId: string;
58
99
  runDir: string;
59
100
  candidateIds: string[];
101
+ policyUpdates?: DreamPolicyUpdateResult[];
60
102
  }
61
103
  export interface BuildDreamPreviewOptions {
62
104
  projectRoot: string;
@@ -66,6 +108,17 @@ export interface BuildDreamPreviewOptions {
66
108
  }
67
109
  export interface WriteDreamRunOptions extends BuildDreamPreviewOptions {
68
110
  preview?: DreamPreview;
111
+ applyPolicy?: boolean;
112
+ optimizer?: DreamOptimizer;
113
+ targetPolicy?: TargetEvolutionPolicy;
114
+ }
115
+ export interface RunAcceptedDreamPolicyUpdateOptions {
116
+ projectRoot: string;
117
+ candidateId: string;
118
+ acceptedBy: string;
119
+ synthesizeEdits?: DreamPolicyEditSynthesizer;
120
+ targetPolicy?: TargetEvolutionPolicy;
121
+ now?: () => Date;
69
122
  }
70
123
  export declare function resolveDreamRunsDir(projectRoot: string): string;
71
124
  export declare function resolveDreamRunDir(projectRoot: string, runId: string): string;
@@ -73,6 +126,7 @@ export declare function collectDreamEvidence(projectRoot: string): Promise<Dream
73
126
  export declare function buildDreamPreview(opts: BuildDreamPreviewOptions): Promise<DreamPreview>;
74
127
  export declare function writeDreamRun(opts: WriteDreamRunOptions): Promise<DreamRunManifest>;
75
128
  export declare function readDreamRun(projectRoot: string, runId: string): Promise<DreamRunManifest>;
129
+ export declare function runAcceptedDreamPolicyUpdate(opts: RunAcceptedDreamPolicyUpdateOptions): Promise<DreamPolicyUpdateResult>;
76
130
  export declare function renderDreamPreview(preview: DreamPreview): string;
77
131
  export declare function renderDreamRun(manifest: DreamRunManifest): string;
78
132
  //# sourceMappingURL=dream.d.ts.map
@@ -1,13 +1,20 @@
1
1
  /**
2
2
  * Supervised Learning Dream: an offline, batch proposal lane for existing
3
3
  * self-evolution targets. Dream reads accumulated evidence and writes review
4
- * candidates. It never edits canonical files and never promotes.
4
+ * candidates by default. The explicit apply mode turns a Dream proposal into a
5
+ * concrete bounded edit, runs the normal static gate, and promotes through the
6
+ * existing candidate promotion channel.
5
7
  */
6
8
  import { promises as fs } from 'node:fs';
7
9
  import * as path from 'node:path';
8
10
  import * as crypto from 'node:crypto';
9
11
  import { CANONICAL_TARGETS, lookupCanonicalTarget, } from './canonical-targets.js';
10
- import { generateCandidateId, resolveCandidateRepo, writeCandidatePackage, } from './candidates.js';
12
+ import { EDITS_JSON_FILE, generateCandidateId, readCandidatePackage, resolveCandidateRepo, writeCandidatePackage, } from './candidates.js';
13
+ import { renderUnifiedDiff, validateCandidateEdits, } from './edits-contract.js';
14
+ import { resolveTargetLocalFiles, } from './local-targets.js';
15
+ import { applyCandidatePromotion, } from './promote.js';
16
+ import { runStaticCandidateGate, } from './candidate-gates.js';
17
+ import { currentPolicyVersion } from './policy/policy-store.js';
11
18
  export const DREAM_RUN_SCHEMA_VERSION = 1;
12
19
  const DREAM_SUBDIR = path.join('.synergyspec-selfevolving', 'self-evolution', 'dream-runs');
13
20
  const DEFAULT_ALLOWED_TARGET_KINDS = Object.freeze([
@@ -64,6 +71,7 @@ export async function buildDreamPreview(opts) {
64
71
  export async function writeDreamRun(opts) {
65
72
  const now = opts.now ?? (() => new Date());
66
73
  const preview = opts.preview ?? (await buildDreamPreview(opts));
74
+ const applyPolicy = opts.applyPolicy === true;
67
75
  const createdAt = preview.createdAt || now().toISOString();
68
76
  const entropy = [
69
77
  createdAt,
@@ -79,21 +87,62 @@ export async function writeDreamRun(opts) {
79
87
  await fs.mkdir(tmpDir, { recursive: true });
80
88
  const candidateIds = [];
81
89
  const createdCandidateDirs = [];
90
+ const policyUpdates = [];
82
91
  const candidateRepo = resolveCandidateRepo(opts.projectRoot);
83
92
  try {
93
+ const evidence = applyPolicy ? await collectDreamEvidence(opts.projectRoot) : [];
94
+ const evidenceById = new Map(evidence.map((item) => [item.id, item]));
84
95
  for (const proposal of preview.proposals) {
85
96
  const candidateId = generateCandidateId({
86
97
  now: new Date(createdAt),
87
98
  entropy: `${runId}:${proposal.targetId}`,
88
99
  });
89
- const pkg = buildDreamCandidatePackage({
90
- candidateId,
91
- createdAt,
92
- proposal,
93
- });
100
+ let pkg;
101
+ let update;
102
+ if (applyPolicy) {
103
+ const applied = await buildDreamApplyCandidatePackage({
104
+ projectRoot: opts.projectRoot,
105
+ candidateId,
106
+ createdAt,
107
+ proposal,
108
+ evidence: proposal.evidenceIds
109
+ .map((id) => evidenceById.get(id))
110
+ .filter((item) => item !== undefined),
111
+ optimizer: opts.optimizer ?? defaultDreamOptimizer,
112
+ });
113
+ pkg = applied.pkg;
114
+ update = {
115
+ candidateId,
116
+ targetId: proposal.targetId,
117
+ outcome: applied.initialOutcome,
118
+ promoted: false,
119
+ gatePassed: false,
120
+ promotedFiles: [],
121
+ policyVersion: null,
122
+ ...(applied.reason ? { reason: applied.reason } : {}),
123
+ };
124
+ }
125
+ else {
126
+ pkg = buildDreamCandidatePackage({
127
+ candidateId,
128
+ createdAt,
129
+ proposal,
130
+ });
131
+ }
94
132
  const written = await writeCandidatePackage(candidateRepo, pkg);
95
133
  createdCandidateDirs.push(written.candidateDir);
96
134
  candidateIds.push(candidateId);
135
+ if (applyPolicy && update) {
136
+ if (pkg.edits && pkg.edits.length > 0) {
137
+ update = await runDreamPolicyUpdate({
138
+ repoRoot: opts.projectRoot,
139
+ candidateId,
140
+ targetId: proposal.targetId,
141
+ targetPolicy: opts.targetPolicy,
142
+ });
143
+ }
144
+ policyUpdates.push(update);
145
+ }
97
146
  }
98
147
  const manifest = {
99
148
  ...preview,
@@ -101,6 +150,11 @@ export async function writeDreamRun(opts) {
101
150
  runId,
102
151
  runDir,
103
152
  candidateIds,
153
+ safety: {
154
+ ...preview.safety,
155
+ autoPromotes: applyPolicy,
156
+ },
157
+ ...(applyPolicy ? { policyUpdates } : {}),
104
158
  };
105
159
  await fs.writeFile(path.join(tmpDir, 'dream-run.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
106
160
  await fs.rename(tmpDir, runDir);
@@ -108,7 +162,9 @@ export async function writeDreamRun(opts) {
108
162
  }
109
163
  catch (err) {
110
164
  await fs.rm(tmpDir, { recursive: true, force: true });
111
- await Promise.all(createdCandidateDirs.map((candidateDir) => fs.rm(candidateDir, { recursive: true, force: true })));
165
+ if (!applyPolicy) {
166
+ await Promise.all(createdCandidateDirs.map((candidateDir) => fs.rm(candidateDir, { recursive: true, force: true })));
167
+ }
112
168
  throw err;
113
169
  }
114
170
  }
@@ -117,6 +173,113 @@ export async function readDreamRun(projectRoot, runId) {
117
173
  const raw = await fs.readFile(path.join(runDir, 'dream-run.json'), 'utf8');
118
174
  return JSON.parse(raw);
119
175
  }
176
+ export async function runAcceptedDreamPolicyUpdate(opts) {
177
+ const layout = resolveCandidateRepo(opts.projectRoot);
178
+ const pkg = await readCandidatePackage(layout, opts.candidateId);
179
+ const targetId = pkg.candidate.targetIds[0] ?? '(none)';
180
+ if (pkg.candidate.source !== 'dream-batch') {
181
+ return {
182
+ candidateId: opts.candidateId,
183
+ targetId,
184
+ outcome: 'error-runtime',
185
+ promoted: false,
186
+ gatePassed: false,
187
+ promotedFiles: [],
188
+ policyVersion: targetId === '(none)' ? null : await currentPolicyVersion(opts.projectRoot, targetId),
189
+ reason: `candidate source is ${pkg.candidate.source}, expected dream-batch`,
190
+ };
191
+ }
192
+ if (targetId === '(none)') {
193
+ return {
194
+ candidateId: opts.candidateId,
195
+ targetId,
196
+ outcome: 'error-runtime',
197
+ promoted: false,
198
+ gatePassed: false,
199
+ promotedFiles: [],
200
+ policyVersion: null,
201
+ reason: 'Dream candidate has no targetIds entry',
202
+ };
203
+ }
204
+ const target = lookupCanonicalTarget(targetId);
205
+ if (!target) {
206
+ return {
207
+ candidateId: opts.candidateId,
208
+ targetId,
209
+ outcome: 'error-runtime',
210
+ promoted: false,
211
+ gatePassed: false,
212
+ promotedFiles: [],
213
+ policyVersion: null,
214
+ reason: `Unknown canonical target: ${targetId}`,
215
+ };
216
+ }
217
+ const resolved = await resolveTargetLocalFiles(targetId, opts.projectRoot);
218
+ if (resolved.files.length === 0) {
219
+ return {
220
+ candidateId: opts.candidateId,
221
+ targetId,
222
+ outcome: 'refused-no-local-files',
223
+ promoted: false,
224
+ gatePassed: false,
225
+ promotedFiles: [],
226
+ policyVersion: await currentPolicyVersion(opts.projectRoot, targetId),
227
+ reason: `no local editable file for target ${targetId}`,
228
+ };
229
+ }
230
+ const synthesizeEdits = opts.synthesizeEdits ?? defaultAcceptedDreamPolicyEditSynthesizer(resolved.files);
231
+ let output;
232
+ try {
233
+ output = await synthesizeEdits({
234
+ candidateId: opts.candidateId,
235
+ targetId,
236
+ acceptedBy: opts.acceptedBy,
237
+ proposalMd: pkg.proposalMd,
238
+ rationaleMd: pkg.rationaleMd,
239
+ evalPlanMd: pkg.evalPlanMd,
240
+ riskReportMd: pkg.riskReportMd,
241
+ });
242
+ }
243
+ catch (err) {
244
+ return {
245
+ candidateId: opts.candidateId,
246
+ targetId,
247
+ outcome: 'refused-optimizer',
248
+ promoted: false,
249
+ gatePassed: false,
250
+ promotedFiles: [],
251
+ policyVersion: await currentPolicyVersion(opts.projectRoot, targetId),
252
+ reason: err instanceof Error ? err.message : String(err),
253
+ };
254
+ }
255
+ const prepared = prepareDreamEditsForPackage({
256
+ pkg,
257
+ targetId,
258
+ files: resolved.files,
259
+ output,
260
+ acceptedBy: opts.acceptedBy,
261
+ now: opts.now ?? (() => new Date()),
262
+ });
263
+ if ('reason' in prepared) {
264
+ return {
265
+ candidateId: opts.candidateId,
266
+ targetId,
267
+ outcome: 'refused-optimizer',
268
+ promoted: false,
269
+ gatePassed: false,
270
+ promotedFiles: [],
271
+ policyVersion: await currentPolicyVersion(opts.projectRoot, targetId),
272
+ reason: prepared.reason,
273
+ };
274
+ }
275
+ await rewriteDreamCandidatePackage(layout, prepared.pkg);
276
+ return runDreamPolicyUpdate({
277
+ repoRoot: opts.projectRoot,
278
+ candidateId: opts.candidateId,
279
+ targetId,
280
+ targetPolicy: opts.targetPolicy,
281
+ });
282
+ }
120
283
  export function renderDreamPreview(preview) {
121
284
  const lines = [
122
285
  'Supervised Learning Dream preview',
@@ -145,7 +308,16 @@ export function renderDreamRun(manifest) {
145
308
  if (manifest.candidateIds.length === 0) {
146
309
  lines.push('No candidates were written.');
147
310
  }
148
- lines.push('Next: review the proposal, author concrete bounded edits through the existing review channel, then run gate/promote or reject.');
311
+ if (manifest.policyUpdates && manifest.policyUpdates.length > 0) {
312
+ const promoted = manifest.policyUpdates.filter((update) => update.promoted);
313
+ lines.push(`Policy updates: ${promoted.length}/${manifest.policyUpdates.length} promoted`);
314
+ for (const update of manifest.policyUpdates) {
315
+ lines.push(`- ${update.candidateId}: ${update.outcome}${update.reason ? ` (${update.reason})` : ''}`);
316
+ }
317
+ }
318
+ else {
319
+ lines.push('Next: review the proposal, author concrete bounded edits through the existing review channel, then run gate/promote or reject.');
320
+ }
149
321
  return lines.join('\n');
150
322
  }
151
323
  function buildDreamCandidatePackage(args) {
@@ -217,6 +389,305 @@ function buildDreamCandidatePackage(args) {
217
389
  riskReportMd,
218
390
  };
219
391
  }
392
+ async function buildDreamApplyCandidatePackage(args) {
393
+ const target = lookupCanonicalTarget(args.proposal.targetId);
394
+ if (!target) {
395
+ return {
396
+ pkg: buildDreamCandidatePackage(args),
397
+ initialOutcome: 'error-runtime',
398
+ reason: `Unknown canonical target: ${args.proposal.targetId}`,
399
+ };
400
+ }
401
+ const resolved = await resolveTargetLocalFiles(target.id, args.projectRoot);
402
+ if (resolved.files.length === 0) {
403
+ return {
404
+ pkg: buildDreamCandidatePackage(args),
405
+ initialOutcome: 'refused-no-local-files',
406
+ reason: `no local editable file for target ${target.id}`,
407
+ };
408
+ }
409
+ let optimized;
410
+ try {
411
+ optimized = await args.optimizer({
412
+ projectRoot: args.projectRoot,
413
+ proposal: args.proposal,
414
+ target,
415
+ evidence: args.evidence,
416
+ files: resolved.files,
417
+ });
418
+ }
419
+ catch (err) {
420
+ return {
421
+ pkg: buildDreamCandidatePackage(args),
422
+ initialOutcome: 'refused-optimizer',
423
+ reason: err instanceof Error ? err.message : String(err),
424
+ };
425
+ }
426
+ const basePkg = buildDreamCandidatePackage(args);
427
+ const prepared = prepareDreamEditsForPackage({
428
+ pkg: basePkg,
429
+ targetId: target.id,
430
+ files: resolved.files,
431
+ output: optimized,
432
+ acceptedBy: 'dream run --apply',
433
+ now: () => new Date(args.createdAt),
434
+ });
435
+ if ('reason' in prepared) {
436
+ return {
437
+ pkg: buildDreamCandidatePackage(args),
438
+ initialOutcome: 'refused-optimizer',
439
+ reason: prepared.reason,
440
+ };
441
+ }
442
+ return {
443
+ pkg: prepared.pkg,
444
+ initialOutcome: 'promoted',
445
+ };
446
+ }
447
+ async function runDreamPolicyUpdate(args) {
448
+ const layout = resolveCandidateRepo(args.repoRoot);
449
+ let gate;
450
+ try {
451
+ gate = await runStaticCandidateGate(layout, args.candidateId, {
452
+ applyTransition: true,
453
+ targetPolicy: args.targetPolicy,
454
+ });
455
+ }
456
+ catch (err) {
457
+ return {
458
+ candidateId: args.candidateId,
459
+ targetId: args.targetId,
460
+ outcome: 'error-runtime',
461
+ promoted: false,
462
+ gatePassed: false,
463
+ promotedFiles: [],
464
+ policyVersion: await currentPolicyVersion(args.repoRoot, args.targetId),
465
+ reason: `static gate error: ${err instanceof Error ? err.message : String(err)}`,
466
+ };
467
+ }
468
+ if (!gate.passed) {
469
+ return {
470
+ candidateId: args.candidateId,
471
+ targetId: args.targetId,
472
+ outcome: 'refused-static-gate',
473
+ promoted: false,
474
+ gatePassed: false,
475
+ promotedFiles: [],
476
+ policyVersion: await currentPolicyVersion(args.repoRoot, args.targetId),
477
+ gate,
478
+ reason: gate.findings
479
+ .filter((finding) => finding.severity === 'error')
480
+ .map((finding) => finding.message)
481
+ .join('; ') || 'static gate failed',
482
+ };
483
+ }
484
+ try {
485
+ const applied = await applyCandidatePromotion(layout, args.candidateId, {
486
+ repoRoot: args.repoRoot,
487
+ policy: args.targetPolicy,
488
+ });
489
+ return {
490
+ candidateId: args.candidateId,
491
+ targetId: args.targetId,
492
+ outcome: 'promoted',
493
+ promoted: true,
494
+ gatePassed: true,
495
+ promotedFiles: applied.appliedFiles.map((file) => file.file),
496
+ policyVersion: await currentPolicyVersion(args.repoRoot, args.targetId),
497
+ gate,
498
+ applied,
499
+ };
500
+ }
501
+ catch (err) {
502
+ return {
503
+ candidateId: args.candidateId,
504
+ targetId: args.targetId,
505
+ outcome: 'error-runtime',
506
+ promoted: false,
507
+ gatePassed: true,
508
+ promotedFiles: [],
509
+ policyVersion: await currentPolicyVersion(args.repoRoot, args.targetId),
510
+ gate,
511
+ reason: `promotion error: ${err instanceof Error ? err.message : String(err)}`,
512
+ };
513
+ }
514
+ }
515
+ function prepareDreamEditsForPackage(args) {
516
+ let edits;
517
+ try {
518
+ edits = validateCandidateEdits(args.output.edits, args.files.map((file) => file.relPath));
519
+ }
520
+ catch (err) {
521
+ return { reason: err instanceof Error ? err.message : String(err) };
522
+ }
523
+ const currentByRel = new Map(args.files.map((file) => [file.relPath, file.content]));
524
+ const changed = edits.filter((edit) => edit.content !== currentByRel.get(edit.relPath));
525
+ if (changed.length === 0) {
526
+ return { reason: 'optimizer produced no byte-changing edits' };
527
+ }
528
+ const diffPatch = changed
529
+ .map((edit) => renderUnifiedDiff(edit.relPath, currentByRel.get(edit.relPath) ?? '', edit.content))
530
+ .join('\n');
531
+ const rationaleMd = [
532
+ '# Rationale',
533
+ '',
534
+ `Because ${args.acceptedBy} accepted the Dream optimizer brief, this bounded edit materializes the supervised batch-learning proposal for ${args.targetId}.`,
535
+ '',
536
+ args.output.rationale,
537
+ '',
538
+ ].join('\n');
539
+ return {
540
+ pkg: {
541
+ ...args.pkg,
542
+ candidate: {
543
+ ...args.pkg.candidate,
544
+ updatedAt: args.now().toISOString(),
545
+ changedFiles: changed.map((edit) => edit.relPath),
546
+ rollbackPlan: 'Use self-evolution promote --rollback for this candidate; promotion writes a rollback snapshot before changing policy files.',
547
+ },
548
+ diffPatch: `${diffPatch}\n`,
549
+ rationaleMd,
550
+ evalPlanMd: args.output.validationPlan ??
551
+ [
552
+ '# Evaluation Plan',
553
+ '',
554
+ args.pkg.evalPlanMd,
555
+ '',
556
+ '- Static candidate gate must pass before promotion.',
557
+ '- Held-out replay should cover the cited Dream evidence before relying on the update.',
558
+ '',
559
+ ].join('\n'),
560
+ riskReportMd: args.output.riskReport ??
561
+ [
562
+ '# Risk Report',
563
+ '',
564
+ args.pkg.riskReportMd,
565
+ '',
566
+ '- Dream apply mode promotes only through the existing candidate promotion channel.',
567
+ '- Rollback metadata is written before any canonical policy file is changed.',
568
+ '- New skill creation remains out of scope.',
569
+ '',
570
+ ].join('\n'),
571
+ edits: changed,
572
+ },
573
+ };
574
+ }
575
+ async function rewriteDreamCandidatePackage(layout, pkg) {
576
+ const dir = path.join(layout.baseDir, pkg.candidate.id);
577
+ await Promise.all([
578
+ fs.writeFile(path.join(dir, 'candidate.json'), `${JSON.stringify(pkg.candidate, null, 2)}\n`, 'utf8'),
579
+ fs.writeFile(path.join(dir, 'proposal.md'), pkg.proposalMd, 'utf8'),
580
+ fs.writeFile(path.join(dir, 'diff.patch'), pkg.diffPatch, 'utf8'),
581
+ fs.writeFile(path.join(dir, 'rationale.md'), pkg.rationaleMd, 'utf8'),
582
+ fs.writeFile(path.join(dir, 'eval-plan.md'), pkg.evalPlanMd, 'utf8'),
583
+ fs.writeFile(path.join(dir, 'risk-report.md'), pkg.riskReportMd, 'utf8'),
584
+ ]);
585
+ if (pkg.edits && pkg.edits.length > 0) {
586
+ await fs.writeFile(path.join(dir, EDITS_JSON_FILE), `${JSON.stringify(pkg.edits, null, 2)}\n`, 'utf8');
587
+ }
588
+ else {
589
+ await fs.rm(path.join(dir, EDITS_JSON_FILE), { force: true });
590
+ }
591
+ }
592
+ function defaultAcceptedDreamPolicyEditSynthesizer(files) {
593
+ return async (input) => {
594
+ const file = files.find((candidate) => candidate.content.trim().length > 0) ?? files[0];
595
+ if (!file) {
596
+ return {
597
+ edits: [],
598
+ rationale: `Dream found no editable files for ${input.targetId}.`,
599
+ };
600
+ }
601
+ const note = renderDreamPolicyNote({
602
+ targetId: input.targetId,
603
+ evidenceIds: [],
604
+ expectedBenefit: `Accepted by ${input.acceptedBy}`,
605
+ validationPlan: 'Static gate plus held-out replay before relying on the updated policy.',
606
+ });
607
+ return {
608
+ edits: [{ relPath: file.relPath, content: applyDreamPolicyNote(file, note) }],
609
+ rationale: 'Accepted Dream optimizer brief; materialize it as one bounded policy-file update with rollback support.',
610
+ };
611
+ };
612
+ }
613
+ async function defaultDreamOptimizer(input) {
614
+ const file = input.files.find((candidate) => candidate.content.trim().length > 0) ?? input.files[0];
615
+ if (!file) {
616
+ return {
617
+ edits: [],
618
+ rationale: `Dream found no editable files for ${input.target.id}.`,
619
+ };
620
+ }
621
+ const evidenceIds = input.proposal.evidenceIds.slice(0, 6);
622
+ const note = renderDreamPolicyNote({
623
+ targetId: input.target.id,
624
+ evidenceIds,
625
+ expectedBenefit: input.proposal.expectedBenefit,
626
+ validationPlan: input.proposal.validationPlan,
627
+ });
628
+ const content = applyDreamPolicyNote(file, note);
629
+ return {
630
+ edits: [{ relPath: file.relPath, content }],
631
+ rationale: `Because ${input.target.id} repeatedly appears in accumulated Dream evidence, ` +
632
+ 'the policy now carries a supervised batch-learning note that makes validation, held-out replay, and rollback expectations explicit for similar future work.',
633
+ validationPlan: [
634
+ '# Evaluation Plan',
635
+ '',
636
+ input.proposal.validationPlan,
637
+ '',
638
+ '- Static candidate gate must pass before promotion.',
639
+ '- Validate with tests or replay for the cited evidence before treating this policy update as reliable.',
640
+ '',
641
+ ].join('\n'),
642
+ riskReport: [
643
+ '# Risk Report',
644
+ '',
645
+ `Risk level: ${input.proposal.riskLevel}`,
646
+ '',
647
+ '- Uses existing policy target only; no new skill is created.',
648
+ '- Promotion writes rollback metadata before mutating local policy files.',
649
+ '- If held-out replay regresses, roll back this candidate promotion.',
650
+ '',
651
+ ].join('\n'),
652
+ };
653
+ }
654
+ function renderDreamPolicyNote(args) {
655
+ return [
656
+ '<!-- SS_SUPERVISED_DREAM_OPTIMIZER_START -->',
657
+ '**Supervised Dream Optimizer Note**',
658
+ '',
659
+ `- Target: ${args.targetId}`,
660
+ `- Evidence batch: ${args.evidenceIds.length > 0 ? args.evidenceIds.join(', ') : 'none'}`,
661
+ `- Expected benefit: ${args.expectedBenefit}`,
662
+ `- Validation: ${args.validationPlan}`,
663
+ '- Behavior: when similar friction appears, make the expected verification, recovery, and rollback check explicit before finishing the workflow.',
664
+ '<!-- SS_SUPERVISED_DREAM_OPTIMIZER_END -->',
665
+ ].join('\n');
666
+ }
667
+ function applyDreamPolicyNote(file, note) {
668
+ const current = file.content;
669
+ const replaceBlock = (text) => {
670
+ const blockRe = /<!-- SS_SUPERVISED_DREAM_OPTIMIZER_START -->[\s\S]*?<!-- SS_SUPERVISED_DREAM_OPTIMIZER_END -->/;
671
+ return blockRe.test(text)
672
+ ? text.replace(blockRe, note)
673
+ : `${text.replace(/\s*$/, '')}\n\n${note}\n`;
674
+ };
675
+ if (/\.ts$/.test(file.relPath)) {
676
+ const marker = 'const INSTRUCTIONS_BODY = `';
677
+ const start = current.indexOf(marker);
678
+ if (start >= 0) {
679
+ const bodyStart = start + marker.length;
680
+ const bodyEnd = current.indexOf('`;', bodyStart);
681
+ if (bodyEnd > bodyStart) {
682
+ const before = current.slice(0, bodyStart);
683
+ const body = current.slice(bodyStart, bodyEnd);
684
+ const after = current.slice(bodyEnd);
685
+ return `${before}${replaceBlock(body)}${after}`;
686
+ }
687
+ }
688
+ }
689
+ return replaceBlock(current);
690
+ }
220
691
  function buildDreamProposals(evidence, targets, limit) {
221
692
  if (limit === 0 || evidence.length === 0)
222
693
  return [];
@@ -196,6 +196,7 @@ export interface RunEpisodeOptions {
196
196
  }
197
197
  export interface RunEpisodeResult {
198
198
  episodeId: string;
199
+ harness?: AgentHarness;
199
200
  /** True when the CRITIC AGENT(基线智能体 baseline agent)arm was skipped. */
200
201
  baselineSkipped: boolean;
201
202
  /** advantage = reward(主臂) − reward(基线臂); null when skipped or abstained. */
@@ -236,6 +237,7 @@ export interface ResumeEpisodeOptions {
236
237
  }
237
238
  export interface ResumeEpisodeResult {
238
239
  episodeId: string;
240
+ harness?: AgentHarness;
239
241
  /** The stage the episode was at when resume was called. */
240
242
  resumedFrom: EpisodeStage;
241
243
  /** The stage it reached after the resume re-ran the remaining steps. */