@evo-dev/evodev 0.0.1-alpha.16 → 0.0.1-alpha.17
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/.claude-plugin/marketplace.json +2 -2
- package/dist/index.js +881 -176
- package/dist/plugins/evodev/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/evodev/.codex-plugin/plugin.json +1 -1
- package/dist/plugins/evodev/package.json +1 -1
- package/dist/ui/app.js +8 -8
- package/dist/ui/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6789,6 +6789,9 @@ function buildCodexArgs(role, startupPrompt) {
|
|
|
6789
6789
|
const args = ["--no-alt-screen"];
|
|
6790
6790
|
if (role.model !== null)
|
|
6791
6791
|
args.push("--model", role.model);
|
|
6792
|
+
if (role.thinkingLevel !== null) {
|
|
6793
|
+
args.push("--config", `model_reasoning_effort=${JSON.stringify(role.thinkingLevel)}`);
|
|
6794
|
+
}
|
|
6792
6795
|
if (shouldPassVisibleStartupPrompt(role))
|
|
6793
6796
|
args.push(startupPrompt);
|
|
6794
6797
|
return args;
|
|
@@ -6797,6 +6800,9 @@ function buildCodexResumeArgs(role, startupPrompt, mode) {
|
|
|
6797
6800
|
const args = ["--no-alt-screen"];
|
|
6798
6801
|
if (role.model !== null)
|
|
6799
6802
|
args.push("--model", role.model);
|
|
6803
|
+
if (role.thinkingLevel !== null) {
|
|
6804
|
+
args.push("--config", `model_reasoning_effort=${JSON.stringify(role.thinkingLevel)}`);
|
|
6805
|
+
}
|
|
6800
6806
|
args.push("resume");
|
|
6801
6807
|
if (mode.type === "resume") {
|
|
6802
6808
|
args.push(mode.sessionId);
|
|
@@ -8380,6 +8386,9 @@ function createDefaultMemorySettings() {
|
|
|
8380
8386
|
}
|
|
8381
8387
|
function createDefaultEvolutionSettings() {
|
|
8382
8388
|
return {
|
|
8389
|
+
schedule: {
|
|
8390
|
+
dailyTime: "02:00"
|
|
8391
|
+
},
|
|
8383
8392
|
automation: {
|
|
8384
8393
|
knowledge: true,
|
|
8385
8394
|
semanticKnowledge: false,
|
|
@@ -8431,6 +8440,10 @@ function mergeSettings(existing, defaults = createDefaultSettings()) {
|
|
|
8431
8440
|
evolution: {
|
|
8432
8441
|
...defaults.evolution,
|
|
8433
8442
|
...existing.evolution,
|
|
8443
|
+
schedule: {
|
|
8444
|
+
...defaults.evolution.schedule,
|
|
8445
|
+
...existing.evolution?.schedule
|
|
8446
|
+
},
|
|
8434
8447
|
automation: {
|
|
8435
8448
|
...defaults.evolution.automation,
|
|
8436
8449
|
...existing.evolution?.automation
|
|
@@ -8489,8 +8502,12 @@ function parseSettings(value) {
|
|
|
8489
8502
|
function parseEvolutionSettings(value, path) {
|
|
8490
8503
|
const input = expectRecord(value, path);
|
|
8491
8504
|
const defaults = createDefaultEvolutionSettings();
|
|
8505
|
+
const schedule = expectRecord(input.schedule ?? defaults.schedule, `${path}.schedule`);
|
|
8492
8506
|
const automation = expectRecord(input.automation ?? defaults.automation, `${path}.automation`);
|
|
8493
8507
|
return {
|
|
8508
|
+
schedule: {
|
|
8509
|
+
dailyTime: parseDailyTime(schedule.dailyTime ?? defaults.schedule.dailyTime, `${path}.schedule.dailyTime`)
|
|
8510
|
+
},
|
|
8494
8511
|
automation: {
|
|
8495
8512
|
knowledge: automation.knowledge === undefined ? defaults.automation.knowledge : expectBoolean(automation.knowledge, `${path}.automation.knowledge`),
|
|
8496
8513
|
semanticKnowledge: automation.semanticKnowledge === undefined ? defaults.automation.semanticKnowledge : expectBoolean(automation.semanticKnowledge, `${path}.automation.semanticKnowledge`),
|
|
@@ -8498,6 +8515,14 @@ function parseEvolutionSettings(value, path) {
|
|
|
8498
8515
|
}
|
|
8499
8516
|
};
|
|
8500
8517
|
}
|
|
8518
|
+
function parseDailyTime(value, path) {
|
|
8519
|
+
const dailyTime = expectString(value, path);
|
|
8520
|
+
const match = /^(\d{2}):(\d{2})$/u.exec(dailyTime);
|
|
8521
|
+
if (match === null || Number(match[1]) > 23 || Number(match[2]) > 59) {
|
|
8522
|
+
throw new EvoDevConfigError(`Invalid ${path}; expected a 24-hour HH:MM time`);
|
|
8523
|
+
}
|
|
8524
|
+
return dailyTime;
|
|
8525
|
+
}
|
|
8501
8526
|
function parseMemorySettings(value, path) {
|
|
8502
8527
|
const input = expectRecord(value, path);
|
|
8503
8528
|
const defaults = createDefaultMemorySettings();
|
|
@@ -15215,6 +15240,7 @@ async function processEvolutionTriggers(input) {
|
|
|
15215
15240
|
pending: 0,
|
|
15216
15241
|
triggerIds: [],
|
|
15217
15242
|
batchIds: [],
|
|
15243
|
+
pendingChangeIds: [],
|
|
15218
15244
|
warnings,
|
|
15219
15245
|
dryRun
|
|
15220
15246
|
};
|
|
@@ -15250,6 +15276,7 @@ async function processEvolutionTriggers(input) {
|
|
|
15250
15276
|
...pending.map((trigger) => trigger.id)
|
|
15251
15277
|
],
|
|
15252
15278
|
batchIds: [],
|
|
15279
|
+
pendingChangeIds: [],
|
|
15253
15280
|
warnings,
|
|
15254
15281
|
dryRun
|
|
15255
15282
|
};
|
|
@@ -15355,6 +15382,7 @@ async function processEvolutionTriggers(input) {
|
|
|
15355
15382
|
});
|
|
15356
15383
|
result.consumed += 1;
|
|
15357
15384
|
result.batchIds.push(batch.id);
|
|
15385
|
+
result.pendingChangeIds.push(...curated.activation.okf.pendingChangeIds);
|
|
15358
15386
|
await updateSegmentTriggers(input.homeDir, [attemptedTrigger], {
|
|
15359
15387
|
status: "consumed",
|
|
15360
15388
|
updatedAt: now,
|
|
@@ -15438,13 +15466,14 @@ async function processEvolutionTriggers(input) {
|
|
|
15438
15466
|
});
|
|
15439
15467
|
if (lock !== null)
|
|
15440
15468
|
await assertEvolutionProcessLockOwned(lock);
|
|
15441
|
-
await activateEvolutionDistillationBatch({
|
|
15469
|
+
const activation = await activateEvolutionDistillationBatch({
|
|
15442
15470
|
homeDir: input.homeDir,
|
|
15443
15471
|
batch,
|
|
15444
15472
|
overwrite: true
|
|
15445
15473
|
});
|
|
15446
15474
|
result.consumed += triggers.length;
|
|
15447
15475
|
result.batchIds.push(batch.id);
|
|
15476
|
+
result.pendingChangeIds.push(...activation.okf.pendingChangeIds);
|
|
15448
15477
|
await updateTriggers(input.homeDir, attemptedTriggers, {
|
|
15449
15478
|
status: "consumed",
|
|
15450
15479
|
updatedAt: now,
|
|
@@ -22886,6 +22915,7 @@ import { createHash as createHash6, randomUUID as randomUUID5 } from "node:crypt
|
|
|
22886
22915
|
import { mkdir as mkdir17, open as open4, rename as rename5, rm as rm8, stat as stat13, writeFile as writeFile15 } from "node:fs/promises";
|
|
22887
22916
|
import { dirname as dirname20, join as join29 } from "node:path";
|
|
22888
22917
|
var EVO_DEV_SETTINGS_KEYS = [
|
|
22918
|
+
"evolution.schedule.dailyTime",
|
|
22889
22919
|
"evolution.automation.knowledge",
|
|
22890
22920
|
"evolution.automation.semanticKnowledge",
|
|
22891
22921
|
"evolution.automation.recommendations",
|
|
@@ -22934,7 +22964,17 @@ async function updateEvoDevSetting(input) {
|
|
|
22934
22964
|
}
|
|
22935
22965
|
function applyEvoDevSettingsMutation(current, key, value) {
|
|
22936
22966
|
let next;
|
|
22937
|
-
if (key
|
|
22967
|
+
if (key === "evolution.schedule.dailyTime") {
|
|
22968
|
+
next = {
|
|
22969
|
+
...current,
|
|
22970
|
+
evolution: {
|
|
22971
|
+
...current.evolution,
|
|
22972
|
+
schedule: {
|
|
22973
|
+
dailyTime: expectDailyTimeValue(key, value)
|
|
22974
|
+
}
|
|
22975
|
+
}
|
|
22976
|
+
};
|
|
22977
|
+
} else if (key.startsWith("evolution.automation.")) {
|
|
22938
22978
|
const field = key.slice("evolution.automation.".length);
|
|
22939
22979
|
next = {
|
|
22940
22980
|
...current,
|
|
@@ -22994,6 +23034,9 @@ function applyEvoDevSettingsMutation(current, key, value) {
|
|
|
22994
23034
|
return parseSettings(next);
|
|
22995
23035
|
}
|
|
22996
23036
|
function parseEvoDevSettingsCliValue(key, value) {
|
|
23037
|
+
if (key === "evolution.schedule.dailyTime") {
|
|
23038
|
+
return expectDailyTimeValue(key, value);
|
|
23039
|
+
}
|
|
22997
23040
|
if (key.startsWith("evolution.automation.") || key.startsWith("memory.") || key === "teamRuntime.recordTranscript") {
|
|
22998
23041
|
if (value === "true")
|
|
22999
23042
|
return true;
|
|
@@ -23018,6 +23061,16 @@ function parseEvoDevSettingsCliValue(key, value) {
|
|
|
23018
23061
|
}
|
|
23019
23062
|
throw new EvoDevSettingsMutationError("invalid", `Unsupported config key: ${key}`);
|
|
23020
23063
|
}
|
|
23064
|
+
function expectDailyTimeValue(key, value) {
|
|
23065
|
+
if (typeof value !== "string")
|
|
23066
|
+
throw invalidValue(key, "a 24-hour HH:MM time");
|
|
23067
|
+
const normalized = value.trim();
|
|
23068
|
+
const match = /^(\d{2}):(\d{2})$/u.exec(normalized);
|
|
23069
|
+
if (match === null || Number(match[1]) > 23 || Number(match[2]) > 59) {
|
|
23070
|
+
throw invalidValue(key, "a 24-hour HH:MM time");
|
|
23071
|
+
}
|
|
23072
|
+
return normalized;
|
|
23073
|
+
}
|
|
23021
23074
|
function formatEvoDevSettingsValue(value) {
|
|
23022
23075
|
return value === null ? "default" : String(value);
|
|
23023
23076
|
}
|
|
@@ -34506,16 +34559,176 @@ function resolveHomeDir11(homeDir) {
|
|
|
34506
34559
|
return envHome;
|
|
34507
34560
|
}
|
|
34508
34561
|
|
|
34562
|
+
// packages/cli/src/daily-schedule.ts
|
|
34563
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
34564
|
+
import { mkdir as mkdir23, readFile as readFile32, rename as rename6, rm as rm11, writeFile as writeFile22 } from "node:fs/promises";
|
|
34565
|
+
import { dirname as dirname27, join as join34 } from "node:path";
|
|
34566
|
+
async function prepareDailyEvolutionSchedule(input) {
|
|
34567
|
+
const now = asDate(input.now);
|
|
34568
|
+
const current = await readDailyEvolutionScheduleState(input.homeDir);
|
|
34569
|
+
const state = {
|
|
34570
|
+
schemaVersion: 1,
|
|
34571
|
+
kind: "daily-evolution-schedule-state",
|
|
34572
|
+
configuredTime: parseDailyTime2(input.dailyTime).value,
|
|
34573
|
+
configuredAt: now.toISOString(),
|
|
34574
|
+
notBefore: nextOccurrence(now, input.dailyTime).toISOString(),
|
|
34575
|
+
lastRunDay: current?.lastRunDay ?? null,
|
|
34576
|
+
lastRunAt: current?.lastRunAt ?? null,
|
|
34577
|
+
lastJobId: current?.lastJobId ?? null,
|
|
34578
|
+
lastJobStatus: current?.lastJobStatus ?? null
|
|
34579
|
+
};
|
|
34580
|
+
await writeState(input.homeDir, state);
|
|
34581
|
+
return state;
|
|
34582
|
+
}
|
|
34583
|
+
async function decideDailyEvolutionTick(input) {
|
|
34584
|
+
const now = asDate(input.now);
|
|
34585
|
+
const time2 = parseDailyTime2(input.dailyTime);
|
|
34586
|
+
const state = await readDailyEvolutionScheduleState(input.homeDir);
|
|
34587
|
+
if (state !== null && Date.parse(state.notBefore) > now.getTime()) {
|
|
34588
|
+
return { due: false, reason: "before-effective-time" };
|
|
34589
|
+
}
|
|
34590
|
+
const day = localDay(now);
|
|
34591
|
+
if (state?.lastRunDay === day)
|
|
34592
|
+
return { due: false, reason: "already-ran" };
|
|
34593
|
+
if (now.getHours() * 60 + now.getMinutes() < time2.hour * 60 + time2.minute) {
|
|
34594
|
+
return { due: false, reason: "before-trigger" };
|
|
34595
|
+
}
|
|
34596
|
+
return { due: true, day };
|
|
34597
|
+
}
|
|
34598
|
+
async function recordDailyEvolutionRun(input) {
|
|
34599
|
+
const now = asDate(input.now);
|
|
34600
|
+
const current = await readDailyEvolutionScheduleState(input.homeDir);
|
|
34601
|
+
const state = {
|
|
34602
|
+
schemaVersion: 1,
|
|
34603
|
+
kind: "daily-evolution-schedule-state",
|
|
34604
|
+
configuredTime: parseDailyTime2(input.dailyTime).value,
|
|
34605
|
+
configuredAt: current?.configuredAt ?? now.toISOString(),
|
|
34606
|
+
notBefore: current?.notBefore ?? now.toISOString(),
|
|
34607
|
+
lastRunDay: localDay(now),
|
|
34608
|
+
lastRunAt: now.toISOString(),
|
|
34609
|
+
lastJobId: input.jobId,
|
|
34610
|
+
lastJobStatus: input.status
|
|
34611
|
+
};
|
|
34612
|
+
await writeState(input.homeDir, state);
|
|
34613
|
+
return state;
|
|
34614
|
+
}
|
|
34615
|
+
async function readDailyEvolutionScheduleStatus(input) {
|
|
34616
|
+
const now = asDate(input.now);
|
|
34617
|
+
const state = await readDailyEvolutionScheduleState(input.homeDir);
|
|
34618
|
+
const next = resolveNextRun(now, input.dailyTime, state);
|
|
34619
|
+
return {
|
|
34620
|
+
dailyTime: parseDailyTime2(input.dailyTime).value,
|
|
34621
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone || "system-local",
|
|
34622
|
+
nextRunAt: next.toISOString(),
|
|
34623
|
+
lastRunAt: state?.lastRunAt ?? null,
|
|
34624
|
+
lastJobId: state?.lastJobId ?? null,
|
|
34625
|
+
lastJobStatus: state?.lastJobStatus ?? null
|
|
34626
|
+
};
|
|
34627
|
+
}
|
|
34628
|
+
async function readDailyEvolutionScheduleState(homeDir) {
|
|
34629
|
+
try {
|
|
34630
|
+
const value = JSON.parse(await readFile32(resolveDailyScheduleStatePath(homeDir), "utf8"));
|
|
34631
|
+
if (value.schemaVersion !== 1 || value.kind !== "daily-evolution-schedule-state" || typeof value.configuredTime !== "string" || typeof value.configuredAt !== "string" || typeof value.notBefore !== "string" || !isIsoTimestamp(value.configuredAt) || !isIsoTimestamp(value.notBefore) || !isNullableLocalDay(value.lastRunDay) || !isNullableTimestamp(value.lastRunAt) || !isNullableString(value.lastJobId) || !isEvolutionJobStatus(value.lastJobStatus)) {
|
|
34632
|
+
return null;
|
|
34633
|
+
}
|
|
34634
|
+
parseDailyTime2(value.configuredTime);
|
|
34635
|
+
return value;
|
|
34636
|
+
} catch {
|
|
34637
|
+
return null;
|
|
34638
|
+
}
|
|
34639
|
+
}
|
|
34640
|
+
function isIsoTimestamp(value) {
|
|
34641
|
+
return Number.isFinite(Date.parse(value));
|
|
34642
|
+
}
|
|
34643
|
+
function isNullableTimestamp(value) {
|
|
34644
|
+
return value === null || typeof value === "string" && isIsoTimestamp(value);
|
|
34645
|
+
}
|
|
34646
|
+
function isNullableLocalDay(value) {
|
|
34647
|
+
return value === null || typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/u.test(value);
|
|
34648
|
+
}
|
|
34649
|
+
function isNullableString(value) {
|
|
34650
|
+
return value === null || typeof value === "string";
|
|
34651
|
+
}
|
|
34652
|
+
function isEvolutionJobStatus(value) {
|
|
34653
|
+
return value === null || value === "queued" || value === "running" || value === "completed" || value === "completed-with-warnings" || value === "failed";
|
|
34654
|
+
}
|
|
34655
|
+
function resolveDailyScheduleStatePath(homeDir) {
|
|
34656
|
+
return join34(resolveEvoDevPaths(homeDir).stateDir, "schedule", "daily-evolution.json");
|
|
34657
|
+
}
|
|
34658
|
+
function resolveNextRun(now, dailyTime, state) {
|
|
34659
|
+
const candidate = occurrenceOnDay(now, dailyTime);
|
|
34660
|
+
const alreadyRan = state?.lastRunDay === localDay(now);
|
|
34661
|
+
const notBefore = state === null ? Number.NEGATIVE_INFINITY : Date.parse(state.notBefore);
|
|
34662
|
+
if (!alreadyRan && candidate.getTime() >= now.getTime() && candidate.getTime() >= notBefore) {
|
|
34663
|
+
return candidate;
|
|
34664
|
+
}
|
|
34665
|
+
if (!alreadyRan && candidate.getTime() < now.getTime() && now.getTime() >= notBefore) {
|
|
34666
|
+
return now;
|
|
34667
|
+
}
|
|
34668
|
+
if (!alreadyRan && notBefore > now.getTime())
|
|
34669
|
+
return new Date(notBefore);
|
|
34670
|
+
const tomorrow = new Date(now);
|
|
34671
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
34672
|
+
return occurrenceOnDay(tomorrow, dailyTime);
|
|
34673
|
+
}
|
|
34674
|
+
function nextOccurrence(now, dailyTime) {
|
|
34675
|
+
const today = occurrenceOnDay(now, dailyTime);
|
|
34676
|
+
if (today.getTime() > now.getTime())
|
|
34677
|
+
return today;
|
|
34678
|
+
const tomorrow = new Date(now);
|
|
34679
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
34680
|
+
return occurrenceOnDay(tomorrow, dailyTime);
|
|
34681
|
+
}
|
|
34682
|
+
function occurrenceOnDay(day, dailyTime) {
|
|
34683
|
+
const time2 = parseDailyTime2(dailyTime);
|
|
34684
|
+
return new Date(day.getFullYear(), day.getMonth(), day.getDate(), time2.hour, time2.minute, 0, 0);
|
|
34685
|
+
}
|
|
34686
|
+
function parseDailyTime2(value) {
|
|
34687
|
+
const match = /^(\d{2}):(\d{2})$/u.exec(value);
|
|
34688
|
+
const hour = Number(match?.[1]);
|
|
34689
|
+
const minute = Number(match?.[2]);
|
|
34690
|
+
if (match === null || hour > 23 || minute > 59) {
|
|
34691
|
+
throw new Error("Evolution schedule time must use 24-hour HH:MM format.");
|
|
34692
|
+
}
|
|
34693
|
+
return { value, hour, minute };
|
|
34694
|
+
}
|
|
34695
|
+
function localDay(value) {
|
|
34696
|
+
return [
|
|
34697
|
+
String(value.getFullYear()).padStart(4, "0"),
|
|
34698
|
+
String(value.getMonth() + 1).padStart(2, "0"),
|
|
34699
|
+
String(value.getDate()).padStart(2, "0")
|
|
34700
|
+
].join("-");
|
|
34701
|
+
}
|
|
34702
|
+
function asDate(value) {
|
|
34703
|
+
const date = value === undefined ? new Date : new Date(value);
|
|
34704
|
+
if (!Number.isFinite(date.getTime()))
|
|
34705
|
+
throw new Error("Invalid daily evolution timestamp.");
|
|
34706
|
+
return date;
|
|
34707
|
+
}
|
|
34708
|
+
async function writeState(homeDir, state) {
|
|
34709
|
+
const path2 = resolveDailyScheduleStatePath(homeDir);
|
|
34710
|
+
await mkdir23(dirname27(path2), { recursive: true });
|
|
34711
|
+
const temporary = `${path2}.${process.pid}.${randomUUID6()}.tmp`;
|
|
34712
|
+
await writeFile22(temporary, `${JSON.stringify(state, null, 2)}
|
|
34713
|
+
`, "utf8");
|
|
34714
|
+
try {
|
|
34715
|
+
await rename6(temporary, path2);
|
|
34716
|
+
} catch (error) {
|
|
34717
|
+
await rm11(temporary, { force: true });
|
|
34718
|
+
throw error;
|
|
34719
|
+
}
|
|
34720
|
+
}
|
|
34721
|
+
|
|
34509
34722
|
// packages/cli/src/evolution-job.ts
|
|
34510
|
-
import { randomUUID as
|
|
34511
|
-
import { mkdir as
|
|
34512
|
-
import { dirname as
|
|
34723
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
34724
|
+
import { mkdir as mkdir26, readFile as readFile36, readdir as readdir21, rename as rename8, rm as rm15, stat as stat26, utimes, writeFile as writeFile26 } from "node:fs/promises";
|
|
34725
|
+
import { dirname as dirname30, join as join38 } from "node:path";
|
|
34513
34726
|
|
|
34514
34727
|
// packages/cli/src/improvement-eval.ts
|
|
34515
34728
|
import { spawn as spawn4 } from "node:child_process";
|
|
34516
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
34729
|
+
import { mkdtemp as mkdtemp2, readFile as readFile33, realpath as realpath5, rm as rm12, stat as stat23, writeFile as writeFile23 } from "node:fs/promises";
|
|
34517
34730
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
34518
|
-
import { join as
|
|
34731
|
+
import { join as join35 } from "node:path";
|
|
34519
34732
|
var DEFAULT_EVAL_LIMIT = 1;
|
|
34520
34733
|
var MODEL_TIMEOUT_MS2 = 2 * 60 * 1000;
|
|
34521
34734
|
var MAX_BASELINE_CHARS = 12000;
|
|
@@ -34667,11 +34880,11 @@ function classifyImprovementEvalAnalysis(analysis3) {
|
|
|
34667
34880
|
}
|
|
34668
34881
|
function createCodexImprovementEvalAnalyzer(input = {}) {
|
|
34669
34882
|
return async (candidate) => {
|
|
34670
|
-
const workDir = await mkdtemp2(
|
|
34671
|
-
const schemaPath =
|
|
34672
|
-
const outputPath =
|
|
34883
|
+
const workDir = await mkdtemp2(join35(tmpdir3(), "evodev-improvement-eval-"));
|
|
34884
|
+
const schemaPath = join35(workDir, "output.schema.json");
|
|
34885
|
+
const outputPath = join35(workDir, "output.json");
|
|
34673
34886
|
try {
|
|
34674
|
-
await
|
|
34887
|
+
await writeFile23(schemaPath, `${JSON.stringify(IMPROVEMENT_EVAL_SCHEMA, null, 2)}
|
|
34675
34888
|
`, "utf8");
|
|
34676
34889
|
const metrics = await runCodexReplay({
|
|
34677
34890
|
command: input.command ?? "codex",
|
|
@@ -34682,11 +34895,11 @@ function createCodexImprovementEvalAnalyzer(input = {}) {
|
|
|
34682
34895
|
timeoutMs: input.timeoutMs ?? MODEL_TIMEOUT_MS2
|
|
34683
34896
|
});
|
|
34684
34897
|
return {
|
|
34685
|
-
...parseImprovementEvalReplayAnalysis(JSON.parse(await
|
|
34898
|
+
...parseImprovementEvalReplayAnalysis(JSON.parse(await readFile33(outputPath, "utf8"))),
|
|
34686
34899
|
metrics
|
|
34687
34900
|
};
|
|
34688
34901
|
} finally {
|
|
34689
|
-
await
|
|
34902
|
+
await rm12(workDir, { recursive: true, force: true });
|
|
34690
34903
|
}
|
|
34691
34904
|
};
|
|
34692
34905
|
}
|
|
@@ -34998,26 +35211,26 @@ var IMPROVEMENT_EVAL_SCHEMA = {
|
|
|
34998
35211
|
|
|
34999
35212
|
// packages/cli/src/session-proposals.ts
|
|
35000
35213
|
import { spawn as spawn6 } from "node:child_process";
|
|
35001
|
-
import { createHash as createHash12, randomUUID as
|
|
35214
|
+
import { createHash as createHash12, randomUUID as randomUUID7 } from "node:crypto";
|
|
35002
35215
|
import {
|
|
35003
|
-
mkdir as
|
|
35216
|
+
mkdir as mkdir25,
|
|
35004
35217
|
mkdtemp as mkdtemp3,
|
|
35005
35218
|
open as open6,
|
|
35006
|
-
readFile as
|
|
35219
|
+
readFile as readFile35,
|
|
35007
35220
|
readdir as readdir20,
|
|
35008
35221
|
realpath as realpath7,
|
|
35009
|
-
rm as
|
|
35222
|
+
rm as rm14,
|
|
35010
35223
|
stat as stat25,
|
|
35011
|
-
writeFile as
|
|
35224
|
+
writeFile as writeFile25
|
|
35012
35225
|
} from "node:fs/promises";
|
|
35013
35226
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
35014
|
-
import { basename as basename8, dirname as
|
|
35227
|
+
import { basename as basename8, dirname as dirname29, isAbsolute as isAbsolute13, join as join37, relative as relative14, resolve as resolve14 } from "node:path";
|
|
35015
35228
|
|
|
35016
35229
|
// packages/cli/src/proposal-execution.ts
|
|
35017
35230
|
import { spawn as spawn5 } from "node:child_process";
|
|
35018
35231
|
import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
|
|
35019
|
-
import { mkdir as
|
|
35020
|
-
import { dirname as
|
|
35232
|
+
import { mkdir as mkdir24, readFile as readFile34, readdir as readdir19, realpath as realpath6, rename as rename7, rm as rm13, stat as stat24, writeFile as writeFile24 } from "node:fs/promises";
|
|
35233
|
+
import { dirname as dirname28, isAbsolute as isAbsolute12, join as join36, relative as relative13, resolve as resolve13 } from "node:path";
|
|
35021
35234
|
var MAX_COMMAND_OUTPUT_BYTES = 1024 * 1024;
|
|
35022
35235
|
var MAX_DIRTY_FILES_IN_RESPONSE = 20;
|
|
35023
35236
|
var MAX_CHANGED_FILES_IN_RECORD = 100;
|
|
@@ -35146,7 +35359,7 @@ class RepoProposalExecutionService {
|
|
|
35146
35359
|
try {
|
|
35147
35360
|
await writeExecutionRecord(input.homeDir, proposal, record);
|
|
35148
35361
|
} catch (error) {
|
|
35149
|
-
await
|
|
35362
|
+
await rm13(lockPath, { force: true });
|
|
35150
35363
|
throw error;
|
|
35151
35364
|
}
|
|
35152
35365
|
const prompt = buildRepoProposalExecutionPrompt({ proposal, repository });
|
|
@@ -35172,7 +35385,7 @@ class RepoProposalExecutionService {
|
|
|
35172
35385
|
changedFiles: []
|
|
35173
35386
|
});
|
|
35174
35387
|
await writeExecutionRecord(input.homeDir, proposal, failed);
|
|
35175
|
-
await
|
|
35388
|
+
await rm13(lockPath, { force: true });
|
|
35176
35389
|
throw new ProposalExecutionError("unavailable", `${agentLabel(input.agent)} could not start.`);
|
|
35177
35390
|
}
|
|
35178
35391
|
record.pid = child.pid ?? null;
|
|
@@ -35195,7 +35408,7 @@ class RepoProposalExecutionService {
|
|
|
35195
35408
|
exitCode,
|
|
35196
35409
|
signal,
|
|
35197
35410
|
launchError
|
|
35198
|
-
}).finally(() =>
|
|
35411
|
+
}).finally(() => rm13(lockPath, { force: true }));
|
|
35199
35412
|
};
|
|
35200
35413
|
child.once("error", () => {
|
|
35201
35414
|
finish(null, null, true).catch(() => {
|
|
@@ -35344,7 +35557,7 @@ async function listRepoProposalExecutionRecords(input) {
|
|
|
35344
35557
|
const records = [];
|
|
35345
35558
|
for (const file of files) {
|
|
35346
35559
|
try {
|
|
35347
|
-
records.push(parseExecutionRecord(JSON.parse(await
|
|
35560
|
+
records.push(parseExecutionRecord(JSON.parse(await readFile34(join36(dir, file), "utf8"))));
|
|
35348
35561
|
} catch {}
|
|
35349
35562
|
}
|
|
35350
35563
|
return records.sort((left2, right2) => left2.startedAt.localeCompare(right2.startedAt));
|
|
@@ -35696,15 +35909,15 @@ function resolveExecutionRecordsDir(homeDir, proposal) {
|
|
|
35696
35909
|
projectKey: proposal.projectKey,
|
|
35697
35910
|
runId: proposal.provenance.runId
|
|
35698
35911
|
});
|
|
35699
|
-
return
|
|
35912
|
+
return join36(paths4.runStateDir, "proposal-executions", proposal.id);
|
|
35700
35913
|
}
|
|
35701
35914
|
function resolveExecutionLockPath(homeDir, proposal) {
|
|
35702
|
-
return
|
|
35915
|
+
return join36(resolveExecutionRecordsDir(homeDir, proposal), ".execution.lock");
|
|
35703
35916
|
}
|
|
35704
35917
|
async function acquireExecutionLock(path2, latest) {
|
|
35705
|
-
await
|
|
35918
|
+
await mkdir24(dirname28(path2), { recursive: true });
|
|
35706
35919
|
try {
|
|
35707
|
-
await
|
|
35920
|
+
await writeFile24(path2, `${JSON.stringify({ version: 1, pid: process.pid })}
|
|
35708
35921
|
`, {
|
|
35709
35922
|
encoding: "utf8",
|
|
35710
35923
|
flag: "wx"
|
|
@@ -35718,8 +35931,8 @@ async function acquireExecutionLock(path2, latest) {
|
|
|
35718
35931
|
if (latest?.status === "running" && isProcessAlive2(latest.pid)) {
|
|
35719
35932
|
throw new ProposalExecutionError("conflict", "This repo proposal is already executing.");
|
|
35720
35933
|
}
|
|
35721
|
-
await
|
|
35722
|
-
await
|
|
35934
|
+
await rm13(path2, { force: true });
|
|
35935
|
+
await writeFile24(path2, `${JSON.stringify({ version: 1, pid: process.pid })}
|
|
35723
35936
|
`, {
|
|
35724
35937
|
encoding: "utf8",
|
|
35725
35938
|
flag: "wx"
|
|
@@ -35728,7 +35941,7 @@ async function acquireExecutionLock(path2, latest) {
|
|
|
35728
35941
|
}
|
|
35729
35942
|
async function isExecutionLockOwnerAlive(path2) {
|
|
35730
35943
|
try {
|
|
35731
|
-
const value = JSON.parse(await
|
|
35944
|
+
const value = JSON.parse(await readFile34(path2, "utf8"));
|
|
35732
35945
|
return isRecord20(value) && typeof value.pid === "number" && isProcessAlive2(value.pid);
|
|
35733
35946
|
} catch {
|
|
35734
35947
|
return false;
|
|
@@ -35736,12 +35949,12 @@ async function isExecutionLockOwnerAlive(path2) {
|
|
|
35736
35949
|
}
|
|
35737
35950
|
async function writeExecutionRecord(homeDir, proposal, record) {
|
|
35738
35951
|
const dir = resolveExecutionRecordsDir(homeDir, proposal);
|
|
35739
|
-
const path2 =
|
|
35952
|
+
const path2 = join36(dir, `${record.id}.json`);
|
|
35740
35953
|
const temporary = `${path2}.${process.pid}.${randomBytes3(4).toString("hex")}.tmp`;
|
|
35741
|
-
await
|
|
35742
|
-
await
|
|
35954
|
+
await mkdir24(dir, { recursive: true });
|
|
35955
|
+
await writeFile24(temporary, `${JSON.stringify(record, null, 2)}
|
|
35743
35956
|
`, "utf8");
|
|
35744
|
-
await
|
|
35957
|
+
await rename7(temporary, path2);
|
|
35745
35958
|
}
|
|
35746
35959
|
function parseExecutionRecord(value) {
|
|
35747
35960
|
if (!isRecord20(value))
|
|
@@ -35782,7 +35995,7 @@ function parseExecutionRecord(value) {
|
|
|
35782
35995
|
"spawn-failed",
|
|
35783
35996
|
"state-conflict"
|
|
35784
35997
|
]);
|
|
35785
|
-
if (Object.keys(value).some((key) => !allowedKeys.has(key)) || value.schemaVersion !== 1 || value.kind !== "repo-proposal-execution" || !isSafeRecordIdentity(value.id) || !isSafeRecordIdentity(value.proposalId) || !isSafeRecordIdentity(value.projectKey) || !isSafeRecordIdentity(value.runId) || value.agent !== "codex" && value.agent !== "claude" || !["running", "succeeded", "failed"].includes(String(value.status)) || value.branch !== null && typeof value.branch !== "string" || typeof value.head !== "string" || !/^[a-f0-9]{40,64}$/u.test(value.head) || typeof value.dirtyAtStart !== "boolean" || !Number.isInteger(value.dirtyFileCountAtStart) || Number(value.dirtyFileCountAtStart) < 0 || value.dirtyConfirmation !== "not-required" && value.dirtyConfirmation !== "approval-covered" && value.dirtyConfirmation !== "confirmed" || !Array.isArray(value.changedFiles) || value.changedFiles.length > MAX_CHANGED_FILES_IN_RECORD || !value.changedFiles.every((path2) => typeof path2 === "string") || typeof value.changedFilesTruncated !== "boolean" || typeof value.startedAt !== "string" || !
|
|
35998
|
+
if (Object.keys(value).some((key) => !allowedKeys.has(key)) || value.schemaVersion !== 1 || value.kind !== "repo-proposal-execution" || !isSafeRecordIdentity(value.id) || !isSafeRecordIdentity(value.proposalId) || !isSafeRecordIdentity(value.projectKey) || !isSafeRecordIdentity(value.runId) || value.agent !== "codex" && value.agent !== "claude" || !["running", "succeeded", "failed"].includes(String(value.status)) || value.branch !== null && typeof value.branch !== "string" || typeof value.head !== "string" || !/^[a-f0-9]{40,64}$/u.test(value.head) || typeof value.dirtyAtStart !== "boolean" || !Number.isInteger(value.dirtyFileCountAtStart) || Number(value.dirtyFileCountAtStart) < 0 || value.dirtyConfirmation !== "not-required" && value.dirtyConfirmation !== "approval-covered" && value.dirtyConfirmation !== "confirmed" || !Array.isArray(value.changedFiles) || value.changedFiles.length > MAX_CHANGED_FILES_IN_RECORD || !value.changedFiles.every((path2) => typeof path2 === "string") || typeof value.changedFilesTruncated !== "boolean" || typeof value.startedAt !== "string" || !isIsoTimestamp2(value.startedAt) || value.finishedAt !== null && (typeof value.finishedAt !== "string" || !isIsoTimestamp2(value.finishedAt)) || value.pid !== null && (!Number.isInteger(value.pid) || Number(value.pid) <= 0) || value.exitCode !== null && !Number.isInteger(value.exitCode) || value.signal !== null && typeof value.signal !== "string" || value.errorCode !== null && (typeof value.errorCode !== "string" || !errorCodes.has(value.errorCode)) || value.metadataOnly !== true || value.rawPromptStored !== false || value.sourceContentStored !== false || value.rawCommandOutputStored !== false) {
|
|
35786
35999
|
throw new Error("Execution record is invalid.");
|
|
35787
36000
|
}
|
|
35788
36001
|
return value;
|
|
@@ -35790,7 +36003,7 @@ function parseExecutionRecord(value) {
|
|
|
35790
36003
|
function isSafeRecordIdentity(value) {
|
|
35791
36004
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/u.test(value);
|
|
35792
36005
|
}
|
|
35793
|
-
function
|
|
36006
|
+
function isIsoTimestamp2(value) {
|
|
35794
36007
|
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/u.test(value);
|
|
35795
36008
|
}
|
|
35796
36009
|
function sanitizeVersion(value) {
|
|
@@ -36123,7 +36336,7 @@ async function readSessionProposalBacklogStatus(input) {
|
|
|
36123
36336
|
}
|
|
36124
36337
|
async function runDailySessionProposalScan(options) {
|
|
36125
36338
|
const now2 = options.now ?? new Date().toISOString();
|
|
36126
|
-
const day =
|
|
36339
|
+
const day = localDay2(now2);
|
|
36127
36340
|
const statePath = resolveDailyStatePath(options.homeDir);
|
|
36128
36341
|
const state = await readDailyState(statePath);
|
|
36129
36342
|
if (!options.force && state?.day === day) {
|
|
@@ -36177,11 +36390,11 @@ async function readDailySessionProposalState(homeDir) {
|
|
|
36177
36390
|
}
|
|
36178
36391
|
function createCodexSessionProposalAnalyzer(input = {}) {
|
|
36179
36392
|
return async (evidence) => {
|
|
36180
|
-
const workDir = await mkdtemp3(
|
|
36181
|
-
const schemaPath =
|
|
36182
|
-
const outputPath =
|
|
36393
|
+
const workDir = await mkdtemp3(join37(tmpdir4(), "evodev-session-proposal-"));
|
|
36394
|
+
const schemaPath = join37(workDir, "output.schema.json");
|
|
36395
|
+
const outputPath = join37(workDir, "output.json");
|
|
36183
36396
|
try {
|
|
36184
|
-
await
|
|
36397
|
+
await writeFile25(schemaPath, `${JSON.stringify(SESSION_PROPOSAL_SCHEMA, null, 2)}
|
|
36185
36398
|
`, "utf8");
|
|
36186
36399
|
await runCodex2({
|
|
36187
36400
|
command: input.command ?? "codex",
|
|
@@ -36191,9 +36404,9 @@ function createCodexSessionProposalAnalyzer(input = {}) {
|
|
|
36191
36404
|
prompt: redactSessionMemoryCredentialText(createAnalyzerPrompt2(evidence)).value,
|
|
36192
36405
|
timeoutMs: input.timeoutMs ?? MODEL_TIMEOUT_MS3
|
|
36193
36406
|
});
|
|
36194
|
-
return parseSessionProposalAnalysis(JSON.parse(await
|
|
36407
|
+
return parseSessionProposalAnalysis(JSON.parse(await readFile35(outputPath, "utf8")));
|
|
36195
36408
|
} finally {
|
|
36196
|
-
await
|
|
36409
|
+
await rm14(workDir, { recursive: true, force: true });
|
|
36197
36410
|
}
|
|
36198
36411
|
};
|
|
36199
36412
|
}
|
|
@@ -36271,10 +36484,10 @@ async function resolveSegmentRepoRoot2(events, homeDir) {
|
|
|
36271
36484
|
async function findRepositoryRoot2(cwd, homeDir) {
|
|
36272
36485
|
let current = cwd;
|
|
36273
36486
|
const stop = resolve14(homeDir);
|
|
36274
|
-
while (current !==
|
|
36275
|
-
if (await pathExists10(
|
|
36487
|
+
while (current !== dirname29(current) && current !== stop) {
|
|
36488
|
+
if (await pathExists10(join37(current, ".git")))
|
|
36276
36489
|
return current;
|
|
36277
|
-
current =
|
|
36490
|
+
current = dirname29(current);
|
|
36278
36491
|
}
|
|
36279
36492
|
return cwd;
|
|
36280
36493
|
}
|
|
@@ -36495,20 +36708,20 @@ function createReceipt(segment, analyzedAt, status, proposalId, reason, candidat
|
|
|
36495
36708
|
};
|
|
36496
36709
|
}
|
|
36497
36710
|
function resolveReceiptPath(homeDir, segmentId) {
|
|
36498
|
-
return
|
|
36711
|
+
return join37(resolveReceiptsDir(homeDir), `${segmentId}.json`);
|
|
36499
36712
|
}
|
|
36500
36713
|
function resolveReceiptsDir(homeDir) {
|
|
36501
|
-
return
|
|
36714
|
+
return join37(resolveEvoDevPaths(homeDir).stateDir, "evolution", "session-proposal-scans");
|
|
36502
36715
|
}
|
|
36503
36716
|
function resolveDailyStatePath(homeDir) {
|
|
36504
|
-
return
|
|
36717
|
+
return join37(resolveEvoDevPaths(homeDir).stateDir, "schedule", "repo-proposals.json");
|
|
36505
36718
|
}
|
|
36506
36719
|
async function acquireScanLock(homeDir) {
|
|
36507
|
-
const lockPath =
|
|
36508
|
-
await
|
|
36720
|
+
const lockPath = join37(resolveEvoDevPaths(homeDir).stateDir, "evolution", ".proposal-scan.lock");
|
|
36721
|
+
await mkdir25(dirname29(lockPath), { recursive: true });
|
|
36509
36722
|
try {
|
|
36510
36723
|
const handle = await open6(lockPath, "wx");
|
|
36511
|
-
const ownerId =
|
|
36724
|
+
const ownerId = randomUUID7();
|
|
36512
36725
|
try {
|
|
36513
36726
|
await handle.writeFile(`${JSON.stringify({ ownerId, pid: process.pid, createdAt: new Date().toISOString() })}
|
|
36514
36727
|
`, "utf8");
|
|
@@ -36516,7 +36729,7 @@ async function acquireScanLock(homeDir) {
|
|
|
36516
36729
|
await handle.close().catch(() => {
|
|
36517
36730
|
return;
|
|
36518
36731
|
});
|
|
36519
|
-
await
|
|
36732
|
+
await rm14(lockPath, { force: true }).catch(() => {
|
|
36520
36733
|
return;
|
|
36521
36734
|
});
|
|
36522
36735
|
throw error;
|
|
@@ -36534,7 +36747,7 @@ async function acquireScanLock(homeDir) {
|
|
|
36534
36747
|
throw error;
|
|
36535
36748
|
const info = await stat25(lockPath).catch(() => null);
|
|
36536
36749
|
if (info !== null && Date.now() - info.mtimeMs > SCAN_LOCK_STALE_MS) {
|
|
36537
|
-
await
|
|
36750
|
+
await rm14(lockPath, { force: true });
|
|
36538
36751
|
return acquireScanLock(homeDir);
|
|
36539
36752
|
}
|
|
36540
36753
|
return null;
|
|
@@ -36547,11 +36760,11 @@ async function releaseScanLock(lock) {
|
|
|
36547
36760
|
});
|
|
36548
36761
|
const currentOwner = await readScanLockOwner(lock.path);
|
|
36549
36762
|
if (currentOwner === lock.ownerId)
|
|
36550
|
-
await
|
|
36763
|
+
await rm14(lock.path, { force: true });
|
|
36551
36764
|
}
|
|
36552
36765
|
async function readScanLockOwner(path2) {
|
|
36553
36766
|
try {
|
|
36554
|
-
const value = JSON.parse(await
|
|
36767
|
+
const value = JSON.parse(await readFile35(path2, "utf8"));
|
|
36555
36768
|
return isRecord21(value) && typeof value.ownerId === "string" ? value.ownerId : null;
|
|
36556
36769
|
} catch {
|
|
36557
36770
|
return null;
|
|
@@ -36613,7 +36826,7 @@ async function listAllCandidateReceipts(homeDir) {
|
|
|
36613
36826
|
throw error;
|
|
36614
36827
|
});
|
|
36615
36828
|
const receipts = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
36616
|
-
const path2 =
|
|
36829
|
+
const path2 = join37(dir, name);
|
|
36617
36830
|
const receipt = await readReceipt(path2);
|
|
36618
36831
|
return receipt === null ? null : { path: path2, receipt };
|
|
36619
36832
|
}));
|
|
@@ -36630,7 +36843,7 @@ function selectIndependentCandidateReceipts(candidates2) {
|
|
|
36630
36843
|
}
|
|
36631
36844
|
async function readReceipt(path2) {
|
|
36632
36845
|
try {
|
|
36633
|
-
const value = JSON.parse(await
|
|
36846
|
+
const value = JSON.parse(await readFile35(path2, "utf8"));
|
|
36634
36847
|
return isRecord21(value) && value.kind === "session-repo-proposal-receipt" ? value : null;
|
|
36635
36848
|
} catch {
|
|
36636
36849
|
return null;
|
|
@@ -36638,7 +36851,7 @@ async function readReceipt(path2) {
|
|
|
36638
36851
|
}
|
|
36639
36852
|
async function readDailyState(path2) {
|
|
36640
36853
|
try {
|
|
36641
|
-
const value = JSON.parse(await
|
|
36854
|
+
const value = JSON.parse(await readFile35(path2, "utf8"));
|
|
36642
36855
|
return isRecord21(value) && value.kind === "session-repo-proposal-daily-state" ? value : null;
|
|
36643
36856
|
} catch {
|
|
36644
36857
|
return null;
|
|
@@ -36648,8 +36861,8 @@ async function writeReceipt(path2, receipt) {
|
|
|
36648
36861
|
await writeJson4(path2, receipt);
|
|
36649
36862
|
}
|
|
36650
36863
|
async function writeJson4(path2, value) {
|
|
36651
|
-
await
|
|
36652
|
-
await
|
|
36864
|
+
await mkdir25(dirname29(path2), { recursive: true });
|
|
36865
|
+
await writeFile25(path2, `${JSON.stringify(value, null, 2)}
|
|
36653
36866
|
`, "utf8");
|
|
36654
36867
|
}
|
|
36655
36868
|
async function pathExists10(path2) {
|
|
@@ -36671,7 +36884,7 @@ function startOfLocalDay(value) {
|
|
|
36671
36884
|
const date = new Date(value);
|
|
36672
36885
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
36673
36886
|
}
|
|
36674
|
-
function
|
|
36887
|
+
function localDay2(value) {
|
|
36675
36888
|
const date = new Date(value);
|
|
36676
36889
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
36677
36890
|
const day = String(date.getDate()).padStart(2, "0");
|
|
@@ -36889,7 +37102,8 @@ class EvolutionJobService {
|
|
|
36889
37102
|
});
|
|
36890
37103
|
await persist();
|
|
36891
37104
|
try {
|
|
36892
|
-
const
|
|
37105
|
+
const processKnowledge = this.#options.processKnowledge ?? defaultKnowledgeProcessor;
|
|
37106
|
+
const knowledgeInput = {
|
|
36893
37107
|
homeDir: this.#options.homeDir,
|
|
36894
37108
|
limit: EVOLUTION_JOB_KNOWLEDGE_LIMIT,
|
|
36895
37109
|
distillSessionKnowledge: createSessionKnowledgeDistiller({
|
|
@@ -36910,7 +37124,22 @@ class EvolutionJobService {
|
|
|
36910
37124
|
});
|
|
36911
37125
|
await persist();
|
|
36912
37126
|
}
|
|
36913
|
-
}
|
|
37127
|
+
};
|
|
37128
|
+
let retryAttempts = 0;
|
|
37129
|
+
let result;
|
|
37130
|
+
try {
|
|
37131
|
+
result = await processKnowledge(knowledgeInput);
|
|
37132
|
+
} catch (error) {
|
|
37133
|
+
if (!isTransientEvolutionFailure(error))
|
|
37134
|
+
throw error;
|
|
37135
|
+
retryAttempts = 1;
|
|
37136
|
+
result = await processKnowledge(knowledgeInput);
|
|
37137
|
+
}
|
|
37138
|
+
if (retryAttempts === 0 && result.pending > 0 && result.warnings.some(isTransientEvolutionFailure)) {
|
|
37139
|
+
retryAttempts = 1;
|
|
37140
|
+
const retried = await processKnowledge(knowledgeInput);
|
|
37141
|
+
result = mergeKnowledgeRetryResults(result, retried);
|
|
37142
|
+
}
|
|
36914
37143
|
job = updateStage(job, "knowledge", {
|
|
36915
37144
|
status: "completed",
|
|
36916
37145
|
detail: `Knowledge pass finished with ${result.consumed} consumed trigger(s).`,
|
|
@@ -36920,9 +37149,14 @@ class EvolutionJobService {
|
|
|
36920
37149
|
consumed: result.consumed,
|
|
36921
37150
|
skipped: result.skipped,
|
|
36922
37151
|
failed: result.failed,
|
|
36923
|
-
pending: result.pending
|
|
37152
|
+
pending: result.pending,
|
|
37153
|
+
retryAttempts
|
|
36924
37154
|
}
|
|
36925
37155
|
});
|
|
37156
|
+
job = addJobOutputs(job, {
|
|
37157
|
+
distillationBatchIds: result.batchIds,
|
|
37158
|
+
knowledgeChangeIds: result.pendingChangeIds
|
|
37159
|
+
});
|
|
36926
37160
|
if (result.failed > 0)
|
|
36927
37161
|
job = addJobWarning(job, `${result.failed} knowledge trigger(s) failed.`);
|
|
36928
37162
|
for (const warning of result.warnings)
|
|
@@ -36944,7 +37178,8 @@ class EvolutionJobService {
|
|
|
36944
37178
|
});
|
|
36945
37179
|
await persist();
|
|
36946
37180
|
try {
|
|
36947
|
-
const
|
|
37181
|
+
const scanRecommendations = this.#options.scanRecommendations ?? defaultRecommendationScanner;
|
|
37182
|
+
const recommendationInput = {
|
|
36948
37183
|
homeDir: this.#options.homeDir,
|
|
36949
37184
|
limit: EVOLUTION_JOB_RECOMMENDATION_LIMIT,
|
|
36950
37185
|
force: input.forceRecommendations === true,
|
|
@@ -36965,7 +37200,22 @@ class EvolutionJobService {
|
|
|
36965
37200
|
});
|
|
36966
37201
|
await persist();
|
|
36967
37202
|
}
|
|
36968
|
-
}
|
|
37203
|
+
};
|
|
37204
|
+
let retryAttempts = 0;
|
|
37205
|
+
let result;
|
|
37206
|
+
try {
|
|
37207
|
+
result = await scanRecommendations(recommendationInput);
|
|
37208
|
+
} catch (error) {
|
|
37209
|
+
if (!isTransientEvolutionFailure(error))
|
|
37210
|
+
throw error;
|
|
37211
|
+
retryAttempts = 1;
|
|
37212
|
+
result = await scanRecommendations({ ...recommendationInput, force: true });
|
|
37213
|
+
}
|
|
37214
|
+
if (retryAttempts === 0 && result.failed > 0 && result.warnings.some(isTransientEvolutionFailure)) {
|
|
37215
|
+
retryAttempts = 1;
|
|
37216
|
+
const retried = await scanRecommendations({ ...recommendationInput, force: true });
|
|
37217
|
+
result = mergeRecommendationRetryResults(result, retried);
|
|
37218
|
+
}
|
|
36969
37219
|
job = updateStage(job, "recommendations", {
|
|
36970
37220
|
status: "completed",
|
|
36971
37221
|
detail: result.ran ? `Recommendation pass produced ${result.proposed} review proposal(s).` : result.skippedReason === "scan-already-running" ? "Another recommendation scan is already running; the daily slot remains available." : "The daily recommendation pass already ran today.",
|
|
@@ -36976,9 +37226,11 @@ class EvolutionJobService {
|
|
|
36976
37226
|
noChange: result.noChange,
|
|
36977
37227
|
failed: result.failed,
|
|
36978
37228
|
skipped: result.skipped,
|
|
36979
|
-
superseded: result.superseded
|
|
37229
|
+
superseded: result.superseded,
|
|
37230
|
+
retryAttempts
|
|
36980
37231
|
}
|
|
36981
37232
|
});
|
|
37233
|
+
job = addJobOutputs(job, { proposalIds: result.proposalIds });
|
|
36982
37234
|
if (result.failed > 0) {
|
|
36983
37235
|
job = addJobWarning(job, `${result.failed} recommendation analysis item(s) failed.`);
|
|
36984
37236
|
}
|
|
@@ -37105,6 +37357,17 @@ async function readEvolutionJob(homeDir, jobId) {
|
|
|
37105
37357
|
async function readLatestEvolutionJob(homeDir) {
|
|
37106
37358
|
return readJobFile(resolveLatestEvolutionJobPath(homeDir));
|
|
37107
37359
|
}
|
|
37360
|
+
async function listEvolutionJobs(homeDir, limit = 30) {
|
|
37361
|
+
const directory = resolveEvolutionJobsDir(homeDir);
|
|
37362
|
+
let names;
|
|
37363
|
+
try {
|
|
37364
|
+
names = await readdir21(directory);
|
|
37365
|
+
} catch {
|
|
37366
|
+
return [];
|
|
37367
|
+
}
|
|
37368
|
+
const jobs = await Promise.all(names.filter((name) => name.startsWith("evolution-job-") && name.endsWith(".json")).map((name) => readJobFile(join38(directory, name))));
|
|
37369
|
+
return jobs.filter((job) => job !== null).sort((left2, right2) => right2.createdAt.localeCompare(left2.createdAt)).slice(0, Math.max(0, limit));
|
|
37370
|
+
}
|
|
37108
37371
|
async function reconcileLatestEvolutionJob(homeDir, now2 = new Date().toISOString()) {
|
|
37109
37372
|
const job = await readLatestEvolutionJob(homeDir);
|
|
37110
37373
|
if (job === null || job.status !== "queued" && job.status !== "running")
|
|
@@ -37112,16 +37375,16 @@ async function reconcileLatestEvolutionJob(homeDir, now2 = new Date().toISOStrin
|
|
|
37112
37375
|
return reconcileEvolutionJob(homeDir, job, now2);
|
|
37113
37376
|
}
|
|
37114
37377
|
function resolveEvolutionJobPath(homeDir, jobId) {
|
|
37115
|
-
return
|
|
37378
|
+
return join38(resolveEvolutionJobsDir(homeDir), `${jobId}.json`);
|
|
37116
37379
|
}
|
|
37117
37380
|
function resolveLatestEvolutionJobPath(homeDir) {
|
|
37118
|
-
return
|
|
37381
|
+
return join38(resolveEvolutionJobsDir(homeDir), "latest.json");
|
|
37119
37382
|
}
|
|
37120
37383
|
function createEvolutionJob(input, now2) {
|
|
37121
37384
|
return {
|
|
37122
37385
|
schemaVersion: 1,
|
|
37123
37386
|
kind: "evolution-job",
|
|
37124
|
-
id: `evolution-job-${
|
|
37387
|
+
id: `evolution-job-${randomUUID8()}`,
|
|
37125
37388
|
source: input.source,
|
|
37126
37389
|
status: "queued",
|
|
37127
37390
|
createdAt: now2,
|
|
@@ -37134,6 +37397,11 @@ function createEvolutionJob(input, now2) {
|
|
|
37134
37397
|
createStage("improvement-evals", "Repo proposal application"),
|
|
37135
37398
|
createStage("finalize", "Evolution refresh")
|
|
37136
37399
|
],
|
|
37400
|
+
outputs: {
|
|
37401
|
+
distillationBatchIds: [],
|
|
37402
|
+
proposalIds: [],
|
|
37403
|
+
knowledgeChangeIds: []
|
|
37404
|
+
},
|
|
37137
37405
|
warnings: [],
|
|
37138
37406
|
metadataOnly: true,
|
|
37139
37407
|
rawContentStored: false
|
|
@@ -37161,6 +37429,70 @@ function addJobWarning(job, warning) {
|
|
|
37161
37429
|
return job;
|
|
37162
37430
|
return { ...job, warnings: [...job.warnings, bounded].slice(-MAX_JOB_WARNINGS) };
|
|
37163
37431
|
}
|
|
37432
|
+
function addJobOutputs(job, output) {
|
|
37433
|
+
const current = job.outputs ?? {
|
|
37434
|
+
distillationBatchIds: [],
|
|
37435
|
+
proposalIds: [],
|
|
37436
|
+
knowledgeChangeIds: []
|
|
37437
|
+
};
|
|
37438
|
+
return {
|
|
37439
|
+
...job,
|
|
37440
|
+
outputs: {
|
|
37441
|
+
distillationBatchIds: unique3([
|
|
37442
|
+
...current.distillationBatchIds,
|
|
37443
|
+
...output.distillationBatchIds ?? []
|
|
37444
|
+
]),
|
|
37445
|
+
proposalIds: unique3([...current.proposalIds, ...output.proposalIds ?? []]),
|
|
37446
|
+
knowledgeChangeIds: unique3([
|
|
37447
|
+
...current.knowledgeChangeIds,
|
|
37448
|
+
...output.knowledgeChangeIds ?? []
|
|
37449
|
+
])
|
|
37450
|
+
}
|
|
37451
|
+
};
|
|
37452
|
+
}
|
|
37453
|
+
function unique3(values) {
|
|
37454
|
+
return [...new Set(values)].slice(0, 100);
|
|
37455
|
+
}
|
|
37456
|
+
function mergeKnowledgeRetryResults(first, retry) {
|
|
37457
|
+
return {
|
|
37458
|
+
processed: first.processed + retry.processed,
|
|
37459
|
+
consumed: first.consumed + retry.consumed,
|
|
37460
|
+
skipped: first.skipped + retry.skipped,
|
|
37461
|
+
failed: retry.failed,
|
|
37462
|
+
pending: retry.pending,
|
|
37463
|
+
triggerIds: unique3([...first.triggerIds, ...retry.triggerIds]),
|
|
37464
|
+
batchIds: unique3([...first.batchIds, ...retry.batchIds]),
|
|
37465
|
+
pendingChangeIds: unique3([...first.pendingChangeIds, ...retry.pendingChangeIds]),
|
|
37466
|
+
warnings: unique3([
|
|
37467
|
+
...first.warnings.filter((warning) => !isTransientEvolutionFailure(warning)),
|
|
37468
|
+
...retry.warnings
|
|
37469
|
+
]),
|
|
37470
|
+
dryRun: false
|
|
37471
|
+
};
|
|
37472
|
+
}
|
|
37473
|
+
function mergeRecommendationRetryResults(first, retry) {
|
|
37474
|
+
return {
|
|
37475
|
+
...retry,
|
|
37476
|
+
ran: first.ran || retry.ran,
|
|
37477
|
+
skippedReason: retry.ran ? null : retry.skippedReason,
|
|
37478
|
+
scanned: first.scanned + retry.scanned,
|
|
37479
|
+
proposed: first.proposed + retry.proposed,
|
|
37480
|
+
candidates: first.candidates + retry.candidates,
|
|
37481
|
+
noChange: first.noChange + retry.noChange,
|
|
37482
|
+
failed: retry.failed,
|
|
37483
|
+
skipped: first.skipped + retry.skipped,
|
|
37484
|
+
superseded: first.superseded + retry.superseded,
|
|
37485
|
+
proposalIds: unique3([...first.proposalIds, ...retry.proposalIds]),
|
|
37486
|
+
warnings: unique3([
|
|
37487
|
+
...first.warnings.filter((warning) => !isTransientEvolutionFailure(warning)),
|
|
37488
|
+
...retry.warnings
|
|
37489
|
+
])
|
|
37490
|
+
};
|
|
37491
|
+
}
|
|
37492
|
+
function isTransientEvolutionFailure(error) {
|
|
37493
|
+
const message = (error instanceof Error ? `${error.name}: ${error.message}` : String(error)).toLowerCase().trim();
|
|
37494
|
+
return /(?:timed?\s*out|timeout|rate[\s-]*limit|too many requests|\b429\b|network|econn(?:reset|refused|aborted)|eai_again|etimedout|socket|temporar|service unavailable|\b50[0234]\b|fetch failed)/u.test(message);
|
|
37495
|
+
}
|
|
37164
37496
|
async function defaultKnowledgeProcessor(input) {
|
|
37165
37497
|
return processEvolutionTriggers(input);
|
|
37166
37498
|
}
|
|
@@ -37177,33 +37509,52 @@ async function writeEvolutionJob(homeDir, job) {
|
|
|
37177
37509
|
]);
|
|
37178
37510
|
}
|
|
37179
37511
|
async function writeAtomicJson(path2, value) {
|
|
37180
|
-
await
|
|
37181
|
-
const temporary = `${path2}.${process.pid}.${
|
|
37182
|
-
await
|
|
37512
|
+
await mkdir26(dirname30(path2), { recursive: true });
|
|
37513
|
+
const temporary = `${path2}.${process.pid}.${randomUUID8()}.tmp`;
|
|
37514
|
+
await writeFile26(temporary, `${JSON.stringify(value, null, 2)}
|
|
37183
37515
|
`, "utf8");
|
|
37184
37516
|
try {
|
|
37185
|
-
await
|
|
37517
|
+
await rename8(temporary, path2);
|
|
37186
37518
|
} catch (error) {
|
|
37187
|
-
await
|
|
37519
|
+
await rm15(temporary, { force: true });
|
|
37188
37520
|
throw error;
|
|
37189
37521
|
}
|
|
37190
37522
|
}
|
|
37191
37523
|
async function readJobFile(path2) {
|
|
37192
37524
|
try {
|
|
37193
|
-
const value = JSON.parse(await
|
|
37194
|
-
|
|
37525
|
+
const value = JSON.parse(await readFile36(path2, "utf8"));
|
|
37526
|
+
if (!isEvolutionJob(value))
|
|
37527
|
+
return null;
|
|
37528
|
+
return {
|
|
37529
|
+
...value,
|
|
37530
|
+
outputs: normalizeJobOutputs(value.outputs)
|
|
37531
|
+
};
|
|
37195
37532
|
} catch {
|
|
37196
37533
|
return null;
|
|
37197
37534
|
}
|
|
37198
37535
|
}
|
|
37536
|
+
function normalizeJobOutputs(value) {
|
|
37537
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
37538
|
+
return { distillationBatchIds: [], proposalIds: [], knowledgeChangeIds: [] };
|
|
37539
|
+
}
|
|
37540
|
+
const outputs = value;
|
|
37541
|
+
return {
|
|
37542
|
+
distillationBatchIds: normalizeOutputIds(outputs.distillationBatchIds),
|
|
37543
|
+
proposalIds: normalizeOutputIds(outputs.proposalIds),
|
|
37544
|
+
knowledgeChangeIds: normalizeOutputIds(outputs.knowledgeChangeIds)
|
|
37545
|
+
};
|
|
37546
|
+
}
|
|
37547
|
+
function normalizeOutputIds(value) {
|
|
37548
|
+
return Array.isArray(value) ? unique3(value.filter((item) => typeof item === "string")) : [];
|
|
37549
|
+
}
|
|
37199
37550
|
function isEvolutionJob(value) {
|
|
37200
37551
|
return typeof value === "object" && value !== null && value.schemaVersion === 1 && value.kind === "evolution-job" && typeof value.id === "string" && Array.isArray(value.stages);
|
|
37201
37552
|
}
|
|
37202
37553
|
async function acquireEvolutionJobLock(homeDir, jobId, now2) {
|
|
37203
|
-
const path2 =
|
|
37204
|
-
await
|
|
37554
|
+
const path2 = join38(resolveEvoDevPaths(homeDir).stateDir, "evolution", ".job.lock");
|
|
37555
|
+
await mkdir26(dirname30(path2), { recursive: true });
|
|
37205
37556
|
try {
|
|
37206
|
-
await
|
|
37557
|
+
await writeFile26(path2, `${JSON.stringify({ schemaVersion: 1, kind: "evolution-job-lock", jobId, pid: process.pid, createdAt: now2 })}
|
|
37207
37558
|
`, { encoding: "utf8", flag: "wx" });
|
|
37208
37559
|
return { path: path2, jobId };
|
|
37209
37560
|
} catch (error) {
|
|
@@ -37211,7 +37562,7 @@ async function acquireEvolutionJobLock(homeDir, jobId, now2) {
|
|
|
37211
37562
|
throw error;
|
|
37212
37563
|
const info = await stat26(path2).catch(() => null);
|
|
37213
37564
|
if (info !== null && Date.now() - info.mtimeMs > EVOLUTION_JOB_LOCK_STALE_MS) {
|
|
37214
|
-
await
|
|
37565
|
+
await rm15(path2, { force: true });
|
|
37215
37566
|
return acquireEvolutionJobLock(homeDir, jobId, now2);
|
|
37216
37567
|
}
|
|
37217
37568
|
return null;
|
|
@@ -37225,12 +37576,12 @@ async function refreshEvolutionJobLock(lock) {
|
|
|
37225
37576
|
}
|
|
37226
37577
|
async function releaseEvolutionJobLock(lock) {
|
|
37227
37578
|
if (await evolutionJobLockIsOwned(lock)) {
|
|
37228
|
-
await
|
|
37579
|
+
await rm15(lock.path, { force: true });
|
|
37229
37580
|
}
|
|
37230
37581
|
}
|
|
37231
37582
|
async function evolutionJobLockIsOwned(lock) {
|
|
37232
37583
|
try {
|
|
37233
|
-
const value = JSON.parse(await
|
|
37584
|
+
const value = JSON.parse(await readFile36(lock.path, "utf8"));
|
|
37234
37585
|
return value.jobId === lock.jobId;
|
|
37235
37586
|
} catch {
|
|
37236
37587
|
return false;
|
|
@@ -37272,9 +37623,9 @@ async function reconcileEvolutionJob(homeDir, job, now2) {
|
|
|
37272
37623
|
return reconciled;
|
|
37273
37624
|
}
|
|
37274
37625
|
async function hasLiveEvolutionJobLock(homeDir, jobId) {
|
|
37275
|
-
const path2 =
|
|
37626
|
+
const path2 = join38(resolveEvoDevPaths(homeDir).stateDir, "evolution", ".job.lock");
|
|
37276
37627
|
try {
|
|
37277
|
-
const [value, info] = await Promise.all([
|
|
37628
|
+
const [value, info] = await Promise.all([readFile36(path2, "utf8"), stat26(path2)]);
|
|
37278
37629
|
const lock = JSON.parse(value);
|
|
37279
37630
|
return lock.jobId === jobId && Date.now() - info.mtimeMs <= EVOLUTION_JOB_LOCK_STALE_MS;
|
|
37280
37631
|
} catch {
|
|
@@ -37282,7 +37633,7 @@ async function hasLiveEvolutionJobLock(homeDir, jobId) {
|
|
|
37282
37633
|
}
|
|
37283
37634
|
}
|
|
37284
37635
|
function resolveEvolutionJobsDir(homeDir) {
|
|
37285
|
-
return
|
|
37636
|
+
return join38(resolveEvoDevPaths(homeDir).stateDir, "schedule", "evolution-jobs");
|
|
37286
37637
|
}
|
|
37287
37638
|
function isFileExistsError3(error) {
|
|
37288
37639
|
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
@@ -37293,21 +37644,23 @@ function safeError3(error) {
|
|
|
37293
37644
|
|
|
37294
37645
|
// packages/cli/src/os-scheduler.ts
|
|
37295
37646
|
import { spawn as spawn7 } from "node:child_process";
|
|
37296
|
-
import { randomUUID as
|
|
37647
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
37297
37648
|
import { constants as constants3 } from "node:fs";
|
|
37298
37649
|
import {
|
|
37299
37650
|
access as access2,
|
|
37300
37651
|
chmod as chmod3,
|
|
37301
|
-
mkdir as
|
|
37652
|
+
mkdir as mkdir27,
|
|
37653
|
+
readFile as readFile37,
|
|
37302
37654
|
realpath as realpath8,
|
|
37303
|
-
rename as
|
|
37304
|
-
rm as
|
|
37655
|
+
rename as rename9,
|
|
37656
|
+
rm as rm16,
|
|
37305
37657
|
stat as stat27,
|
|
37306
|
-
writeFile as
|
|
37658
|
+
writeFile as writeFile27
|
|
37307
37659
|
} from "node:fs/promises";
|
|
37308
|
-
import { dirname as
|
|
37660
|
+
import { dirname as dirname31, join as join39 } from "node:path";
|
|
37309
37661
|
var EVOLUTION_SCHEDULE_LABEL = "com.evodev.evolution";
|
|
37310
37662
|
var EVOLUTION_SCHEDULE_INTERVAL_SECONDS = 15 * 60;
|
|
37663
|
+
var DEFAULT_EVOLUTION_SCHEDULE_DAILY_TIME = "02:00";
|
|
37311
37664
|
|
|
37312
37665
|
class OsEvolutionScheduler {
|
|
37313
37666
|
#options;
|
|
@@ -37321,23 +37674,26 @@ class OsEvolutionScheduler {
|
|
|
37321
37674
|
}
|
|
37322
37675
|
if (!await isFile3(path2))
|
|
37323
37676
|
return createStatus("not-installed", path2);
|
|
37677
|
+
const schedule = await readInstalledSchedule(path2);
|
|
37324
37678
|
const result = await this.#runLaunchctl(["print", this.#serviceTarget()]);
|
|
37325
|
-
return createStatus(result.exitCode === 0 ? "installed-loaded" : "installed-not-loaded", path2);
|
|
37679
|
+
return createStatus(result.exitCode === 0 ? "installed-loaded" : "installed-not-loaded", path2, schedule);
|
|
37326
37680
|
}
|
|
37327
|
-
async install() {
|
|
37681
|
+
async install(dailyTime = DEFAULT_EVOLUTION_SCHEDULE_DAILY_TIME) {
|
|
37328
37682
|
this.#assertDarwin();
|
|
37683
|
+
const parsedTime = parseDailyTime3(dailyTime);
|
|
37329
37684
|
const commandPath = await this.#resolveCommandPath();
|
|
37330
37685
|
const path2 = resolveLaunchAgentPath(this.#options.homeDir);
|
|
37331
37686
|
const paths4 = resolveEvoDevPaths(this.#options.homeDir);
|
|
37332
|
-
const logDir =
|
|
37333
|
-
await
|
|
37334
|
-
await
|
|
37687
|
+
const logDir = join39(paths4.logsDir, "schedule");
|
|
37688
|
+
await mkdir27(dirname31(path2), { recursive: true });
|
|
37689
|
+
await mkdir27(logDir, { recursive: true });
|
|
37335
37690
|
const plist = renderLaunchAgentPlist({
|
|
37336
37691
|
commandPath,
|
|
37337
37692
|
homeDir: this.#options.homeDir,
|
|
37338
37693
|
pathValue: this.#options.pathValue ?? process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin",
|
|
37339
|
-
stdoutPath:
|
|
37340
|
-
stderrPath:
|
|
37694
|
+
stdoutPath: join39(logDir, "evolution.stdout.log"),
|
|
37695
|
+
stderrPath: join39(logDir, "evolution.stderr.log"),
|
|
37696
|
+
dailyTime: parsedTime.value
|
|
37341
37697
|
});
|
|
37342
37698
|
const bootout = await this.#runLaunchctl(["bootout", this.#serviceTarget()]);
|
|
37343
37699
|
if (bootout.exitCode !== 0 && !isLaunchctlServiceNotLoaded(bootout)) {
|
|
@@ -37373,7 +37729,7 @@ class OsEvolutionScheduler {
|
|
|
37373
37729
|
if (!isLaunchctlServiceNotLoaded(verification)) {
|
|
37374
37730
|
throw new Error(`Could not verify EvoDev evolution schedule shutdown; its LaunchAgent file was preserved: ${safeCommandError(verification)}`);
|
|
37375
37731
|
}
|
|
37376
|
-
await
|
|
37732
|
+
await rm16(path2, { force: true });
|
|
37377
37733
|
return createStatus("not-installed", path2);
|
|
37378
37734
|
}
|
|
37379
37735
|
#assertDarwin() {
|
|
@@ -37417,9 +37773,10 @@ function isLaunchctlServiceNotLoaded(result) {
|
|
|
37417
37773
|
${result.stderr}`);
|
|
37418
37774
|
}
|
|
37419
37775
|
function resolveLaunchAgentPath(homeDir) {
|
|
37420
|
-
return
|
|
37776
|
+
return join39(homeDir, "Library", "LaunchAgents", `${EVOLUTION_SCHEDULE_LABEL}.plist`);
|
|
37421
37777
|
}
|
|
37422
37778
|
function renderLaunchAgentPlist(input) {
|
|
37779
|
+
const dailyTime = parseDailyTime3(input.dailyTime);
|
|
37423
37780
|
return [
|
|
37424
37781
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
37425
37782
|
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
@@ -37442,8 +37799,13 @@ function renderLaunchAgentPlist(input) {
|
|
|
37442
37799
|
" </dict>",
|
|
37443
37800
|
" <key>RunAtLoad</key>",
|
|
37444
37801
|
" <true/>",
|
|
37445
|
-
" <key>
|
|
37446
|
-
|
|
37802
|
+
" <key>StartCalendarInterval</key>",
|
|
37803
|
+
" <dict>",
|
|
37804
|
+
" <key>Hour</key>",
|
|
37805
|
+
` <integer>${dailyTime.hour}</integer>`,
|
|
37806
|
+
" <key>Minute</key>",
|
|
37807
|
+
` <integer>${dailyTime.minute}</integer>`,
|
|
37808
|
+
" </dict>",
|
|
37447
37809
|
" <key>ProcessType</key>",
|
|
37448
37810
|
" <string>Background</string>",
|
|
37449
37811
|
" <key>StandardOutPath</key>",
|
|
@@ -37476,12 +37838,12 @@ async function runSchedulerCommand(command, args) {
|
|
|
37476
37838
|
});
|
|
37477
37839
|
}
|
|
37478
37840
|
async function writeAtomic(path2, value) {
|
|
37479
|
-
const temporary = `${path2}.${process.pid}.${
|
|
37480
|
-
await
|
|
37841
|
+
const temporary = `${path2}.${process.pid}.${randomUUID9()}.tmp`;
|
|
37842
|
+
await writeFile27(temporary, value, "utf8");
|
|
37481
37843
|
try {
|
|
37482
|
-
await
|
|
37844
|
+
await rename9(temporary, path2);
|
|
37483
37845
|
} catch (error) {
|
|
37484
|
-
await
|
|
37846
|
+
await rm16(temporary, { force: true });
|
|
37485
37847
|
throw error;
|
|
37486
37848
|
}
|
|
37487
37849
|
}
|
|
@@ -37492,14 +37854,47 @@ async function isFile3(path2) {
|
|
|
37492
37854
|
return false;
|
|
37493
37855
|
}
|
|
37494
37856
|
}
|
|
37495
|
-
function createStatus(state, path2
|
|
37857
|
+
function createStatus(state, path2, schedule = {
|
|
37858
|
+
scheduleKind: "none",
|
|
37859
|
+
dailyTime: null,
|
|
37860
|
+
intervalSeconds: null
|
|
37861
|
+
}) {
|
|
37496
37862
|
return {
|
|
37497
37863
|
state,
|
|
37498
37864
|
label: EVOLUTION_SCHEDULE_LABEL,
|
|
37499
37865
|
path: path2,
|
|
37500
|
-
|
|
37866
|
+
...schedule
|
|
37501
37867
|
};
|
|
37502
37868
|
}
|
|
37869
|
+
async function readInstalledSchedule(path2) {
|
|
37870
|
+
try {
|
|
37871
|
+
const plist = await readFile37(path2, "utf8");
|
|
37872
|
+
const hour = /<key>Hour<\/key>\s*<integer>(\d{1,2})<\/integer>/u.exec(plist)?.[1];
|
|
37873
|
+
const minute = /<key>Minute<\/key>\s*<integer>(\d{1,2})<\/integer>/u.exec(plist)?.[1];
|
|
37874
|
+
if (plist.includes("<key>StartCalendarInterval</key>") && hour !== undefined) {
|
|
37875
|
+
const normalized = parseDailyTime3(`${hour.padStart(2, "0")}:${(minute ?? "0").padStart(2, "0")}`);
|
|
37876
|
+
return { scheduleKind: "daily", dailyTime: normalized.value, intervalSeconds: null };
|
|
37877
|
+
}
|
|
37878
|
+
const interval = /<key>StartInterval<\/key>\s*<integer>(\d+)<\/integer>/u.exec(plist)?.[1];
|
|
37879
|
+
if (interval !== undefined) {
|
|
37880
|
+
return {
|
|
37881
|
+
scheduleKind: "legacy-interval",
|
|
37882
|
+
dailyTime: null,
|
|
37883
|
+
intervalSeconds: Number.parseInt(interval, 10)
|
|
37884
|
+
};
|
|
37885
|
+
}
|
|
37886
|
+
} catch {}
|
|
37887
|
+
return { scheduleKind: "unknown", dailyTime: null, intervalSeconds: null };
|
|
37888
|
+
}
|
|
37889
|
+
function parseDailyTime3(value) {
|
|
37890
|
+
const match = /^(\d{2}):(\d{2})$/u.exec(value);
|
|
37891
|
+
const hour = Number(match?.[1]);
|
|
37892
|
+
const minute = Number(match?.[2]);
|
|
37893
|
+
if (match === null || hour > 23 || minute > 59) {
|
|
37894
|
+
throw new Error("Evolution schedule time must use 24-hour HH:MM format.");
|
|
37895
|
+
}
|
|
37896
|
+
return { value, hour, minute };
|
|
37897
|
+
}
|
|
37503
37898
|
function xmlEscape(value) {
|
|
37504
37899
|
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37505
37900
|
}
|
|
@@ -37519,6 +37914,7 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37519
37914
|
}
|
|
37520
37915
|
if (subcommand === "status") {
|
|
37521
37916
|
const flags = parseScheduleStatusFlags(argv.slice(1));
|
|
37917
|
+
const settingsPromise = readEvolutionSettings(homeDir);
|
|
37522
37918
|
const [
|
|
37523
37919
|
status,
|
|
37524
37920
|
segmentTriggers,
|
|
@@ -37527,7 +37923,8 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37527
37923
|
proposalDaily,
|
|
37528
37924
|
osStatus,
|
|
37529
37925
|
settings,
|
|
37530
|
-
latestEvolutionJob
|
|
37926
|
+
latestEvolutionJob,
|
|
37927
|
+
dailySchedule
|
|
37531
37928
|
] = await Promise.all([
|
|
37532
37929
|
readLazyEvolutionStatus({
|
|
37533
37930
|
homeDir,
|
|
@@ -37554,8 +37951,13 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37554
37951
|
}),
|
|
37555
37952
|
readDailySessionProposalState(homeDir),
|
|
37556
37953
|
osScheduler.status(),
|
|
37557
|
-
|
|
37558
|
-
readLatestEvolutionJob(homeDir)
|
|
37954
|
+
settingsPromise,
|
|
37955
|
+
readLatestEvolutionJob(homeDir),
|
|
37956
|
+
settingsPromise.then((value) => readDailyEvolutionScheduleStatus({
|
|
37957
|
+
homeDir,
|
|
37958
|
+
dailyTime: value.schedule.dailyTime,
|
|
37959
|
+
now: options.now?.()
|
|
37960
|
+
}))
|
|
37559
37961
|
]);
|
|
37560
37962
|
write(formatKnowledgeScheduleStatus({
|
|
37561
37963
|
projectKey: flags.projectKey,
|
|
@@ -37569,7 +37971,8 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37569
37971
|
proposalBacklog,
|
|
37570
37972
|
proposalLastRunAt: proposalDaily?.lastRunAt ?? null,
|
|
37571
37973
|
osStatus,
|
|
37572
|
-
automation: settings,
|
|
37974
|
+
automation: settings.automation,
|
|
37975
|
+
dailySchedule,
|
|
37573
37976
|
latestEvolutionJob
|
|
37574
37977
|
}));
|
|
37575
37978
|
return 0;
|
|
@@ -37577,7 +37980,13 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37577
37980
|
if (subcommand === "install") {
|
|
37578
37981
|
if (argv.length !== 1)
|
|
37579
37982
|
throw new Error(`Unknown schedule install option: ${argv[1]}`);
|
|
37580
|
-
const
|
|
37983
|
+
const settings = await readEvolutionSettings(homeDir);
|
|
37984
|
+
await prepareDailyEvolutionSchedule({
|
|
37985
|
+
homeDir,
|
|
37986
|
+
dailyTime: settings.schedule.dailyTime,
|
|
37987
|
+
now: options.now?.()
|
|
37988
|
+
});
|
|
37989
|
+
const status = await osScheduler.install(settings.schedule.dailyTime);
|
|
37581
37990
|
write(formatOsScheduleMutation("installed", status));
|
|
37582
37991
|
return 0;
|
|
37583
37992
|
}
|
|
@@ -37591,7 +38000,16 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37591
38000
|
if (subcommand === "tick") {
|
|
37592
38001
|
if (argv.length !== 1)
|
|
37593
38002
|
throw new Error(`Unknown schedule tick option: ${argv[1]}`);
|
|
37594
|
-
const settings = await
|
|
38003
|
+
const settings = await readEvolutionSettings(homeDir);
|
|
38004
|
+
const decision = await decideDailyEvolutionTick({
|
|
38005
|
+
homeDir,
|
|
38006
|
+
dailyTime: settings.schedule.dailyTime,
|
|
38007
|
+
now: options.now?.()
|
|
38008
|
+
});
|
|
38009
|
+
if (!decision.due) {
|
|
38010
|
+
write(formatScheduleTickSkipped(decision.reason));
|
|
38011
|
+
return 0;
|
|
38012
|
+
}
|
|
37595
38013
|
const service = options.evolutionJobService ?? new EvolutionJobService({
|
|
37596
38014
|
homeDir,
|
|
37597
38015
|
now: options.now,
|
|
@@ -37600,13 +38018,33 @@ async function runScheduleCommand(argv, options = {}) {
|
|
|
37600
38018
|
});
|
|
37601
38019
|
const started = await service.start({
|
|
37602
38020
|
source: "schedule",
|
|
37603
|
-
knowledge: settings.knowledge,
|
|
37604
|
-
semanticKnowledge: settings.semanticKnowledge,
|
|
37605
|
-
recommendations: settings.recommendations,
|
|
38021
|
+
knowledge: settings.automation.knowledge,
|
|
38022
|
+
semanticKnowledge: settings.automation.semanticKnowledge,
|
|
38023
|
+
recommendations: settings.automation.recommendations,
|
|
37606
38024
|
improvementEvals: false,
|
|
37607
38025
|
forceRecommendations: false
|
|
37608
38026
|
});
|
|
38027
|
+
if (!started.started && started.job.source === "manual-ui") {
|
|
38028
|
+
write("Scheduled evolution remains due because an independent manual evolution batch is running.");
|
|
38029
|
+
return 0;
|
|
38030
|
+
}
|
|
38031
|
+
await recordDailyEvolutionRun({
|
|
38032
|
+
homeDir,
|
|
38033
|
+
dailyTime: settings.schedule.dailyTime,
|
|
38034
|
+
jobId: started.job.id,
|
|
38035
|
+
status: started.job.status,
|
|
38036
|
+
now: options.now?.()
|
|
38037
|
+
});
|
|
37609
38038
|
const job = started.started ? await service.wait(started.job.id) : started.job;
|
|
38039
|
+
if (job.status !== started.job.status) {
|
|
38040
|
+
await recordDailyEvolutionRun({
|
|
38041
|
+
homeDir,
|
|
38042
|
+
dailyTime: settings.schedule.dailyTime,
|
|
38043
|
+
jobId: job.id,
|
|
38044
|
+
status: job.status,
|
|
38045
|
+
now: options.now?.()
|
|
38046
|
+
});
|
|
38047
|
+
}
|
|
37610
38048
|
write(formatScheduleTick(job.status, job.id, started.started));
|
|
37611
38049
|
return job.status === "failed" ? 1 : 0;
|
|
37612
38050
|
}
|
|
@@ -37684,7 +38122,9 @@ function getScheduleHelpText() {
|
|
|
37684
38122
|
"",
|
|
37685
38123
|
"Notes:",
|
|
37686
38124
|
" schedule status is read-only.",
|
|
37687
|
-
" schedule install registers a macOS user LaunchAgent
|
|
38125
|
+
" schedule install registers a macOS user LaunchAgent at evolution.schedule.dailyTime.",
|
|
38126
|
+
" schedule tick runs at most once per local day and catches up once after sleep or restart.",
|
|
38127
|
+
" changing a past daily time starts with the next occurrence; use Evolution Run now for immediate work.",
|
|
37688
38128
|
" schedule tick reads evolution.automation settings; it does not modify repository files.",
|
|
37689
38129
|
" model-backed knowledge and recommendations send bounded, credential-redacted Session Evidence to the configured local Code Agent; that Agent may use its configured model provider.",
|
|
37690
38130
|
" EvoDev does not send telemetry or upload evidence directly.",
|
|
@@ -37765,7 +38205,12 @@ function formatKnowledgeScheduleStatus(input) {
|
|
|
37765
38205
|
`Project: ${input.projectKey ?? "all"}`,
|
|
37766
38206
|
`Run: ${input.runId ?? "all"}`,
|
|
37767
38207
|
`OS schedule: ${input.osStatus.state}`,
|
|
37768
|
-
`OS schedule
|
|
38208
|
+
`OS schedule kind: ${input.osStatus.scheduleKind}`,
|
|
38209
|
+
`Daily trigger: ${input.dailySchedule.dailyTime} (${input.dailySchedule.timeZone})`,
|
|
38210
|
+
`Installed daily trigger: ${input.osStatus.dailyTime ?? "not configured"}`,
|
|
38211
|
+
`Next daily run: ${input.dailySchedule.nextRunAt}`,
|
|
38212
|
+
`Last daily run: ${input.dailySchedule.lastRunAt ?? "never"}`,
|
|
38213
|
+
`Last daily status: ${input.dailySchedule.lastJobStatus ?? "never"}`,
|
|
37769
38214
|
`Automatic knowledge: ${input.automation.knowledge ? "enabled" : "disabled"}`,
|
|
37770
38215
|
`Semantic knowledge model: ${input.automation.semanticKnowledge ? "enabled" : "disabled"}`,
|
|
37771
38216
|
`Automatic recommendations: ${input.automation.recommendations ? "enabled" : "disabled"}`,
|
|
@@ -37794,12 +38239,16 @@ function formatOsScheduleMutation(action, status) {
|
|
|
37794
38239
|
`EvoDev evolution schedule ${action}`,
|
|
37795
38240
|
"",
|
|
37796
38241
|
`State: ${status.state}`,
|
|
37797
|
-
`
|
|
38242
|
+
action === "installed" ? `Schedule: daily at ${status.dailyTime ?? "unknown"} local time` : "Schedule: disabled",
|
|
37798
38243
|
`LaunchAgent: ${status.path}`,
|
|
37799
38244
|
"Repository files are never modified by scheduled evolution."
|
|
37800
38245
|
].join(`
|
|
37801
38246
|
`);
|
|
37802
38247
|
}
|
|
38248
|
+
function formatScheduleTickSkipped(reason) {
|
|
38249
|
+
return ["EvoDev evolution schedule tick", "", "Started: no", `Reason: ${reason}`].join(`
|
|
38250
|
+
`);
|
|
38251
|
+
}
|
|
37803
38252
|
function formatScheduleTick(status, jobId, started) {
|
|
37804
38253
|
return [
|
|
37805
38254
|
"EvoDev evolution schedule tick",
|
|
@@ -37841,12 +38290,12 @@ function resolveHomeDir12(homeDir) {
|
|
|
37841
38290
|
}
|
|
37842
38291
|
return envHome;
|
|
37843
38292
|
}
|
|
37844
|
-
async function
|
|
38293
|
+
async function readEvolutionSettings(homeDir) {
|
|
37845
38294
|
try {
|
|
37846
|
-
return (await createCoreConfigStore(homeDir).readSettings()).evolution
|
|
38295
|
+
return (await createCoreConfigStore(homeDir).readSettings()).evolution;
|
|
37847
38296
|
} catch (error) {
|
|
37848
38297
|
if (error instanceof Error && error.message.includes("ENOENT")) {
|
|
37849
|
-
return createDefaultSettings().evolution
|
|
38298
|
+
return createDefaultSettings().evolution;
|
|
37850
38299
|
}
|
|
37851
38300
|
throw error;
|
|
37852
38301
|
}
|
|
@@ -38649,7 +39098,7 @@ function resolveHomeDir14(homeDir) {
|
|
|
38649
39098
|
}
|
|
38650
39099
|
|
|
38651
39100
|
// packages/cli/src/ui/server.ts
|
|
38652
|
-
import { spawn as
|
|
39101
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
38653
39102
|
import { randomBytes as randomBytes4, timingSafeEqual } from "node:crypto";
|
|
38654
39103
|
import { createServer as createServer3 } from "node:http";
|
|
38655
39104
|
|
|
@@ -38766,8 +39215,8 @@ function safeError4(error) {
|
|
|
38766
39215
|
}
|
|
38767
39216
|
|
|
38768
39217
|
// packages/cli/src/ui/assets.ts
|
|
38769
|
-
import { readFile as
|
|
38770
|
-
import { dirname as
|
|
39218
|
+
import { readFile as readFile38 } from "node:fs/promises";
|
|
39219
|
+
import { dirname as dirname32, join as join40 } from "node:path";
|
|
38771
39220
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
38772
39221
|
var cachedAssets = null;
|
|
38773
39222
|
function loadUiWebAssets() {
|
|
@@ -38775,9 +39224,9 @@ function loadUiWebAssets() {
|
|
|
38775
39224
|
return cachedAssets;
|
|
38776
39225
|
}
|
|
38777
39226
|
async function loadUiWebAssetsUncached() {
|
|
38778
|
-
const moduleDir =
|
|
38779
|
-
const sourceEntry =
|
|
38780
|
-
const sourceStyles =
|
|
39227
|
+
const moduleDir = dirname32(fileURLToPath4(import.meta.url));
|
|
39228
|
+
const sourceEntry = join40(moduleDir, "web", "App.tsx");
|
|
39229
|
+
const sourceStyles = join40(moduleDir, "web", "styles.css");
|
|
38781
39230
|
if (await fileExists2(sourceEntry)) {
|
|
38782
39231
|
if (typeof Bun === "undefined") {
|
|
38783
39232
|
throw new Error("Source UI assets require Bun; run bun run build:cli for Node execution.");
|
|
@@ -38797,21 +39246,21 @@ async function loadUiWebAssetsUncached() {
|
|
|
38797
39246
|
throw new Error("EvoDev UI browser bundle produced no entry point.");
|
|
38798
39247
|
return { script: await entry.text(), styles: await compileUiStyles(sourceStyles) };
|
|
38799
39248
|
}
|
|
38800
|
-
const distAssetsDir =
|
|
39249
|
+
const distAssetsDir = join40(moduleDir, "ui");
|
|
38801
39250
|
return {
|
|
38802
|
-
script: await
|
|
38803
|
-
styles: await
|
|
39251
|
+
script: await readFile38(join40(distAssetsDir, "app.js"), "utf8"),
|
|
39252
|
+
styles: await readFile38(join40(distAssetsDir, "styles.css"), "utf8")
|
|
38804
39253
|
};
|
|
38805
39254
|
}
|
|
38806
39255
|
async function compileUiStyles(inputPath, options = {}) {
|
|
38807
39256
|
const [{ default: postcss }, { default: tailwindcss }, source] = await Promise.all([
|
|
38808
39257
|
import("postcss"),
|
|
38809
39258
|
import("@tailwindcss/postcss"),
|
|
38810
|
-
|
|
39259
|
+
readFile38(inputPath, "utf8")
|
|
38811
39260
|
]);
|
|
38812
39261
|
const result = await postcss([
|
|
38813
39262
|
tailwindcss({
|
|
38814
|
-
base:
|
|
39263
|
+
base: dirname32(inputPath),
|
|
38815
39264
|
optimize: options.optimize ?? false,
|
|
38816
39265
|
transformAssetUrls: false
|
|
38817
39266
|
})
|
|
@@ -38820,7 +39269,7 @@ async function compileUiStyles(inputPath, options = {}) {
|
|
|
38820
39269
|
}
|
|
38821
39270
|
async function fileExists2(path2) {
|
|
38822
39271
|
try {
|
|
38823
|
-
await
|
|
39272
|
+
await readFile38(path2, "utf8");
|
|
38824
39273
|
return true;
|
|
38825
39274
|
} catch {
|
|
38826
39275
|
return false;
|
|
@@ -38828,7 +39277,7 @@ async function fileExists2(path2) {
|
|
|
38828
39277
|
}
|
|
38829
39278
|
|
|
38830
39279
|
// packages/cli/src/ui/knowledge-detail.ts
|
|
38831
|
-
import { lstat as lstat5, readFile as
|
|
39280
|
+
import { lstat as lstat5, readFile as readFile39, realpath as realpath9 } from "node:fs/promises";
|
|
38832
39281
|
import { isAbsolute as isAbsolute14, relative as relative15 } from "node:path";
|
|
38833
39282
|
class UiKnowledgeDetailError extends Error {
|
|
38834
39283
|
code;
|
|
@@ -38853,7 +39302,7 @@ async function readUiKnowledgeConceptDetail(input) {
|
|
|
38853
39302
|
homeDir: input.homeDir,
|
|
38854
39303
|
path: concept.path
|
|
38855
39304
|
});
|
|
38856
|
-
const markdown = await
|
|
39305
|
+
const markdown = await readFile39(path2, "utf8");
|
|
38857
39306
|
if (detectSessionMemorySensitivity(markdown).classification === "credential") {
|
|
38858
39307
|
throw new UiKnowledgeDetailError("unsafe", "Knowledge detail is unavailable because the local file contains credential-like content.");
|
|
38859
39308
|
}
|
|
@@ -39198,14 +39647,14 @@ function isExecFileExitError(error) {
|
|
|
39198
39647
|
}
|
|
39199
39648
|
|
|
39200
39649
|
// packages/cli/src/ui/runtime.ts
|
|
39201
|
-
import { mkdir as
|
|
39202
|
-
import { join as
|
|
39650
|
+
import { mkdir as mkdir28, readFile as readFile40, rm as rm17, writeFile as writeFile28 } from "node:fs/promises";
|
|
39651
|
+
import { join as join41 } from "node:path";
|
|
39203
39652
|
function resolveUiRuntimePaths(homeDir) {
|
|
39204
|
-
const rootDir =
|
|
39653
|
+
const rootDir = join41(resolveEvoDevPaths(homeDir).stateDir, "ui");
|
|
39205
39654
|
return {
|
|
39206
39655
|
rootDir,
|
|
39207
|
-
runtimePath:
|
|
39208
|
-
tokenPath:
|
|
39656
|
+
runtimePath: join41(rootDir, "runtime.json"),
|
|
39657
|
+
tokenPath: join41(rootDir, "token")
|
|
39209
39658
|
};
|
|
39210
39659
|
}
|
|
39211
39660
|
async function readUiRuntimeBundle(homeDir) {
|
|
@@ -39225,16 +39674,16 @@ async function writeUiRuntimeState(input) {
|
|
|
39225
39674
|
const paths4 = resolveUiRuntimePaths(input.homeDir);
|
|
39226
39675
|
let tokenCreated = false;
|
|
39227
39676
|
let runtimeCreated = false;
|
|
39228
|
-
await
|
|
39677
|
+
await mkdir28(paths4.rootDir, { recursive: true });
|
|
39229
39678
|
try {
|
|
39230
|
-
await
|
|
39679
|
+
await writeFile28(paths4.tokenPath, `${token}
|
|
39231
39680
|
`, {
|
|
39232
39681
|
encoding: "utf8",
|
|
39233
39682
|
flag: "wx",
|
|
39234
39683
|
mode: 384
|
|
39235
39684
|
});
|
|
39236
39685
|
tokenCreated = true;
|
|
39237
|
-
await
|
|
39686
|
+
await writeFile28(paths4.runtimePath, `${JSON.stringify(state, null, 2)}
|
|
39238
39687
|
`, {
|
|
39239
39688
|
encoding: "utf8",
|
|
39240
39689
|
flag: "wx",
|
|
@@ -39244,11 +39693,11 @@ async function writeUiRuntimeState(input) {
|
|
|
39244
39693
|
return paths4;
|
|
39245
39694
|
} catch (error) {
|
|
39246
39695
|
if (runtimeCreated)
|
|
39247
|
-
await
|
|
39696
|
+
await rm17(paths4.runtimePath, { force: true }).catch(() => {
|
|
39248
39697
|
return;
|
|
39249
39698
|
});
|
|
39250
39699
|
if (tokenCreated)
|
|
39251
|
-
await
|
|
39700
|
+
await rm17(paths4.tokenPath, { force: true }).catch(() => {
|
|
39252
39701
|
return;
|
|
39253
39702
|
});
|
|
39254
39703
|
throw error;
|
|
@@ -39264,11 +39713,11 @@ async function removeUiRuntimeState(input) {
|
|
|
39264
39713
|
return [];
|
|
39265
39714
|
const removed = [];
|
|
39266
39715
|
if (current.state !== null) {
|
|
39267
|
-
await
|
|
39716
|
+
await rm17(paths4.runtimePath, { force: true });
|
|
39268
39717
|
removed.push(paths4.runtimePath);
|
|
39269
39718
|
}
|
|
39270
39719
|
if (current.token !== null) {
|
|
39271
|
-
await
|
|
39720
|
+
await rm17(paths4.tokenPath, { force: true });
|
|
39272
39721
|
removed.push(paths4.tokenPath);
|
|
39273
39722
|
}
|
|
39274
39723
|
return removed;
|
|
@@ -39311,7 +39760,7 @@ function parseUiRuntimeToken(value) {
|
|
|
39311
39760
|
}
|
|
39312
39761
|
async function readOptionalFile(path2) {
|
|
39313
39762
|
try {
|
|
39314
|
-
return await
|
|
39763
|
+
return await readFile40(path2, "utf8");
|
|
39315
39764
|
} catch (error) {
|
|
39316
39765
|
if (isNodeError5(error) && error.code === "ENOENT")
|
|
39317
39766
|
return null;
|
|
@@ -39322,6 +39771,154 @@ function isNodeError5(error) {
|
|
|
39322
39771
|
return error instanceof Error && "code" in error;
|
|
39323
39772
|
}
|
|
39324
39773
|
|
|
39774
|
+
// packages/cli/src/runtime-discovery.ts
|
|
39775
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
39776
|
+
import { readFile as readFile41, stat as stat28 } from "node:fs/promises";
|
|
39777
|
+
import { join as join42 } from "node:path";
|
|
39778
|
+
var MAX_CODEX_CONFIG_BYTES = 1024 * 1024;
|
|
39779
|
+
var MAX_CODEX_CATALOG_BYTES = 8 * 1024 * 1024;
|
|
39780
|
+
var CODEX_DISCOVERY_TIMEOUT_MS = 1e4;
|
|
39781
|
+
var MAX_CODEX_MODELS = 100;
|
|
39782
|
+
var MAX_REASONING_EFFORTS = 20;
|
|
39783
|
+
async function discoverCodexRuntime(input) {
|
|
39784
|
+
const defaults = await readAllowlistedCodexDefaults(input.codexHome ?? process.env.CODEX_HOME ?? join42(input.homeDir, ".codex"));
|
|
39785
|
+
try {
|
|
39786
|
+
const result = await (input.commandRunner ?? runCommand)(input.command ?? "codex", [
|
|
39787
|
+
"debug",
|
|
39788
|
+
"models"
|
|
39789
|
+
]);
|
|
39790
|
+
if (result.exitCode !== 0)
|
|
39791
|
+
throw new Error("Codex model discovery failed.");
|
|
39792
|
+
const models = parseCodexModelCatalog(result.stdout);
|
|
39793
|
+
if (models.length === 0)
|
|
39794
|
+
throw new Error("Codex returned an empty model catalog.");
|
|
39795
|
+
const selected = models.find((model) => model.id === defaults.model) ?? models[0] ?? null;
|
|
39796
|
+
return {
|
|
39797
|
+
available: true,
|
|
39798
|
+
defaultModel: defaults.model ?? selected?.id ?? null,
|
|
39799
|
+
defaultReasoningEffort: defaults.reasoningEffort ?? selected?.defaultReasoningEffort ?? null,
|
|
39800
|
+
models,
|
|
39801
|
+
warning: null
|
|
39802
|
+
};
|
|
39803
|
+
} catch {
|
|
39804
|
+
return {
|
|
39805
|
+
available: false,
|
|
39806
|
+
defaultModel: defaults.model,
|
|
39807
|
+
defaultReasoningEffort: defaults.reasoningEffort,
|
|
39808
|
+
models: [],
|
|
39809
|
+
warning: "Codex model catalog is unavailable; runtime defaults will still be inherited."
|
|
39810
|
+
};
|
|
39811
|
+
}
|
|
39812
|
+
}
|
|
39813
|
+
function parseCodexModelCatalog(value) {
|
|
39814
|
+
let parsed;
|
|
39815
|
+
try {
|
|
39816
|
+
parsed = JSON.parse(value);
|
|
39817
|
+
} catch {
|
|
39818
|
+
return [];
|
|
39819
|
+
}
|
|
39820
|
+
if (!isRecord22(parsed) || !Array.isArray(parsed.models))
|
|
39821
|
+
return [];
|
|
39822
|
+
const models = [];
|
|
39823
|
+
const seenModels = new Set;
|
|
39824
|
+
for (const entry of parsed.models) {
|
|
39825
|
+
if (models.length >= MAX_CODEX_MODELS)
|
|
39826
|
+
break;
|
|
39827
|
+
if (!isRecord22(entry))
|
|
39828
|
+
continue;
|
|
39829
|
+
const id = printableText(entry.slug, 120);
|
|
39830
|
+
if (id === null || seenModels.has(id))
|
|
39831
|
+
continue;
|
|
39832
|
+
const label = printableText(entry.display_name, 120) ?? id;
|
|
39833
|
+
const defaultReasoningEffort = printableText(entry.default_reasoning_level, 40) ?? "medium";
|
|
39834
|
+
const supportedReasoningEfforts = Array.isArray(entry.supported_reasoning_levels) ? [
|
|
39835
|
+
...new Set(entry.supported_reasoning_levels.map((option) => isRecord22(option) ? printableText(option.effort, 40) : null).filter((effort) => effort !== null))
|
|
39836
|
+
].slice(0, MAX_REASONING_EFFORTS) : [];
|
|
39837
|
+
seenModels.add(id);
|
|
39838
|
+
models.push({ id, label, defaultReasoningEffort, supportedReasoningEfforts });
|
|
39839
|
+
}
|
|
39840
|
+
return models;
|
|
39841
|
+
}
|
|
39842
|
+
async function readAllowlistedCodexDefaults(codexHome) {
|
|
39843
|
+
const path2 = join42(codexHome, "config.toml");
|
|
39844
|
+
try {
|
|
39845
|
+
const metadata = await stat28(path2);
|
|
39846
|
+
if (!metadata.isFile() || metadata.size > MAX_CODEX_CONFIG_BYTES) {
|
|
39847
|
+
return { model: null, reasoningEffort: null };
|
|
39848
|
+
}
|
|
39849
|
+
const config2 = await readFile41(path2, "utf8");
|
|
39850
|
+
if (Buffer.byteLength(config2, "utf8") > MAX_CODEX_CONFIG_BYTES) {
|
|
39851
|
+
return { model: null, reasoningEffort: null };
|
|
39852
|
+
}
|
|
39853
|
+
let inTopLevel = true;
|
|
39854
|
+
let model = null;
|
|
39855
|
+
let reasoningEffort = null;
|
|
39856
|
+
for (const line of config2.split(/\r?\n/u)) {
|
|
39857
|
+
const trimmed = line.trim();
|
|
39858
|
+
if (trimmed.startsWith("["))
|
|
39859
|
+
inTopLevel = false;
|
|
39860
|
+
if (!inTopLevel || trimmed === "" || trimmed.startsWith("#"))
|
|
39861
|
+
continue;
|
|
39862
|
+
const match = /^(model|model_reasoning_effort)\s*=\s*(["'])([^"']+)\2\s*(?:#.*)?$/u.exec(trimmed);
|
|
39863
|
+
if (match === null)
|
|
39864
|
+
continue;
|
|
39865
|
+
const parsed = printableText(match[3], 120);
|
|
39866
|
+
if (match[1] === "model")
|
|
39867
|
+
model = parsed;
|
|
39868
|
+
else
|
|
39869
|
+
reasoningEffort = parsed;
|
|
39870
|
+
}
|
|
39871
|
+
return { model, reasoningEffort };
|
|
39872
|
+
} catch {
|
|
39873
|
+
return { model: null, reasoningEffort: null };
|
|
39874
|
+
}
|
|
39875
|
+
}
|
|
39876
|
+
async function runCommand(command, args) {
|
|
39877
|
+
return await new Promise((resolvePromise, reject) => {
|
|
39878
|
+
const child = spawn10(command, args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
39879
|
+
let stdout = "";
|
|
39880
|
+
let settled = false;
|
|
39881
|
+
const finish = (result) => {
|
|
39882
|
+
if (settled)
|
|
39883
|
+
return;
|
|
39884
|
+
settled = true;
|
|
39885
|
+
clearTimeout(timeout);
|
|
39886
|
+
if (result instanceof Error)
|
|
39887
|
+
reject(result);
|
|
39888
|
+
else
|
|
39889
|
+
resolvePromise(result);
|
|
39890
|
+
};
|
|
39891
|
+
const timeout = setTimeout(() => {
|
|
39892
|
+
child.kill("SIGTERM");
|
|
39893
|
+
finish(new Error("Codex model discovery timed out."));
|
|
39894
|
+
}, CODEX_DISCOVERY_TIMEOUT_MS);
|
|
39895
|
+
child.stdout?.on("data", (chunk) => {
|
|
39896
|
+
stdout += String(chunk);
|
|
39897
|
+
if (Buffer.byteLength(stdout, "utf8") > MAX_CODEX_CATALOG_BYTES) {
|
|
39898
|
+
child.kill("SIGTERM");
|
|
39899
|
+
finish(new Error("Codex model catalog exceeded the local size limit."));
|
|
39900
|
+
}
|
|
39901
|
+
});
|
|
39902
|
+
child.on("error", finish);
|
|
39903
|
+
child.on("close", (code) => finish({ exitCode: code ?? 1, stdout }));
|
|
39904
|
+
});
|
|
39905
|
+
}
|
|
39906
|
+
function printableText(value, maxLength) {
|
|
39907
|
+
if (typeof value !== "string")
|
|
39908
|
+
return null;
|
|
39909
|
+
const normalized = value.trim();
|
|
39910
|
+
if (normalized === "" || normalized.length > maxLength || [...normalized].some((character) => {
|
|
39911
|
+
const code = character.charCodeAt(0);
|
|
39912
|
+
return code < 32 || code === 127;
|
|
39913
|
+
})) {
|
|
39914
|
+
return null;
|
|
39915
|
+
}
|
|
39916
|
+
return normalized;
|
|
39917
|
+
}
|
|
39918
|
+
function isRecord22(value) {
|
|
39919
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39920
|
+
}
|
|
39921
|
+
|
|
39325
39922
|
// packages/cli/src/ui/settings.ts
|
|
39326
39923
|
class UiSettingsError extends Error {
|
|
39327
39924
|
code;
|
|
@@ -39333,11 +39930,17 @@ class UiSettingsError extends Error {
|
|
|
39333
39930
|
}
|
|
39334
39931
|
async function readUiSettingsSnapshot(input) {
|
|
39335
39932
|
const scheduler = input.osScheduler ?? new OsEvolutionScheduler({ homeDir: input.homeDir });
|
|
39336
|
-
const
|
|
39337
|
-
|
|
39338
|
-
scheduler.status()
|
|
39933
|
+
const settings = await readEvoDevSettingsSnapshot(input.homeDir);
|
|
39934
|
+
const [scheduleStatus, dailySchedule, codex] = await Promise.all([
|
|
39935
|
+
scheduler.status(),
|
|
39936
|
+
readDailyEvolutionScheduleStatus({
|
|
39937
|
+
homeDir: input.homeDir,
|
|
39938
|
+
dailyTime: settings.settings.evolution.schedule.dailyTime,
|
|
39939
|
+
now: input.now
|
|
39940
|
+
}),
|
|
39941
|
+
(input.discoverCodex ?? (() => discoverCodexRuntime({ homeDir: input.homeDir })))()
|
|
39339
39942
|
]);
|
|
39340
|
-
return createUiSettingsSnapshot(settings.settings, settings.revision, scheduleStatus);
|
|
39943
|
+
return createUiSettingsSnapshot(settings.settings, settings.revision, scheduleStatus, dailySchedule, codex);
|
|
39341
39944
|
}
|
|
39342
39945
|
async function updateUiSetting(input) {
|
|
39343
39946
|
const scheduler = input.osScheduler ?? new OsEvolutionScheduler({ homeDir: input.homeDir });
|
|
@@ -39348,7 +39951,23 @@ async function updateUiSetting(input) {
|
|
|
39348
39951
|
value: input.value,
|
|
39349
39952
|
expectedRevision: input.expectedRevision
|
|
39350
39953
|
});
|
|
39351
|
-
|
|
39954
|
+
let installedStatus = scheduleStatus;
|
|
39955
|
+
if (input.key === "evolution.schedule.dailyTime") {
|
|
39956
|
+
const dailyTime = settings.settings.evolution.schedule.dailyTime;
|
|
39957
|
+
await prepareDailyEvolutionSchedule({ homeDir: input.homeDir, dailyTime, now: input.now });
|
|
39958
|
+
if (scheduleStatus.state === "installed-loaded" || scheduleStatus.state === "installed-not-loaded") {
|
|
39959
|
+
installedStatus = await scheduler.install(dailyTime);
|
|
39960
|
+
}
|
|
39961
|
+
}
|
|
39962
|
+
const [dailySchedule, codex] = await Promise.all([
|
|
39963
|
+
readDailyEvolutionScheduleStatus({
|
|
39964
|
+
homeDir: input.homeDir,
|
|
39965
|
+
dailyTime: settings.settings.evolution.schedule.dailyTime,
|
|
39966
|
+
now: input.now
|
|
39967
|
+
}),
|
|
39968
|
+
(input.discoverCodex ?? (() => discoverCodexRuntime({ homeDir: input.homeDir })))()
|
|
39969
|
+
]);
|
|
39970
|
+
return createUiSettingsSnapshot(settings.settings, settings.revision, installedStatus, dailySchedule, codex);
|
|
39352
39971
|
}
|
|
39353
39972
|
async function updateUiEvolutionScheduler(input) {
|
|
39354
39973
|
const scheduler = input.osScheduler ?? new OsEvolutionScheduler({ homeDir: input.homeDir });
|
|
@@ -39360,21 +39979,41 @@ async function updateUiEvolutionScheduler(input) {
|
|
|
39360
39979
|
throw new UiSettingsError("unsupported", "OS evolution scheduling is not supported on this platform.");
|
|
39361
39980
|
}
|
|
39362
39981
|
let after;
|
|
39982
|
+
const settings = await readEvoDevSettingsSnapshot(input.homeDir);
|
|
39363
39983
|
if (input.enabled) {
|
|
39364
|
-
|
|
39984
|
+
await prepareDailyEvolutionSchedule({
|
|
39985
|
+
homeDir: input.homeDir,
|
|
39986
|
+
dailyTime: settings.settings.evolution.schedule.dailyTime,
|
|
39987
|
+
now: input.now
|
|
39988
|
+
});
|
|
39989
|
+
after = before.state === "installed-loaded" && before.scheduleKind === "daily" && before.dailyTime === settings.settings.evolution.schedule.dailyTime ? before : await scheduler.install(settings.settings.evolution.schedule.dailyTime);
|
|
39365
39990
|
} else {
|
|
39366
39991
|
after = before.state === "not-installed" ? before : await scheduler.uninstall();
|
|
39367
39992
|
}
|
|
39368
|
-
const
|
|
39369
|
-
|
|
39993
|
+
const [dailySchedule, codex] = await Promise.all([
|
|
39994
|
+
readDailyEvolutionScheduleStatus({
|
|
39995
|
+
homeDir: input.homeDir,
|
|
39996
|
+
dailyTime: settings.settings.evolution.schedule.dailyTime,
|
|
39997
|
+
now: input.now
|
|
39998
|
+
}),
|
|
39999
|
+
(input.discoverCodex ?? (() => discoverCodexRuntime({ homeDir: input.homeDir })))()
|
|
40000
|
+
]);
|
|
40001
|
+
return createUiSettingsSnapshot(settings.settings, settings.revision, after, dailySchedule, codex);
|
|
39370
40002
|
}
|
|
39371
|
-
function createUiSettingsSnapshot(settings, revision, scheduler) {
|
|
40003
|
+
function createUiSettingsSnapshot(settings, revision, scheduler, dailySchedule, codex) {
|
|
39372
40004
|
return {
|
|
39373
40005
|
schemaVersion: 1,
|
|
39374
40006
|
revision,
|
|
39375
40007
|
scheduler: {
|
|
39376
40008
|
state: scheduler.state,
|
|
39377
|
-
|
|
40009
|
+
scheduleKind: scheduler.scheduleKind,
|
|
40010
|
+
configuredDailyTime: settings.evolution.schedule.dailyTime,
|
|
40011
|
+
installedDailyTime: scheduler.dailyTime,
|
|
40012
|
+
legacyIntervalSeconds: scheduler.intervalSeconds,
|
|
40013
|
+
timeZone: dailySchedule.timeZone,
|
|
40014
|
+
nextRunAt: dailySchedule.nextRunAt,
|
|
40015
|
+
lastRunAt: dailySchedule.lastRunAt,
|
|
40016
|
+
lastJobStatus: dailySchedule.lastJobStatus
|
|
39378
40017
|
},
|
|
39379
40018
|
evolution: settings.evolution.automation,
|
|
39380
40019
|
knowledge: {
|
|
@@ -39382,7 +40021,13 @@ function createUiSettingsSnapshot(settings, revision, scheduler) {
|
|
|
39382
40021
|
runtimeInjection: settings.memory.runtimeInjection,
|
|
39383
40022
|
staleReview: settings.memory.staleReview
|
|
39384
40023
|
},
|
|
39385
|
-
teamRuntime: settings.teamRuntime
|
|
40024
|
+
teamRuntime: settings.teamRuntime,
|
|
40025
|
+
runtimeDiscovery: {
|
|
40026
|
+
codex,
|
|
40027
|
+
claude: {
|
|
40028
|
+
reasoningEfforts: ["low", "medium", "high", "xhigh", "max"]
|
|
40029
|
+
}
|
|
40030
|
+
}
|
|
39386
40031
|
};
|
|
39387
40032
|
}
|
|
39388
40033
|
|
|
@@ -39755,7 +40400,8 @@ async function readUiDashboardSnapshot(input) {
|
|
|
39755
40400
|
okfKnowledge,
|
|
39756
40401
|
knowledgeChanges,
|
|
39757
40402
|
staleReviewRemindersEnabled,
|
|
39758
|
-
segmentTriggers
|
|
40403
|
+
segmentTriggers,
|
|
40404
|
+
evolutionJobs
|
|
39759
40405
|
] = await Promise.all([
|
|
39760
40406
|
readSessionBundles(input.homeDir, warnings),
|
|
39761
40407
|
listSessionEvidenceSegments({ homeDir: input.homeDir }).catch(() => {
|
|
@@ -39813,6 +40459,10 @@ async function readUiDashboardSnapshot(input) {
|
|
|
39813
40459
|
listSegmentEvolutionTriggers({ homeDir: input.homeDir }).catch(() => {
|
|
39814
40460
|
warnings.push("Segment trigger index unavailable.");
|
|
39815
40461
|
return [];
|
|
40462
|
+
}),
|
|
40463
|
+
listEvolutionJobs(input.homeDir).catch(() => {
|
|
40464
|
+
warnings.push("Evolution job history unavailable.");
|
|
40465
|
+
return [];
|
|
39816
40466
|
})
|
|
39817
40467
|
]);
|
|
39818
40468
|
const roles = await buildRoleSummaries({
|
|
@@ -39852,6 +40502,11 @@ async function readUiDashboardSnapshot(input) {
|
|
|
39852
40502
|
const pending = [
|
|
39853
40503
|
...evolutionSnapshot.repoProposals.filter((proposal) => (proposal.reviewState === "pending" || proposal.reviewState === "deferred") && hasConcreteRepoProposalChanges(proposal)).map(summarizePendingProposal)
|
|
39854
40504
|
].sort(compareReviewItemsByCreatedAt);
|
|
40505
|
+
const reviewBatches = summarizeReviewBatches({
|
|
40506
|
+
jobs: evolutionJobs,
|
|
40507
|
+
proposals: evolutionSnapshot.repoProposals,
|
|
40508
|
+
knowledgeChanges
|
|
40509
|
+
});
|
|
39855
40510
|
const progressionEvents = buildProgressionEvents({
|
|
39856
40511
|
homeDir: input.homeDir,
|
|
39857
40512
|
repoRoot,
|
|
@@ -39918,7 +40573,8 @@ async function readUiDashboardSnapshot(input) {
|
|
|
39918
40573
|
evolution: {
|
|
39919
40574
|
artifacts,
|
|
39920
40575
|
knowledge: knowledge2,
|
|
39921
|
-
knowledgeChanges: summarizedKnowledgeChanges
|
|
40576
|
+
knowledgeChanges: summarizedKnowledgeChanges,
|
|
40577
|
+
reviewBatches
|
|
39922
40578
|
},
|
|
39923
40579
|
reviews: {
|
|
39924
40580
|
pending,
|
|
@@ -39934,6 +40590,40 @@ async function readUiDashboardSnapshot(input) {
|
|
|
39934
40590
|
warnings
|
|
39935
40591
|
};
|
|
39936
40592
|
}
|
|
40593
|
+
function summarizeReviewBatches(input) {
|
|
40594
|
+
const proposals = new Map(input.proposals.map((proposal) => [proposal.id, proposal]));
|
|
40595
|
+
const changes2 = new Map(input.knowledgeChanges.map((change) => [change.id, change]));
|
|
40596
|
+
return input.jobs.map((job) => {
|
|
40597
|
+
const proposalIds = (job.outputs?.proposalIds ?? []).filter((id) => {
|
|
40598
|
+
const proposal = proposals.get(id);
|
|
40599
|
+
return proposal !== undefined && hasConcreteRepoProposalChanges(proposal);
|
|
40600
|
+
});
|
|
40601
|
+
const knowledgeChangeIds = (job.outputs?.knowledgeChangeIds ?? []).filter((id) => changes2.has(id));
|
|
40602
|
+
if (proposalIds.length === 0 && knowledgeChangeIds.length === 0)
|
|
40603
|
+
return null;
|
|
40604
|
+
const pendingProposalIds = proposalIds.filter((id) => {
|
|
40605
|
+
const state = proposals.get(id)?.reviewState;
|
|
40606
|
+
return state === "pending" || state === "deferred";
|
|
40607
|
+
});
|
|
40608
|
+
const pendingKnowledgeChangeIds = knowledgeChangeIds.filter((id) => {
|
|
40609
|
+
const state = changes2.get(id)?.state;
|
|
40610
|
+
return state === "pending" || state === "deferred" || state === "accepted";
|
|
40611
|
+
});
|
|
40612
|
+
return {
|
|
40613
|
+
id: job.id,
|
|
40614
|
+
source: job.source === "schedule" ? "daily" : "manual",
|
|
40615
|
+
state: pendingProposalIds.length + pendingKnowledgeChangeIds.length > 0 ? "pending" : "reviewed",
|
|
40616
|
+
jobStatus: job.status,
|
|
40617
|
+
createdAt: job.createdAt,
|
|
40618
|
+
finishedAt: job.finishedAt,
|
|
40619
|
+
proposalIds,
|
|
40620
|
+
knowledgeChangeIds,
|
|
40621
|
+
pendingProposalIds,
|
|
40622
|
+
pendingKnowledgeChangeIds,
|
|
40623
|
+
warningCount: job.warnings.length
|
|
40624
|
+
};
|
|
40625
|
+
}).filter((batch) => batch !== null);
|
|
40626
|
+
}
|
|
39937
40627
|
async function buildRoleSummaries(input) {
|
|
39938
40628
|
const roleIds = new Set(["main"]);
|
|
39939
40629
|
const agentsByRole = new Map;
|
|
@@ -40787,6 +41477,7 @@ async function runUiCommand(argv, options = {}) {
|
|
|
40787
41477
|
proposalApplicationRunner: options.proposalApplicationRunner,
|
|
40788
41478
|
knowledgeFileLauncher: options.knowledgeFileLauncher,
|
|
40789
41479
|
osScheduler: options.osScheduler,
|
|
41480
|
+
discoverCodex: options.discoverCodex,
|
|
40790
41481
|
platform: options.platform
|
|
40791
41482
|
});
|
|
40792
41483
|
}
|
|
@@ -41377,7 +42068,8 @@ async function startUiServer(input) {
|
|
|
41377
42068
|
return;
|
|
41378
42069
|
const settings = await readUiSettingsSnapshot({
|
|
41379
42070
|
homeDir: input.homeDir,
|
|
41380
|
-
osScheduler: input.osScheduler
|
|
42071
|
+
osScheduler: input.osScheduler,
|
|
42072
|
+
discoverCodex: input.discoverCodex
|
|
41381
42073
|
});
|
|
41382
42074
|
writeJsonResponse(response, 200, { ok: true, data: settings });
|
|
41383
42075
|
return;
|
|
@@ -41390,6 +42082,7 @@ async function startUiServer(input) {
|
|
|
41390
42082
|
const settings = await updateUiSetting({
|
|
41391
42083
|
homeDir: input.homeDir,
|
|
41392
42084
|
osScheduler: input.osScheduler,
|
|
42085
|
+
discoverCodex: input.discoverCodex,
|
|
41393
42086
|
...mutation
|
|
41394
42087
|
});
|
|
41395
42088
|
writeJsonResponse(response, 200, { ok: true, data: settings });
|
|
@@ -41403,6 +42096,7 @@ async function startUiServer(input) {
|
|
|
41403
42096
|
const settings = await updateUiEvolutionScheduler({
|
|
41404
42097
|
homeDir: input.homeDir,
|
|
41405
42098
|
osScheduler: input.osScheduler,
|
|
42099
|
+
discoverCodex: input.discoverCodex,
|
|
41406
42100
|
...mutation
|
|
41407
42101
|
});
|
|
41408
42102
|
writeJsonResponse(response, 200, { ok: true, data: settings });
|
|
@@ -42134,7 +42828,7 @@ function isAllowedLocalOrigin2(origin) {
|
|
|
42134
42828
|
return false;
|
|
42135
42829
|
}
|
|
42136
42830
|
}
|
|
42137
|
-
function openUiBrowser(url, write, spawnProcess =
|
|
42831
|
+
function openUiBrowser(url, write, spawnProcess = spawn11) {
|
|
42138
42832
|
const platform = process.platform;
|
|
42139
42833
|
const command = platform === "darwin" ? "open" : platform === "win32" ? "cmd" : "xdg-open";
|
|
42140
42834
|
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
@@ -42157,7 +42851,7 @@ function resolveHomeDir15(homeDir) {
|
|
|
42157
42851
|
return envHome;
|
|
42158
42852
|
}
|
|
42159
42853
|
// packages/cli/src/workflow.ts
|
|
42160
|
-
import { join as
|
|
42854
|
+
import { join as join44 } from "node:path";
|
|
42161
42855
|
function getWorkflowHelpText() {
|
|
42162
42856
|
return [
|
|
42163
42857
|
"EvoDev workflow",
|
|
@@ -42220,7 +42914,7 @@ function parseWorkflowDryRunFlags(argv) {
|
|
|
42220
42914
|
return { workflowId };
|
|
42221
42915
|
}
|
|
42222
42916
|
function resolveDefaultWorkflowsDir() {
|
|
42223
|
-
return
|
|
42917
|
+
return join44(resolveDefaultAssetsRootDir(), "workflows");
|
|
42224
42918
|
}
|
|
42225
42919
|
function isHelpArg3(arg) {
|
|
42226
42920
|
return arg === undefined || arg === "help" || arg === "--help" || arg === "-h";
|
|
@@ -42280,13 +42974,14 @@ function getHelpText() {
|
|
|
42280
42974
|
" evo process --once Consume queued evolution triggers",
|
|
42281
42975
|
" evo import --source <source> --path <file> [--project <key>] (--dry-run [--json] | --apply <preview-id> --yes) Preview or apply bounded historical evidence",
|
|
42282
42976
|
" schedule status Show OS schedule, automation settings, and pending work",
|
|
42283
|
-
" schedule install|uninstall Manage the macOS user evolution LaunchAgent",
|
|
42284
|
-
" schedule tick Run
|
|
42977
|
+
" schedule install|uninstall Manage the daily macOS user evolution LaunchAgent",
|
|
42978
|
+
" schedule tick Run the due daily knowledge/recommendation job",
|
|
42285
42979
|
" schedule run knowledge --dry-run|--activate Run the local knowledge processor once",
|
|
42286
42980
|
" schedule run proposals --dry-run|--activate Analyze prior session corrections into repo proposals",
|
|
42287
42981
|
" config set evolution.automation.knowledge true|false Toggle scheduled local knowledge",
|
|
42288
42982
|
" config set evolution.automation.semanticKnowledge true|false Toggle scheduled model-backed semantic knowledge",
|
|
42289
42983
|
" config set evolution.automation.recommendations true|false Toggle scheduled model-backed recommendations",
|
|
42984
|
+
" config set evolution.schedule.dailyTime HH:MM Set the system-local daily trigger time",
|
|
42290
42985
|
" config set memory.reviewKnowledgeUpdates true|false Review runtime-affecting knowledge updates",
|
|
42291
42986
|
" config set memory.runtimeInjection true|false Toggle verified knowledge runtime delivery",
|
|
42292
42987
|
" config set memory.staleReview true|false Toggle review-due reminders",
|
|
@@ -42315,7 +43010,7 @@ function getHelpText() {
|
|
|
42315
43010
|
].join(`
|
|
42316
43011
|
`);
|
|
42317
43012
|
}
|
|
42318
|
-
var CLI_VERSION = "0.0.1-alpha.
|
|
43013
|
+
var CLI_VERSION = "0.0.1-alpha.17";
|
|
42319
43014
|
function getVersionText() {
|
|
42320
43015
|
return `evodev ${CLI_VERSION}`;
|
|
42321
43016
|
}
|
|
@@ -42351,7 +43046,7 @@ async function runWithCliLogging(argv, options) {
|
|
|
42351
43046
|
});
|
|
42352
43047
|
}
|
|
42353
43048
|
try {
|
|
42354
|
-
const exitCode = await
|
|
43049
|
+
const exitCode = await runCommand2(commandArgv, options);
|
|
42355
43050
|
if (!suppressRuntimeSideEffects && exitCode === 0) {
|
|
42356
43051
|
await runLazyEvolutionFallbackSafely(commandArgv, options, debug);
|
|
42357
43052
|
await runPostCommandMaintenanceSafely(commandArgv, options, debug);
|
|
@@ -42408,7 +43103,7 @@ function isReadOnlyCommand(argv) {
|
|
|
42408
43103
|
function isBackgroundCommand(argv) {
|
|
42409
43104
|
return argv[0] === "schedule" && argv[1] === "tick";
|
|
42410
43105
|
}
|
|
42411
|
-
async function
|
|
43106
|
+
async function runCommand2(argv, options) {
|
|
42412
43107
|
const command = argv[0];
|
|
42413
43108
|
if (command === undefined || command === "help" || command === "--help" || command === "-h") {
|
|
42414
43109
|
console.log(getHelpText());
|
|
@@ -43044,8 +43739,10 @@ export {
|
|
|
43044
43739
|
resolveInitProjectCandidates,
|
|
43045
43740
|
resolveEvolutionJobPath,
|
|
43046
43741
|
resolveDefaultCodeAgentSelections,
|
|
43742
|
+
resolveDailyScheduleStatePath,
|
|
43047
43743
|
renderUiDashboardHtml,
|
|
43048
43744
|
renderLaunchAgentPlist,
|
|
43745
|
+
recordDailyEvolutionRun,
|
|
43049
43746
|
readUiSettingsSnapshot,
|
|
43050
43747
|
readUiDashboardSnapshot,
|
|
43051
43748
|
readSessionProposalBacklogStatus,
|
|
@@ -43054,13 +43751,18 @@ export {
|
|
|
43054
43751
|
readEvolutionJob,
|
|
43055
43752
|
readEvoDevSettingsSnapshot,
|
|
43056
43753
|
readDailySessionProposalState,
|
|
43754
|
+
readDailyEvolutionScheduleStatus,
|
|
43755
|
+
readDailyEvolutionScheduleState,
|
|
43057
43756
|
previewTrajectoryImport,
|
|
43058
43757
|
prepareTrajectoryImport,
|
|
43758
|
+
prepareDailyEvolutionSchedule,
|
|
43059
43759
|
parseSyncFlags,
|
|
43060
43760
|
parseSessionProposalAnalysis,
|
|
43061
43761
|
parseSemanticKnowledgeAnalysis,
|
|
43062
43762
|
parseInitFlags,
|
|
43063
43763
|
parseImprovementEvalReplayAnalysis,
|
|
43764
|
+
parseCodexModelCatalog,
|
|
43765
|
+
listEvolutionJobs,
|
|
43064
43766
|
getWorkflowHelpText,
|
|
43065
43767
|
getVersionText,
|
|
43066
43768
|
getUiHelpText,
|
|
@@ -43073,6 +43775,8 @@ export {
|
|
|
43073
43775
|
formatTrajectoryImportApplyResult,
|
|
43074
43776
|
formatSyncOutput,
|
|
43075
43777
|
formatDoctorOutput,
|
|
43778
|
+
discoverCodexRuntime,
|
|
43779
|
+
decideDailyEvolutionTick,
|
|
43076
43780
|
createSessionKnowledgeDistiller,
|
|
43077
43781
|
createInteractivePrompter,
|
|
43078
43782
|
createInitProjectChoices,
|
|
@@ -43098,6 +43802,7 @@ export {
|
|
|
43098
43802
|
EVOLUTION_SCHEDULE_INTERVAL_SECONDS,
|
|
43099
43803
|
DEFAULT_LAZY_EVOLUTION_LIMIT,
|
|
43100
43804
|
DEFAULT_LAZY_EVOLUTION_INTERVAL_MS,
|
|
43805
|
+
DEFAULT_EVOLUTION_SCHEDULE_DAILY_TIME,
|
|
43101
43806
|
DEFAULT_CODE_AGENT_MARKETPLACE_SOURCE,
|
|
43102
43807
|
DEFAULT_CODEX_MARKETPLACE_SOURCE,
|
|
43103
43808
|
DEFAULT_CODEX_MARKETPLACE_FALLBACK_SOURCE,
|