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.
- package/dist/commands/learn.js +80 -24
- package/dist/commands/self-evolution-dream.d.ts +15 -1
- package/dist/commands/self-evolution-dream.js +111 -6
- package/dist/commands/self-evolution-episode.d.ts +3 -0
- package/dist/commands/self-evolution-episode.js +157 -108
- package/dist/commands/workflow/status.js +4 -0
- package/dist/core/archive.js +17 -9
- package/dist/core/change-readiness.d.ts +16 -1
- package/dist/core/change-readiness.js +441 -15
- package/dist/core/fitness/loss.d.ts +3 -5
- package/dist/core/fitness/loss.js +2 -2
- package/dist/core/fitness/test-metrics.d.ts +1 -0
- package/dist/core/fitness/test-metrics.js +49 -0
- package/dist/core/learn.js +129 -11
- package/dist/core/migration.d.ts +6 -14
- package/dist/core/migration.js +63 -21
- package/dist/core/runner-evidence.d.ts +53 -0
- package/dist/core/runner-evidence.js +613 -0
- package/dist/core/self-evolution/candidates.js +0 -2
- package/dist/core/self-evolution/dream.d.ts +57 -3
- package/dist/core/self-evolution/dream.js +480 -9
- package/dist/core/self-evolution/episode-orchestrator.d.ts +2 -0
- package/dist/core/self-evolution/episode-orchestrator.js +17 -5
- package/dist/core/self-evolution/episode-store.d.ts +5 -0
- package/dist/core/self-evolution/episode-store.js +6 -2
- package/dist/core/self-evolution/evolving-agent.d.ts +33 -4
- package/dist/core/self-evolution/evolving-agent.js +138 -11
- package/dist/core/self-evolution/host-harness.d.ts +35 -12
- package/dist/core/self-evolution/host-harness.js +188 -49
- package/dist/core/self-evolution/reward-aggregator.js +2 -2
- package/dist/core/templates/workflows/archive-change.js +18 -18
- package/dist/core/templates/workflows/dream.js +57 -47
- package/dist/core/templates/workflows/learn.js +7 -5
- package/dist/core/templates/workflows/run-tests.js +48 -29
- package/dist/core/templates/workflows/self-evolving.js +11 -8
- package/dist/core/trajectory/facts.d.ts +1 -1
- package/dist/core/trajectory/registry.js +39 -8
- package/package.json +1 -1
package/dist/commands/learn.js
CHANGED
|
@@ -4,10 +4,10 @@ import { detectFrozenHealthRoutingObservations, detectUnbindableHintObservations
|
|
|
4
4
|
import { readProjectConfig } from '../core/project-config.js';
|
|
5
5
|
import { assembleTrajectoryContext, } from '../core/learn/trajectory-assembler.js';
|
|
6
6
|
import { findTranscriptsForChange, resolveChangeDir, validateExplicitTrajectoryHandle, } from '../core/learn/trajectory-discovery.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getTrajectoryResultForChange } from '../core/trajectory/registry.js';
|
|
8
8
|
import { toTrajectoryFacts, describeRunnerResults, extractExpectedTestPaths } from '../core/trajectory/facts.js';
|
|
9
9
|
import { toActionSkeleton } from '../core/trajectory/skeleton.js';
|
|
10
|
-
import { resolveHostHarness,
|
|
10
|
+
import { resolveHostHarness, resolveHostHarnessDetailsForRepo, persistHostHarness, seedHostHarnessForRepo, } from '../core/self-evolution/host-harness.js';
|
|
11
11
|
import { mineSuccessSignals } from '../core/self-evolution/success-channel.js';
|
|
12
12
|
import { captureMainArm, runEpisode, } from '../core/self-evolution/episode-orchestrator.js';
|
|
13
13
|
import { buildLLMSummaryCandidates, ingestLearnHandoff, } from '../core/learn/llm-summary.js';
|
|
@@ -15,6 +15,13 @@ function collect(value, previous) {
|
|
|
15
15
|
previous.push(value);
|
|
16
16
|
return previous;
|
|
17
17
|
}
|
|
18
|
+
function parseAgentHarness(value) {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (value === 'claude' || value === 'codex' || value === 'opencode')
|
|
22
|
+
return value;
|
|
23
|
+
throw new Error(`Invalid harness '${value}'. Expected one of: claude, codex, opencode`);
|
|
24
|
+
}
|
|
18
25
|
export function registerLearnCommand(program, deps = {}) {
|
|
19
26
|
const runEpisodeImpl = deps.runEpisode ?? runEpisode;
|
|
20
27
|
const learnCmd = program
|
|
@@ -31,25 +38,32 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
31
38
|
.option('--no-focus', 'Disable the evolution-focus switch for this run: when the policy is frozen-by-default, frozen-kind signals are dropped silently instead of being re-aimed at the explicitly evolvable target(s) as a policy-focus hint (config: selfEvolution.focus)')
|
|
32
39
|
.option('--transcript <path>', 'Explicit transcript .jsonl to grade (bypasses change-window discovery; Claude transcript store only)')
|
|
33
40
|
.option('--session-id <id>', 'Explicit Claude session id to grade (bypasses change-window discovery; Claude transcript store only)')
|
|
41
|
+
.option('--harness <name>', 'Force the observed-run/code-agent harness (claude|codex|opencode)')
|
|
42
|
+
.option('--rerun', 'Force a new loop-v2 episode instead of reusing a completed matching episode')
|
|
43
|
+
.option('--force-new-episode', 'Alias for --rerun')
|
|
34
44
|
.option('--json', 'Output as JSON')
|
|
35
45
|
.option('--no-interactive', 'Disable interactive prompts (learn is non-interactive by default)')
|
|
36
46
|
.action(async (change, options) => {
|
|
37
47
|
try {
|
|
38
48
|
const projectRoot = process.cwd();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
// `.synergyspec-selfevolving/host-harness.json`, so the subagent reads it
|
|
46
|
-
// back instead of guessing. Best-effort: a persistence failure must never
|
|
47
|
-
// fail the learn run (a missing seed only degrades to today's behavior).
|
|
49
|
+
const forcedHarness = parseAgentHarness(options.harness);
|
|
50
|
+
let reportHarness = forcedHarness;
|
|
51
|
+
// Resolve the report harness without writing the repo sidecar. Preview
|
|
52
|
+
// and diagnostic learn runs are read-only: a weak ambient CODEX_*/
|
|
53
|
+
// OPENCODE_* signal must not be promoted into durable provenance before
|
|
54
|
+
// we know an episode will actually spawn.
|
|
48
55
|
try {
|
|
49
|
-
|
|
56
|
+
if (!forcedHarness) {
|
|
57
|
+
const resolution = await resolveHostHarnessDetailsForRepo(projectRoot);
|
|
58
|
+
if (resolution.source === 'override' ||
|
|
59
|
+
resolution.source === 'env' ||
|
|
60
|
+
resolution.source === 'persisted') {
|
|
61
|
+
reportHarness = resolution.harness;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
50
64
|
}
|
|
51
65
|
catch {
|
|
52
|
-
// best-effort
|
|
66
|
+
// best-effort read only.
|
|
53
67
|
}
|
|
54
68
|
// USER-TYPED handle flags are validated up front and fail LOUD
|
|
55
69
|
// (exit 1) on a miss — unlike the env-var channel, which keeps the
|
|
@@ -68,8 +82,11 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
68
82
|
// (same set/restore shape as debug-trajectory's --harness) so it
|
|
69
83
|
// reaches the registry adapter inside generateLearnReport without
|
|
70
84
|
// changing any core signature. Restored as soon as the report exists.
|
|
85
|
+
const prevHarnessEnv = process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS;
|
|
71
86
|
const prevTranscriptEnv = process.env.SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT;
|
|
72
87
|
const prevSessionEnv = process.env.SYNERGYSPEC_SELFEVOLVING_SESSION_ID;
|
|
88
|
+
if (reportHarness)
|
|
89
|
+
process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS = reportHarness;
|
|
73
90
|
if (options.transcript)
|
|
74
91
|
process.env.SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT = options.transcript;
|
|
75
92
|
if (options.sessionId)
|
|
@@ -79,6 +96,14 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
79
96
|
report = await generateLearnReport({ projectRoot, changeName: change });
|
|
80
97
|
}
|
|
81
98
|
finally {
|
|
99
|
+
if (reportHarness) {
|
|
100
|
+
if (prevHarnessEnv === undefined) {
|
|
101
|
+
delete process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS = prevHarnessEnv;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
82
107
|
if (options.transcript) {
|
|
83
108
|
if (prevTranscriptEnv === undefined) {
|
|
84
109
|
delete process.env.SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT;
|
|
@@ -198,18 +223,19 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
198
223
|
// orderSwap / tamperCheck / divergenceCheck). Omitted ⇒ the orchestrator's
|
|
199
224
|
// single-sample, divergence-routing default (no extra spawns).
|
|
200
225
|
const episodeConfig = readProjectConfig(projectRoot);
|
|
201
|
-
// Pass the host-resolved harness EXPLICITLY
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
226
|
+
// Pass the host-resolved harness EXPLICITLY only when the resolver has
|
|
227
|
+
// a real host signal. A silent default must remain "unknown" rather
|
|
228
|
+
// than being laundered into a concrete Claude provenance value.
|
|
229
|
+
const harness = forcedHarness
|
|
230
|
+
? (await persistHostHarness(projectRoot, forcedHarness), forcedHarness)
|
|
231
|
+
: await seedHostHarnessForRepo(projectRoot).then((resolution) => resolution.source === 'default' ? undefined : resolution.harness);
|
|
206
232
|
episodeOutcome = await runEpisodeImpl({
|
|
207
233
|
repoRoot: projectRoot,
|
|
208
234
|
targetId: concreteEvolveTarget.targetId,
|
|
209
235
|
changeName: report.changeName,
|
|
210
236
|
changeDirPath: report.changeDir,
|
|
211
237
|
mainArm,
|
|
212
|
-
harness,
|
|
238
|
+
...(harness ? { harness } : {}),
|
|
213
239
|
...(episodeConfig?.selfEvolution?.reward
|
|
214
240
|
? { reward: episodeConfig.selfEvolution.reward }
|
|
215
241
|
: {}),
|
|
@@ -218,6 +244,9 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
218
244
|
...(episodeConfig?.selfEvolution?.agentTimeoutMs !== undefined
|
|
219
245
|
? { agentTimeoutMs: episodeConfig.selfEvolution.agentTimeoutMs }
|
|
220
246
|
: {}),
|
|
247
|
+
...(options.rerun === true || options.forceNewEpisode === true
|
|
248
|
+
? { forceNewEpisode: true }
|
|
249
|
+
: {}),
|
|
221
250
|
});
|
|
222
251
|
}
|
|
223
252
|
if (options.json) {
|
|
@@ -261,6 +290,23 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
261
290
|
// handle is honored either way.
|
|
262
291
|
const opts = command.optsWithGlobals();
|
|
263
292
|
try {
|
|
293
|
+
const forcedHarness = parseAgentHarness(opts.harness);
|
|
294
|
+
let debugHarness = forcedHarness;
|
|
295
|
+
let debugHarnessSource = forcedHarness ? 'override' : null;
|
|
296
|
+
try {
|
|
297
|
+
if (!forcedHarness) {
|
|
298
|
+
const resolution = await resolveHostHarnessDetailsForRepo(projectRoot);
|
|
299
|
+
debugHarnessSource = resolution.source;
|
|
300
|
+
if (resolution.source === 'override' ||
|
|
301
|
+
resolution.source === 'env' ||
|
|
302
|
+
resolution.source === 'persisted') {
|
|
303
|
+
debugHarness = resolution.harness;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// best-effort read only; the adapter block below will surface any miss.
|
|
309
|
+
}
|
|
264
310
|
const discovered = await findTranscriptsForChange(change, projectRoot, {
|
|
265
311
|
transcriptPath: opts.transcript,
|
|
266
312
|
sessionId: opts.sessionId,
|
|
@@ -290,8 +336,8 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
290
336
|
const prevHarnessEnv = process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS;
|
|
291
337
|
const prevTranscriptEnv = process.env.SYNERGYSPEC_SELFEVOLVING_TRANSCRIPT;
|
|
292
338
|
const prevSessionEnv = process.env.SYNERGYSPEC_SELFEVOLVING_SESSION_ID;
|
|
293
|
-
if (
|
|
294
|
-
process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS =
|
|
339
|
+
if (debugHarness)
|
|
340
|
+
process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS = debugHarness;
|
|
295
341
|
// The explicit trajectory handle must reach the adapter's own discovery
|
|
296
342
|
// call too (env is the only channel through the registry), so the
|
|
297
343
|
// introspected payload reflects the same override as `discovery` above.
|
|
@@ -300,7 +346,8 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
300
346
|
if (opts.sessionId)
|
|
301
347
|
process.env.SYNERGYSPEC_SELFEVOLVING_SESSION_ID = opts.sessionId;
|
|
302
348
|
try {
|
|
303
|
-
const
|
|
349
|
+
const adapterResult = await getTrajectoryResultForChange(projectRoot, change);
|
|
350
|
+
const adapterTrajectory = adapterResult.trajectory;
|
|
304
351
|
// Change-scope guard input so debug-trajectory's facts + per-runner
|
|
305
352
|
// detail reflect the same scope demotion the loop uses (surfaces a
|
|
306
353
|
// green-but-out-of-scope graded run instead of hiding it).
|
|
@@ -309,6 +356,9 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
309
356
|
.catch(() => undefined));
|
|
310
357
|
payload.adapter = {
|
|
311
358
|
resolvedHarness: resolveHostHarness(),
|
|
359
|
+
resolvedHarnessSource: debugHarnessSource,
|
|
360
|
+
sourceHarness: adapterResult.sourceHarness,
|
|
361
|
+
reason: adapterResult.reason ?? null,
|
|
312
362
|
sessionId: adapterTrajectory?.sessionId ?? null,
|
|
313
363
|
turns: adapterTrajectory?.turns.length ?? 0,
|
|
314
364
|
sourcePaths: adapterTrajectory ? [...new Set(adapterTrajectory.sourcePaths)] : [],
|
|
@@ -324,7 +374,7 @@ export function registerLearnCommand(program, deps = {}) {
|
|
|
324
374
|
};
|
|
325
375
|
}
|
|
326
376
|
finally {
|
|
327
|
-
if (
|
|
377
|
+
if (debugHarness) {
|
|
328
378
|
if (prevHarnessEnv === undefined) {
|
|
329
379
|
delete process.env.SYNERGYSPEC_SELFEVOLVING_HOST_HARNESS;
|
|
330
380
|
}
|
|
@@ -625,6 +675,7 @@ function printJson(report, applied, evolutionPreview, hintsPath, episodeOutcome)
|
|
|
625
675
|
? { busy: true, reason: episodeOutcome.reason }
|
|
626
676
|
: {
|
|
627
677
|
episodeId: episodeOutcome.episodeId,
|
|
678
|
+
...(episodeOutcome.harness ? { harness: episodeOutcome.harness } : {}),
|
|
628
679
|
baselineSkipped: episodeOutcome.baselineSkipped,
|
|
629
680
|
advantage: episodeOutcome.advantage,
|
|
630
681
|
decision: episodeOutcome.decision,
|
|
@@ -876,7 +927,8 @@ function renderLearnTransparency(report, applied, evolutionPreview, hintsPath, o
|
|
|
876
927
|
}
|
|
877
928
|
/**
|
|
878
929
|
* The loop-v2 autonomous entrance command (CS6-F):
|
|
879
|
-
* `self-evolution episode --change "<name>" [--target <id>] [--
|
|
930
|
+
* `self-evolution episode --change "<name>" [--target <id>] [--harness <h>]
|
|
931
|
+
* [--session-id <id>]`.
|
|
880
932
|
* Replaces the GA `auto-evolve` / autonomous `evolve-from-edits` suggestions as
|
|
881
933
|
* the loop-v2 path. The `--target` pin is included only when a concrete target
|
|
882
934
|
* id resolved; `--session-id` is threaded through when the operator pinned an
|
|
@@ -886,8 +938,12 @@ function renderEpisodeCommand(changeName, targetId, options) {
|
|
|
886
938
|
const parts = [`synergyspec-selfevolving self-evolution episode --change "${changeName}"`];
|
|
887
939
|
if (targetId)
|
|
888
940
|
parts.push(`--target ${targetId}`);
|
|
941
|
+
if (options.harness)
|
|
942
|
+
parts.push(`--harness ${options.harness}`);
|
|
889
943
|
if (options.sessionId)
|
|
890
944
|
parts.push(`--session-id ${options.sessionId}`);
|
|
945
|
+
if (options.rerun === true || options.forceNewEpisode === true)
|
|
946
|
+
parts.push('--rerun');
|
|
891
947
|
return parts.join(' ');
|
|
892
948
|
}
|
|
893
949
|
/** The loop-v2 next-step line shown when a concrete target is pinned. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { type DreamPreview, type DreamRunManifest } from '../core/self-evolution/index.js';
|
|
2
|
+
import { type DreamPolicyEditSynthesizer, type DreamPolicyUpdateResult, type DreamPreview, type DreamRunManifest } from '../core/self-evolution/index.js';
|
|
3
3
|
export declare function attachSelfEvolutionDreamCommands(parent: Command): void;
|
|
4
4
|
export interface RunDreamPreviewArgs {
|
|
5
5
|
target?: string;
|
|
@@ -11,6 +11,7 @@ export interface RunDreamCommandOptions {
|
|
|
11
11
|
stdout?: (line: string) => void;
|
|
12
12
|
stderr?: (line: string) => void;
|
|
13
13
|
now?: () => Date;
|
|
14
|
+
synthesizeEdits?: DreamPolicyEditSynthesizer;
|
|
14
15
|
}
|
|
15
16
|
export interface RunDreamPreviewResult {
|
|
16
17
|
exitCode: number;
|
|
@@ -19,6 +20,8 @@ export interface RunDreamPreviewResult {
|
|
|
19
20
|
}
|
|
20
21
|
export declare function runDreamPreviewCommand(args: RunDreamPreviewArgs, opts: RunDreamCommandOptions): Promise<RunDreamPreviewResult>;
|
|
21
22
|
export interface RunDreamRunArgs extends RunDreamPreviewArgs {
|
|
23
|
+
applyPolicy?: boolean;
|
|
24
|
+
yes?: boolean;
|
|
22
25
|
}
|
|
23
26
|
export interface RunDreamRunResult {
|
|
24
27
|
exitCode: number;
|
|
@@ -26,6 +29,17 @@ export interface RunDreamRunResult {
|
|
|
26
29
|
error?: string;
|
|
27
30
|
}
|
|
28
31
|
export declare function runDreamRunCommand(args: RunDreamRunArgs, opts: RunDreamCommandOptions): Promise<RunDreamRunResult>;
|
|
32
|
+
export interface RunDreamPolicyUpdateArgs {
|
|
33
|
+
candidateId: string;
|
|
34
|
+
acceptedBy?: string;
|
|
35
|
+
yes?: boolean;
|
|
36
|
+
json?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface RunDreamPolicyUpdateResult extends DreamPolicyUpdateResult {
|
|
39
|
+
exitCode: number;
|
|
40
|
+
error?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function runDreamPolicyUpdateCommand(args: RunDreamPolicyUpdateArgs, opts: RunDreamCommandOptions): Promise<RunDreamPolicyUpdateResult>;
|
|
29
43
|
export interface RunDreamShowArgs {
|
|
30
44
|
runId?: string;
|
|
31
45
|
json?: boolean;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Offline Supervised Learning Dream commands.
|
|
3
3
|
*
|
|
4
|
-
* Dream
|
|
5
|
-
*
|
|
6
|
-
* existing
|
|
4
|
+
* Dream reads accumulated evidence and writes review candidates by default.
|
|
5
|
+
* Explicit accepted-candidate paths can synthesize bounded edits, run the
|
|
6
|
+
* static gate, and promote through the existing ledger/rollback channel.
|
|
7
7
|
*/
|
|
8
8
|
import { promises as fs } from 'node:fs';
|
|
9
9
|
import * as path from 'node:path';
|
|
10
|
-
import { buildDreamPreview, readDreamRun, renderDreamPreview, renderDreamRun, resolveDreamRunsDir, writeDreamRun, } from '../core/self-evolution/index.js';
|
|
10
|
+
import { buildDreamPreview, readDreamRun, renderDreamPreview, renderDreamRun, resolveDreamRunsDir, runAcceptedDreamPolicyUpdate, writeDreamRun, } from '../core/self-evolution/index.js';
|
|
11
11
|
export function attachSelfEvolutionDreamCommands(parent) {
|
|
12
12
|
const dream = parent
|
|
13
13
|
.command('dream')
|
|
14
|
-
.description('Offline Supervised Learning Dream: batch-mine
|
|
14
|
+
.description('Offline Supervised Learning Dream: batch-mine evidence into candidates and apply accepted policy updates');
|
|
15
15
|
dream
|
|
16
16
|
.command('preview')
|
|
17
17
|
.description('Preview Dream batch proposals without writing candidates or policy files')
|
|
@@ -28,14 +28,33 @@ export function attachSelfEvolutionDreamCommands(parent) {
|
|
|
28
28
|
});
|
|
29
29
|
dream
|
|
30
30
|
.command('run')
|
|
31
|
-
.description('Run Dream batch proposal generation
|
|
31
|
+
.description('Run Dream batch proposal generation; plain run writes drafts, --apply --yes attempts gated policy updates')
|
|
32
32
|
.option('--target <id>', 'restrict Dream to one existing canonical target')
|
|
33
33
|
.option('--limit <n>', 'maximum number of Dream candidates to write', parseDreamLimitOption, 3)
|
|
34
|
+
.option('--apply', 'synthesize bounded edits, run the static gate, and promote passing Dream candidates')
|
|
35
|
+
.option('--yes', 'confirm non-interactive Dream policy updates when --apply is used')
|
|
34
36
|
.option('--json', 'output JSON')
|
|
35
37
|
.action(async (options) => {
|
|
36
38
|
const result = await runDreamRunCommand({
|
|
37
39
|
target: options.target,
|
|
38
40
|
limit: options.limit,
|
|
41
|
+
applyPolicy: options.apply,
|
|
42
|
+
yes: options.yes,
|
|
43
|
+
json: options.json,
|
|
44
|
+
}, { repoRoot: process.cwd() });
|
|
45
|
+
process.exitCode = result.exitCode;
|
|
46
|
+
});
|
|
47
|
+
dream
|
|
48
|
+
.command('policy-update <candidateId>')
|
|
49
|
+
.description('Promote an accepted dream-batch candidate through bounded edits, static gate, and policy ledger update')
|
|
50
|
+
.option('--accepted-by <name>', 'actor that accepted the Dream candidate', 'maintainer')
|
|
51
|
+
.option('--yes', 'confirm non-interactive policy update')
|
|
52
|
+
.option('--json', 'output JSON')
|
|
53
|
+
.action(async (candidateId, options) => {
|
|
54
|
+
const result = await runDreamPolicyUpdateCommand({
|
|
55
|
+
candidateId,
|
|
56
|
+
acceptedBy: options.acceptedBy,
|
|
57
|
+
yes: options.yes,
|
|
39
58
|
json: options.json,
|
|
40
59
|
}, { repoRoot: process.cwd() });
|
|
41
60
|
process.exitCode = result.exitCode;
|
|
@@ -74,12 +93,21 @@ export async function runDreamPreviewCommand(args, opts) {
|
|
|
74
93
|
export async function runDreamRunCommand(args, opts) {
|
|
75
94
|
const stdout = opts.stdout ?? ((line) => console.log(line));
|
|
76
95
|
const stderr = opts.stderr ?? ((line) => console.error(line));
|
|
96
|
+
if (args.applyPolicy && !args.yes) {
|
|
97
|
+
const error = '--yes is required when using dream run --apply because it can promote policy updates.';
|
|
98
|
+
if (args.json)
|
|
99
|
+
stdout(JSON.stringify({ error }, null, 2));
|
|
100
|
+
else
|
|
101
|
+
stderr(error);
|
|
102
|
+
return { exitCode: 2, error };
|
|
103
|
+
}
|
|
77
104
|
try {
|
|
78
105
|
const manifest = await writeDreamRun({
|
|
79
106
|
projectRoot: opts.repoRoot,
|
|
80
107
|
target: args.target,
|
|
81
108
|
limit: args.limit,
|
|
82
109
|
now: opts.now,
|
|
110
|
+
applyPolicy: args.applyPolicy,
|
|
83
111
|
});
|
|
84
112
|
stdout(args.json ? JSON.stringify(manifest, null, 2) : renderDreamRun(manifest));
|
|
85
113
|
return { exitCode: 0, manifest };
|
|
@@ -93,6 +121,83 @@ export async function runDreamRunCommand(args, opts) {
|
|
|
93
121
|
return { exitCode: 1, error };
|
|
94
122
|
}
|
|
95
123
|
}
|
|
124
|
+
export async function runDreamPolicyUpdateCommand(args, opts) {
|
|
125
|
+
const stdout = opts.stdout ?? ((line) => console.log(line));
|
|
126
|
+
const stderr = opts.stderr ?? ((line) => console.error(line));
|
|
127
|
+
if (!args.yes) {
|
|
128
|
+
const error = '--yes is required because Dream policy-update can modify local policy files.';
|
|
129
|
+
const result = {
|
|
130
|
+
exitCode: 2,
|
|
131
|
+
candidateId: args.candidateId,
|
|
132
|
+
targetId: '(unknown)',
|
|
133
|
+
outcome: 'error-runtime',
|
|
134
|
+
promoted: false,
|
|
135
|
+
gatePassed: false,
|
|
136
|
+
promotedFiles: [],
|
|
137
|
+
policyVersion: null,
|
|
138
|
+
error,
|
|
139
|
+
reason: error,
|
|
140
|
+
};
|
|
141
|
+
if (args.json)
|
|
142
|
+
stdout(JSON.stringify(result, null, 2));
|
|
143
|
+
else
|
|
144
|
+
stderr(error);
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const update = await runAcceptedDreamPolicyUpdate({
|
|
149
|
+
projectRoot: opts.repoRoot,
|
|
150
|
+
candidateId: args.candidateId,
|
|
151
|
+
acceptedBy: args.acceptedBy ?? 'maintainer',
|
|
152
|
+
synthesizeEdits: opts.synthesizeEdits,
|
|
153
|
+
now: opts.now,
|
|
154
|
+
});
|
|
155
|
+
const result = {
|
|
156
|
+
exitCode: update.outcome === 'error-runtime' ? 1 : 0,
|
|
157
|
+
...update,
|
|
158
|
+
};
|
|
159
|
+
if (args.json)
|
|
160
|
+
stdout(JSON.stringify(result, null, 2));
|
|
161
|
+
else
|
|
162
|
+
stdout(renderDreamPolicyUpdateResult(result));
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
167
|
+
const result = {
|
|
168
|
+
exitCode: 1,
|
|
169
|
+
candidateId: args.candidateId,
|
|
170
|
+
targetId: '(unknown)',
|
|
171
|
+
outcome: 'error-runtime',
|
|
172
|
+
promoted: false,
|
|
173
|
+
gatePassed: false,
|
|
174
|
+
promotedFiles: [],
|
|
175
|
+
policyVersion: null,
|
|
176
|
+
error,
|
|
177
|
+
reason: error,
|
|
178
|
+
};
|
|
179
|
+
if (args.json)
|
|
180
|
+
stdout(JSON.stringify(result, null, 2));
|
|
181
|
+
else
|
|
182
|
+
stderr(error);
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function renderDreamPolicyUpdateResult(result) {
|
|
187
|
+
const lines = [
|
|
188
|
+
`Dream policy update: ${result.outcome}`,
|
|
189
|
+
`Candidate: ${result.candidateId}`,
|
|
190
|
+
`Target: ${result.targetId}`,
|
|
191
|
+
`Gate passed: ${result.gatePassed ? 'yes' : 'no'}`,
|
|
192
|
+
`Policy version: ${result.policyVersion ?? '(unchanged)'}`,
|
|
193
|
+
];
|
|
194
|
+
if (result.promotedFiles.length > 0) {
|
|
195
|
+
lines.push(`Promoted files: ${result.promotedFiles.join(', ')}`);
|
|
196
|
+
}
|
|
197
|
+
if (result.reason)
|
|
198
|
+
lines.push(`Reason: ${result.reason}`);
|
|
199
|
+
return lines.join('\n');
|
|
200
|
+
}
|
|
96
201
|
export async function runDreamShowCommand(args, opts) {
|
|
97
202
|
const stdout = opts.stdout ?? ((line) => console.log(line));
|
|
98
203
|
const stderr = opts.stderr ?? ((line) => console.error(line));
|
|
@@ -58,6 +58,8 @@ export interface RunEpisodeCommandArgs {
|
|
|
58
58
|
target?: string;
|
|
59
59
|
/** Skip the CRITIC AGENT(基线智能体 baseline agent)arm for this episode. */
|
|
60
60
|
noBaseline?: boolean;
|
|
61
|
+
/** Force the observed-run/code-agent harness for this episode. */
|
|
62
|
+
harness?: string;
|
|
61
63
|
/** Explicit transcript handle (Claude transcript store only). */
|
|
62
64
|
transcript?: string;
|
|
63
65
|
/** Explicit Claude session id handle. */
|
|
@@ -108,6 +110,7 @@ export interface RunEpisodeCommandResult {
|
|
|
108
110
|
export declare function runEpisodeCommand(args: RunEpisodeCommandArgs, opts: RunEpisodeCommandOptions): Promise<RunEpisodeCommandResult>;
|
|
109
111
|
export interface RunResumeEpisodeCommandArgs {
|
|
110
112
|
episodeId: string;
|
|
113
|
+
harness?: string;
|
|
111
114
|
json?: boolean;
|
|
112
115
|
}
|
|
113
116
|
export interface RunResumeEpisodeCommandResult {
|